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