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