]> git.vomp.tv Git - vompserver.git/blob - vompclient.c
Server side timeouts
[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 "vompclient.h"
22
23 #include "responsepacket.h"
24
25 #ifndef VOMPSTANDALONE
26 #include <vdr/channels.h>
27 #include <vdr/recording.h>
28 #include "recplayer.h"
29 #include "mvpreceiver.h"
30 #endif
31
32
33
34 pthread_mutex_t threadClientMutex;
35 int VompClient::nr_clients = 0;
36
37
38 VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
39  : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
40 {
41 #ifndef VOMPSTANDALONE
42   lp = NULL;
43   recplayer = NULL;
44   recordingManager = NULL;
45 #endif
46   imageFile = 0;
47   log = Log::getInstance();
48   loggedIn = false;
49   configDir = tconfigDir;
50   log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
51   baseConfig = cfgBase;
52   incClients();
53   
54   rrproc.init();
55 }
56
57 VompClient::~VompClient()
58 {
59   log->log("Client", Log::DEBUG, "Vomp client destructor");
60 #ifndef VOMPSTANDALONE  
61   if (lp)
62   {
63     delete lp;
64     lp = NULL;
65   }
66   else if (recplayer)
67   {
68     writeResumeData();
69
70     delete recplayer;
71     delete recordingManager;
72     recplayer = NULL;
73     recordingManager = NULL;
74   }
75 #endif
76   if (loggedIn) cleanConfig();
77   decClients();
78 }
79
80 void VompClient::incClients()
81 {
82   pthread_mutex_lock(&threadClientMutex);
83   VompClient::nr_clients++;
84   pthread_mutex_unlock(&threadClientMutex);
85 }
86
87 void VompClient::decClients()
88 {
89   pthread_mutex_lock(&threadClientMutex);
90   VompClient::nr_clients--;
91   pthread_mutex_unlock(&threadClientMutex);
92 }
93
94 int VompClient::getNrClients()
95 {
96   int nrClients;
97   pthread_mutex_lock(&threadClientMutex);
98   nrClients = VompClient::nr_clients;
99   pthread_mutex_unlock(&threadClientMutex);
100   return nrClients;
101 }
102
103
104 void VompClient::cleanConfig()
105 {
106   log->log("Client", Log::DEBUG, "Clean config");
107
108 #ifndef VOMPSTANDALONE
109
110   cRecordings Recordings;
111   Recordings.Load();
112
113   int numReturns;
114   int length;
115   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
116   char* position = resumes;
117   for(int k = 0; k < numReturns; k++)
118   {
119     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
120
121     cRecording* recording = Recordings.GetByName(position);
122     if (!recording)
123     {
124       // doesn't exist anymore
125       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
126       config.deleteValue("ResumeData", position);
127     }
128     else
129     {
130       log->log("Client", Log::DEBUG, "This recording still exists");
131     }
132
133     position += strlen(position) + 1;
134   }
135
136   delete[] resumes;
137 #endif
138 }
139
140 void VompClientStartThread(void* arg)
141 {
142   VompClient* m = (VompClient*)arg;
143   m->run2();
144   // Nothing external to this class has a reference to it
145   // This is the end of the thread.. so delete m
146   delete m;
147   pthread_exit(NULL);
148 }
149
150 int VompClient::run()
151 {
152   if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0;
153   log->log("Client", Log::DEBUG, "VompClient run success");
154   return 1;
155 }
156
157 void VompClient::run2()
158 {
159   // Thread stuff
160   sigset_t sigset;
161   sigfillset(&sigset);
162   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
163   pthread_detach(runThread);  // Detach
164
165 //  tcp.disableReadTimeout();
166
167 //  tcp.setSoKeepTime(3);
168   tcp.setNonBlocking();
169
170   ULONG channelID;
171   ULONG requestID;
172   ULONG opcode;
173   ULONG extraDataLength;
174   UCHAR* data;
175   
176   ULONG kaTimeStamp;
177
178   while(1)
179   {
180     log->log("Client", Log::DEBUG, "Waiting");
181     
182     if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG)))
183     {
184       // If this read fails then the client just hasn't sent anything.
185       // If any of the lower reads fail, then break, the connection is probably dead
186
187       // check connection is ok
188 //      if (tcp.isConnected()) continue;
189       
190       log->log("Client", Log::DEBUG, "Disconnection detected");
191       break;
192     }     
193     
194     channelID = ntohl(channelID);
195     if (channelID == 1)
196     {
197       if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
198       requestID = ntohl(requestID);
199
200       if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
201       opcode = ntohl(opcode);
202
203       if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
204       extraDataLength = ntohl(extraDataLength);
205       if (extraDataLength > 200000) // a random sanity limit
206       {
207         log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
208         break;
209       }
210
211       if (extraDataLength)
212       {
213         data = (UCHAR*)malloc(extraDataLength);
214         if (!data)
215         {
216           log->log("Client", Log::ERR, "Extra data buffer malloc error");
217           break;
218         }
219         
220         if (!tcp.readData(data, extraDataLength))
221         {
222           log->log("Client", Log::ERR, "Could not read extradata");
223           free(data);
224           break;
225         }      
226       }
227       else
228       {
229         data = NULL;
230       }
231
232       log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
233
234       if (!loggedIn && (opcode != 1))
235       {
236         log->log("Client", Log::ERR, "Not logged in and opcode != 1");
237         if (data) free(data);
238         break;
239       }
240
241       RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
242       rrproc.recvRequest(req);
243     }
244     else if (channelID == 3)
245     {
246       if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
247       kaTimeStamp = ntohl(kaTimeStamp);
248
249       log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);    
250
251       UCHAR buffer[8];
252       *(ULONG*)&buffer[0] = htonl(3); // KA CHANNEL
253       *(ULONG*)&buffer[4] = htonl(kaTimeStamp);
254       if (!tcp.sendPacket(buffer, 8))
255       {
256         log->log("Client", Log::ERR, "Could not send back KA reply");
257         break;
258       }      
259     }
260     else
261     {
262       log->log("Client", Log::ERR, "Incoming channel number unknown");
263       break;
264     }
265   }
266 }
267
268 ULLONG VompClient::ntohll(ULLONG a)
269 {
270   return htonll(a);
271 }
272
273 ULLONG VompClient::htonll(ULLONG a)
274 {
275   #if BYTE_ORDER == BIG_ENDIAN
276     return a;
277   #else
278     ULLONG b = 0;
279
280     b = ((a << 56) & 0xFF00000000000000ULL)
281       | ((a << 40) & 0x00FF000000000000ULL)
282       | ((a << 24) & 0x0000FF0000000000ULL)
283       | ((a <<  8) & 0x000000FF00000000ULL)
284       | ((a >>  8) & 0x00000000FF000000ULL)
285       | ((a >> 24) & 0x0000000000FF0000ULL)
286       | ((a >> 40) & 0x000000000000FF00ULL)
287       | ((a >> 56) & 0x00000000000000FFULL) ;
288
289     return b;
290   #endif
291 }
292
293 #ifndef VOMPSTANDALONE
294
295 cChannel* VompClient::channelFromNumber(ULONG channelNumber)
296 {
297   cChannel* channel = NULL;
298
299   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
300   {
301     if (!channel->GroupSep())
302     {
303       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
304
305       if (channel->Number() == (int)channelNumber)
306       {
307         int vpid = channel->Vpid();
308 #if VDRVERSNUM < 10300
309         int apid1 = channel->Apid1();
310 #else
311         int apid1 = channel->Apid(0);
312 #endif
313         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
314         return channel;
315       }
316     }
317   }
318
319   if (!channel)
320   {
321     log->log("Client", Log::DEBUG, "Channel not found");
322   }
323
324   return channel;
325 }
326
327 void VompClient::writeResumeData()
328 {
329   config.setValueLong("ResumeData",
330                           (char*)recplayer->getCurrentRecording()->FileName(),
331                           recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );
332 }
333
334 #endif
335