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