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 <vdr/plugin.h>
33 #include "recplayer.h"
34 #include "mvpreceiver.h"
35 #include "picturereader.h"
40 pthread_mutex_t threadClientMutex;
41 int VompClient::nr_clients = 0;
42 cPlugin *VompClient::scraper = NULL;
43 time_t VompClient::lastScrapQuery = 0;
45 VompClient::VompClient(Config* cfgBase, char* tconfigDir, char* tlogoDir,
46 char *tresourceDir, char * timageDir, char * tcacheDir, int tsocket)
47 : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
49 #ifndef VOMPSTANDALONE
52 pict = new PictureReader(this);
53 if (!scraper) scrapQuery();
55 resourceDir = tresourceDir;
59 log = Log::getInstance();
61 configDir = tconfigDir;
62 log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
65 media=new MediaPlayer();
66 mediaprovider=new ServerMediaFile(cfgBase,media);
68 charcoding=1; //latin1 is default
71 setCharset(charcoding);
76 VompClient::~VompClient()
78 log->log("Client", Log::DEBUG, "Vomp client destructor");
79 #ifndef VOMPSTANDALONE
82 lp->detachMVPReceiver();
94 //if (loggedIn) cleanConfig();
100 delete mediaprovider;
102 if (charconvsys) delete charconvsys;
103 if (charconvutf8) delete charconvutf8;
113 cPlugin *VompClient::scrapQuery()
115 if (scraper) return scraper;
116 if ((time(NULL)-lastScrapQuery) > 5*60) {
117 lastScrapQuery = time(NULL);
118 if (!scraper) scraper = cPluginManager::GetPlugin("scraper2vdr");
123 void VompClient::setCharset(int charset)
126 cCharSetConv *oldcharconvsys=charconvsys;
127 cCharSetConv *oldcharconvutf8=charconvutf8;
128 switch (charcoding) {
130 charconvsys=new cCharSetConv(NULL,"UTF-8");
131 charconvutf8=new cCharSetConv("UTF-8","UTF-8");
135 charconvsys=new cCharSetConv(NULL,"ISO-8859-1");
136 charconvutf8=new cCharSetConv("UTF-8","ISO-8859-1");
139 if (oldcharconvsys) delete oldcharconvsys;
140 if (oldcharconvutf8) delete oldcharconvutf8;
144 void VompClient::incClients()
146 pthread_mutex_lock(&threadClientMutex);
147 VompClient::nr_clients++;
148 pthread_mutex_unlock(&threadClientMutex);
151 void VompClient::decClients()
153 pthread_mutex_lock(&threadClientMutex);
154 VompClient::nr_clients--;
155 pthread_mutex_unlock(&threadClientMutex);
158 int VompClient::getNrClients()
161 pthread_mutex_lock(&threadClientMutex);
162 nrClients = VompClient::nr_clients;
163 pthread_mutex_unlock(&threadClientMutex);
168 void VompClient::cleanConfig()
170 log->log("Client", Log::DEBUG, "Clean config");
172 #ifndef VOMPSTANDALONE
174 cRecordings Recordings;
179 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
180 char* position = resumes;
181 for(int k = 0; k < numReturns; k++)
183 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
185 cRecording* recording = Recordings.GetByName(position);
188 // doesn't exist anymore
189 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
190 config.deleteValue("ResumeData", position);
194 log->log("Client", Log::DEBUG, "This recording still exists");
197 position += strlen(position) + 1;
204 void VompClient::netLog()
206 // Hook, called from rrproc login after client has logged in.
207 // See if this MVP config says to do network logging, if so open a log
208 // The config object will be usable now since it's set up in login
210 char* doNetLogging = config.getValueString("Advanced", "Network logging");
213 if (!strcasecmp(doNetLogging, "on"))
215 char* netLogFileName = config.getValueString("Advanced", "Network logging file");
218 netLogFile = fopen(netLogFileName, "a");
219 if (netLogFile) log->log("Client", Log::DEBUG, "Client network logging started");
221 delete[] netLogFileName;
225 delete[] doNetLogging;
229 void VompClientStartThread(void* arg)
231 VompClient* m = (VompClient*)arg;
233 // Nothing external to this class has a reference to it
234 // This is the end of the thread.. so delete m
239 int VompClient::run()
241 if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0;
242 log->log("Client", Log::DEBUG, "VompClient run success");
246 void VompClient::run2()
251 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
252 pthread_detach(runThread); // Detach
254 // tcp.disableReadTimeout();
256 // tcp.setSoKeepTime(3);
257 tcp.setNonBlocking();
263 ULONG extraDataLength;
271 log->log("Client", Log::DEBUG, "Waiting");
273 if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG)))
275 // If this read fails then the client just hasn't sent anything.
276 // If any of the lower reads fail, then break, the connection is probably dead
278 // check connection is ok
279 // if (tcp.isConnected()) continue;
281 log->log("Client", Log::DEBUG, "Disconnection detected");
285 channelID = ntohl(channelID);
288 if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
289 requestID = ntohl(requestID);
291 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
292 opcode = ntohl(opcode);
294 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
295 extraDataLength = ntohl(extraDataLength);
296 if (extraDataLength > 200000) // a random sanity limit
298 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
304 data = (UCHAR*)malloc(extraDataLength);
307 log->log("Client", Log::ERR, "Extra data buffer malloc error");
311 if (!tcp.readData(data, extraDataLength))
313 log->log("Client", Log::ERR, "Could not read extradata");
323 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
325 if (!loggedIn && (opcode != 1))
327 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
328 if (data) free(data);
332 RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
333 rrproc.recvRequest(req);
335 else if (channelID == 3)
337 if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
338 kaTimeStamp = ntohl(kaTimeStamp);
340 log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);
344 p = (ULONG*)&buffer[0]; *p = htonl(3); // KA CHANNEL
345 p = (ULONG*)&buffer[4]; *p = htonl(kaTimeStamp);
346 if (!tcp.sendPacket(buffer, 8))
348 log->log("Client", Log::ERR, "Could not send back KA reply");
352 else if (channelID == 4)
354 if (!tcp.readData((UCHAR*)&logStringLen, sizeof(ULONG))) break;
355 logStringLen = ntohl(logStringLen);
357 log->log("Client", Log::DEBUG, "Received chan=%lu loglen=%lu", channelID, logStringLen);
359 UCHAR buffer[logStringLen + 1];
360 if (!tcp.readData((UCHAR*)&buffer, logStringLen)) break;
361 buffer[logStringLen] = '\0';
363 // log->log("Client", Log::INFO, "Client said: '%s'", buffer);
366 if (fputs((const char*)buffer, netLogFile) == EOF)
376 log->log("Client", Log::ERR, "Incoming channel number unknown");
382 ULLONG VompClient::ntohll(ULLONG a)
387 ULLONG VompClient::htonll(ULLONG a)
389 #if BYTE_ORDER == BIG_ENDIAN
394 b = ((a << 56) & 0xFF00000000000000ULL)
395 | ((a << 40) & 0x00FF000000000000ULL)
396 | ((a << 24) & 0x0000FF0000000000ULL)
397 | ((a << 8) & 0x000000FF00000000ULL)
398 | ((a >> 8) & 0x00000000FF000000ULL)
399 | ((a >> 24) & 0x0000000000FF0000ULL)
400 | ((a >> 40) & 0x000000000000FF00ULL)
401 | ((a >> 56) & 0x00000000000000FFULL) ;
407 #ifndef VOMPSTANDALONE
409 void VompClient::writeResumeData()
411 /*config.setValueLong("ResumeData",
412 (char*)recplayer->getCurrentRecording()->FileName(),
413 recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );*/
415 /* write to vdr resume file */
416 int resume = recplayer->frameNumberFromPosition(recplayer->getLastPosition());
417 char* ResumeIdC = config.getValueString("General", "ResumeId");
420 ResumeId = atoi(ResumeIdC);
425 cCondWait::SleepMs(100);
427 int OldSetupResumeID = Setup.ResumeID;
428 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
429 #if VDRVERSNUM < 10703
430 cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName()); //get corresponding resume file
432 cResumeFile ResumeFile((char*)recplayer->getCurrentRecording()->FileName(),(char*)recplayer->getCurrentRecording()->IsPesRecording()); //get corresponding resume file
434 Setup.ResumeID = OldSetupResumeID; //and restore it back
435 ResumeIDLock = false;
436 ResumeFile.Save(resume);
437 //isyslog("VOMPDEBUG: Saving resume = %i, ResumeId = %i",resume, ResumeId);