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.setValueLong("ResumeData",
118 (char*)rp->getCurrentRecording()->FileName(),
119 rp->frameNumberFromPosition(rp->getLastPosition()) );
122 void MVPClient::sendULONG(ULONG ul)
125 *(ULONG*)&sendBuffer[0] = htonl(4);
126 *(ULONG*)&sendBuffer[4] = htonl(ul);
128 tcp.sendPacket(sendBuffer, 8);
129 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
132 void MVPClientStartThread(void* arg)
134 MVPClient* m = (MVPClient*)arg;
136 // Nothing external to this class has a reference to it
137 // This is the end of the thread.. so delete m
144 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
145 log->log("Client", Log::DEBUG, "MVPClient run success");
149 void MVPClient::run2()
154 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
155 pthread_detach(runThread); // Detach
157 tcp.disableReadTimeout();
159 tcp.setSoKeepTime(3);
160 tcp.setNonBlocking();
170 log->log("Client", Log::DEBUG, "Waiting");
171 buffer = (UCHAR*)tcp.receivePacket();
172 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
175 log->log("Client", Log::DEBUG, "Detected connection closed");
179 packetLength = tcp.getDataLength() - 4;
180 opcode = ntohl(*(ULONG*)buffer);
183 if (!loggedIn && (opcode != 1))
189 log->log("Client", Log::DEBUG, "SwitchOp");
193 result = processLogin(data, packetLength);
196 result = processGetRecordingsList(data, packetLength);
199 result = processDeleteRecording(data, packetLength);
202 result = processGetChannelsList(data, packetLength);
205 result = processStartStreamingChannel(data, packetLength);
208 result = processGetBlock(data, packetLength);
211 result = processStopStreaming(data, packetLength);
214 result = processStartStreamingRecording(data, packetLength);
217 result = processGetChannelSchedule(data, packetLength);
220 result = processConfigSave(data, packetLength);
223 result = processConfigLoad(data, packetLength);
226 result = processReScanRecording(data, packetLength); // FIXME obselete
229 result = processGetTimers(data, packetLength);
232 result = processSetTimer(data, packetLength);
235 result = processPositionFromFrameNumber(data, packetLength);
238 result = processFrameNumberFromPosition(data, packetLength);
241 result = processMoveRecording(data, packetLength);
244 result = processGetIFrame(data, packetLength);
247 result = processGetRecInfo(data, packetLength);
256 int MVPClient::processLogin(UCHAR* buffer, int length)
258 if (length != 6) return 0;
262 const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
265 log->log("Client", Log::DEBUG, "No config dir!");
269 char configFileName[PATH_MAX];
270 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]);
271 config.init(configFileName);
273 // Send the login reply
275 time_t timeNow = time(NULL);
276 struct tm* timeStruct = localtime(&timeNow);
277 int timeOffset = timeStruct->tm_gmtoff;
279 UCHAR sendBuffer[12];
280 *(ULONG*)&sendBuffer[0] = htonl(8);
281 *(ULONG*)&sendBuffer[4] = htonl(timeNow);
282 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
284 tcp.sendPacket(sendBuffer, 12);
285 log->log("Client", Log::DEBUG, "written login reply");
291 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
293 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
294 int count = 4; // leave space for the packet length
299 int Percent = VideoDiskSpace(&FreeMB);
300 int Total = (FreeMB / (100 - Percent)) * 100;
302 *(ULONG*)&sendBuffer[count] = htonl(Total);
303 count += sizeof(ULONG);
304 *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
305 count += sizeof(ULONG);
306 *(ULONG*)&sendBuffer[count] = htonl(Percent);
307 count += sizeof(ULONG);
310 cRecordings Recordings;
313 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
315 if (count > 49000) break; // just how big is that hard disk?!
316 *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
319 point = (char*)recording->Name();
320 strcpy((char*)&sendBuffer[count], point);
321 count += strlen(point) + 1;
323 point = (char*)recording->FileName();
324 strcpy((char*)&sendBuffer[count], point);
325 count += strlen(point) + 1;
328 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
330 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
332 tcp.sendPacket(sendBuffer, count);
334 log->log("Client", Log::DEBUG, "Written list");
339 int MVPClient::processDeleteRecording(UCHAR* data, int length)
341 // data is a pointer to the fileName string
343 cRecordings Recordings;
344 Recordings.Load(); // probably have to do this
346 cRecording* recording = Recordings.GetByName((char*)data);
348 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
352 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
354 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
357 if (recording->Delete())
359 // Copy svdrp's way of doing this, see if it works
360 #if VDRVERSNUM > 10300
361 ::Recordings.DelByName(recording->FileName());
383 int MVPClient::processMoveRecording(UCHAR* data, int length)
385 log->log("Client", Log::DEBUG, "Process move recording");
386 char* fileName = (char*)data;
387 char* newPath = NULL;
389 for (int k = 0; k < length; k++)
393 newPath = (char*)&data[k+1];
397 if (!newPath) return 0;
399 cRecordings Recordings;
400 Recordings.Load(); // probably have to do this
402 cRecording* recording = Recordings.GetByName((char*)fileName);
404 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
408 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
411 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
412 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
413 log->log("Client", Log::DEBUG, "to: %s", newPath);
415 const char* t = recording->FileName();
417 char* dateDirName = NULL; int k;
418 char* titleDirName = NULL; int j;
420 // Find the datedirname
421 for(k = strlen(t) - 1; k >= 0; k--)
425 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
426 dateDirName = new char[strlen(&t[k+1]) + 1];
427 strcpy(dateDirName, &t[k+1]);
432 // Find the titledirname
434 for(j = k-1; j >= 0; j--)
438 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
439 titleDirName = new char[(k - j - 1) + 1];
440 memcpy(titleDirName, &t[j+1], k - j - 1);
441 titleDirName[k - j - 1] = '\0';
446 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
447 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
449 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
451 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
452 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
453 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
455 // FIXME Check whether this already exists before mkdiring it
457 log->log("Client", Log::DEBUG, "%s", newContainer);
461 int statret = stat(newContainer, &dstat);
462 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
464 log->log("Client", Log::DEBUG, "new dir does not exist");
465 int mkdirret = mkdir(newContainer, 0755);
468 delete[] dateDirName;
469 delete[] titleDirName;
470 delete[] newContainer;
475 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
477 delete[] dateDirName;
478 delete[] titleDirName;
479 delete[] newContainer;
484 // Ok, the directory container has been made, or it pre-existed.
486 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
487 sprintf(newDir, "%s/%s", newContainer, dateDirName);
489 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
490 int renameret = rename(t, newDir);
493 // Success. Test for remove old dir containter
494 char* oldTitleDir = new char[k+1];
495 memcpy(oldTitleDir, t, k);
496 oldTitleDir[k] = '\0';
497 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
498 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
499 delete[] oldTitleDir;
504 #if VDRVERSNUM > 10311
506 ::Recordings.Update();
508 // Success. Send a different packet from just a ulong
509 int totalLength = 4 + 4 + strlen(newDir) + 1;
510 UCHAR* sendBuffer = new UCHAR[totalLength];
511 *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
512 *(ULONG*)&sendBuffer[4] = htonl(1); // success
513 strcpy((char*)&sendBuffer[8], newDir);
514 tcp.sendPacket(sendBuffer, totalLength);
522 delete[] dateDirName;
523 delete[] titleDirName;
524 delete[] newContainer;
540 int MVPClient::processGetChannelsList(UCHAR* data, int length)
542 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
543 int count = 4; // leave space for the packet length
547 char* chanConfig = config.getValueString("General", "Channels");
549 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
551 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
553 #if VDRVERSNUM < 10300
554 if (!channel->GroupSep() && (!channel->Ca() || allChans))
556 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
559 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
561 if (channel->Vpid()) type = 1;
562 #if VDRVERSNUM < 10300
565 else if (channel->Apid(0)) type = 2;
569 if (count > 49000) break;
570 *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
573 *(ULONG*)&sendBuffer[count] = htonl(type);
576 point = (char*)channel->Name();
577 strcpy((char*)&sendBuffer[count], point);
578 count += strlen(point) + 1;
582 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
584 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
586 tcp.sendPacket(sendBuffer, count);
588 log->log("Client", Log::DEBUG, "Written channels list");
593 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
595 log->log("Client", Log::DEBUG, "length = %i", length);
596 ULONG channelNumber = ntohl(*(ULONG*)data);
598 cChannel* channel = channelFromNumber(channelNumber);
605 // get the priority we should use
607 int priority = config.getValueLong("General", "Live priority", &fail);
610 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
614 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
619 if (priority < 0) priority = 0;
620 if (priority > 99) priority = 99;
622 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
623 lp = MVPReceiver::create(channel, priority);
643 int MVPClient::processStopStreaming(UCHAR* data, int length)
645 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
656 delete recordingManager;
658 recordingManager = NULL;
665 int MVPClient::processGetBlock(UCHAR* data, int length)
669 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
673 ULLONG position = ntohll(*(ULLONG*)data);
674 data += sizeof(ULLONG);
675 ULONG amount = ntohl(*(ULONG*)data);
677 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
679 UCHAR sendBuffer[amount + 4];
680 ULONG amountReceived = 0; // compiler moan.
683 log->log("Client", Log::DEBUG, "getting from live");
684 amountReceived = lp->getBlock(&sendBuffer[4], amount);
688 // vdr has possibly disconnected the receiver
689 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
696 log->log("Client", Log::DEBUG, "getting from recording");
697 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
703 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
707 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
708 tcp.sendPacket(sendBuffer, amountReceived + 4);
709 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
715 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
717 // data is a pointer to the fileName string
719 recordingManager = new cRecordings;
720 recordingManager->Load();
722 cRecording* recording = recordingManager->GetByName((char*)data);
724 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
728 rp = new RecPlayer(recording);
730 UCHAR sendBuffer[16];
731 *(ULONG*)&sendBuffer[0] = htonl(12);
732 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
733 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
735 tcp.sendPacket(sendBuffer, 16);
736 log->log("Client", Log::DEBUG, "written totalLength");
740 delete recordingManager;
741 recordingManager = NULL;
746 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
750 ULONG frameNumber = ntohl(*(ULONG*)data);
755 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
759 retval = rp->positionFromFrameNumber(frameNumber);
762 UCHAR sendBuffer[12];
763 *(ULONG*)&sendBuffer[0] = htonl(8);
764 *(ULLONG*)&sendBuffer[4] = htonll(retval);
766 tcp.sendPacket(sendBuffer, 12);
767 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
771 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
775 ULLONG position = ntohll(*(ULLONG*)data);
780 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
784 retval = rp->frameNumberFromPosition(position);
788 *(ULONG*)&sendBuffer[0] = htonl(4);
789 *(ULONG*)&sendBuffer[4] = htonl(retval);
791 tcp.sendPacket(sendBuffer, 8);
792 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
796 int MVPClient::processGetIFrame(UCHAR* data, int length)
798 bool success = false;
800 ULONG frameNumber = ntohl(*(ULONG*)data);
802 ULONG direction = ntohl(*(ULONG*)data);
805 ULLONG rfilePosition = 0;
806 ULONG rframeNumber = 0;
807 ULONG rframeLength = 0;
811 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
815 success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
818 // returns file position, frame number, length
820 UCHAR sendBuffer[20];
826 *(ULONG*)&sendBuffer[0] = htonl(16);
827 *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
828 *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
829 *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
834 *(ULONG*)&sendBuffer[0] = htonl(4);
835 *(ULONG*)&sendBuffer[4] = 0;
838 log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
840 tcp.sendPacket(sendBuffer, packetLength);
841 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
845 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
847 ULONG channelNumber = ntohl(*(ULONG*)data);
849 ULONG startTime = ntohl(*(ULONG*)data);
851 ULONG duration = ntohl(*(ULONG*)data);
853 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
855 cChannel* channel = channelFromNumber(channelNumber);
859 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
863 log->log("Client", Log::DEBUG, "Got channel");
865 #if VDRVERSNUM < 10300
866 cMutexLock MutexLock;
867 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
869 cSchedulesLock MutexLock;
870 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
875 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
879 log->log("Client", Log::DEBUG, "Got schedule!s! object");
881 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
885 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
889 log->log("Client", Log::DEBUG, "Got schedule object");
891 UCHAR* sendBuffer = (UCHAR*)malloc(100000);
892 ULONG sendBufferLength = 100000;
893 ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
897 // assign all the event info to temp vars then we know exactly what size they are
900 ULONG thisEventDuration;
901 const char* thisEventTitle;
902 const char* thisEventSubTitle;
903 const char* thisEventDescription;
905 ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
906 ULONG thisEventLength;
908 #if VDRVERSNUM < 10300
910 const cEventInfo *event;
911 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
913 event = Schedule->GetEventNumber(eventNumber);
915 thisEventID = event->GetEventID();
916 thisEventTime = event->GetTime();
917 thisEventDuration = event->GetDuration();
918 thisEventTitle = event->GetTitle();
919 thisEventSubTitle = event->GetSubtitle();
920 thisEventDescription = event->GetExtendedDescription();
924 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
926 thisEventID = event->EventID();
927 thisEventTime = event->StartTime();
928 thisEventDuration = event->Duration();
929 thisEventTitle = event->Title();
930 thisEventSubTitle = NULL;
931 thisEventDescription = event->Description();
935 log->log("Client", Log::DEBUG, "Got an event object %p", event);
938 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
941 if ((thisEventTime + thisEventDuration) <= startTime) continue;
944 if (thisEventTime >= (startTime + duration)) continue;
946 if (!thisEventTitle) thisEventTitle = empty;
947 if (!thisEventSubTitle) thisEventSubTitle = empty;
948 if (!thisEventDescription) thisEventDescription = empty;
950 thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
952 log->log("Client", Log::DEBUG, "Done s1");
954 // now extend the buffer if necessary
955 if ((sendBufferUsed + thisEventLength) > sendBufferLength)
957 log->log("Client", Log::DEBUG, "Extending buffer");
958 sendBufferLength += 100000;
959 UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
963 UCHAR sendBuffer2[8];
964 *(ULONG*)&sendBuffer2[0] = htonl(4);
965 *(ULONG*)&sendBuffer2[4] = htonl(0);
966 tcp.sendPacket(sendBuffer2, 8);
967 log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
973 log->log("Client", Log::DEBUG, "Done s2");
975 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID); sendBufferUsed += sizeof(ULONG);
976 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime); sendBufferUsed += sizeof(ULONG);
977 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
979 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle); sendBufferUsed += strlen(thisEventTitle) + 1;
980 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle); sendBufferUsed += strlen(thisEventSubTitle) + 1;
981 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
983 log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
986 log->log("Client", Log::DEBUG, "Got all event data");
988 if (sendBufferUsed == sizeof(ULONG))
992 log->log("Client", Log::DEBUG, "Written 0 because no data");
996 // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
997 *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
998 tcp.sendPacket(sendBuffer, sendBufferUsed);
999 log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1007 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1009 char* section = (char*)buffer;
1013 for (int k = 0; k < length; k++)
1015 if (buffer[k] == '\0')
1019 key = (char*)&buffer[k+1];
1023 value = (char*)&buffer[k+1];
1029 // if the last string (value) doesnt have null terminator, give up
1030 if (buffer[length - 1] != '\0') return 0;
1032 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1033 if (config.setValueString(section, key, value))
1045 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1047 char* section = (char*)buffer;
1050 for (int k = 0; k < length; k++)
1052 if (buffer[k] == '\0')
1054 key = (char*)&buffer[k+1];
1059 char* value = config.getValueString(section, key);
1063 UCHAR sendBuffer[4 + strlen(value) + 1];
1064 *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1065 strcpy((char*)&sendBuffer[4], value);
1066 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1068 log->log("Client", Log::DEBUG, "Written config load packet");
1073 UCHAR sendBuffer[8];
1074 *(ULONG*)&sendBuffer[0] = htonl(4);
1075 *(ULONG*)&sendBuffer[4] = htonl(0);
1076 tcp.sendPacket(sendBuffer, 8);
1078 log->log("Client", Log::DEBUG, "Written config load failed packet");
1084 void MVPClient::cleanConfig()
1086 log->log("Client", Log::DEBUG, "Clean config");
1088 cRecordings Recordings;
1093 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1094 char* position = resumes;
1095 for(int k = 0; k < numReturns; k++)
1097 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1099 cRecording* recording = Recordings.GetByName(position);
1102 // doesn't exist anymore
1103 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1104 config.deleteValue("ResumeData", position);
1108 log->log("Client", Log::DEBUG, "This recording still exists");
1111 position += strlen(position) + 1;
1123 event = Schedule->GetPresentEvent();
1125 fprintf(f, "\n\nCurrent event\n\n");
1127 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1128 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1129 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1130 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1132 event = Schedule->GetFollowingEvent();
1134 fprintf(f, "\n\nFollowing event\n\n");
1136 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1137 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1138 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1139 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1145 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1146 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1147 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1148 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1156 void MVPClient::test2()
1158 FILE* f = fopen("/tmp/s.txt", "w");
1160 #if VDRVERSNUM < 10300
1161 cMutexLock MutexLock;
1162 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1164 cSchedulesLock MutexLock;
1165 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1170 fprintf(f, "Schedules = NULL\n");
1175 fprintf(f, "Schedules dump:\n");
1179 const cSchedule *Schedule;
1180 int scheduleNumber = 0;
1183 cChannel *thisChannel;
1185 #if VDRVERSNUM < 10300
1186 const cEventInfo *event;
1187 int eventNumber = 0;
1189 const cEvent *event;
1192 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1193 // Schedule = Schedules->GetSchedule();
1194 Schedule = Schedules->First();
1197 fprintf(f, "First Schedule = NULL\n");
1204 fprintf(f, "Schedule #%i\n", scheduleNumber);
1205 fprintf(f, "-------------\n\n");
1207 #if VDRVERSNUM < 10300
1208 tchid = Schedule->GetChannelID();
1210 tchid = Schedule->ChannelID();
1213 #if VDRVERSNUM < 10300
1214 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1215 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1217 // put the count at the end.
1220 thisChannel = Channels.GetByChannelID(tchid, true);
1223 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1227 fprintf(f, "thisChannel = NULL for tchid\n");
1230 #if VDRVERSNUM < 10300
1231 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1233 event = Schedule->GetEventNumber(eventNumber);
1234 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1235 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1236 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1237 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1238 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1239 fprintf(f, "Event %u dump:\n", eventNumber);
1244 // This whole section needs rewriting to walk the list.
1245 event = Schedule->Events()->First();
1247 event = Schedule->Events()->Next(event);
1252 fprintf(f, "\nDump from object:\n");
1254 fprintf(f, "\nEND\n");
1264 fprintf(f, "End of current Schedule\n\n\n");
1266 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1280 const cEventInfo *GetPresentEvent(void) const;
1281 const cEventInfo *GetFollowingEvent(void) const;
1282 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1283 const cEventInfo *GetEventAround(time_t tTime) const;
1284 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1287 const unsigned char GetTableID(void) const;
1288 const char *GetTimeString(void) const;
1289 const char *GetEndTimeString(void) const;
1290 const char *GetDate(void) const;
1291 bool IsFollowing(void) const;
1292 bool IsPresent(void) const;
1293 const char *GetExtendedDescription(void) const;
1294 const char *GetSubtitle(void) const;
1295 const char *GetTitle(void) const;
1296 unsigned short GetEventID(void) const;
1297 long GetDuration(void) const;
1298 time_t GetTime(void) const;
1299 tChannelID GetChannelID(void) const;
1300 int GetChannelNumber(void) const { return nChannelNumber; }
1301 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1302 void Dump(FILE *f, const char *Prefix = "") const;
1308 void MVPClient::test(int channelNumber)
1310 FILE* f = fopen("/tmp/test.txt", "w");
1312 cMutexLock MutexLock;
1313 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1317 fprintf(f, "Schedules = NULL\n");
1322 fprintf(f, "Schedules dump:\n");
1323 // Schedules->Dump(f);
1325 const cSchedule *Schedule;
1326 cChannel *thisChannel;
1327 const cEventInfo *event;
1329 thisChannel = channelFromNumber(channelNumber);
1332 fprintf(f, "thisChannel = NULL\n");
1337 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1338 // Schedule = Schedules->GetSchedule();
1339 // Schedule = Schedules->First();
1342 fprintf(f, "First Schedule = NULL\n");
1347 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1349 // For some channels VDR seems to pick a random point in time to
1350 // start dishing out events, but they are in order
1351 // at some point in the list the time snaps to the current event
1356 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1358 event = Schedule->GetEventNumber(eventNumber);
1359 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1360 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1361 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1365 fprintf(f, "\nEND\n");
1379 Schedules = the collection of all the Schedule objects
1380 Schedule = One schedule, contants all the events for a channel
1381 Event = One programme
1390 Subtitle (used for "Programmes resume at ...")
1393 IsPresent ? easy to work out tho. Oh it doesn't always work
1398 void MVPClient::test2()
1400 log->log("-", Log::DEBUG, "Timers List");
1402 for (int i = 0; i < Timers.Count(); i++)
1404 cTimer *timer = Timers.Get(i);
1405 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1406 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1407 #if VDRVERSNUM < 10300
1408 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());
1410 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());
1412 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1413 log->log("-", Log::DEBUG, "");
1416 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
1417 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1418 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1423 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1424 recording is a bool, 0 for not currently recording, 1 for currently recording
1425 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
1429 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1431 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1432 int count = 4; // leave space for the packet length
1434 const char* fileName;
1436 int numTimers = Timers.Count();
1438 *(ULONG*)&sendBuffer[count] = htonl(numTimers); count += 4;
1440 for (int i = 0; i < numTimers; i++)
1442 if (count > 49000) break;
1444 timer = Timers.Get(i);
1446 #if VDRVERSNUM < 10300
1447 *(ULONG*)&sendBuffer[count] = htonl(timer->Active()); count += 4;
1449 *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive)); count += 4;
1451 *(ULONG*)&sendBuffer[count] = htonl(timer->Recording()); count += 4;
1452 *(ULONG*)&sendBuffer[count] = htonl(timer->Pending()); count += 4;
1453 *(ULONG*)&sendBuffer[count] = htonl(timer->Priority()); count += 4;
1454 *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime()); count += 4;
1455 *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number()); count += 4;
1456 *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime()); count += 4;
1457 *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime()); count += 4;
1459 fileName = timer->File();
1460 strcpy((char*)&sendBuffer[count], fileName);
1461 count += strlen(fileName) + 1;
1464 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1466 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1468 tcp.sendPacket(sendBuffer, count);
1469 delete[] sendBuffer;
1470 log->log("Client", Log::DEBUG, "Written timers list");
1475 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1477 char* timerString = new char[strlen((char*)buffer) + 1];
1478 strcpy(timerString, (char*)buffer);
1480 #if VDRVERSNUM < 10300
1482 // If this is VDR 1.2 the date part of the timer string must be reduced
1483 // to just DD rather than YYYY-MM-DD
1485 int s = 0; // source
1486 int d = 0; // destination
1488 while(c != 2) // copy up to date section, including the second ':'
1490 timerString[d] = buffer[s];
1491 if (buffer[s] == ':') c++;
1495 // now it has copied up to the date section
1497 while(c != 2) // waste YYYY-MM-
1499 if (buffer[s] == '-') c++;
1502 // now source is at the DD
1503 memcpy(&timerString[d], &buffer[s], length - s);
1505 timerString[d] = '\0';
1507 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1508 log->log("Client", Log::DEBUG, "%s", timerString);
1512 cTimer *timer = new cTimer;
1513 if (timer->Parse((char*)timerString))
1515 cTimer *t = Timers.GetTimer(timer);
1519 #if VDRVERSNUM < 10300
1522 Timers.SetModified();
1540 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1542 // data is a pointer to the fileName string
1544 cRecordings Recordings;
1545 Recordings.Load(); // probably have to do this
1547 cRecording *recording = Recordings.GetByName((char*)data);
1549 time_t timerStart = 0;
1550 time_t timerStop = 0;
1551 char* summary = NULL;
1552 ULONG resumePoint = 0;
1556 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1561 ULONG sendBufferSize = 10000;
1562 UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1563 ULONG pos = 4; // leave first 4 bytes for size field
1567 4 bytes: start time for timer
1568 4 bytes: end time for timer
1569 4 bytes: resume point
1571 4 bytes: num components
1581 // Get current timer
1583 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1586 timerStart = rc->Timer()->StartTime();
1587 timerStop = rc->Timer()->StopTime();
1588 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1591 *(time_t*)&sendBuffer[pos] = htonl(timerStart); pos += 4;
1592 *(time_t*)&sendBuffer[pos] = htonl(timerStop); pos += 4;
1596 char* value = config.getValueString("ResumeData", (char*)data);
1599 resumePoint = strtoul(value, NULL, 10);
1602 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1604 *(ULONG*)&sendBuffer[pos] = htonl(resumePoint); pos += 4;
1609 #if VDRVERSNUM < 10300
1610 summary = (char*)recording->Summary();
1612 const cRecordingInfo *Info = recording->Info();
1613 summary = (char*)Info->ShortText();
1614 if (isempty(summary)) summary = (char*)Info->Description();
1616 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1619 // memory insanity...
1620 if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1622 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1625 sendBuffer = newBuffer;
1626 sendBufferSize += strlen(summary) + 10000;
1636 strcpy((char*)&sendBuffer[pos], summary);
1637 pos += strlen(summary) + 1;
1641 strcpy((char*)&sendBuffer[pos], "");
1648 #if VDRVERSNUM < 10300
1650 // Send 0 for numchannels - this signals the client this info is not available
1651 *(ULONG*)&sendBuffer[pos] = 0; pos += 4;
1654 const cComponents* components = Info->Components();
1656 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1660 *(ULONG*)&sendBuffer[pos] = htonl(0); pos += 4;
1664 *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents()); pos += 4;
1666 tComponent* component;
1667 for (int i = 0; i < components->NumComponents(); i++)
1669 component = components->Component(i);
1671 // memory insanity...
1672 ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1673 + (component->description ? strlen(component->description) : 0) + 2;
1675 if ((sendBufferSize - pos) < extraNeeded)
1677 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1680 sendBuffer = newBuffer;
1681 sendBufferSize += extraNeeded + 10000;
1691 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1692 sendBuffer[pos] = component->stream; pos += 1;
1693 sendBuffer[pos] = component->type; pos += 1;
1694 if (component->language)
1696 strcpy((char*)&sendBuffer[pos], component->language);
1697 pos += strlen(component->language) + 1;
1701 strcpy((char*)&sendBuffer[pos], "");
1704 if (component->description)
1706 strcpy((char*)&sendBuffer[pos], component->description);
1707 pos += strlen(component->description) + 1;
1711 strcpy((char*)&sendBuffer[pos], "");
1722 *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 : take off the size field
1724 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1726 tcp.sendPacket(sendBuffer, pos);
1727 delete[] sendBuffer;
1728 log->log("Client", Log::DEBUG, "Written getrecinfo");
1738 int MVPClient::processReScanRecording(UCHAR* data, int length)
1742 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1748 UCHAR sendBuffer[16];
1749 *(ULONG*)&sendBuffer[0] = htonl(12);
1750 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1751 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1753 tcp.sendPacket(sendBuffer, 16);
1754 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1758 // FIXME without client calling rescan, getblock wont work even tho more data is avail