2 Copyright 2004-2008 Chris Tallon
4 This file is part of VOMP.
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.
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.
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.
21 #include "mediaplayer.h"
23 #include "servermediafile.h"
24 #include "vdrcommand.h"
25 #include "vompclient.h"
27 #include "responsepacket.h"
29 #ifndef VOMPSTANDALONE
30 #include <vdr/channels.h>
31 #include <vdr/recording.h>
32 #include "recplayer.h"
33 #include "mvpreceiver.h"
38 pthread_mutex_t threadClientMutex;
39 int VompClient::nr_clients = 0;
42 VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
43 : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
45 #ifndef VOMPSTANDALONE
48 recordingManager = NULL;
50 log = Log::getInstance();
52 configDir = tconfigDir;
53 log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
56 media=new MediaPlayer();
57 mediaprovider=new ServerMediaFile(cfgBase,media);
63 VompClient::~VompClient()
65 log->log("Client", Log::DEBUG, "Vomp client destructor");
66 #ifndef VOMPSTANDALONE
77 delete recordingManager;
79 recordingManager = NULL;
82 //if (loggedIn) cleanConfig();
95 void VompClient::incClients()
97 pthread_mutex_lock(&threadClientMutex);
98 VompClient::nr_clients++;
99 pthread_mutex_unlock(&threadClientMutex);
102 void VompClient::decClients()
104 pthread_mutex_lock(&threadClientMutex);
105 VompClient::nr_clients--;
106 pthread_mutex_unlock(&threadClientMutex);
109 int VompClient::getNrClients()
112 pthread_mutex_lock(&threadClientMutex);
113 nrClients = VompClient::nr_clients;
114 pthread_mutex_unlock(&threadClientMutex);
119 void VompClient::cleanConfig()
121 log->log("Client", Log::DEBUG, "Clean config");
123 #ifndef VOMPSTANDALONE
125 cRecordings Recordings;
130 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
131 char* position = resumes;
132 for(int k = 0; k < numReturns; k++)
134 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
136 cRecording* recording = Recordings.GetByName(position);
139 // doesn't exist anymore
140 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
141 config.deleteValue("ResumeData", position);
145 log->log("Client", Log::DEBUG, "This recording still exists");
148 position += strlen(position) + 1;
155 void VompClient::netLog()
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
161 char* doNetLogging = config.getValueString("Advanced", "Network logging");
164 if (!strcasecmp(doNetLogging, "on"))
166 char* netLogFileName = config.getValueString("Advanced", "Network logging file");
169 netLogFile = fopen(netLogFileName, "a");
170 if (netLogFile) log->log("Client", Log::DEBUG, "Client network logging started");
172 delete[] netLogFileName;
176 delete[] doNetLogging;
180 void VompClientStartThread(void* arg)
182 VompClient* m = (VompClient*)arg;
184 // Nothing external to this class has a reference to it
185 // This is the end of the thread.. so delete m
190 int VompClient::run()
192 if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0;
193 log->log("Client", Log::DEBUG, "VompClient run success");
197 void VompClient::run2()
202 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
203 pthread_detach(runThread); // Detach
205 // tcp.disableReadTimeout();
207 // tcp.setSoKeepTime(3);
208 tcp.setNonBlocking();
213 ULONG extraDataLength;
221 log->log("Client", Log::DEBUG, "Waiting");
223 if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG)))
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
228 // check connection is ok
229 // if (tcp.isConnected()) continue;
231 log->log("Client", Log::DEBUG, "Disconnection detected");
235 channelID = ntohl(channelID);
238 if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
239 requestID = ntohl(requestID);
241 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
242 opcode = ntohl(opcode);
244 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
245 extraDataLength = ntohl(extraDataLength);
246 if (extraDataLength > 200000) // a random sanity limit
248 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
254 data = (UCHAR*)malloc(extraDataLength);
257 log->log("Client", Log::ERR, "Extra data buffer malloc error");
261 if (!tcp.readData(data, extraDataLength))
263 log->log("Client", Log::ERR, "Could not read extradata");
273 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
275 if (!loggedIn && (opcode != 1))
277 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
278 if (data) free(data);
282 RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
283 rrproc.recvRequest(req);
285 else if (channelID == 3)
287 if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
288 kaTimeStamp = ntohl(kaTimeStamp);
290 log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);
293 *(ULONG*)&buffer[0] = htonl(3); // KA CHANNEL
294 *(ULONG*)&buffer[4] = htonl(kaTimeStamp);
295 if (!tcp.sendPacket(buffer, 8))
297 log->log("Client", Log::ERR, "Could not send back KA reply");
301 else if (channelID == 4)
303 if (!tcp.readData((UCHAR*)&logStringLen, sizeof(ULONG))) break;
304 logStringLen = ntohl(logStringLen);
306 log->log("Client", Log::DEBUG, "Received chan=%lu loglen=%lu", channelID, logStringLen);
308 UCHAR buffer[logStringLen + 1];
309 if (!tcp.readData((UCHAR*)&buffer, logStringLen)) break;
310 buffer[logStringLen] = '\0';
312 // log->log("Client", Log::INFO, "Client said: '%s'", buffer);
315 if (fputs((const char*)buffer, netLogFile) == EOF)
324 log->log("Client", Log::ERR, "Incoming channel number unknown");
330 ULLONG VompClient::ntohll(ULLONG a)
335 ULLONG VompClient::htonll(ULLONG a)
337 #if BYTE_ORDER == BIG_ENDIAN
342 b = ((a << 56) & 0xFF00000000000000ULL)
343 | ((a << 40) & 0x00FF000000000000ULL)
344 | ((a << 24) & 0x0000FF0000000000ULL)
345 | ((a << 8) & 0x000000FF00000000ULL)
346 | ((a >> 8) & 0x00000000FF000000ULL)
347 | ((a >> 24) & 0x0000000000FF0000ULL)
348 | ((a >> 40) & 0x000000000000FF00ULL)
349 | ((a >> 56) & 0x00000000000000FFULL) ;
355 #ifndef VOMPSTANDALONE
357 cChannel* VompClient::channelFromNumber(ULONG channelNumber)
359 cChannel* channel = NULL;
361 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
363 if (!channel->GroupSep())
365 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
367 if (channel->Number() == (int)channelNumber)
369 int vpid = channel->Vpid();
370 #if VDRVERSNUM < 10300
371 int apid1 = channel->Apid1();
373 int apid1 = channel->Apid(0);
375 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
383 log->log("Client", Log::DEBUG, "Channel not found");
389 void VompClient::writeResumeData()
391 /*config.setValueLong("ResumeData",
392 (char*)recplayer->getCurrentRecording()->FileName(),
393 recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );*/
395 /* write to vdr resume file */
396 int resume = recplayer->frameNumberFromPosition(recplayer->getLastPosition());
397 char* ResumeIdC = config.getValueString("General", "ResumeId");
400 ResumeId = atoi(ResumeIdC);
405 cCondWait::SleepMs(100);
407 int OldSetupResumeID = Setup.ResumeID;
408 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
409 #if VDRVERSNUM < 10703
410 cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName()); //get corresponding resume file
412 cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName(),(char*)recplayer->getCurrentRecording()->IsPesRecording()); //get corresponding resume file
414 Setup.ResumeID = OldSetupResumeID; //and restore it back
415 ResumeIDLock = false;
416 ResumeFile.Save(resume);
417 //isyslog("VOMPDEBUG: Saving resume = %i, ResumeId = %i",resume, ResumeId);