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