]> git.vomp.tv Git - vompserver.git/blob - vompclient.c
Async server protocol
[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))) break;
183     channelID = ntohl(channelID);
184     if (channelID == 1)
185     {
186       if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
187       requestID = ntohl(requestID);
188
189       if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
190       opcode = ntohl(opcode);
191
192       if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
193       extraDataLength = ntohl(extraDataLength);
194       if (extraDataLength > 200000) // a random sanity limit
195       {
196         log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
197         break;
198       }
199
200       if (extraDataLength)
201       {
202         data = (UCHAR*)malloc(extraDataLength);
203         if (!data)
204         {
205           log->log("Client", Log::ERR, "Extra data buffer malloc error");
206           break;
207         }
208         
209         if (!tcp.readData(data, extraDataLength))
210         {
211           log->log("Client", Log::ERR, "Could not read extradata");
212           free(data);
213           break;
214         }      
215       }
216       else
217       {
218         data = NULL;
219       }
220
221       log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
222
223       if (!loggedIn && (opcode != 1))
224       {
225         log->log("Client", Log::ERR, "Not logged in and opcode != 1");
226         if (data) free(data);
227         break;
228       }
229
230       RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
231       rrproc.recvRequest(req);
232     }
233     else if (channelID == 3)
234     {
235       if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
236       kaTimeStamp = ntohl(kaTimeStamp);
237
238       log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);    
239
240       UCHAR buffer[8];
241       *(ULONG*)&buffer[0] = htonl(3); // KA CHANNEL
242       *(ULONG*)&buffer[4] = htonl(kaTimeStamp);
243       if (!tcp.sendPacket(buffer, 8))
244       {
245         log->log("Client", Log::ERR, "Could not send back KA reply");
246         break;
247       }      
248     }
249     else
250     {
251       log->log("Client", Log::ERR, "Incoming channel number unknown");
252       break;
253     }
254   }
255 }
256
257 ULLONG VompClient::ntohll(ULLONG a)
258 {
259   return htonll(a);
260 }
261
262 ULLONG VompClient::htonll(ULLONG a)
263 {
264   #if BYTE_ORDER == BIG_ENDIAN
265     return a;
266   #else
267     ULLONG b = 0;
268
269     b = ((a << 56) & 0xFF00000000000000ULL)
270       | ((a << 40) & 0x00FF000000000000ULL)
271       | ((a << 24) & 0x0000FF0000000000ULL)
272       | ((a <<  8) & 0x000000FF00000000ULL)
273       | ((a >>  8) & 0x00000000FF000000ULL)
274       | ((a >> 24) & 0x0000000000FF0000ULL)
275       | ((a >> 40) & 0x000000000000FF00ULL)
276       | ((a >> 56) & 0x00000000000000FFULL) ;
277
278     return b;
279   #endif
280 }
281
282 #ifndef VOMPSTANDALONE
283
284 cChannel* VompClient::channelFromNumber(ULONG channelNumber)
285 {
286   cChannel* channel = NULL;
287
288   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
289   {
290     if (!channel->GroupSep())
291     {
292       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
293
294       if (channel->Number() == (int)channelNumber)
295       {
296         int vpid = channel->Vpid();
297 #if VDRVERSNUM < 10300
298         int apid1 = channel->Apid1();
299 #else
300         int apid1 = channel->Apid(0);
301 #endif
302         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
303         return channel;
304       }
305     }
306   }
307
308   if (!channel)
309   {
310     log->log("Client", Log::DEBUG, "Channel not found");
311   }
312
313   return channel;
314 }
315
316 void VompClient::writeResumeData()
317 {
318   config.setValueLong("ResumeData",
319                           (char*)recplayer->getCurrentRecording()->FileName(),
320                           recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );
321 }
322
323 #endif
324