2 Copyright 2004-2005 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "mvpclient.h"
23 // This is here else it causes compile errors with something in libdvbmpeg
26 MVPClient::MVPClient(char* tconfigDirExtra, int tsocket)
31 recordingManager = NULL;
32 log = Log::getInstance();
34 configDirExtra = tconfigDirExtra;
37 MVPClient::~MVPClient()
39 log->log("Client", Log::DEBUG, "MVP client destructor");
50 delete recordingManager;
52 recordingManager = NULL;
55 if (loggedIn) cleanConfig();
58 ULLONG MVPClient::ntohll(ULLONG a)
63 ULLONG MVPClient::htonll(ULLONG a)
65 #if BYTE_ORDER == BIG_ENDIAN
70 b = ((a << 56) & 0xFF00000000000000ULL)
71 | ((a << 40) & 0x00FF000000000000ULL)
72 | ((a << 24) & 0x0000FF0000000000ULL)
73 | ((a << 8) & 0x000000FF00000000ULL)
74 | ((a >> 8) & 0x00000000FF000000ULL)
75 | ((a >> 24) & 0x0000000000FF0000ULL)
76 | ((a >> 40) & 0x000000000000FF00ULL)
77 | ((a >> 56) & 0x00000000000000FFULL) ;
83 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
85 cChannel* channel = NULL;
87 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
89 if (!channel->GroupSep())
91 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
93 if (channel->Number() == (int)channelNumber)
95 int vpid = channel->Vpid();
96 #if VDRVERSNUM < 10300
97 int apid1 = channel->Apid1();
99 int apid1 = channel->Apid(0);
101 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
109 log->log("Client", Log::DEBUG, "Channel not found");
115 void MVPClient::writeResumeData()
117 config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
120 void MVPClient::sendULONG(ULONG ul)
123 *(ULONG*)&sendBuffer[0] = htonl(4);
124 *(ULONG*)&sendBuffer[4] = htonl(ul);
126 tcp.sendPacket(sendBuffer, 8);
127 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
130 void MVPClientStartThread(void* arg)
132 MVPClient* m = (MVPClient*)arg;
134 // Nothing external to this class has a reference to it
135 // This is the end of the thread.. so delete m
142 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
143 log->log("Client", Log::DEBUG, "MVPClient run success");
147 void MVPClient::run2()
152 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
153 pthread_detach(runThread); // Detach
155 tcp.disableReadTimeout();
157 tcp.setSoKeepTime(3);
158 tcp.setNonBlocking();
168 log->log("Client", Log::DEBUG, "Waiting");
169 buffer = (UCHAR*)tcp.receivePacket();
170 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
173 log->log("Client", Log::DEBUG, "Detected connection closed");
177 packetLength = tcp.getDataLength() - 4;
178 opcode = ntohl(*(ULONG*)buffer);
181 if (!loggedIn && (opcode != 1))
187 log->log("Client", Log::DEBUG, "SwitchOp");
191 result = processLogin(data, packetLength);
194 result = processGetRecordingsList(data, packetLength);
197 result = processDeleteRecording(data, packetLength);
200 result = processGetSummary(data, packetLength);
203 result = processGetChannelsList(data, packetLength);
206 result = processStartStreamingChannel(data, packetLength);
209 result = processGetBlock(data, packetLength);
212 result = processStopStreaming(data, packetLength);
215 result = processStartStreamingRecording(data, packetLength);
218 result = processGetChannelSchedule(data, packetLength);
221 result = processConfigSave(data, packetLength);
224 result = processConfigLoad(data, packetLength);
227 result = processReScanRecording(data, packetLength);
230 result = processGetTimers(data, packetLength);
233 result = processSetTimer(data, packetLength);
236 result = processPositionFromFrameNumber(data, packetLength);
239 result = processFrameNumberFromPosition(data, packetLength);
242 result = processMoveRecording(data, packetLength);
251 int MVPClient::processLogin(UCHAR* buffer, int length)
253 if (length != 6) return 0;
257 const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
260 log->log("Client", Log::DEBUG, "No config dir!");
264 char configFileName[PATH_MAX];
265 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]);
266 config.init(configFileName);
268 // Send the login reply
270 time_t timeNow = time(NULL);
271 struct tm* timeStruct = localtime(&timeNow);
272 int timeOffset = timeStruct->tm_gmtoff;
274 UCHAR sendBuffer[12];
275 *(ULONG*)&sendBuffer[0] = htonl(8);
276 *(ULONG*)&sendBuffer[4] = htonl(timeNow);
277 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
279 tcp.sendPacket(sendBuffer, 12);
280 log->log("Client", Log::DEBUG, "written login reply");
286 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
288 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
289 int count = 4; // leave space for the packet length
294 int Percent = VideoDiskSpace(&FreeMB);
295 int Total = (FreeMB / (100 - Percent)) * 100;
297 *(ULONG*)&sendBuffer[count] = htonl(Total);
298 count += sizeof(ULONG);
299 *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
300 count += sizeof(ULONG);
301 *(ULONG*)&sendBuffer[count] = htonl(Percent);
302 count += sizeof(ULONG);
305 cRecordings Recordings;
308 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
310 if (count > 49000) break; // just how big is that hard disk?!
311 *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
314 point = (char*)recording->Name();
315 strcpy((char*)&sendBuffer[count], point);
316 count += strlen(point) + 1;
318 point = (char*)recording->FileName();
319 strcpy((char*)&sendBuffer[count], point);
320 count += strlen(point) + 1;
323 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
325 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
327 tcp.sendPacket(sendBuffer, count);
329 log->log("Client", Log::DEBUG, "Written list");
334 int MVPClient::processDeleteRecording(UCHAR* data, int length)
336 // data is a pointer to the fileName string
338 cRecordings Recordings;
339 Recordings.Load(); // probably have to do this
341 cRecording* recording = Recordings.GetByName((char*)data);
343 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
347 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
349 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
352 if (recording->Delete())
354 // Copy svdrp's way of doing this, see if it works
355 #if VDRVERSNUM > 10300
356 ::Recordings.DelByName(recording->FileName());
378 int MVPClient::processMoveRecording(UCHAR* data, int length)
380 log->log("Client", Log::DEBUG, "Process move recording");
381 char* fileName = (char*)data;
382 char* newPath = NULL;
384 for (int k = 0; k < length; k++)
388 newPath = (char*)&data[k+1];
392 if (!newPath) return 0;
394 cRecordings Recordings;
395 Recordings.Load(); // probably have to do this
397 cRecording* recording = Recordings.GetByName((char*)fileName);
399 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
403 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
406 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
407 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
408 log->log("Client", Log::DEBUG, "to: %s", newPath);
410 const char* t = recording->FileName();
412 char* dateDirName = NULL; int k;
413 char* titleDirName = NULL; int j;
415 // Find the datedirname
416 for(k = strlen(t) - 1; k >= 0; k--)
420 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
421 dateDirName = new char[strlen(&t[k+1]) + 1];
422 strcpy(dateDirName, &t[k+1]);
427 // Find the titledirname
429 for(j = k-1; j >= 0; j--)
433 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
434 titleDirName = new char[(k - j - 1) + 1];
435 memcpy(titleDirName, &t[j+1], k - j - 1);
436 titleDirName[k - j - 1] = '\0';
441 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
442 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
444 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
446 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
447 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
448 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
450 // FIXME Check whether this already exists before mkdiring it
452 log->log("Client", Log::DEBUG, "%s", newContainer);
456 int statret = stat(newContainer, &dstat);
457 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
459 log->log("Client", Log::DEBUG, "new dir does not exist");
460 int mkdirret = mkdir(newContainer, 0755);
463 delete[] dateDirName;
464 delete[] titleDirName;
465 delete[] newContainer;
470 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
472 delete[] dateDirName;
473 delete[] titleDirName;
474 delete[] newContainer;
479 // Ok, the directory container has been made, or it pre-existed.
481 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
482 sprintf(newDir, "%s/%s", newContainer, dateDirName);
484 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
485 int renameret = rename(t, newDir);
488 // Success. Test for remove old dir containter
489 char* oldTitleDir = new char[k+1];
490 memcpy(oldTitleDir, t, k);
491 oldTitleDir[k] = '\0';
492 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
493 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
494 delete[] oldTitleDir;
500 ::Recordings.Update();
502 // Success. Send a different packet from just a ulong
503 int totalLength = 4 + 4 + strlen(newDir) + 1;
504 UCHAR* sendBuffer = new UCHAR[totalLength];
505 *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
506 *(ULONG*)&sendBuffer[4] = htonl(1); // success
507 strcpy((char*)&sendBuffer[8], newDir);
508 tcp.sendPacket(sendBuffer, totalLength);
516 delete[] dateDirName;
517 delete[] titleDirName;
518 delete[] newContainer;
534 int MVPClient::processGetSummary(UCHAR* data, int length)
536 // data is a pointer to the fileName string
538 cRecordings Recordings;
539 Recordings.Load(); // probably have to do this
541 cRecording *recording = Recordings.GetByName((char*)data);
543 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
547 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
548 int count = 4; // leave space for the packet length
551 #if VDRVERSNUM < 10300
552 point = (char*)recording->Summary();
554 const cRecordingInfo *Info = recording->Info();
555 point = (char*)Info->ShortText();
556 log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
559 point = (char*)Info->Description();
560 log->log("Client", Log::DEBUG, "description pointer %p", point);
566 strcpy((char*)&sendBuffer[count], point);
567 count += strlen(point) + 1;
571 strcpy((char*)&sendBuffer[count], "");
575 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
577 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
579 tcp.sendPacket(sendBuffer, count);
581 log->log("Client", Log::DEBUG, "Written summary");
593 int MVPClient::processGetChannelsList(UCHAR* data, int length)
595 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
596 int count = 4; // leave space for the packet length
600 char* chanConfig = config.getValueString("General", "Channels");
602 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
604 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
606 #if VDRVERSNUM < 10300
607 if (!channel->GroupSep() && (!channel->Ca() || allChans))
609 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
612 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
614 if (channel->Vpid()) type = 1;
615 #if VDRVERSNUM < 10300
618 else if (channel->Apid(0)) type = 2;
622 if (count > 49000) break;
623 *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
626 *(ULONG*)&sendBuffer[count] = htonl(type);
629 point = (char*)channel->Name();
630 strcpy((char*)&sendBuffer[count], point);
631 count += strlen(point) + 1;
635 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
637 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
639 tcp.sendPacket(sendBuffer, count);
641 log->log("Client", Log::DEBUG, "Written channels list");
646 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
648 log->log("Client", Log::DEBUG, "length = %i", length);
649 ULONG channelNumber = ntohl(*(ULONG*)data);
651 cChannel* channel = channelFromNumber(channelNumber);
658 // get the priority we should use
660 int priority = config.getValueLong("General", "Live priority", &fail);
663 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
667 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
672 if (priority < 0) priority = 0;
673 if (priority > 99) priority = 99;
675 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
676 lp = MVPReceiver::create(channel, priority);
696 int MVPClient::processStopStreaming(UCHAR* data, int length)
698 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
709 delete recordingManager;
711 recordingManager = NULL;
718 int MVPClient::processGetBlock(UCHAR* data, int length)
722 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
726 ULLONG position = ntohll(*(ULLONG*)data);
727 data += sizeof(ULLONG);
728 ULONG amount = ntohl(*(ULONG*)data);
730 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
732 UCHAR sendBuffer[amount + 4];
733 ULONG amountReceived = 0; // compiler moan.
736 log->log("Client", Log::DEBUG, "getting from live");
737 amountReceived = lp->getBlock(&sendBuffer[4], amount);
741 // vdr has possibly disconnected the receiver
742 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
749 log->log("Client", Log::DEBUG, "getting from recording");
750 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
756 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
760 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
761 tcp.sendPacket(sendBuffer, amountReceived + 4);
762 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
768 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
770 // data is a pointer to the fileName string
772 recordingManager = new cRecordings;
773 recordingManager->Load();
775 cRecording* recording = recordingManager->GetByName((char*)data);
777 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
781 rp = new RecPlayer(recording);
783 UCHAR sendBuffer[16];
784 *(ULONG*)&sendBuffer[0] = htonl(12);
785 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
786 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
788 tcp.sendPacket(sendBuffer, 16);
789 log->log("Client", Log::DEBUG, "written totalLength");
793 delete recordingManager;
794 recordingManager = NULL;
799 int MVPClient::processReScanRecording(UCHAR* data, int length)
803 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
809 UCHAR sendBuffer[16];
810 *(ULONG*)&sendBuffer[0] = htonl(12);
811 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
812 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
814 tcp.sendPacket(sendBuffer, 16);
815 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
819 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
823 ULONG frameNumber = ntohl(*(ULONG*)data);
828 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
832 retval = rp->positionFromFrameNumber(frameNumber);
835 UCHAR sendBuffer[12];
836 *(ULONG*)&sendBuffer[0] = htonl(8);
837 *(ULLONG*)&sendBuffer[4] = htonll(retval);
839 tcp.sendPacket(sendBuffer, 12);
840 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
844 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
848 ULLONG position = ntohll(*(ULLONG*)data);
853 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
857 retval = rp->frameNumberFromPosition(position);
861 *(ULONG*)&sendBuffer[0] = htonl(4);
862 *(ULONG*)&sendBuffer[4] = htonl(retval);
864 tcp.sendPacket(sendBuffer, 8);
865 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
869 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
871 ULONG channelNumber = ntohl(*(ULONG*)data);
873 ULONG startTime = ntohl(*(ULONG*)data);
875 ULONG duration = ntohl(*(ULONG*)data);
877 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
879 cChannel* channel = channelFromNumber(channelNumber);
883 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
887 log->log("Client", Log::DEBUG, "Got channel");
889 #if VDRVERSNUM < 10300
890 cMutexLock MutexLock;
891 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
893 cSchedulesLock MutexLock;
894 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
899 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
903 log->log("Client", Log::DEBUG, "Got schedule!s! object");
905 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
909 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
913 log->log("Client", Log::DEBUG, "Got schedule object");
915 UCHAR* sendBuffer = (UCHAR*)malloc(100000);
916 ULONG sendBufferLength = 100000;
917 ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
921 // assign all the event info to temp vars then we know exactly what size they are
924 ULONG thisEventDuration;
925 const char* thisEventTitle;
926 const char* thisEventSubTitle;
927 const char* thisEventDescription;
929 ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
930 ULONG thisEventLength;
932 #if VDRVERSNUM < 10300
934 const cEventInfo *event;
935 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
937 event = Schedule->GetEventNumber(eventNumber);
939 thisEventID = event->GetEventID();
940 thisEventTime = event->GetTime();
941 thisEventDuration = event->GetDuration();
942 thisEventTitle = event->GetTitle();
943 thisEventSubTitle = event->GetSubtitle();
944 thisEventDescription = event->GetExtendedDescription();
948 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
950 thisEventID = event->EventID();
951 thisEventTime = event->StartTime();
952 thisEventDuration = event->Duration();
953 thisEventTitle = event->Title();
954 thisEventSubTitle = NULL;
955 thisEventDescription = event->Description();
959 log->log("Client", Log::DEBUG, "Got an event object %p", event);
962 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
965 if ((thisEventTime + thisEventDuration) <= startTime) continue;
968 if (thisEventTime >= (startTime + duration)) continue;
970 if (!thisEventTitle) thisEventTitle = empty;
971 if (!thisEventSubTitle) thisEventSubTitle = empty;
972 if (!thisEventDescription) thisEventDescription = empty;
974 thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
976 log->log("Client", Log::DEBUG, "Done s1");
978 // now extend the buffer if necessary
979 if ((sendBufferUsed + thisEventLength) > sendBufferLength)
981 log->log("Client", Log::DEBUG, "Extending buffer");
982 sendBufferLength += 100000;
983 UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
987 UCHAR sendBuffer2[8];
988 *(ULONG*)&sendBuffer2[0] = htonl(4);
989 *(ULONG*)&sendBuffer2[4] = htonl(0);
990 tcp.sendPacket(sendBuffer2, 8);
991 log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
997 log->log("Client", Log::DEBUG, "Done s2");
999 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID); sendBufferUsed += sizeof(ULONG);
1000 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime); sendBufferUsed += sizeof(ULONG);
1001 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1003 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle); sendBufferUsed += strlen(thisEventTitle) + 1;
1004 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle); sendBufferUsed += strlen(thisEventSubTitle) + 1;
1005 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1007 log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1010 log->log("Client", Log::DEBUG, "Got all event data");
1012 if (sendBufferUsed == sizeof(ULONG))
1016 log->log("Client", Log::DEBUG, "Written 0 because no data");
1020 // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1021 *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1022 tcp.sendPacket(sendBuffer, sendBufferUsed);
1023 log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1031 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1033 char* section = (char*)buffer;
1037 for (int k = 0; k < length; k++)
1039 if (buffer[k] == '\0')
1043 key = (char*)&buffer[k+1];
1047 value = (char*)&buffer[k+1];
1053 // if the last string (value) doesnt have null terminator, give up
1054 if (buffer[length - 1] != '\0') return 0;
1056 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1057 if (config.setValueString(section, key, value))
1069 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1071 char* section = (char*)buffer;
1074 for (int k = 0; k < length; k++)
1076 if (buffer[k] == '\0')
1078 key = (char*)&buffer[k+1];
1083 char* value = config.getValueString(section, key);
1087 UCHAR sendBuffer[4 + strlen(value) + 1];
1088 *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1089 strcpy((char*)&sendBuffer[4], value);
1090 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1092 log->log("Client", Log::DEBUG, "Written config load packet");
1097 UCHAR sendBuffer[8];
1098 *(ULONG*)&sendBuffer[0] = htonl(4);
1099 *(ULONG*)&sendBuffer[4] = htonl(0);
1100 tcp.sendPacket(sendBuffer, 8);
1102 log->log("Client", Log::DEBUG, "Written config load failed packet");
1108 void MVPClient::cleanConfig()
1110 log->log("Client", Log::DEBUG, "Clean config");
1112 cRecordings Recordings;
1117 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1118 char* position = resumes;
1119 for(int k = 0; k < numReturns; k++)
1121 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1123 cRecording* recording = Recordings.GetByName(position);
1126 // doesn't exist anymore
1127 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1128 config.deleteValue("ResumeData", position);
1132 log->log("Client", Log::DEBUG, "This recording still exists");
1135 position += strlen(position) + 1;
1147 event = Schedule->GetPresentEvent();
1149 fprintf(f, "\n\nCurrent event\n\n");
1151 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1152 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1153 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1154 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1156 event = Schedule->GetFollowingEvent();
1158 fprintf(f, "\n\nFollowing event\n\n");
1160 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1161 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1162 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1163 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1169 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1170 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1171 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1172 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1180 void MVPClient::test2()
1182 FILE* f = fopen("/tmp/s.txt", "w");
1184 #if VDRVERSNUM < 10300
1185 cMutexLock MutexLock;
1186 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1188 cSchedulesLock MutexLock;
1189 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1194 fprintf(f, "Schedules = NULL\n");
1199 fprintf(f, "Schedules dump:\n");
1203 const cSchedule *Schedule;
1204 int scheduleNumber = 0;
1207 cChannel *thisChannel;
1209 #if VDRVERSNUM < 10300
1210 const cEventInfo *event;
1211 int eventNumber = 0;
1213 const cEvent *event;
1216 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1217 // Schedule = Schedules->GetSchedule();
1218 Schedule = Schedules->First();
1221 fprintf(f, "First Schedule = NULL\n");
1228 fprintf(f, "Schedule #%i\n", scheduleNumber);
1229 fprintf(f, "-------------\n\n");
1231 #if VDRVERSNUM < 10300
1232 tchid = Schedule->GetChannelID();
1234 tchid = Schedule->ChannelID();
1237 #if VDRVERSNUM < 10300
1238 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1239 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1241 // put the count at the end.
1244 thisChannel = Channels.GetByChannelID(tchid, true);
1247 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1251 fprintf(f, "thisChannel = NULL for tchid\n");
1254 #if VDRVERSNUM < 10300
1255 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1257 event = Schedule->GetEventNumber(eventNumber);
1258 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1259 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1260 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1261 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1262 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1263 fprintf(f, "Event %u dump:\n", eventNumber);
1268 // This whole section needs rewriting to walk the list.
1269 event = Schedule->Events()->First();
1271 event = Schedule->Events()->Next(event);
1276 fprintf(f, "\nDump from object:\n");
1278 fprintf(f, "\nEND\n");
1288 fprintf(f, "End of current Schedule\n\n\n");
1290 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1304 const cEventInfo *GetPresentEvent(void) const;
1305 const cEventInfo *GetFollowingEvent(void) const;
1306 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1307 const cEventInfo *GetEventAround(time_t tTime) const;
1308 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1311 const unsigned char GetTableID(void) const;
1312 const char *GetTimeString(void) const;
1313 const char *GetEndTimeString(void) const;
1314 const char *GetDate(void) const;
1315 bool IsFollowing(void) const;
1316 bool IsPresent(void) const;
1317 const char *GetExtendedDescription(void) const;
1318 const char *GetSubtitle(void) const;
1319 const char *GetTitle(void) const;
1320 unsigned short GetEventID(void) const;
1321 long GetDuration(void) const;
1322 time_t GetTime(void) const;
1323 tChannelID GetChannelID(void) const;
1324 int GetChannelNumber(void) const { return nChannelNumber; }
1325 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1326 void Dump(FILE *f, const char *Prefix = "") const;
1332 void MVPClient::test(int channelNumber)
1334 FILE* f = fopen("/tmp/test.txt", "w");
1336 cMutexLock MutexLock;
1337 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1341 fprintf(f, "Schedules = NULL\n");
1346 fprintf(f, "Schedules dump:\n");
1347 // Schedules->Dump(f);
1349 const cSchedule *Schedule;
1350 cChannel *thisChannel;
1351 const cEventInfo *event;
1353 thisChannel = channelFromNumber(channelNumber);
1356 fprintf(f, "thisChannel = NULL\n");
1361 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1362 // Schedule = Schedules->GetSchedule();
1363 // Schedule = Schedules->First();
1366 fprintf(f, "First Schedule = NULL\n");
1371 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1373 // For some channels VDR seems to pick a random point in time to
1374 // start dishing out events, but they are in order
1375 // at some point in the list the time snaps to the current event
1380 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1382 event = Schedule->GetEventNumber(eventNumber);
1383 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1384 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1385 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1389 fprintf(f, "\nEND\n");
1403 Schedules = the collection of all the Schedule objects
1404 Schedule = One schedule, contants all the events for a channel
1405 Event = One programme
1414 Subtitle (used for "Programmes resume at ...")
1417 IsPresent ? easy to work out tho. Oh it doesn't always work
1422 void MVPClient::test2()
1424 log->log("-", Log::DEBUG, "Timers List");
1426 for (int i = 0; i < Timers.Count(); i++)
1428 cTimer *timer = Timers.Get(i);
1429 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1430 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1431 #if VDRVERSNUM < 10300
1432 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());
1434 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());
1436 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1437 log->log("-", Log::DEBUG, "");
1440 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
1441 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1442 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1447 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1448 recording is a bool, 0 for not currently recording, 1 for currently recording
1449 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
1453 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1455 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1456 int count = 4; // leave space for the packet length
1458 const char* fileName;
1460 int numTimers = Timers.Count();
1462 *(ULONG*)&sendBuffer[count] = htonl(numTimers); count += 4;
1464 for (int i = 0; i < numTimers; i++)
1466 if (count > 49000) break;
1468 timer = Timers.Get(i);
1470 #if VDRVERSNUM < 10300
1471 *(ULONG*)&sendBuffer[count] = htonl(timer->Active()); count += 4;
1473 *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive)); count += 4;
1475 *(ULONG*)&sendBuffer[count] = htonl(timer->Recording()); count += 4;
1476 *(ULONG*)&sendBuffer[count] = htonl(timer->Pending()); count += 4;
1477 *(ULONG*)&sendBuffer[count] = htonl(timer->Priority()); count += 4;
1478 *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime()); count += 4;
1479 *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number()); count += 4;
1480 *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime()); count += 4;
1481 *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime()); count += 4;
1483 fileName = timer->File();
1484 strcpy((char*)&sendBuffer[count], fileName);
1485 count += strlen(fileName) + 1;
1488 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1490 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1492 tcp.sendPacket(sendBuffer, count);
1493 delete[] sendBuffer;
1494 log->log("Client", Log::DEBUG, "Written timers list");
1499 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1501 char* timerString = new char[strlen((char*)buffer) + 1];
1502 strcpy(timerString, (char*)buffer);
1504 #if VDRVERSNUM < 10300
1506 // If this is VDR 1.2 the date part of the timer string must be reduced
1507 // to just DD rather than YYYY-MM-DD
1509 int s = 0; // source
1510 int d = 0; // destination
1512 while(c != 2) // copy up to date section, including the second ':'
1514 timerString[d] = buffer[s];
1515 if (buffer[s] == ':') c++;
1519 // now it has copied up to the date section
1521 while(c != 2) // waste YYYY-MM-
1523 if (buffer[s] == '-') c++;
1526 // now source is at the DD
1527 memcpy(&timerString[d], &buffer[s], length - s);
1529 timerString[d] = '\0';
1531 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1532 log->log("Client", Log::DEBUG, "%s", timerString);
1536 cTimer *timer = new cTimer;
1537 if (timer->Parse((char*)timerString))
1539 cTimer *t = Timers.GetTimer(timer);
1543 #if VDRVERSNUM < 10300
1546 Timers.SetModified();