From 3f68028f4e3be037344d30476de8c8d12d56fb61 Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Sat, 31 May 2008 17:33:21 +0000 Subject: [PATCH] Async server protocol --- Makefile | 4 +- mvpclient.c | 2162 -------------------------------------------- mvpclient.h | 128 --- mvpreceiver.c | 2 + mvpserver.c | 2 +- mvpserver.h | 2 +- thread.c | 16 +- thread.h | 4 +- vompclient.c | 324 +++++++ vompclient.h | 106 +++ vompclientrrproc.c | 1700 ++++++++++++++++++++++++++++++++++ vompclientrrproc.h | 92 ++ vompserver.c | 4 +- 13 files changed, 2246 insertions(+), 2300 deletions(-) delete mode 100644 mvpclient.c delete mode 100644 mvpclient.h create mode 100644 vompclient.c create mode 100644 vompclient.h create mode 100644 vompclientrrproc.c create mode 100644 vompclientrrproc.h diff --git a/Makefile b/Makefile index fda7d9e..9b05004 100644 --- a/Makefile +++ b/Makefile @@ -46,8 +46,8 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o dsock.o mvpserver.o udpreplier.o bootpd.o tftpd.o i18n.o mvpclient.o tcp.o \ - ringbuffer.o mvprelay.o \ +OBJS = $(PLUGIN).o dsock.o mvpserver.o udpreplier.o bootpd.o tftpd.o i18n.o vompclient.o tcp.o \ + ringbuffer.o mvprelay.o vompclientrrproc.o \ recplayer.o config.o log.o thread.o mvpreceiver.o tftpclient.o \ media.o responsepacket.o diff --git a/mvpclient.c b/mvpclient.c deleted file mode 100644 index ec171b7..0000000 --- a/mvpclient.c +++ /dev/null @@ -1,2162 +0,0 @@ -/* - Copyright 2004-2008 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "mvpclient.h" - -#include "responsepacket.h" - -// This is here else it causes compile errors with something in libdvbmpeg -//#include - -pthread_mutex_t threadClientMutex; -int MVPClient::nr_clients = 0; - - -MVPClient::MVPClient(Config* cfgBase, char* tconfigDir, int tsocket) - : tcp(tsocket), i18n(tconfigDir) -{ -#ifndef VOMPSTANDALONE - lp = NULL; - recplayer = NULL; - recordingManager = NULL; -#endif - imageFile = 0; - log = Log::getInstance(); - loggedIn = false; - configDir = tconfigDir; - log->log("Client", Log::DEBUG, "Config dir: %s", configDir); - baseConfig = cfgBase; - incClients(); -} - -MVPClient::~MVPClient() -{ - log->log("Client", Log::DEBUG, "MVP client destructor"); -#ifndef VOMPSTANDALONE - if (lp) - { - delete lp; - lp = NULL; - } - else if (recplayer) - { - writeResumeData(); - - delete recplayer; - delete recordingManager; - recplayer = NULL; - recordingManager = NULL; - } -#endif - if (loggedIn) cleanConfig(); - decClients(); -} - -void MVPClient::incClients() -{ - pthread_mutex_lock(&threadClientMutex); - MVPClient::nr_clients++; - pthread_mutex_unlock(&threadClientMutex); -} - -void MVPClient::decClients() -{ - pthread_mutex_lock(&threadClientMutex); - MVPClient::nr_clients--; - pthread_mutex_unlock(&threadClientMutex); -} - -int MVPClient::getNrClients() -{ - int nrClients; - pthread_mutex_lock(&threadClientMutex); - nrClients = MVPClient::nr_clients; - pthread_mutex_unlock(&threadClientMutex); - return nrClients; -} - -ULLONG MVPClient::ntohll(ULLONG a) -{ - return htonll(a); -} - -ULLONG MVPClient::htonll(ULLONG a) -{ - #if BYTE_ORDER == BIG_ENDIAN - return a; - #else - ULLONG b = 0; - - b = ((a << 56) & 0xFF00000000000000ULL) - | ((a << 40) & 0x00FF000000000000ULL) - | ((a << 24) & 0x0000FF0000000000ULL) - | ((a << 8) & 0x000000FF00000000ULL) - | ((a >> 8) & 0x00000000FF000000ULL) - | ((a >> 24) & 0x0000000000FF0000ULL) - | ((a >> 40) & 0x000000000000FF00ULL) - | ((a >> 56) & 0x00000000000000FFULL) ; - - return b; - #endif -} - -#ifndef VOMPSTANDALONE -cChannel* MVPClient::channelFromNumber(ULONG channelNumber) -{ - cChannel* channel = NULL; - - for (channel = Channels.First(); channel; channel = Channels.Next(channel)) - { - if (!channel->GroupSep()) - { - log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name()); - - if (channel->Number() == (int)channelNumber) - { - int vpid = channel->Vpid(); -#if VDRVERSNUM < 10300 - int apid1 = channel->Apid1(); -#else - int apid1 = channel->Apid(0); -#endif - log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1); - return channel; - } - } - } - - if (!channel) - { - log->log("Client", Log::DEBUG, "Channel not found"); - } - - return channel; -} - -void MVPClient::writeResumeData() -{ - config.setValueLong("ResumeData", - (char*)recplayer->getCurrentRecording()->FileName(), - recplayer->frameNumberFromPosition(recplayer->getLastPosition()) ); -} -#endif - -void MVPClient::cleanConfig() -{ - log->log("Client", Log::DEBUG, "Clean config"); - -#ifndef VOMPSTANDALONE - - cRecordings Recordings; - Recordings.Load(); - - int numReturns; - int length; - char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length); - char* position = resumes; - for(int k = 0; k < numReturns; k++) - { - log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position); - - cRecording* recording = Recordings.GetByName(position); - if (!recording) - { - // doesn't exist anymore - log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore"); - config.deleteValue("ResumeData", position); - } - else - { - log->log("Client", Log::DEBUG, "This recording still exists"); - } - - position += strlen(position) + 1; - } - - delete[] resumes; -#endif -} - -void MVPClientStartThread(void* arg) -{ - MVPClient* m = (MVPClient*)arg; - m->run2(); - // Nothing external to this class has a reference to it - // This is the end of the thread.. so delete m - delete m; - pthread_exit(NULL); -} - -int MVPClient::run() -{ - if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0; - log->log("Client", Log::DEBUG, "MVPClient run success"); - return 1; -} - -void MVPClient::run2() -{ - // Thread stuff - sigset_t sigset; - sigfillset(&sigset); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); - pthread_detach(runThread); // Detach - - tcp.disableReadTimeout(); - - tcp.setSoKeepTime(3); - tcp.setNonBlocking(); - - ULONG channelID; - ULONG requestID; - ULONG opcode; - ULONG extraDataLength; - UCHAR* data; - int result; - - while(1) - { - log->log("Client", Log::DEBUG, "Waiting"); - result = 0; - - if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break; - channelID = ntohl(channelID); - if (channelID != 1) - { - log->log("Client", Log::ERR, "Incoming channel number not 1!"); - break; - } - - if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break; - requestID = ntohl(requestID); - - if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break; - opcode = ntohl(opcode); - - if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break; - extraDataLength = ntohl(extraDataLength); - if (extraDataLength > 200000) // a random sanity limit - { - log->log("Client", Log::ERR, "ExtraDataLength > 200000!"); - break; - } - - if (extraDataLength) - { - data = (UCHAR*)malloc(extraDataLength); - if (!data) - { - log->log("Client", Log::ERR, "Extra data buffer malloc error"); - break; - } - - if (!tcp.readData(data, extraDataLength)) - { - log->log("Client", Log::ERR, "Could not read extradata"); - free(data); - break; - } - } - else - { - data = NULL; - } - - log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength); - - if (!loggedIn && (opcode != 1)) - { - log->log("Client", Log::ERR, "Not logged in and opcode != 1"); - if (data) free(data); - break; - } - - ResponsePacket* rp = new ResponsePacket(); - if (!rp->init(requestID)) - { - log->log("Client", Log::ERR, "response packet init fail"); - delete rp; - break; - } - - switch(opcode) - { - case 1: - result = processLogin(data, extraDataLength, rp); - break; -#ifndef VOMPSTANDALONE - case 2: - result = processGetRecordingsList(data, extraDataLength, rp); - break; - case 3: - result = processDeleteRecording(data, extraDataLength, rp); - break; - case 5: - result = processGetChannelsList(data, extraDataLength, rp); - break; - case 6: - result = processStartStreamingChannel(data, extraDataLength, requestID, rp); - break; - case 7: - result = processGetBlock(data, extraDataLength, rp); - break; - case 8: - result = processStopStreaming(data, extraDataLength, rp); - break; - case 9: - result = processStartStreamingRecording(data, extraDataLength, rp); - break; -#endif - case 10: - result = processGetChannelSchedule(data, extraDataLength, rp); - break; - case 11: - result = processConfigSave(data, extraDataLength, rp); - break; - case 12: - result = processConfigLoad(data, extraDataLength, rp); - break; -#ifndef VOMPSTANDALONE - case 13: - result = processReScanRecording(data, extraDataLength, rp); // FIXME obselete - break; - case 14: - result = processGetTimers(data, extraDataLength, rp); - break; - case 15: - result = processSetTimer(data, extraDataLength, rp); - break; - case 16: - result = processPositionFromFrameNumber(data, extraDataLength, rp); - break; - case 17: - result = processFrameNumberFromPosition(data, extraDataLength, rp); - break; - case 18: - result = processMoveRecording(data, extraDataLength, rp); - break; - case 19: - result = processGetIFrame(data, extraDataLength, rp); - break; - case 20: - result = processGetRecInfo(data, extraDataLength, rp); - break; - case 21: - result = processGetMarks(data, extraDataLength, rp); - break; - case 22: - result = processGetChannelPids(data, extraDataLength, rp); - break; - case 23: - result = processDeleteTimer(data, extraDataLength, rp); - break; -#endif - case 30: - result = processGetMediaList(data, extraDataLength, rp); - break; - case 31: - result = processGetPicture(data, extraDataLength, rp); - break; - case 32: - result = processGetImageBlock(data, extraDataLength, rp); - break; - case 33: - result = processGetLanguageList(data, extraDataLength, rp); - break; - case 34: - result = processGetLanguageContent(data, extraDataLength, rp); - break; - } - - delete rp; - if (data) free(data); - if (!result) break; - } -} - -int MVPClient::processLogin(UCHAR* buffer, int length, ResponsePacket* rp) -{ - if (length != 6) return 0; - - // Open the config - - char configFileName[PATH_MAX]; - snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); - config.init(configFileName); - - // Send the login reply - - time_t timeNow = time(NULL); - struct tm* timeStruct = localtime(&timeNow); - int timeOffset = timeStruct->tm_gmtoff; - - rp->addULONG(timeNow); - rp->addLONG(timeOffset); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - log->log("Client", Log::DEBUG, "written login reply len %lu", rp->getLen()); - - loggedIn = true; - return 1; -} - -#ifndef VOMPSTANDALONE -int MVPClient::processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp) -{ - int FreeMB; - int Percent = VideoDiskSpace(&FreeMB); - int Total = (FreeMB / (100 - Percent)) * 100; - - rp->addULONG(Total); - rp->addULONG(FreeMB); - rp->addULONG(Percent); - - cRecordings Recordings; - Recordings.Load(); - - for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) - { - rp->addULONG(recording->start); - rp->addString(recording->Name()); - rp->addString(recording->FileName()); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written recordings list"); - - return 1; -} - -int MVPClient::processDeleteRecording(UCHAR* data, int length, ResponsePacket* rp) -{ - // data is a pointer to the fileName string - - cRecordings Recordings; - Recordings.Load(); // probably have to do this - - cRecording* recording = Recordings.GetByName((char*)data); - - log->log("Client", Log::DEBUG, "recording pointer %p", recording); - - if (recording) - { - log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name()); - - cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); - if (!rc) - { - if (recording->Delete()) - { - // Copy svdrp's way of doing this, see if it works -#if VDRVERSNUM > 10300 - ::Recordings.DelByName(recording->FileName()); -#endif - rp->addULONG(1); - } - else - { - rp->addULONG(2); - } - } - else - { - rp->addULONG(3); - } - } - else - { - rp->addULONG(4); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - return 1; -} - -int MVPClient::processMoveRecording(UCHAR* data, int length, ResponsePacket* rp) -{ - log->log("Client", Log::DEBUG, "Process move recording"); - char* fileName = (char*)data; - char* newPath = NULL; - - for (int k = 0; k < length; k++) - { - if (data[k] == '\0') - { - newPath = (char*)&data[k+1]; - break; - } - } - if (!newPath) return 0; - - cRecordings Recordings; - Recordings.Load(); // probably have to do this - - cRecording* recording = Recordings.GetByName((char*)fileName); - - log->log("Client", Log::DEBUG, "recording pointer %p", recording); - - if (recording) - { - cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); - if (!rc) - { - log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name()); - log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName()); - log->log("Client", Log::DEBUG, "to: %s", newPath); - - const char* t = recording->FileName(); - - char* dateDirName = NULL; int k; - char* titleDirName = NULL; int j; - - // Find the datedirname - for(k = strlen(t) - 1; k >= 0; k--) - { - if (t[k] == '/') - { - log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1); - dateDirName = new char[strlen(&t[k+1]) + 1]; - strcpy(dateDirName, &t[k+1]); - break; - } - } - - // Find the titledirname - - for(j = k-1; j >= 0; j--) - { - if (t[j] == '/') - { - log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1); - titleDirName = new char[(k - j - 1) + 1]; - memcpy(titleDirName, &t[j+1], k - j - 1); - titleDirName[k - j - 1] = '\0'; - break; - } - } - - log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName); - log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName); - log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory); - - char* newPathConv = new char[strlen(newPath)+1]; - strcpy(newPathConv, newPath); - ExchangeChars(newPathConv, true); - log->log("Client", Log::DEBUG, "EC: %s", newPathConv); - - char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1]; - log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1); - sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName); - delete[] newPathConv; - - log->log("Client", Log::DEBUG, "%s", newContainer); - - struct stat dstat; - int statret = stat(newContainer, &dstat); - if ((statret == -1) && (errno == ENOENT)) // Dir does not exist - { - log->log("Client", Log::DEBUG, "new dir does not exist"); - int mkdirret = mkdir(newContainer, 0755); - if (mkdirret != 0) - { - delete[] dateDirName; - delete[] titleDirName; - delete[] newContainer; - - rp->addULONG(5); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - } - else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir - { - delete[] dateDirName; - delete[] titleDirName; - delete[] newContainer; - - rp->addULONG(5); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - // Ok, the directory container has been made, or it pre-existed. - - char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1]; - sprintf(newDir, "%s/%s", newContainer, dateDirName); - - log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir); - int renameret = rename(t, newDir); - if (renameret == 0) - { - // Success. Test for remove old dir containter - char* oldTitleDir = new char[k+1]; - memcpy(oldTitleDir, t, k); - oldTitleDir[k] = '\0'; - log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir); - rmdir(oldTitleDir); // can't do anything about a fail result at this point. - delete[] oldTitleDir; - } - - if (renameret == 0) - { -#if VDRVERSNUM > 10311 - // Tell VDR - ::Recordings.Update(); -#endif - // Success. Send a different packet from just a ulong - rp->addULONG(1); // success - rp->addString(newDir); - } - else - { - rp->addULONG(5); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - delete[] dateDirName; - delete[] titleDirName; - delete[] newContainer; - delete[] newDir; - } - else - { - rp->addULONG(3); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - } - } - else - { - rp->addULONG(4); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - } - - return 1; -} - -int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp) -{ - ULONG type; - - char* chanConfig = config.getValueString("General", "Channels"); - int allChans = 1; - if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only"); - - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) - { -#if VDRVERSNUM < 10300 - if (!channel->GroupSep() && (!channel->Ca() || allChans)) -#else - if (!channel->GroupSep() && (!channel->Ca(0) || allChans)) -#endif - { - log->log("Client", Log::DEBUG, "name: '%s'", channel->Name()); - - if (channel->Vpid()) type = 1; -#if VDRVERSNUM < 10300 - else type = 2; -#else - else if (channel->Apid(0)) type = 2; - else continue; -#endif - - rp->addULONG(channel->Number()); - rp->addULONG(type); - rp->addString(channel->Name()); - } - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written channels list"); - - return 1; -} - -int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp) -{ - ULONG channelNumber = ntohl(*(ULONG*)data); - - cChannel* channel = channelFromNumber(channelNumber); - if (!channel) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - ULONG numApids = 0; - ULONG numDpids = 0; - ULONG numSpids = 0; - - -#if VDRVERSNUM < 10300 - - log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1()); - log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2()); - - if (channel->Apid2()) - numApids = 2; - else if (channel->Apid1()) - numApids = 1; - else - numApids = 0; - -#else - - for (const int *Apid = channel->Apids(); *Apid; Apid++) - { - numApids++; - } - for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++) - { - numDpids++; - } - for (const int *Spid = channel->Spids(); *Spid; Spid++) - { - numSpids++; - } -#endif - - - // Format of response - // vpid - // number of apids - // { - // apid - // lang string - // } - // number of dpids - // { - // dpid - // lang string - // } - // number of spids - // { - // spid - // lang string - // } - // tpid - - rp->addULONG(channel->Vpid()); - rp->addULONG(numApids); - -#if VDRVERSNUM < 10300 - if (numApids >= 1) - { - rp->addULONG(channel->Apid1()); - rp->addString(""); - } - if (numApids == 2) - { - rp->addULONG(channel->Apid2()); - rp->addString(""); - } - rp->addULONG(0); - rp->addULONG(0); -#else - for (ULONG i = 0; i < numApids; i++) - { - rp->addULONG(channel->Apid(i)); - rp->addString(channel->Alang(i)); - } - rp->addULONG(numDpids); - for (ULONG i = 0; i < numDpids; i++) - { - rp->addULONG(channel->Dpid(i)); - rp->addString(channel->Dlang(i)); - } - rp->addULONG(numSpids); - for (ULONG i = 0; i < numSpids; i++) - { - rp->addULONG(channel->Spid(i)); - rp->addString(channel->Slang(i)); - } -#endif - rp->addULONG(channel->Tpid()); - - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written channels pids"); - - return 1; -} - -int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp) -{ - if (lp) - { - log->log("Client", Log::ERR, "Client called start streaming twice"); - return 0; - } - - log->log("Client", Log::DEBUG, "length = %i", length); - ULONG channelNumber = ntohl(*(ULONG*)data); - - cChannel* channel = channelFromNumber(channelNumber); - if (!channel) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - // get the priority we should use - int fail = 1; - int priority = config.getValueLong("General", "Live priority", &fail); - if (!fail) - { - log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority); - } - else - { - log->log("Client", Log::DEBUG, "Config: Live TV priority config fail"); - priority = 0; - } - - // a bit of sanity.. - if (priority < 0) priority = 0; - if (priority > 99) priority = 99; - - log->log("Client", Log::DEBUG, "Using live TV priority %i", priority); - lp = MVPReceiver::create(channel, priority); - - if (!lp) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - if (!lp->init(&tcp, streamID)) - { - delete lp; - lp = NULL; - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - rp->addULONG(1); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; -} - -int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp) -{ - log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED"); - if (lp) - { - delete lp; - lp = NULL; - } - else if (recplayer) - { - writeResumeData(); - - delete recplayer; - delete recordingManager; - recplayer = NULL; - recordingManager = NULL; - } - - rp->addULONG(1); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; -} - -int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp) -{ - if (!lp && !recplayer) - { - log->log("Client", Log::DEBUG, "Get block called when no streaming happening!"); - return 0; - } - - ULLONG position = ntohll(*(ULLONG*)data); - data += sizeof(ULLONG); - ULONG amount = ntohl(*(ULONG*)data); - - log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount); - - UCHAR sendBuffer[amount]; - ULONG amountReceived = 0; // compiler moan. - if (lp) - { - log->log("Client", Log::DEBUG, "getting from live"); - amountReceived = lp->getBlock(&sendBuffer[0], amount); - - if (!amountReceived) - { - // vdr has possibly disconnected the receiver - log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver"); - delete lp; - lp = NULL; - } - } - else if (recplayer) - { - log->log("Client", Log::DEBUG, "getting from recording"); - amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount); - } - - if (!amountReceived) - { - rp->addULONG(0); - log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0"); - } - else - { - rp->copyin(sendBuffer, amountReceived); - log->log("Client", Log::DEBUG, "written %lu", amountReceived); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen()); - return 1; -} - -int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp) -{ - // data is a pointer to the fileName string - - recordingManager = new cRecordings; - recordingManager->Load(); - - cRecording* recording = recordingManager->GetByName((char*)data); - - log->log("Client", Log::DEBUG, "recording pointer %p", recording); - - if (recording) - { - recplayer = new RecPlayer(recording); - - rp->addULLONG(recplayer->getLengthBytes()); - rp->addULONG(recplayer->getLengthFrames()); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "written totalLength"); - } - else - { - delete recordingManager; - recordingManager = NULL; - } - return 1; -} - -int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp) -{ - ULLONG retval = 0; - - ULONG frameNumber = ntohl(*(ULONG*)data); - data += 4; - - if (!recplayer) - { - log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!"); - } - else - { - retval = recplayer->positionFromFrameNumber(frameNumber); - } - - rp->addULLONG(retval); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client"); - return 1; -} - -int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp) -{ - ULONG retval = 0; - - ULLONG position = ntohll(*(ULLONG*)data); - data += 8; - - if (!recplayer) - { - log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!"); - } - else - { - retval = recplayer->frameNumberFromPosition(position); - } - - rp->addULONG(retval); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client"); - return 1; -} - -int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp) -{ - bool success = false; - - ULONG frameNumber = ntohl(*(ULONG*)data); - data += 4; - ULONG direction = ntohl(*(ULONG*)data); - data += 4; - - ULLONG rfilePosition = 0; - ULONG rframeNumber = 0; - ULONG rframeLength = 0; - - if (!recplayer) - { - log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!"); - } - else - { - success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength); - } - - // returns file position, frame number, length - - if (success) - { - rp->addULLONG(rfilePosition); - rp->addULONG(rframeNumber); - rp->addULONG(rframeLength); - } - else - { - rp->addULONG(0); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength); - return 1; -} - -int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp) -{ - ULONG channelNumber = ntohl(*(ULONG*)data); - data += 4; - ULONG startTime = ntohl(*(ULONG*)data); - data += 4; - ULONG duration = ntohl(*(ULONG*)data); - - log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber); - - cChannel* channel = channelFromNumber(channelNumber); - if (!channel) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "written 0 because channel = NULL"); - return 1; - } - - log->log("Client", Log::DEBUG, "Got channel"); - -#if VDRVERSNUM < 10300 - cMutexLock MutexLock; - const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); -#else - cSchedulesLock MutexLock; - const cSchedules *Schedules = cSchedules::Schedules(MutexLock); -#endif - if (!Schedules) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL"); - return 1; - } - - log->log("Client", Log::DEBUG, "Got schedule!s! object"); - - const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); - if (!Schedule) - { - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL"); - return 1; - } - - log->log("Client", Log::DEBUG, "Got schedule object"); - - const char* empty = ""; - bool atLeastOneEvent = false; - - ULONG thisEventID; - ULONG thisEventTime; - ULONG thisEventDuration; - const char* thisEventTitle; - const char* thisEventSubTitle; - const char* thisEventDescription; - -#if VDRVERSNUM < 10300 - - const cEventInfo *event; - for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++) - { - event = Schedule->GetEventNumber(eventNumber); - - thisEventID = event->GetEventID(); - thisEventTime = event->GetTime(); - thisEventDuration = event->GetDuration(); - thisEventTitle = event->GetTitle(); - thisEventSubTitle = event->GetSubtitle(); - thisEventDescription = event->GetExtendedDescription(); - -#else - - for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event)) - { - thisEventID = event->EventID(); - thisEventTime = event->StartTime(); - thisEventDuration = event->Duration(); - thisEventTitle = event->Title(); - thisEventSubTitle = NULL; - thisEventDescription = event->Description(); - -#endif - - //in the past filter - if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue; - - //start time filter - if ((thisEventTime + thisEventDuration) <= startTime) continue; - - //duration filter - if (thisEventTime >= (startTime + duration)) continue; - - if (!thisEventTitle) thisEventTitle = empty; - if (!thisEventSubTitle) thisEventSubTitle = empty; - if (!thisEventDescription) thisEventDescription = empty; - - rp->addULONG(thisEventID); - rp->addULONG(thisEventTime); - rp->addULONG(thisEventDuration); - - rp->addString(thisEventTitle); - rp->addString(thisEventSubTitle); - rp->addString(thisEventDescription); - - atLeastOneEvent = true; - } - - log->log("Client", Log::DEBUG, "Got all event data"); - - if (!atLeastOneEvent) - { - rp->addULONG(0); - log->log("Client", Log::DEBUG, "Written 0 because no data"); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "written schedules packet"); - - return 1; -} - -#endif //VOMPSTANDALONE - -int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp) -{ - char* section = (char*)buffer; - char* key = NULL; - char* value = NULL; - - for (int k = 0; k < length; k++) - { - if (buffer[k] == '\0') - { - if (!key) - { - key = (char*)&buffer[k+1]; - } - else - { - value = (char*)&buffer[k+1]; - break; - } - } - } - - // if the last string (value) doesnt have null terminator, give up - if (buffer[length - 1] != '\0') return 0; - - log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value); - if (config.setValueString(section, key, value)) - { - rp->addULONG(1); - } - else - { - rp->addULONG(0); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - return 1; -} - -int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp) -{ - char* section = (char*)buffer; - char* key = NULL; - - for (int k = 0; k < length; k++) - { - if (buffer[k] == '\0') - { - key = (char*)&buffer[k+1]; - break; - } - } - - char* value = config.getValueString(section, key); - - if (value) - { - rp->addString(value); - log->log("Client", Log::DEBUG, "Written config load packet"); - delete[] value; - } - else - { - rp->addULONG(0); - log->log("Client", Log::DEBUG, "Written config load failed packet"); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - return 1; -} - -#ifndef VOMPSTANDALONE - -int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp) -{ - cTimer *timer; - int numTimers = Timers.Count(); - - rp->addULONG(numTimers); - - for (int i = 0; i < numTimers; i++) - { - timer = Timers.Get(i); - -#if VDRVERSNUM < 10300 - rp->addULONG(timer->Active()); -#else - rp->addULONG(timer->HasFlags(tfActive)); -#endif - rp->addULONG(timer->Recording()); - rp->addULONG(timer->Pending()); - rp->addULONG(timer->Priority()); - rp->addULONG(timer->Lifetime()); - rp->addULONG(timer->Channel()->Number()); - rp->addULONG(timer->StartTime()); - rp->addULONG(timer->StopTime()); - rp->addULONG(timer->Day()); - rp->addULONG(timer->WeekDays()); - rp->addString(timer->File()); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written timers list"); - - return 1; -} - -int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp) -{ - char* timerString = new char[strlen((char*)buffer) + 1]; - strcpy(timerString, (char*)buffer); - -#if VDRVERSNUM < 10300 - - // If this is VDR 1.2 the date part of the timer string must be reduced - // to just DD rather than YYYY-MM-DD - - int s = 0; // source - int d = 0; // destination - int c = 0; // count - while(c != 2) // copy up to date section, including the second ':' - { - timerString[d] = buffer[s]; - if (buffer[s] == ':') c++; - ++s; - ++d; - } - // now it has copied up to the date section - c = 0; - while(c != 2) // waste YYYY-MM- - { - if (buffer[s] == '-') c++; - ++s; - } - // now source is at the DD - memcpy(&timerString[d], &buffer[s], length - s); - d += length - s; - timerString[d] = '\0'; - - log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:"); - log->log("Client", Log::DEBUG, "%s", timerString); - -#endif - - cTimer *timer = new cTimer; - if (timer->Parse((char*)timerString)) - { - cTimer *t = Timers.GetTimer(timer); - if (!t) - { - Timers.Add(timer); -#if VDRVERSNUM < 10300 - Timers.Save(); -#else - Timers.SetModified(); -#endif - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - else - { - rp->addULONG(1); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - } - } - else - { - rp->addULONG(2); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - } - delete timer; - return 1; -} - -int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp) -{ - log->log("Client", Log::DEBUG, "Delete timer called"); - // get timer - - int position = 0; - - INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4; - INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4; - INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4; - INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4; - INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4; - - cTimer* ti = NULL; - for (ti = Timers.First(); ti; ti = Timers.Next(ti)) - { - if ( (ti->Channel()->Number() == delChannel) - && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay))) - && (ti->StartTime() == delStart) - && (ti->StopTime() == delStop) ) - break; - } - - if (!ti) - { - rp->addULONG(4); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - if (!Timers.BeingEdited()) - { - if (!ti->Recording()) - { - Timers.Del(ti); - Timers.SetModified(); - rp->addULONG(10); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - else - { - log->log("Client", Log::ERR, "Unable to delete timer - timer is running"); - rp->addULONG(3); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - } - else - { - log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR"); - rp->addULONG(1); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } -} - -int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp) -{ - // data is a pointer to the fileName string - - cRecordings Recordings; - Recordings.Load(); // probably have to do this - - cRecording *recording = Recordings.GetByName((char*)data); - - time_t timerStart = 0; - time_t timerStop = 0; - char* summary = NULL; - ULONG resumePoint = 0; - - if (!recording) - { - log->log("Client", Log::ERR, "GetRecInfo found no recording"); - rp->addULONG(0); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; - } - - /* Return packet: - 4 bytes: start time for timer - 4 bytes: end time for timer - 4 bytes: resume point - string: summary - 4 bytes: num components - { - 1 byte: stream - 1 byte: type - string: language - string: description - } - - */ - - // Get current timer - - cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); - if (rc) - { - timerStart = rc->Timer()->StartTime(); - timerStop = rc->Timer()->StopTime(); - log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop); - } - - rp->addULONG(timerStart); - rp->addULONG(timerStop); - - // Get resume point - - char* value = config.getValueString("ResumeData", (char*)data); - if (value) - { - resumePoint = strtoul(value, NULL, 10); - delete[] value; - } - log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint); - - rp->addULONG(resumePoint); - - - // Get summary - -#if VDRVERSNUM < 10300 - summary = (char*)recording->Summary(); -#else - const cRecordingInfo *Info = recording->Info(); - summary = (char*)Info->ShortText(); - if (isempty(summary)) summary = (char*)Info->Description(); -#endif - log->log("Client", Log::DEBUG, "GRI: S: %s", summary); - if (summary) - { - rp->addString(summary); - } - else - { - rp->addString(""); - } - - // Get channels - -#if VDRVERSNUM < 10300 - - // Send 0 for numchannels - this signals the client this info is not available - rp->addULONG(0); - -#else - const cComponents* components = Info->Components(); - - log->log("Client", Log::DEBUG, "GRI: D1: %p", components); - - if (!components) - { - rp->addULONG(0); - } - else - { - rp->addULONG(components->NumComponents()); - - tComponent* component; - for (int i = 0; i < components->NumComponents(); i++) - { - component = components->Component(i); - - log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description); - - rp->addUCHAR(component->stream); - rp->addUCHAR(component->type); - - if (component->language) - { - rp->addString(component->language); - } - else - { - rp->addString(""); - } - if (component->description) - { - rp->addString(component->description); - } - else - { - rp->addString(""); - } - } - } - -#endif - - // Done. send it - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written getrecinfo"); - - return 1; -} - - - - -// FIXME obselete - -int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp) -{ - if (!recplayer) - { - log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!"); - return 0; - } - - recplayer->scan(); - - rp->addULLONG(recplayer->getLengthBytes()); - rp->addULONG(recplayer->getLengthFrames()); - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client"); - return 1; -} - -// FIXME without client calling rescan, getblock wont work even tho more data is avail - - -int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp) -{ - // data is a pointer to the fileName string - - cMarks Marks; - cRecordings Recordings; - Recordings.Load(); // probably have to do this - - cRecording *recording = Recordings.GetByName((char*)data); - - log->log("Client", Log::DEBUG, "recording pointer %p", recording); - - if (recording) - { - Marks.Load(recording->FileName()); - if (Marks.Count()) - { - for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) - { - log->log("Client", Log::DEBUG, "found Mark %i", m->position); - - rp->addULONG(m->position); - } - } - else - { - log->log("Client", Log::DEBUG, "no marks found, sending 0-mark"); - rp->addULONG(0); - } - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written Marks list"); - - return 1; -} - -#endif //VOMPSTANDALONE - -/** - * media List Request: - * 4 length - * 4 VDR_GETMEDIALIST - * 4 flags (currently unused) - * n dirname - * n+1 0 - * Media List response: - * 4 length - * 4 VDR_ - * 4 numentries - * per entry: - * 4 media type - * 4 time stamp - * 4 flags - * 4 strlen (incl. 0 Byte) - * string - * 0 -*/ - -int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp) -{ - if (length < 4) { - log->log("Client", Log::ERR, "getMediaList packet too short %d", length); - return 0; - } - char * dirname=NULL; - if (length > 4) { - //we have a dirname provided - dirname=(char *)&data[4]; - log->log("Client", Log::DEBUG, "getMediaList for %s", dirname); - } - - MediaList * ml=MediaList::readList(baseConfig,dirname); - if (ml == NULL) { - log->log("Client", Log::ERR, "getMediaList returned NULL"); - return 0; - } - - //response code (not yet set) - rp->addULONG(0); - - //numentries - rp->addULONG(ml->size()); - - for (MediaList::iterator nm=ml->begin(); nmend(); nm++) - { - Media *m=*nm; - log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType()); - rp->addULONG(m->getType()); - //time stamp - rp->addULONG(m->getTime()); - //flags - rp->addULONG(0); - int len=strlen(m->getFilename()); - //strlen - rp->addULONG(len+1); - rp->addString(m->getFilename()); - } - delete ml; - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written Media list"); - return 1; -} - -/** - * get image Request: - * 4 flags (currently unused) - * 4 x size - * 4 y size - * n filename - * n+1 0 - * get image response: - * 4 length - * 4 VDR_GETIMAGE - * 4 len of image -*/ - -int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp) -{ - if (length < 12) { - log->log("Client", Log::ERR, "getPicture packet too short %d", length); - return 0; - } - if (imageFile) { - fclose(imageFile); - imageFile=NULL; - } - char * filename=NULL; - if (length > 12) { - //we have a dirname provided - filename=(char *)&data[12]; - log->log("Client", Log::DEBUG, "getPicture %s", filename); - } - else { - log->log("Client", Log::ERR, "getPicture empty filename"); - } - if (filename) { - imageFile=fopen(filename,"r"); - if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename); - } - int size=0; - if (imageFile) { - struct stat st; - if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size; - } - //response code (not yet set) - rp->addULONG(31); - //size - rp->addULONG(size); - - log->log("Client", Log::DEBUG, "getPicture size %u", size); - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - log->log("Client", Log::DEBUG, "Written getPicture"); - - return 1; -} - - -int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp) -{ - if (!imageFile) - { - log->log("Client", Log::DEBUG, "Get image block called when no image active"); - return 0; - } - - ULLONG position = ntohll(*(ULLONG*)data); - data += sizeof(ULLONG); - ULONG amount = ntohl(*(ULONG*)data); - - log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount); - - UCHAR sendBuffer[amount]; - ULONG amountReceived = 0; // compiler moan. - ULLONG cpos=ftell(imageFile); - if (position != cpos) { - fseek(imageFile,position-cpos,SEEK_CUR); - } - if (position != (ULLONG)ftell(imageFile)) { - log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position); - } - else { - amountReceived=fread(&sendBuffer[0],1,amount,imageFile); - } - - if (!amountReceived) - { - rp->addULONG(0); - log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0"); - } - else - { - rp->copyin(sendBuffer, amount); - log->log("Client", Log::DEBUG, "written %lu", amountReceived); - } - - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - - return 1; -} - - -int MVPClient::processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp) -{ - i18n.findLanguages(); - const I18n::lang_code_list& languages = i18n.getLanguageList(); - std::string result; - I18n::lang_code_list::const_iterator iter; - for (iter = languages.begin(); iter != languages.end(); ++iter) - { - rp->addString(iter->first.c_str()); - rp->addString(iter->second.c_str()); - } - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; -} - -int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp) -{ - if (length <= 0) return 0; - std::string code, result; - code.assign((char*)data, length - 1); - i18n.findLanguages(); - I18n::trans_table texts = i18n.getLanguageContent(code); - I18n::trans_table::const_iterator iter; - for (iter = texts.begin(); iter != texts.end(); ++iter) - { - rp->addString(iter->first.c_str()); - rp->addString(iter->second.c_str()); - } - rp->finalise(); - tcp.sendPacket(rp->getPtr(), rp->getLen()); - return 1; -} - - - - - - - - - - - - - - -/* - event = Schedule->GetPresentEvent(); - - fprintf(f, "\n\nCurrent event\n\n"); - - fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration()); - fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle()); - fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription()); - fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent()); - - event = Schedule->GetFollowingEvent(); - - fprintf(f, "\n\nFollowing event\n\n"); - - fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration()); - fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle()); - fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription()); - fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent()); - - fprintf(f, "\n\n"); - - fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration()); - fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle()); - fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription()); - fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent()); - - fprintf(f, "\n\n"); - - - -void MVPClient::test2() -{ - FILE* f = fopen("/tmp/s.txt", "w"); - -#if VDRVERSNUM < 10300 - cMutexLock MutexLock; - const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); -#else - cSchedulesLock MutexLock; - const cSchedules *Schedules = cSchedules::Schedules(MutexLock); -#endif - - if (!Schedules) - { - fprintf(f, "Schedules = NULL\n"); - fclose(f); - return; - } - - fprintf(f, "Schedules dump:\n"); - Schedules->Dump(f); - - - const cSchedule *Schedule; - int scheduleNumber = 0; - - tChannelID tchid; - cChannel *thisChannel; - -#if VDRVERSNUM < 10300 - const cEventInfo *event; - int eventNumber = 0; -#else - const cEvent *event; -#endif - -// Schedule = Schedules->GetSchedule(channel->GetChannelID()); -// Schedule = Schedules->GetSchedule(); - Schedule = Schedules->First(); - if (!Schedule) - { - fprintf(f, "First Schedule = NULL\n"); - fclose(f); - return; - } - - while (Schedule) - { - fprintf(f, "Schedule #%i\n", scheduleNumber); - fprintf(f, "-------------\n\n"); - -#if VDRVERSNUM < 10300 - tchid = Schedule->GetChannelID(); -#else - tchid = Schedule->ChannelID(); -#endif - -#if VDRVERSNUM < 10300 - fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString()); - fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents()); -#else -// put the count at the end. -#endif - - thisChannel = Channels.GetByChannelID(tchid, true); - if (thisChannel) - { - fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number()); - } - else - { - fprintf(f, "thisChannel = NULL for tchid\n"); - } - -#if VDRVERSNUM < 10300 - for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++) - { - event = Schedule->GetEventNumber(eventNumber); - fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString()); - fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent()); - fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription()); - fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle()); - fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber()); - fprintf(f, "Event %u dump:\n", eventNumber); - event->Dump(f); - fprintf(f, "\n\n"); - } -#else -// This whole section needs rewriting to walk the list. - event = Schedule->Events()->First(); - while (event) { - event = Schedule->Events()->Next(event); - } -#endif - - - fprintf(f, "\nDump from object:\n"); - Schedule->Dump(f); - fprintf(f, "\nEND\n"); - - - - - fprintf(f, "End of current Schedule\n\n\n"); - - Schedule = (const cSchedule *)Schedules->Next(Schedule); - scheduleNumber++; - } - - fclose(f); -} - - - const cEventInfo *GetPresentEvent(void) const; - const cEventInfo *GetFollowingEvent(void) const; - const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const; - const cEventInfo *GetEventAround(time_t tTime) const; - const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); } - - - const unsigned char GetTableID(void) const; - const char *GetTimeString(void) const; - const char *GetEndTimeString(void) const; - const char *GetDate(void) const; - bool IsFollowing(void) const; - bool IsPresent(void) const; - const char *GetExtendedDescription(void) const; - const char *GetSubtitle(void) const; - const char *GetTitle(void) const; - unsigned short GetEventID(void) const; - long GetDuration(void) const; - time_t GetTime(void) const; - tChannelID GetChannelID(void) const; - int GetChannelNumber(void) const { return nChannelNumber; } - void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const' - void Dump(FILE *f, const char *Prefix = "") const; - - - -void MVPClient::test(int channelNumber) -{ - FILE* f = fopen("/tmp/test.txt", "w"); - - cMutexLock MutexLock; - const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); - - if (!Schedules) - { - fprintf(f, "Schedules = NULL\n"); - fclose(f); - return; - } - - fprintf(f, "Schedules dump:\n"); -// Schedules->Dump(f); - - const cSchedule *Schedule; - cChannel *thisChannel; - const cEventInfo *event; - - thisChannel = channelFromNumber(channelNumber); - if (!thisChannel) - { - fprintf(f, "thisChannel = NULL\n"); - fclose(f); - return; - } - - Schedule = Schedules->GetSchedule(thisChannel->GetChannelID()); -// Schedule = Schedules->GetSchedule(); -// Schedule = Schedules->First(); - if (!Schedule) - { - fprintf(f, "First Schedule = NULL\n"); - fclose(f); - return; - } - - fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents()); - - // For some channels VDR seems to pick a random point in time to - // start dishing out events, but they are in order - // at some point in the list the time snaps to the current event - - - - - for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++) - { - event = Schedule->GetEventNumber(eventNumber); - fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration()); - fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle()); - fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription()); - fprintf(f, "\n\n"); - } - - fprintf(f, "\nEND\n"); - - fclose(f); -} - - - -Right, so - -Schedules = the collection of all the Schedule objects -Schedule = One schedule, contants all the events for a channel -Event = One programme - - -Want: - -Event ID -Time -Duration -Title -Subtitle (used for "Programmes resume at ...") -Description - -IsPresent ? easy to work out tho. Oh it doesn't always work - - -void MVPClient::test2() -{ - log->log("-", Log::DEBUG, "Timers List"); - - for (int i = 0; i < Timers.Count(); i++) - { - cTimer *timer = Timers.Get(i); - //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText()); - log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1); -#if VDRVERSNUM < 10300 - log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->Active(), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime()); -#else - log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->HasFlags(tfActive), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime()); -#endif - log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary()); - log->log("-", Log::DEBUG, ""); - } - - // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n", -// active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())), -// PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : ""); -} - - -Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time -recording is a bool, 0 for not currently recording, 1 for currently recording -pending is a bool, 0 for would not be trying to record this right now, 1 for would/is trying to record this right now -*/ - diff --git a/mvpclient.h b/mvpclient.h deleted file mode 100644 index 2551b22..0000000 --- a/mvpclient.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef MVPCLIENT_H -#define MVPCLIENT_H - -#include -#include -#include -#include - -#include // sleep - -#ifndef VOMPSTANDALONE -#include -#include -#include -#include -#include -#include -#include "recplayer.h" -#include "mvpreceiver.h" -#endif - -#include "defines.h" -#include "tcp.h" -#include "config.h" -#include "media.h" -#include "i18n.h" - -class ResponsePacket; - -class MVPClient -{ - public: - MVPClient(Config* baseConfig, char* configDir, int tsocket); - ~MVPClient(); - - int run(); - // not for external use - void run2(); - static int getNrClients(); - - private: - static int nr_clients; - pthread_t runThread; - int initted; - Log* log; - TCP tcp; - Config config; - Config* baseConfig; - I18n i18n; - bool loggedIn; - char* configDir; - FILE* imageFile; - -#ifndef VOMPSTANDALONE - MVPReceiver* lp; - cRecordings* recordingManager; - RecPlayer* recplayer; -#endif - - int processLogin(UCHAR* buffer, int length, ResponsePacket* rp); -#ifndef VOMPSTANDALONE - int processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp); - int processDeleteRecording(UCHAR* data, int length, ResponsePacket* rp); - int processMoveRecording(UCHAR* data, int length, ResponsePacket* rp); - int processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp); - int processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp); - int processGetBlock(UCHAR* data, int length, ResponsePacket* rp); - int processStopStreaming(UCHAR* data, int length, ResponsePacket* rp); - int processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp); - int processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp); - int processGetTimers(UCHAR* data, int length, ResponsePacket* rp); - int processSetTimer(UCHAR* data, int length, ResponsePacket* rp); - int processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp); - int processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp); - int processGetIFrame(UCHAR* data, int length, ResponsePacket* rp); - int processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp); - int processGetMarks(UCHAR* data, int length, ResponsePacket* rp); - int processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp); - int processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp); - - int processReScanRecording(UCHAR* data, int length, ResponsePacket* rp); // FIXME obselete -#endif - int processConfigSave(UCHAR* data, int length, ResponsePacket* rp); - int processConfigLoad(UCHAR* data, int length, ResponsePacket* rp); - int processGetMediaList(UCHAR* data, int length, ResponsePacket* rp); - int processGetPicture(UCHAR* data, int length, ResponsePacket* rp); - int processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp); - int processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp); - int processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp); - - void incClients(); - void decClients(); - -#ifndef VOMPSTANDALONE - cChannel* channelFromNumber(ULONG channelNumber); - void writeResumeData(); -#endif - void cleanConfig(); - void sendULONG(ULONG ul); - - ULLONG ntohll(ULLONG a); - ULLONG htonll(ULLONG a); - - void test(int num); - void test2(); -}; - -#endif diff --git a/mvpreceiver.c b/mvpreceiver.c index f901a70..2135e3c 100755 --- a/mvpreceiver.c +++ b/mvpreceiver.c @@ -122,7 +122,9 @@ void MVPReceiver::threadMethod() while(1) { + threadLock(); threadWaitForSignal(); + threadUnlock(); threadCheckExit(); do diff --git a/mvpserver.c b/mvpserver.c index 4ce198f..9a2eabf 100644 --- a/mvpserver.c +++ b/mvpserver.c @@ -260,7 +260,7 @@ void MVPServer::threadMethod() while(1) { clientSocket = accept(listeningSocket,(struct sockaddr *)&address, &length); - MVPClient* m = new MVPClient(&config, configDir, clientSocket); + VompClient* m = new VompClient(&config, configDir, clientSocket); m->run(); } } diff --git a/mvpserver.h b/mvpserver.h index 9bf2549..07ca1c9 100644 --- a/mvpserver.h +++ b/mvpserver.h @@ -30,7 +30,7 @@ #include "mvprelay.h" #include "bootpd.h" #include "tftpd.h" -#include "mvpclient.h" +#include "vompclient.h" #include "thread.h" #include "config.h" diff --git a/thread.c b/thread.c index 97a41c1..c89c8ef 100755 --- a/thread.c +++ b/thread.c @@ -1,5 +1,5 @@ /* - Copyright 2004-2005 Chris Tallon + Copyright 2004-2008 Chris Tallon This file is part of VOMP. @@ -93,12 +93,22 @@ void Thread::threadSignalNoLock() void Thread::threadWaitForSignal() { - pthread_mutex_lock(&threadCondMutex); pthread_cond_wait(&threadCond, &threadCondMutex); - pthread_mutex_unlock(&threadCondMutex); } void Thread::threadDetach() { pthread_detach(pthread); } + +void Thread::threadLock() +{ + pthread_mutex_lock(&threadCondMutex); +} + +void Thread::threadUnlock() +{ + pthread_mutex_unlock(&threadCondMutex); +} + + diff --git a/thread.h b/thread.h index 5943677..7308940 100755 --- a/thread.h +++ b/thread.h @@ -1,5 +1,5 @@ /* - Copyright 2004-2005 Chris Tallon + Copyright 2004-2008 Chris Tallon This file is part of VOMP. @@ -43,6 +43,8 @@ class Thread void threadCheckExit(); // terminates thread if threadStop() has been called void threadWaitForSignal(); // pauses thread until threadSignal() is called void threadDetach(); // Detaches the thread + void threadLock(); // locks the mutex used for internal cond/signal stuff + void threadUnlock(); // unlocks. // Internal bits and pieces diff --git a/vompclient.c b/vompclient.c new file mode 100644 index 0000000..2b28b97 --- /dev/null +++ b/vompclient.c @@ -0,0 +1,324 @@ +/* + Copyright 2004-2008 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vompclient.h" + +#include "responsepacket.h" + +#ifndef VOMPSTANDALONE +#include +#include +#include "recplayer.h" +#include "mvpreceiver.h" +#endif + + + +pthread_mutex_t threadClientMutex; +int VompClient::nr_clients = 0; + + +VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket) + : rrproc(*this), tcp(tsocket), i18n(tconfigDir) +{ +#ifndef VOMPSTANDALONE + lp = NULL; + recplayer = NULL; + recordingManager = NULL; +#endif + imageFile = 0; + log = Log::getInstance(); + loggedIn = false; + configDir = tconfigDir; + log->log("Client", Log::DEBUG, "Config dir: %s", configDir); + baseConfig = cfgBase; + incClients(); + + rrproc.init(); +} + +VompClient::~VompClient() +{ + log->log("Client", Log::DEBUG, "Vomp client destructor"); +#ifndef VOMPSTANDALONE + if (lp) + { + delete lp; + lp = NULL; + } + else if (recplayer) + { + writeResumeData(); + + delete recplayer; + delete recordingManager; + recplayer = NULL; + recordingManager = NULL; + } +#endif + if (loggedIn) cleanConfig(); + decClients(); +} + +void VompClient::incClients() +{ + pthread_mutex_lock(&threadClientMutex); + VompClient::nr_clients++; + pthread_mutex_unlock(&threadClientMutex); +} + +void VompClient::decClients() +{ + pthread_mutex_lock(&threadClientMutex); + VompClient::nr_clients--; + pthread_mutex_unlock(&threadClientMutex); +} + +int VompClient::getNrClients() +{ + int nrClients; + pthread_mutex_lock(&threadClientMutex); + nrClients = VompClient::nr_clients; + pthread_mutex_unlock(&threadClientMutex); + return nrClients; +} + + +void VompClient::cleanConfig() +{ + log->log("Client", Log::DEBUG, "Clean config"); + +#ifndef VOMPSTANDALONE + + cRecordings Recordings; + Recordings.Load(); + + int numReturns; + int length; + char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length); + char* position = resumes; + for(int k = 0; k < numReturns; k++) + { + log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position); + + cRecording* recording = Recordings.GetByName(position); + if (!recording) + { + // doesn't exist anymore + log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore"); + config.deleteValue("ResumeData", position); + } + else + { + log->log("Client", Log::DEBUG, "This recording still exists"); + } + + position += strlen(position) + 1; + } + + delete[] resumes; +#endif +} + +void VompClientStartThread(void* arg) +{ + VompClient* m = (VompClient*)arg; + m->run2(); + // Nothing external to this class has a reference to it + // This is the end of the thread.. so delete m + delete m; + pthread_exit(NULL); +} + +int VompClient::run() +{ + if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0; + log->log("Client", Log::DEBUG, "VompClient run success"); + return 1; +} + +void VompClient::run2() +{ + // Thread stuff + sigset_t sigset; + sigfillset(&sigset); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + pthread_detach(runThread); // Detach + + tcp.disableReadTimeout(); + + tcp.setSoKeepTime(3); + tcp.setNonBlocking(); + + ULONG channelID; + ULONG requestID; + ULONG opcode; + ULONG extraDataLength; + UCHAR* data; + + ULONG kaTimeStamp; + + while(1) + { + log->log("Client", Log::DEBUG, "Waiting"); + + if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break; + channelID = ntohl(channelID); + if (channelID == 1) + { + if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break; + requestID = ntohl(requestID); + + if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break; + opcode = ntohl(opcode); + + if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break; + extraDataLength = ntohl(extraDataLength); + if (extraDataLength > 200000) // a random sanity limit + { + log->log("Client", Log::ERR, "ExtraDataLength > 200000!"); + break; + } + + if (extraDataLength) + { + data = (UCHAR*)malloc(extraDataLength); + if (!data) + { + log->log("Client", Log::ERR, "Extra data buffer malloc error"); + break; + } + + if (!tcp.readData(data, extraDataLength)) + { + log->log("Client", Log::ERR, "Could not read extradata"); + free(data); + break; + } + } + else + { + data = NULL; + } + + log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength); + + if (!loggedIn && (opcode != 1)) + { + log->log("Client", Log::ERR, "Not logged in and opcode != 1"); + if (data) free(data); + break; + } + + RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength); + rrproc.recvRequest(req); + } + else if (channelID == 3) + { + if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break; + kaTimeStamp = ntohl(kaTimeStamp); + + log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp); + + UCHAR buffer[8]; + *(ULONG*)&buffer[0] = htonl(3); // KA CHANNEL + *(ULONG*)&buffer[4] = htonl(kaTimeStamp); + if (!tcp.sendPacket(buffer, 8)) + { + log->log("Client", Log::ERR, "Could not send back KA reply"); + break; + } + } + else + { + log->log("Client", Log::ERR, "Incoming channel number unknown"); + break; + } + } +} + +ULLONG VompClient::ntohll(ULLONG a) +{ + return htonll(a); +} + +ULLONG VompClient::htonll(ULLONG a) +{ + #if BYTE_ORDER == BIG_ENDIAN + return a; + #else + ULLONG b = 0; + + b = ((a << 56) & 0xFF00000000000000ULL) + | ((a << 40) & 0x00FF000000000000ULL) + | ((a << 24) & 0x0000FF0000000000ULL) + | ((a << 8) & 0x000000FF00000000ULL) + | ((a >> 8) & 0x00000000FF000000ULL) + | ((a >> 24) & 0x0000000000FF0000ULL) + | ((a >> 40) & 0x000000000000FF00ULL) + | ((a >> 56) & 0x00000000000000FFULL) ; + + return b; + #endif +} + +#ifndef VOMPSTANDALONE + +cChannel* VompClient::channelFromNumber(ULONG channelNumber) +{ + cChannel* channel = NULL; + + for (channel = Channels.First(); channel; channel = Channels.Next(channel)) + { + if (!channel->GroupSep()) + { + log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name()); + + if (channel->Number() == (int)channelNumber) + { + int vpid = channel->Vpid(); +#if VDRVERSNUM < 10300 + int apid1 = channel->Apid1(); +#else + int apid1 = channel->Apid(0); +#endif + log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1); + return channel; + } + } + } + + if (!channel) + { + log->log("Client", Log::DEBUG, "Channel not found"); + } + + return channel; +} + +void VompClient::writeResumeData() +{ + config.setValueLong("ResumeData", + (char*)recplayer->getCurrentRecording()->FileName(), + recplayer->frameNumberFromPosition(recplayer->getLastPosition()) ); +} + +#endif + diff --git a/vompclient.h b/vompclient.h new file mode 100644 index 0000000..c728bf5 --- /dev/null +++ b/vompclient.h @@ -0,0 +1,106 @@ +/* + Copyright 2004-2008 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* + The new async protocol design forced the server side change from the mvpclient + class to the mess it is now. Maybe in a couple of versions time it will become + more apparent how better to design all this. + + The reason for the class split is that with the current Thread class system, + you can only have one thread per object. The vompclient class would have needed + two, one for RR stuff and one for the keepalives. + + So the new design is this: the VompClient class represents one connection from + one client as the MVPClient class did before. But because two threads are now + needed, a helper class VompClientRRProc contains all the RR processing functions, + and the thread needed to process them. All the state data is still kept in the + VompClient class. +*/ + +#ifndef VOMPCLIENT_H +#define VOMPCLIENT_H + +#include +#include +#include +#include + +#include // sleep + +#ifndef VOMPSTANDALONE +class RecPlayer; +class MVPReceiver; +class cChannel; +class cRecordings; +#endif + +#include "defines.h" +#include "tcp.h" +#include "config.h" +#include "i18n.h" +#include "vompclientrrproc.h" + +class VompClient +{ + friend class VompClientRRProc; + + public: + VompClient(Config* baseConfig, char* configDir, int tsocket); + ~VompClient(); + + int run(); + // not for external use + void run2(); + static int getNrClients(); + + private: + static int nr_clients; + void incClients(); + void decClients(); + + static ULLONG ntohll(ULLONG a); + static ULLONG htonll(ULLONG a); + + VompClientRRProc rrproc; + pthread_t runThread; + int initted; + Log* log; + TCP tcp; + Config config; + Config* baseConfig; + I18n i18n; + bool loggedIn; + char* configDir; + FILE* imageFile; + + void cleanConfig(); + +#ifndef VOMPSTANDALONE + cChannel* channelFromNumber(ULONG channelNumber); + void writeResumeData(); + + MVPReceiver* lp; + cRecordings* recordingManager; + RecPlayer* recplayer; +#endif +}; + +#endif + diff --git a/vompclientrrproc.c b/vompclientrrproc.c new file mode 100644 index 0000000..a2277ed --- /dev/null +++ b/vompclientrrproc.c @@ -0,0 +1,1700 @@ +/* + Copyright 2008 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include + +#ifndef VOMPSTANDALONE +#include +#include +#include +#include +#include +#include +#include "recplayer.h" +#include "mvpreceiver.h" +#endif + +#include "vompclientrrproc.h" +#include "vompclient.h" +#include "log.h" +#include "media.h" +#include "i18n.h" + +VompClientRRProc::VompClientRRProc(VompClient& x) + : x(x) +{ + log = Log::getInstance(); + req = NULL; + resp = NULL; +} + +bool VompClientRRProc::init() +{ + return threadStart(); +} + +bool VompClientRRProc::recvRequest(RequestPacket* newRequest) +{ + /* + Accept a new request + Currently only one at once is supported but this + could be upgraded to a queueing system + */ + + threadLock(); + + if (req) + { + log->log("RRProc", Log::ERR, "recvReq err 1"); + threadUnlock(); + return false; + } + + req = newRequest; + threadSignalNoLock(); + + log->log("RRProc", Log::DEBUG, "recvReq set req and signalled"); + + threadUnlock(); + return true; +} + +void VompClientRRProc::threadMethod() +{ + threadLock(); + log->log("RRProc", Log::DEBUG, "threadMethod startup"); + + while(1) + { + if (req) + { + log->log("RRProc", Log::ERR, "threadMethod err 1"); + threadUnlock(); + return; + } + + log->log("RRProc", Log::DEBUG, "threadMethod waiting"); + threadWaitForSignal(); + if (!req) + { + log->log("RRProc", Log::ERR, "threadMethod err 2"); + threadUnlock(); + return; + } + + log->log("RRProc", Log::DEBUG, "thread woken with req"); + + processPacket(); + } +} + +bool VompClientRRProc::processPacket() +{ + resp = new ResponsePacket(); + if (!resp->init(req->requestID)) + { + log->log("RRProc", Log::ERR, "response packet init fail"); + delete resp; + return false; + } + + int result = 0; + + switch(req->opcode) + { + case 1: + result = processLogin(); + break; +#ifndef VOMPSTANDALONE + case 2: + result = processGetRecordingsList(); + break; + case 3: + result = processDeleteRecording(); + break; + case 5: + result = processGetChannelsList(); + break; + case 6: + result = processStartStreamingChannel(); + break; + case 7: + result = processGetBlock(); + break; + case 8: + result = processStopStreaming(); + break; + case 9: + result = processStartStreamingRecording(); + break; +#endif + case 10: + result = processGetChannelSchedule(); + break; + case 11: + result = processConfigSave(); + break; + case 12: + result = processConfigLoad(); + break; +#ifndef VOMPSTANDALONE + case 13: + result = processReScanRecording(); // FIXME obselete + break; + case 14: + result = processGetTimers(); + break; + case 15: + result = processSetTimer(); + break; + case 16: + result = processPositionFromFrameNumber(); + break; + case 17: + result = processFrameNumberFromPosition(); + break; + case 18: + result = processMoveRecording(); + break; + case 19: + result = processGetIFrame(); + break; + case 20: + result = processGetRecInfo(); + break; + case 21: + result = processGetMarks(); + break; + case 22: + result = processGetChannelPids(); + break; + case 23: + result = processDeleteTimer(); + break; +#endif + case 30: + result = processGetMediaList(); + break; + case 31: + result = processGetPicture(); + break; + case 32: + result = processGetImageBlock(); + break; + case 33: + result = processGetLanguageList(); + break; + case 34: + result = processGetLanguageContent(); + break; + } + + delete resp; + resp = NULL; + + if (req->data) free(req->data); + delete req; + req = NULL; + + if (result) return true; + return false; +} + +int VompClientRRProc::processLogin() +{ + if (req->dataLength != 6) return 0; + + // Open the config + + char configFileName[PATH_MAX]; + snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]); + x.config.init(configFileName); + + // Send the login reply + + time_t timeNow = time(NULL); + struct tm* timeStruct = localtime(&timeNow); + int timeOffset = timeStruct->tm_gmtoff; + + resp->addULONG(timeNow); + resp->addLONG(timeOffset); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen()); + + x.loggedIn = true; + return 1; +} + +int VompClientRRProc::processConfigSave() +{ + char* section = (char*)req->data; + char* key = NULL; + char* value = NULL; + + for (UINT k = 0; k < req->dataLength; k++) + { + if (req->data[k] == '\0') + { + if (!key) + { + key = (char*)&req->data[k+1]; + } + else + { + value = (char*)&req->data[k+1]; + break; + } + } + } + + // if the last string (value) doesnt have null terminator, give up + if (req->data[req->dataLength - 1] != '\0') return 0; + + log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value); + if (x.config.setValueString(section, key, value)) + { + resp->addULONG(1); + } + else + { + resp->addULONG(0); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + return 1; +} + +int VompClientRRProc::processConfigLoad() +{ + char* section = (char*)req->data; + char* key = NULL; + + for (UINT k = 0; k < req->dataLength; k++) + { + if (req->data[k] == '\0') + { + key = (char*)&req->data[k+1]; + break; + } + } + + char* value = x.config.getValueString(section, key); + + if (value) + { + resp->addString(value); + log->log("RRProc", Log::DEBUG, "Written config load packet"); + delete[] value; + } + else + { + resp->addULONG(0); + log->log("RRProc", Log::DEBUG, "Written config load failed packet"); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + return 1; +} + +/** + * media List Request: + * 4 length + * 4 VDR_GETMEDIALIST + * 4 flags (currently unused) + * n dirname + * n+1 0 + * Media List response: + * 4 length + * 4 VDR_ + * 4 numentries + * per entry: + * 4 media type + * 4 time stamp + * 4 flags + * 4 strlen (incl. 0 Byte) + * string + * 0 +*/ + +int VompClientRRProc::processGetMediaList() +{ + if (req->dataLength < 4) { + log->log("RRProc", Log::ERR, "getMediaList packet too short %d", req->dataLength); + return 0; + } + char * dirname=NULL; + if (req->dataLength > 4) { + //we have a dirname provided + dirname=(char *)&req->data[4]; + log->log("RRProc", Log::DEBUG, "getMediaList for %s", dirname); + } + + MediaList * ml=MediaList::readList(x.baseConfig, dirname); + if (ml == NULL) { + log->log("RRProc", Log::ERR, "getMediaList returned NULL"); + return 0; + } + + //response code (not yet set) + resp->addULONG(0); + + //numentries + resp->addULONG(ml->size()); + + for (MediaList::iterator nm=ml->begin(); nmend(); nm++) + { + Media *m=*nm; + log->log("RRProc", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType()); + resp->addULONG(m->getType()); + //time stamp + resp->addULONG(m->getTime()); + //flags + resp->addULONG(0); + int len=strlen(m->getFilename()); + //strlen + resp->addULONG(len+1); + resp->addString(m->getFilename()); + } + delete ml; + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written Media list"); + return 1; +} + +/** + * get image Request: + * 4 flags (currently unused) + * 4 x size + * 4 y size + * n filename + * n+1 0 + * get image response: + * 4 length + * 4 VDR_GETIMAGE + * 4 len of image +*/ + +int VompClientRRProc::processGetPicture() +{ + if (req->dataLength < 12) { + log->log("RRProc", Log::ERR, "getPicture packet too short %d", req->dataLength); + return 0; + } + if (x.imageFile) { + fclose(x.imageFile); + x.imageFile=NULL; + } + char * filename=NULL; + if (req->dataLength > 12) { + //we have a dirname provided + filename=(char *)&req->data[12]; + log->log("RRProc", Log::DEBUG, "getPicture %s", filename); + } + else { + log->log("RRProc", Log::ERR, "getPicture empty filename"); + } + if (filename) { + x.imageFile=fopen(filename,"r"); + if (!x.imageFile) log->log("RRProc", Log::ERR, "getPicture unable to open %s",filename); + } + int size=0; + if (x.imageFile) { + struct stat st; + if ( fstat(fileno(x.imageFile),&st) == 0) size=st.st_size; + } + //response code (not yet set) + resp->addULONG(31); + //size + resp->addULONG(size); + + log->log("RRProc", Log::DEBUG, "getPicture size %u", size); + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written getPicture"); + + return 1; +} + + +int VompClientRRProc::processGetImageBlock() +{ + if (!x.imageFile) + { + log->log("RRProc", Log::DEBUG, "Get image block called when no image active"); + return 0; + } + + UCHAR* data = req->data; + + ULLONG position = x.ntohll(*(ULLONG*)data); + data += sizeof(ULLONG); + ULONG amount = ntohl(*(ULONG*)data); + + log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount); + + UCHAR sendBuffer[amount]; + ULONG amountReceived = 0; // compiler moan. + ULLONG cpos=ftell(x.imageFile); + if (position != cpos) { + fseek(x.imageFile,position-cpos,SEEK_CUR); + } + if (position != (ULLONG)ftell(x.imageFile)) { + log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu not available", position); + } + else { + amountReceived=fread(&sendBuffer[0],1,amount,x.imageFile); + } + + if (!amountReceived) + { + resp->addULONG(0); + log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0"); + } + else + { + resp->copyin(sendBuffer, amount); + log->log("RRProc", Log::DEBUG, "written %lu", amountReceived); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + return 1; +} + + +int VompClientRRProc::processGetLanguageList() +{ + x.i18n.findLanguages(); + const I18n::lang_code_list& languages = x.i18n.getLanguageList(); + std::string result; + I18n::lang_code_list::const_iterator iter; + for (iter = languages.begin(); iter != languages.end(); ++iter) + { + resp->addString(iter->first.c_str()); + resp->addString(iter->second.c_str()); + } + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; +} + +int VompClientRRProc::processGetLanguageContent() +{ + if (req->dataLength <= 0) return 0; + std::string code, result; + code.assign((char*)req->data, req->dataLength - 1); + x.i18n.findLanguages(); + I18n::trans_table texts = x.i18n.getLanguageContent(code); + I18n::trans_table::const_iterator iter; + for (iter = texts.begin(); iter != texts.end(); ++iter) + { + resp->addString(iter->first.c_str()); + resp->addString(iter->second.c_str()); + } + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; +} + +#ifndef VOMPSTANDALONE + +int VompClientRRProc::processGetRecordingsList() +{ + log->log("RRProc", Log::DEBUG, "Test sleep start"); + sleep(20); + log->log("RRProc", Log::DEBUG, "Test sleep end"); + + int FreeMB; + int Percent = VideoDiskSpace(&FreeMB); + int Total = (FreeMB / (100 - Percent)) * 100; + + resp->addULONG(Total); + resp->addULONG(FreeMB); + resp->addULONG(Percent); + + cRecordings Recordings; + Recordings.Load(); + + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) + { + resp->addULONG(recording->start); + resp->addString(recording->Name()); + resp->addString(recording->FileName()); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written recordings list"); + + return 1; +} + +int VompClientRRProc::processDeleteRecording() +{ + // data is a pointer to the fileName string + + cRecordings Recordings; + Recordings.Load(); // probably have to do this + + cRecording* recording = Recordings.GetByName((char*)req->data); + + log->log("RRProc", Log::DEBUG, "recording pointer %p", recording); + + if (recording) + { + log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name()); + + cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); + if (!rc) + { + if (recording->Delete()) + { + // Copy svdrp's way of doing this, see if it works +#if VDRVERSNUM > 10300 + ::Recordings.DelByName(recording->FileName()); +#endif + resp->addULONG(1); + } + else + { + resp->addULONG(2); + } + } + else + { + resp->addULONG(3); + } + } + else + { + resp->addULONG(4); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + return 1; +} + +int VompClientRRProc::processMoveRecording() +{ + log->log("RRProc", Log::DEBUG, "Process move recording"); + char* fileName = (char*)req->data; + char* newPath = NULL; + + for (UINT k = 0; k < req->dataLength; k++) + { + if (req->data[k] == '\0') + { + newPath = (char*)&req->data[k+1]; + break; + } + } + if (!newPath) return 0; + + cRecordings Recordings; + Recordings.Load(); // probably have to do this + + cRecording* recording = Recordings.GetByName((char*)fileName); + + log->log("RRProc", Log::DEBUG, "recording pointer %p", recording); + + if (recording) + { + cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); + if (!rc) + { + log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name()); + log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName()); + log->log("RRProc", Log::DEBUG, "to: %s", newPath); + + const char* t = recording->FileName(); + + char* dateDirName = NULL; int k; + char* titleDirName = NULL; int j; + + // Find the datedirname + for(k = strlen(t) - 1; k >= 0; k--) + { + if (t[k] == '/') + { + log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1); + dateDirName = new char[strlen(&t[k+1]) + 1]; + strcpy(dateDirName, &t[k+1]); + break; + } + } + + // Find the titledirname + + for(j = k-1; j >= 0; j--) + { + if (t[j] == '/') + { + log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1); + titleDirName = new char[(k - j - 1) + 1]; + memcpy(titleDirName, &t[j+1], k - j - 1); + titleDirName[k - j - 1] = '\0'; + break; + } + } + + log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName); + log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName); + log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory); + + char* newPathConv = new char[strlen(newPath)+1]; + strcpy(newPathConv, newPath); + ExchangeChars(newPathConv, true); + log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv); + + char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1]; + log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1); + sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName); + delete[] newPathConv; + + log->log("RRProc", Log::DEBUG, "%s", newContainer); + + struct stat dstat; + int statret = stat(newContainer, &dstat); + if ((statret == -1) && (errno == ENOENT)) // Dir does not exist + { + log->log("RRProc", Log::DEBUG, "new dir does not exist"); + int mkdirret = mkdir(newContainer, 0755); + if (mkdirret != 0) + { + delete[] dateDirName; + delete[] titleDirName; + delete[] newContainer; + + resp->addULONG(5); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + } + else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir + { + delete[] dateDirName; + delete[] titleDirName; + delete[] newContainer; + + resp->addULONG(5); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + // Ok, the directory container has been made, or it pre-existed. + + char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1]; + sprintf(newDir, "%s/%s", newContainer, dateDirName); + + log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir); + int renameret = rename(t, newDir); + if (renameret == 0) + { + // Success. Test for remove old dir containter + char* oldTitleDir = new char[k+1]; + memcpy(oldTitleDir, t, k); + oldTitleDir[k] = '\0'; + log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir); + rmdir(oldTitleDir); // can't do anything about a fail result at this point. + delete[] oldTitleDir; + } + + if (renameret == 0) + { +#if VDRVERSNUM > 10311 + // Tell VDR + ::Recordings.Update(); +#endif + // Success. Send a different packet from just a ulong + resp->addULONG(1); // success + resp->addString(newDir); + } + else + { + resp->addULONG(5); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + delete[] dateDirName; + delete[] titleDirName; + delete[] newContainer; + delete[] newDir; + } + else + { + resp->addULONG(3); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + } + } + else + { + resp->addULONG(4); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + } + + return 1; +} + +int VompClientRRProc::processGetChannelsList() +{ + ULONG type; + + char* chanConfig = x.config.getValueString("General", "Channels"); + int allChans = 1; + if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only"); + + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) + { +#if VDRVERSNUM < 10300 + if (!channel->GroupSep() && (!channel->Ca() || allChans)) +#else + if (!channel->GroupSep() && (!channel->Ca(0) || allChans)) +#endif + { + log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name()); + + if (channel->Vpid()) type = 1; +#if VDRVERSNUM < 10300 + else type = 2; +#else + else if (channel->Apid(0)) type = 2; + else continue; +#endif + + resp->addULONG(channel->Number()); + resp->addULONG(type); + resp->addString(channel->Name()); + } + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written channels list"); + + return 1; +} + +int VompClientRRProc::processGetChannelPids() +{ + ULONG channelNumber = ntohl(*(ULONG*)req->data); + + cChannel* channel = x.channelFromNumber(channelNumber); + if (!channel) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + ULONG numApids = 0; + ULONG numDpids = 0; + ULONG numSpids = 0; + + +#if VDRVERSNUM < 10300 + + log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1()); + log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2()); + + if (channel->Apid2()) + numApids = 2; + else if (channel->Apid1()) + numApids = 1; + else + numApids = 0; + +#else + + for (const int *Apid = channel->Apids(); *Apid; Apid++) + { + numApids++; + } + for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++) + { + numDpids++; + } + for (const int *Spid = channel->Spids(); *Spid; Spid++) + { + numSpids++; + } +#endif + + + // Format of response + // vpid + // number of apids + // { + // apid + // lang string + // } + // number of dpids + // { + // dpid + // lang string + // } + // number of spids + // { + // spid + // lang string + // } + // tpid + + resp->addULONG(channel->Vpid()); + resp->addULONG(numApids); + +#if VDRVERSNUM < 10300 + if (numApids >= 1) + { + resp->addULONG(channel->Apid1()); + resp->addString(""); + } + if (numApids == 2) + { + resp->addULONG(channel->Apid2()); + resp->addString(""); + } + resp->addULONG(0); + resp->addULONG(0); +#else + for (ULONG i = 0; i < numApids; i++) + { + resp->addULONG(channel->Apid(i)); + resp->addString(channel->Alang(i)); + } + resp->addULONG(numDpids); + for (ULONG i = 0; i < numDpids; i++) + { + resp->addULONG(channel->Dpid(i)); + resp->addString(channel->Dlang(i)); + } + resp->addULONG(numSpids); + for (ULONG i = 0; i < numSpids; i++) + { + resp->addULONG(channel->Spid(i)); + resp->addString(channel->Slang(i)); + } +#endif + resp->addULONG(channel->Tpid()); + + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written channels pids"); + + return 1; +} + +int VompClientRRProc::processStartStreamingChannel() +{ + if (x.lp) + { + log->log("RRProc", Log::ERR, "Client called start streaming twice"); + return 0; + } + + log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength); + ULONG channelNumber = ntohl(*(ULONG*)req->data); + + cChannel* channel = x.channelFromNumber(channelNumber); + if (!channel) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + // get the priority we should use + int fail = 1; + int priority = x.config.getValueLong("General", "Live priority", &fail); + if (!fail) + { + log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority); + } + else + { + log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail"); + priority = 0; + } + + // a bit of sanity.. + if (priority < 0) priority = 0; + if (priority > 99) priority = 99; + + log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority); + x.lp = MVPReceiver::create(channel, priority); + + if (!x.lp) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + if (!x.lp->init(&x.tcp, req->requestID)) + { + delete x.lp; + x.lp = NULL; + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + resp->addULONG(1); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; +} + +int VompClientRRProc::processStopStreaming() +{ + log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED"); + if (x.lp) + { + delete x.lp; + x.lp = NULL; + } + else if (x.recplayer) + { + x.writeResumeData(); + + delete x.recplayer; + delete x.recordingManager; + x.recplayer = NULL; + x.recordingManager = NULL; + } + + resp->addULONG(1); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; +} + +int VompClientRRProc::processGetBlock() +{ + if (!x.lp && !x.recplayer) + { + log->log("RRProc", Log::DEBUG, "Get block called when no streaming happening!"); + return 0; + } + + UCHAR* data = req->data; + + ULLONG position = x.ntohll(*(ULLONG*)data); + data += sizeof(ULLONG); + ULONG amount = ntohl(*(ULONG*)data); + + log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount); + + UCHAR sendBuffer[amount]; + ULONG amountReceived = 0; // compiler moan. + if (x.lp) + { + log->log("RRProc", Log::DEBUG, "getting from live"); + amountReceived = x.lp->getBlock(&sendBuffer[0], amount); + + if (!amountReceived) + { + // vdr has possibly disconnected the receiver + log->log("RRProc", Log::DEBUG, "VDR has disconnected the live receiver"); + delete x.lp; + x.lp = NULL; + } + } + else if (x.recplayer) + { + log->log("RRProc", Log::DEBUG, "getting from recording"); + amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount); + } + + if (!amountReceived) + { + resp->addULONG(0); + log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0"); + } + else + { + resp->copyin(sendBuffer, amountReceived); + log->log("RRProc", Log::DEBUG, "written %lu", amountReceived); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen()); + return 1; +} + +int VompClientRRProc::processStartStreamingRecording() +{ + // data is a pointer to the fileName string + + x.recordingManager = new cRecordings; + x.recordingManager->Load(); + + cRecording* recording = x.recordingManager->GetByName((char*)req->data); + + log->log("RRProc", Log::DEBUG, "recording pointer %p", recording); + + if (recording) + { + x.recplayer = new RecPlayer(recording); + + resp->addULLONG(x.recplayer->getLengthBytes()); + resp->addULONG(x.recplayer->getLengthFrames()); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "written totalLength"); + } + else + { + delete x.recordingManager; + x.recordingManager = NULL; + } + return 1; +} + +int VompClientRRProc::processPositionFromFrameNumber() +{ + ULLONG retval = 0; + + ULONG frameNumber = ntohl(*(ULONG*)req->data); + + if (!x.recplayer) + { + log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!"); + } + else + { + retval = x.recplayer->positionFromFrameNumber(frameNumber); + } + + resp->addULLONG(retval); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client"); + return 1; +} + +int VompClientRRProc::processFrameNumberFromPosition() +{ + ULONG retval = 0; + + ULLONG position = x.ntohll(*(ULLONG*)req->data); + + if (!x.recplayer) + { + log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!"); + } + else + { + retval = x.recplayer->frameNumberFromPosition(position); + } + + resp->addULONG(retval); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client"); + return 1; +} + +int VompClientRRProc::processGetIFrame() +{ + bool success = false; + + ULONG* data = (ULONG*)req->data; + + ULONG frameNumber = ntohl(*data); + data++; + ULONG direction = ntohl(*data); + + ULLONG rfilePosition = 0; + ULONG rframeNumber = 0; + ULONG rframeLength = 0; + + if (!x.recplayer) + { + log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!"); + } + else + { + success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength); + } + + // returns file position, frame number, length + + if (success) + { + resp->addULLONG(rfilePosition); + resp->addULONG(rframeNumber); + resp->addULONG(rframeLength); + } + else + { + resp->addULONG(0); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength); + return 1; +} + +int VompClientRRProc::processGetChannelSchedule() +{ + ULONG* data = (ULONG*)req->data; + + ULONG channelNumber = ntohl(*data); + data++; + ULONG startTime = ntohl(*data); + data++; + ULONG duration = ntohl(*data); + + log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber); + + cChannel* channel = x.channelFromNumber(channelNumber); + if (!channel) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL"); + return 1; + } + + log->log("RRProc", Log::DEBUG, "Got channel"); + +#if VDRVERSNUM < 10300 + cMutexLock MutexLock; + const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); +#else + cSchedulesLock MutexLock; + const cSchedules *Schedules = cSchedules::Schedules(MutexLock); +#endif + if (!Schedules) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL"); + return 1; + } + + log->log("RRProc", Log::DEBUG, "Got schedule!s! object"); + + const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); + if (!Schedule) + { + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL"); + return 1; + } + + log->log("RRProc", Log::DEBUG, "Got schedule object"); + + const char* empty = ""; + bool atLeastOneEvent = false; + + ULONG thisEventID; + ULONG thisEventTime; + ULONG thisEventDuration; + const char* thisEventTitle; + const char* thisEventSubTitle; + const char* thisEventDescription; + +#if VDRVERSNUM < 10300 + + const cEventInfo *event; + for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++) + { + event = Schedule->GetEventNumber(eventNumber); + + thisEventID = event->GetEventID(); + thisEventTime = event->GetTime(); + thisEventDuration = event->GetDuration(); + thisEventTitle = event->GetTitle(); + thisEventSubTitle = event->GetSubtitle(); + thisEventDescription = event->GetExtendedDescription(); + +#else + + for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event)) + { + thisEventID = event->EventID(); + thisEventTime = event->StartTime(); + thisEventDuration = event->Duration(); + thisEventTitle = event->Title(); + thisEventSubTitle = NULL; + thisEventDescription = event->Description(); + +#endif + + //in the past filter + if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue; + + //start time filter + if ((thisEventTime + thisEventDuration) <= startTime) continue; + + //duration filter + if (thisEventTime >= (startTime + duration)) continue; + + if (!thisEventTitle) thisEventTitle = empty; + if (!thisEventSubTitle) thisEventSubTitle = empty; + if (!thisEventDescription) thisEventDescription = empty; + + resp->addULONG(thisEventID); + resp->addULONG(thisEventTime); + resp->addULONG(thisEventDuration); + + resp->addString(thisEventTitle); + resp->addString(thisEventSubTitle); + resp->addString(thisEventDescription); + + atLeastOneEvent = true; + } + + log->log("RRProc", Log::DEBUG, "Got all event data"); + + if (!atLeastOneEvent) + { + resp->addULONG(0); + log->log("RRProc", Log::DEBUG, "Written 0 because no data"); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "written schedules packet"); + + return 1; +} + +int VompClientRRProc::processGetTimers() +{ + cTimer *timer; + int numTimers = Timers.Count(); + + resp->addULONG(numTimers); + + for (int i = 0; i < numTimers; i++) + { + timer = Timers.Get(i); + +#if VDRVERSNUM < 10300 + resp->addULONG(timer->Active()); +#else + resp->addULONG(timer->HasFlags(tfActive)); +#endif + resp->addULONG(timer->Recording()); + resp->addULONG(timer->Pending()); + resp->addULONG(timer->Priority()); + resp->addULONG(timer->Lifetime()); + resp->addULONG(timer->Channel()->Number()); + resp->addULONG(timer->StartTime()); + resp->addULONG(timer->StopTime()); + resp->addULONG(timer->Day()); + resp->addULONG(timer->WeekDays()); + resp->addString(timer->File()); + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written timers list"); + + return 1; +} + +int VompClientRRProc::processSetTimer() +{ + char* timerString = new char[strlen((char*)req->data) + 1]; + strcpy(timerString, (char*)req->data); + +#if VDRVERSNUM < 10300 + + // If this is VDR 1.2 the date part of the timer string must be reduced + // to just DD rather than YYYY-MM-DD + + int s = 0; // source + int d = 0; // destination + int c = 0; // count + while(c != 2) // copy up to date section, including the second ':' + { + timerString[d] = req->data[s]; + if (req->data[s] == ':') c++; + ++s; + ++d; + } + // now it has copied up to the date section + c = 0; + while(c != 2) // waste YYYY-MM- + { + if (req->data[s] == '-') c++; + ++s; + } + // now source is at the DD + memcpy(&timerString[d], &req->data[s], req->dataLength - s); + d += req->dataLength - s; + timerString[d] = '\0'; + + log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:"); + log->log("RRProc", Log::DEBUG, "%s", timerString); + +#endif + + cTimer *timer = new cTimer; + if (timer->Parse((char*)timerString)) + { + cTimer *t = Timers.GetTimer(timer); + if (!t) + { + Timers.Add(timer); +#if VDRVERSNUM < 10300 + Timers.Save(); +#else + Timers.SetModified(); +#endif + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + else + { + resp->addULONG(1); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + } + } + else + { + resp->addULONG(2); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + } + delete timer; + return 1; +} + +int VompClientRRProc::processDeleteTimer() +{ + log->log("RRProc", Log::DEBUG, "Delete timer called"); + // get timer + + int position = 0; + + INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4; + INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4; + INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4; + INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4; + INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4; + + cTimer* ti = NULL; + for (ti = Timers.First(); ti; ti = Timers.Next(ti)) + { + if ( (ti->Channel()->Number() == delChannel) + && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay))) + && (ti->StartTime() == delStart) + && (ti->StopTime() == delStop) ) + break; + } + + if (!ti) + { + resp->addULONG(4); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + if (!Timers.BeingEdited()) + { + if (!ti->Recording()) + { + Timers.Del(ti); + Timers.SetModified(); + resp->addULONG(10); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + else + { + log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running"); + resp->addULONG(3); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + } + else + { + log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR"); + resp->addULONG(1); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } +} + +int VompClientRRProc::processGetRecInfo() +{ + // data is a pointer to the fileName string + + cRecordings Recordings; + Recordings.Load(); // probably have to do this + + cRecording *recording = Recordings.GetByName((char*)req->data); + + time_t timerStart = 0; + time_t timerStop = 0; + char* summary = NULL; + ULONG resumePoint = 0; + + if (!recording) + { + log->log("RRProc", Log::ERR, "GetRecInfo found no recording"); + resp->addULONG(0); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + return 1; + } + + /* Return packet: + 4 bytes: start time for timer + 4 bytes: end time for timer + 4 bytes: resume point + string: summary + 4 bytes: num components + { + 1 byte: stream + 1 byte: type + string: language + string: description + } + + */ + + // Get current timer + + cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); + if (rc) + { + timerStart = rc->Timer()->StartTime(); + timerStop = rc->Timer()->StopTime(); + log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop); + } + + resp->addULONG(timerStart); + resp->addULONG(timerStop); + + // Get resume point + + char* value = x.config.getValueString("ResumeData", (char*)req->data); + if (value) + { + resumePoint = strtoul(value, NULL, 10); + delete[] value; + } + log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint); + + resp->addULONG(resumePoint); + + + // Get summary + +#if VDRVERSNUM < 10300 + summary = (char*)recording->Summary(); +#else + const cRecordingInfo *Info = recording->Info(); + summary = (char*)Info->ShortText(); + if (isempty(summary)) summary = (char*)Info->Description(); +#endif + log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary); + if (summary) + { + resp->addString(summary); + } + else + { + resp->addString(""); + } + + // Get channels + +#if VDRVERSNUM < 10300 + + // Send 0 for numchannels - this signals the client this info is not available + resp->addULONG(0); + +#else + const cComponents* components = Info->Components(); + + log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components); + + if (!components) + { + resp->addULONG(0); + } + else + { + resp->addULONG(components->NumComponents()); + + tComponent* component; + for (int i = 0; i < components->NumComponents(); i++) + { + component = components->Component(i); + + log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description); + + resp->addUCHAR(component->stream); + resp->addUCHAR(component->type); + + if (component->language) + { + resp->addString(component->language); + } + else + { + resp->addString(""); + } + if (component->description) + { + resp->addString(component->description); + } + else + { + resp->addString(""); + } + } + } + +#endif + + // Done. send it + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written getrecinfo"); + + return 1; +} + + + + +// FIXME obselete + +int VompClientRRProc::processReScanRecording() +{ + if (!x.recplayer) + { + log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!"); + return 0; + } + + x.recplayer->scan(); + + resp->addULLONG(x.recplayer->getLengthBytes()); + resp->addULONG(x.recplayer->getLengthFrames()); + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client"); + return 1; +} + +// FIXME without client calling rescan, getblock wont work even tho more data is avail + +int VompClientRRProc::processGetMarks() +{ + // data is a pointer to the fileName string + + cMarks Marks; + cRecordings Recordings; + Recordings.Load(); // probably have to do this + + cRecording *recording = Recordings.GetByName((char*)req->data); + + log->log("RRProc", Log::DEBUG, "recording pointer %p", recording); + + if (recording) + { + Marks.Load(recording->FileName()); + if (Marks.Count()) + { + for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) + { + log->log("RRProc", Log::DEBUG, "found Mark %i", m->position); + + resp->addULONG(m->position); + } + } + else + { + log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark"); + resp->addULONG(0); + } + } + + resp->finalise(); + x.tcp.sendPacket(resp->getPtr(), resp->getLen()); + + log->log("RRProc", Log::DEBUG, "Written Marks list"); + + return 1; +} + +#endif // !VOMPSTANDALONE + + diff --git a/vompclientrrproc.h b/vompclientrrproc.h new file mode 100644 index 0000000..64305a5 --- /dev/null +++ b/vompclientrrproc.h @@ -0,0 +1,92 @@ +/* + Copyright 2008 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef VOMPCLIENTRRPROC_H +#define VOMPCLIENTRRPROC_H + +#include "thread.h" +#include "responsepacket.h" + +class VompClient; +class Log; + +class RequestPacket +{ + public: + RequestPacket(ULONG requestID, ULONG opcode, UCHAR* data, ULONG dataLength) + : requestID(requestID), opcode(opcode), data(data), dataLength(dataLength) {} + + ULONG requestID; + ULONG opcode; + UCHAR* data; + ULONG dataLength; +}; + +class VompClientRRProc : public Thread +{ + public: + VompClientRRProc(VompClient& x); + bool init(); + bool recvRequest(RequestPacket*); + + private: + bool processPacket(); + +#ifndef VOMPSTANDALONE + int processGetRecordingsList(); + int processDeleteRecording(); + int processMoveRecording(); + int processGetChannelsList(); + int processStartStreamingChannel(); + int processGetBlock(); + int processStopStreaming(); + int processStartStreamingRecording(); + int processGetChannelSchedule(); + int processGetTimers(); + int processSetTimer(); + int processPositionFromFrameNumber(); + int processFrameNumberFromPosition(); + int processGetIFrame(); + int processGetRecInfo(); + int processGetMarks(); + int processGetChannelPids(); + int processDeleteTimer(); + int processReScanRecording(); // FIXME obselete +#endif + int processLogin(); + int processConfigSave(); + int processConfigLoad(); + int processGetMediaList(); + int processGetPicture(); + int processGetImageBlock(); + int processGetLanguageList(); + int processGetLanguageContent(); + + void threadMethod(); + + VompClient& x; + RequestPacket* req; + ResponsePacket* resp; + + Log* log; +}; + +#endif + diff --git a/vompserver.c b/vompserver.c index 236f568..ed88170 100644 --- a/vompserver.c +++ b/vompserver.c @@ -25,7 +25,7 @@ #include #include "mvpserver.h" -#include "mvpclient.h" +//#include "vompclient.h" static const char *VERSION = "0.2.7"; static const char *DESCRIPTION = "VDR on MVP plugin by Chris Tallon"; @@ -141,7 +141,7 @@ bool cPluginVompserver::SetupParse(const char *Name, const char *Value) cString cPluginVompserver::Active(void) { - if(MVPClient::getNrClients() != 0) return tr("VOMP client(s) connected"); + if(VompClient::getNrClients() != 0) return tr("VOMP client(s) connected"); return NULL; } -- 2.39.2