]> git.vomp.tv Git - vompserver.git/blob - vompclient.c
Fix all compiler warnings
[vompserver.git] / vompclient.c
1 /*
2     Copyright 2004-2008 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "mediaplayer.h"
22 #include "media.h"
23 #include "servermediafile.h"
24 #include "vdrcommand.h"
25 #include "vompclient.h"
26
27 #include "responsepacket.h"
28
29 #ifndef VOMPSTANDALONE
30 #include <vdr/channels.h>
31 #include <vdr/recording.h>
32 #include "recplayer.h"
33 #include "mvpreceiver.h"
34 #endif
35
36
37
38 pthread_mutex_t threadClientMutex;
39 int VompClient::nr_clients = 0;
40
41
42 VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
43  : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
44 {
45 #ifndef VOMPSTANDALONE
46   lp = NULL;
47   recplayer = NULL;
48   recordingManager = NULL;
49 #endif
50   log = Log::getInstance();
51   loggedIn = false;
52   configDir = tconfigDir;
53   log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
54   baseConfig = cfgBase;
55   incClients();
56   media=new MediaPlayer();
57   mediaprovider=new ServerMediaFile(cfgBase,media);
58   netLogFile = NULL;
59   charcoding=1; //latin1 is default
60   charconvsys=NULL;
61   charconvutf8=NULL;
62   setCharset(charcoding);
63   
64   rrproc.init();
65 }
66
67 VompClient::~VompClient()
68 {
69   log->log("Client", Log::DEBUG, "Vomp client destructor");
70 #ifndef VOMPSTANDALONE  
71   if (lp)
72   {
73     delete lp;
74     lp = NULL;
75   }
76   else if (recplayer)
77   {
78     writeResumeData();
79
80     delete recplayer;
81     delete recordingManager;
82     recplayer = NULL;
83     recordingManager = NULL;
84   }
85 #endif
86   //if (loggedIn) cleanConfig();
87   decClients();
88   
89   delete media;
90   delete mediaprovider;
91
92   if (charconvsys) delete charconvsys;
93   if (charconvutf8) delete charconvutf8;
94
95   
96   if (netLogFile)
97   {
98     fclose(netLogFile);
99     netLogFile = NULL;
100   }
101 }
102
103 void VompClient::setCharset(int charset)
104 {
105    charcoding=charset;
106    cCharSetConv *oldcharconvsys=charconvsys;
107    cCharSetConv *oldcharconvutf8=charconvutf8;
108    switch (charcoding) {
109    case 2: //UTF-8
110    charconvsys=new cCharSetConv(NULL,"UTF-8");
111    charconvutf8=new cCharSetConv("UTF-8","UTF-8");
112    break;
113    case 1:
114    default://latin1
115    charconvsys=new cCharSetConv(NULL,"ISO-8859-1");
116    charconvutf8=new cCharSetConv("UTF-8","ISO-8859-1");
117    break;
118    };
119    if (oldcharconvsys) delete oldcharconvsys;
120    if (oldcharconvutf8) delete oldcharconvutf8;
121
122 }
123
124 void VompClient::incClients()
125 {
126   pthread_mutex_lock(&threadClientMutex);
127   VompClient::nr_clients++;
128   pthread_mutex_unlock(&threadClientMutex);
129 }
130
131 void VompClient::decClients()
132 {
133   pthread_mutex_lock(&threadClientMutex);
134   VompClient::nr_clients--;
135   pthread_mutex_unlock(&threadClientMutex);
136 }
137
138 int VompClient::getNrClients()
139 {
140   int nrClients;
141   pthread_mutex_lock(&threadClientMutex);
142   nrClients = VompClient::nr_clients;
143   pthread_mutex_unlock(&threadClientMutex);
144   return nrClients;
145 }
146
147 /*
148 void VompClient::cleanConfig()
149 {
150   log->log("Client", Log::DEBUG, "Clean config");
151
152 #ifndef VOMPSTANDALONE
153
154   cRecordings Recordings;
155   Recordings.Load();
156
157   int numReturns;
158   int length;
159   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
160   char* position = resumes;
161   for(int k = 0; k < numReturns; k++)
162   {
163     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
164
165     cRecording* recording = Recordings.GetByName(position);
166     if (!recording)
167     {
168       // doesn't exist anymore
169       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
170       config.deleteValue("ResumeData", position);
171     }
172     else
173     {
174       log->log("Client", Log::DEBUG, "This recording still exists");
175     }
176
177     position += strlen(position) + 1;
178   }
179
180   delete[] resumes;
181 #endif
182 } */
183
184 void VompClient::netLog()
185 {
186   // Hook, called from rrproc login after client has logged in.
187   // See if this MVP config says to do network logging, if so open a log
188   // The config object will be usable now since it's set up in login
189   
190   char* doNetLogging = config.getValueString("Advanced", "Network logging");
191   if (doNetLogging)
192   {
193     if (!strcasecmp(doNetLogging, "on"))
194     {
195       char* netLogFileName = config.getValueString("Advanced", "Network logging file");
196       if (netLogFileName)
197       {
198         netLogFile = fopen(netLogFileName, "a");
199         if (netLogFile) log->log("Client", Log::DEBUG, "Client network logging started");
200
201         delete[] netLogFileName;
202       }
203     }
204     
205     delete[] doNetLogging;
206   }
207 }
208
209 void VompClientStartThread(void* arg)
210 {
211   VompClient* m = (VompClient*)arg;
212   m->run2();
213   // Nothing external to this class has a reference to it
214   // This is the end of the thread.. so delete m
215   delete m;
216   pthread_exit(NULL);
217 }
218
219 int VompClient::run()
220 {
221   if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0;
222   log->log("Client", Log::DEBUG, "VompClient run success");
223   return 1;
224 }
225
226 void VompClient::run2()
227 {
228   // Thread stuff
229   sigset_t sigset;
230   sigfillset(&sigset);
231   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
232   pthread_detach(runThread);  // Detach
233
234 //  tcp.disableReadTimeout();
235
236 //  tcp.setSoKeepTime(3);
237   tcp.setNonBlocking();
238
239   ULONG channelID;
240   ULONG requestID;
241   ULONG opcode;
242   ULONG extraDataLength;
243   UCHAR* data;
244   
245   ULONG kaTimeStamp;
246   ULONG logStringLen;
247
248   while(1)
249   {
250     log->log("Client", Log::DEBUG, "Waiting");
251     
252     if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG)))
253     {
254       // If this read fails then the client just hasn't sent anything.
255       // If any of the lower reads fail, then break, the connection is probably dead
256
257       // check connection is ok
258 //      if (tcp.isConnected()) continue;
259       
260       log->log("Client", Log::DEBUG, "Disconnection detected");
261       break;
262     }     
263     
264     channelID = ntohl(channelID);
265     if (channelID == 1)
266     {
267       if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
268       requestID = ntohl(requestID);
269
270       if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
271       opcode = ntohl(opcode);
272
273       if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
274       extraDataLength = ntohl(extraDataLength);
275       if (extraDataLength > 200000) // a random sanity limit
276       {
277         log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
278         break;
279       }
280
281       if (extraDataLength)
282       {
283         data = (UCHAR*)malloc(extraDataLength);
284         if (!data)
285         {
286           log->log("Client", Log::ERR, "Extra data buffer malloc error");
287           break;
288         }
289         
290         if (!tcp.readData(data, extraDataLength))
291         {
292           log->log("Client", Log::ERR, "Could not read extradata");
293           free(data);
294           break;
295         }      
296       }
297       else
298       {
299         data = NULL;
300       }
301
302       log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
303
304       if (!loggedIn && (opcode != 1))
305       {
306         log->log("Client", Log::ERR, "Not logged in and opcode != 1");
307         if (data) free(data);
308         break;
309       }
310
311       RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
312       rrproc.recvRequest(req);
313     }
314     else if (channelID == 3)
315     {
316       if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
317       kaTimeStamp = ntohl(kaTimeStamp);
318
319       log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);    
320
321       ULONG* p;
322       UCHAR buffer[8];
323       p = (ULONG*)&buffer[0]; *p = htonl(3); // KA CHANNEL
324       p = (ULONG*)&buffer[4]; *p = htonl(kaTimeStamp);
325       if (!tcp.sendPacket(buffer, 8))
326       {
327         log->log("Client", Log::ERR, "Could not send back KA reply");
328         break;
329       }      
330     }
331     else if (channelID == 4)
332     {
333       if (!tcp.readData((UCHAR*)&logStringLen, sizeof(ULONG))) break;
334       logStringLen = ntohl(logStringLen);
335
336       log->log("Client", Log::DEBUG, "Received chan=%lu loglen=%lu", channelID, logStringLen);    
337
338       UCHAR buffer[logStringLen + 1];
339       if (!tcp.readData((UCHAR*)&buffer, logStringLen)) break;
340       buffer[logStringLen] = '\0';
341
342 //      log->log("Client", Log::INFO, "Client said: '%s'", buffer);
343       if (netLogFile)
344       {
345         if (fputs((const char*)buffer, netLogFile) == EOF)
346         {
347           fclose(netLogFile);
348           netLogFile = NULL;
349         }
350         fflush(NULL);
351       }
352     }    
353     else
354     {
355       log->log("Client", Log::ERR, "Incoming channel number unknown");
356       break;
357     }
358   }
359 }
360
361 ULLONG VompClient::ntohll(ULLONG a)
362 {
363   return htonll(a);
364 }
365
366 ULLONG VompClient::htonll(ULLONG a)
367 {
368   #if BYTE_ORDER == BIG_ENDIAN
369     return a;
370   #else
371     ULLONG b = 0;
372
373     b = ((a << 56) & 0xFF00000000000000ULL)
374       | ((a << 40) & 0x00FF000000000000ULL)
375       | ((a << 24) & 0x0000FF0000000000ULL)
376       | ((a <<  8) & 0x000000FF00000000ULL)
377       | ((a >>  8) & 0x00000000FF000000ULL)
378       | ((a >> 24) & 0x0000000000FF0000ULL)
379       | ((a >> 40) & 0x000000000000FF00ULL)
380       | ((a >> 56) & 0x00000000000000FFULL) ;
381
382     return b;
383   #endif
384 }
385
386 #ifndef VOMPSTANDALONE
387
388 cChannel* VompClient::channelFromNumber(ULONG channelNumber)
389 {
390   cChannel* channel = NULL;
391
392   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
393   {
394     if (!channel->GroupSep())
395     {
396       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
397
398       if (channel->Number() == (int)channelNumber)
399       {
400         int vpid = channel->Vpid();
401 #if VDRVERSNUM < 10300
402         int apid1 = channel->Apid1();
403 #else
404         int apid1 = channel->Apid(0);
405 #endif
406         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
407         return channel;
408       }
409     }
410   }
411
412   if (!channel)
413   {
414     log->log("Client", Log::DEBUG, "Channel not found");
415   }
416
417   return channel;
418 }
419
420 void VompClient::writeResumeData()
421 {
422   /*config.setValueLong("ResumeData",
423                           (char*)recplayer->getCurrentRecording()->FileName(),
424                           recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );*/
425
426   /* write to vdr resume file */
427   int resume = recplayer->frameNumberFromPosition(recplayer->getLastPosition());
428   char* ResumeIdC = config.getValueString("General", "ResumeId");
429   int ResumeId;
430   if (ResumeIdC)
431     ResumeId = atoi(ResumeIdC);
432   else
433     ResumeId = 0;
434
435   while (ResumeIDLock)
436           cCondWait::SleepMs(100);
437   ResumeIDLock = true;
438   int OldSetupResumeID = Setup.ResumeID;
439   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
440 #if VDRVERSNUM < 10703
441   cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName());  //get corresponding resume file
442 #else
443   cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName(),(char*)recplayer->getCurrentRecording()->IsPesRecording());        //get corresponding resume file
444 #endif
445   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
446   ResumeIDLock = false;
447   ResumeFile.Save(resume);
448   //isyslog("VOMPDEBUG: Saving resume = %i, ResumeId = %i",resume, ResumeId);
449 }
450
451 #endif
452