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(int tsocket)
31 recordingManager = NULL;
32 log = Log::getInstance();
36 MVPClient::~MVPClient()
38 log->log("Client", Log::DEBUG, "MVP client destructor");
49 delete recordingManager;
51 recordingManager = NULL;
54 if (loggedIn) cleanConfig();
57 ULLONG MVPClient::ntohll(ULLONG a)
62 ULLONG MVPClient::htonll(ULLONG a)
64 #if BYTE_ORDER == BIG_ENDIAN
69 b = ((a << 56) & 0xFF00000000000000ULL)
70 | ((a << 40) & 0x00FF000000000000ULL)
71 | ((a << 24) & 0x0000FF0000000000ULL)
72 | ((a << 8) & 0x000000FF00000000ULL)
73 | ((a >> 8) & 0x00000000FF000000ULL)
74 | ((a >> 24) & 0x0000000000FF0000ULL)
75 | ((a >> 40) & 0x000000000000FF00ULL)
76 | ((a >> 56) & 0x00000000000000FFULL) ;
82 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
84 cChannel* channel = NULL;
86 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
88 if (!channel->GroupSep())
90 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
92 if (channel->Number() == (int)channelNumber)
94 int vpid = channel->Vpid();
95 #if VDRVERSNUM < 10300
96 int apid1 = channel->Apid1();
98 int apid1 = channel->Apid(0);
100 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
108 log->log("Client", Log::DEBUG, "Channel not found");
114 void MVPClient::writeResumeData()
116 config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
119 void MVPClient::sendULONG(ULONG ul)
122 *(ULONG*)&sendBuffer[0] = htonl(4);
123 *(ULONG*)&sendBuffer[4] = htonl(ul);
125 tcp.sendPacket(sendBuffer, 8);
126 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
129 void MVPClientStartThread(void* arg)
131 MVPClient* m = (MVPClient*)arg;
133 // Nothing external to this class has a reference to it
134 // This is the end of the thread.. so delete m
141 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
142 log->log("Client", Log::DEBUG, "MVPClient run success");
146 void MVPClient::run2()
151 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
152 pthread_detach(runThread); // Detach
154 tcp.disableReadTimeout();
156 tcp.setSoKeepTime(3);
157 tcp.setNonBlocking();
167 log->log("Client", Log::DEBUG, "Waiting");
168 buffer = (UCHAR*)tcp.receivePacket();
169 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
172 log->log("Client", Log::DEBUG, "Detected connection closed");
176 packetLength = tcp.getDataLength() - 4;
177 opcode = ntohl(*(ULONG*)buffer);
180 if (!loggedIn && (opcode != 1))
189 result = processLogin(data, packetLength);
192 result = processGetRecordingsList(data, packetLength);
195 result = processDeleteRecording(data, packetLength);
198 result = processGetSummary(data, packetLength);
201 result = processGetChannelsList(data, packetLength);
204 result = processStartStreamingChannel(data, packetLength);
207 result = processGetBlock(data, packetLength);
210 result = processStopStreaming(data, packetLength);
213 result = processStartStreamingRecording(data, packetLength);
216 result = processGetChannelSchedule(data, packetLength);
219 result = processConfigSave(data, packetLength);
222 result = processConfigLoad(data, packetLength);
225 result = processReScanRecording(data, packetLength);
228 result = processGetTimers(data, packetLength);
231 result = processSetTimer(data, packetLength);
234 result = processPositionFromFrameNumber(data, packetLength);
243 int MVPClient::processLogin(UCHAR* buffer, int length)
245 if (length != 6) return 0;
249 const char* configDir = cPlugin::ConfigDirectory();
252 log->log("Client", Log::DEBUG, "No config dir!");
256 char configFileName[PATH_MAX];
257 snprintf(configFileName, PATH_MAX - strlen(configDir) - 17 - 20, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
258 //( ^^^^^^^^^^^^^eh?^^^^^^^^^^^^^)
259 config.init(configFileName);
261 // Send the login reply
263 time_t timeNow = time(NULL);
264 struct tm* timeStruct = localtime(&timeNow);
265 int timeOffset = timeStruct->tm_gmtoff;
267 UCHAR sendBuffer[12];
268 *(ULONG*)&sendBuffer[0] = htonl(8);
269 *(ULONG*)&sendBuffer[4] = htonl(timeNow);
270 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
272 tcp.sendPacket(sendBuffer, 12);
273 log->log("Client", Log::DEBUG, "written login reply");
279 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
281 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
282 int count = 4; // leave space for the packet length
287 int Percent = VideoDiskSpace(&FreeMB);
288 int Total = (FreeMB / (100 - Percent)) * 100;
290 *(ULONG*)&sendBuffer[count] = htonl(Total);
291 count += sizeof(ULONG);
292 *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
293 count += sizeof(ULONG);
294 *(ULONG*)&sendBuffer[count] = htonl(Percent);
295 count += sizeof(ULONG);
298 cRecordings Recordings;
301 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
303 if (count > 49000) break; // just how big is that hard disk?!
304 *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
307 point = (char*)recording->Name();
308 strcpy((char*)&sendBuffer[count], point);
309 count += strlen(point) + 1;
311 point = (char*)recording->FileName();
312 strcpy((char*)&sendBuffer[count], point);
313 count += strlen(point) + 1;
316 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
318 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
320 tcp.sendPacket(sendBuffer, count);
322 log->log("Client", Log::DEBUG, "Written list");
327 int MVPClient::processDeleteRecording(UCHAR* data, int length)
329 // data is a pointer to the fileName string
331 cRecordings Recordings;
332 Recordings.Load(); // probably have to do this
334 cRecording* recording = Recordings.GetByName((char*)data);
336 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
340 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
342 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
345 if (recording->Delete())
367 int MVPClient::processGetSummary(UCHAR* data, int length)
369 // data is a pointer to the fileName string
371 cRecordings Recordings;
372 Recordings.Load(); // probably have to do this
374 cRecording *recording = Recordings.GetByName((char*)data);
376 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
380 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
381 int count = 4; // leave space for the packet length
384 #if VDRVERSNUM < 10300
385 point = (char*)recording->Summary();
387 const cRecordingInfo *Info = recording->Info();
388 point = (char*)Info->ShortText();
389 log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
392 point = (char*)Info->Description();
393 log->log("Client", Log::DEBUG, "description pointer %p", point);
399 strcpy((char*)&sendBuffer[count], point);
400 count += strlen(point) + 1;
404 strcpy((char*)&sendBuffer[count], "");
408 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
410 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
412 tcp.sendPacket(sendBuffer, count);
414 log->log("Client", Log::DEBUG, "Written summary");
426 int MVPClient::processGetChannelsList(UCHAR* data, int length)
428 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
429 int count = 4; // leave space for the packet length
433 char* chanConfig = config.getValueString("General", "Channels");
435 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
437 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
439 #if VDRVERSNUM < 10300
440 if (!channel->GroupSep() && (!channel->Ca() || allChans))
442 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
445 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
447 if (channel->Vpid()) type = 1;
448 #if VDRVERSNUM < 10300
451 else if (channel->Apid(0)) type = 2;
455 if (count > 49000) break;
456 *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
459 *(ULONG*)&sendBuffer[count] = htonl(type);
462 point = (char*)channel->Name();
463 strcpy((char*)&sendBuffer[count], point);
464 count += strlen(point) + 1;
468 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
470 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
472 tcp.sendPacket(sendBuffer, count);
474 log->log("Client", Log::DEBUG, "Written channels list");
479 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
481 log->log("Client", Log::DEBUG, "length = %i", length);
482 ULONG channelNumber = ntohl(*(ULONG*)data);
484 cChannel* channel = channelFromNumber(channelNumber);
491 // get the priority we should use
493 int priority = config.getValueLong("General", "Live priority", &fail);
496 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
500 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
505 if (priority < 0) priority = 0;
506 if (priority > 99) priority = 99;
508 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
509 lp = MVPReceiver::create(channel, priority);
529 int MVPClient::processStopStreaming(UCHAR* data, int length)
531 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
542 delete recordingManager;
544 recordingManager = NULL;
551 int MVPClient::processGetBlock(UCHAR* data, int length)
555 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
559 ULLONG position = ntohll(*(ULLONG*)data);
560 data += sizeof(ULLONG);
561 ULONG amount = ntohl(*(ULONG*)data);
563 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
565 UCHAR sendBuffer[amount + 4];
566 ULONG amountReceived = 0; // compiler moan.
569 log->log("Client", Log::DEBUG, "getting from live");
570 amountReceived = lp->getBlock(&sendBuffer[4], amount);
574 // vdr has possibly disconnected the receiver
575 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
582 log->log("Client", Log::DEBUG, "getting from recording");
583 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
589 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
593 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
594 tcp.sendPacket(sendBuffer, amountReceived + 4);
595 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
601 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
603 // data is a pointer to the fileName string
605 recordingManager = new cRecordings;
606 recordingManager->Load();
608 cRecording* recording = recordingManager->GetByName((char*)data);
610 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
614 rp = new RecPlayer(recording);
616 UCHAR sendBuffer[12];
617 *(ULONG*)&sendBuffer[0] = htonl(8);
618 *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
620 tcp.sendPacket(sendBuffer, 12);
621 log->log("Client", Log::DEBUG, "written totalLength");
625 delete recordingManager;
626 recordingManager = NULL;
631 int MVPClient::processReScanRecording(UCHAR* data, int length)
637 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
642 retval = rp->getTotalLength();
645 UCHAR sendBuffer[12];
646 *(ULONG*)&sendBuffer[0] = htonl(8);
647 *(ULLONG*)&sendBuffer[4] = htonll(retval);
649 tcp.sendPacket(sendBuffer, 12);
650 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
654 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
658 ULONG frameNumber = ntohl(*(ULONG*)data);
663 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
667 retval = rp->positionFromFrameNumber(frameNumber);
670 UCHAR sendBuffer[12];
671 *(ULONG*)&sendBuffer[0] = htonl(8);
672 *(ULLONG*)&sendBuffer[4] = htonll(retval);
674 tcp.sendPacket(sendBuffer, 12);
675 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
679 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
681 ULONG channelNumber = ntohl(*(ULONG*)data);
683 ULONG startTime = ntohl(*(ULONG*)data);
685 ULONG duration = ntohl(*(ULONG*)data);
687 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
689 cChannel* channel = channelFromNumber(channelNumber);
693 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
697 log->log("Client", Log::DEBUG, "Got channel");
699 #if VDRVERSNUM < 10300
700 cMutexLock MutexLock;
701 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
703 cSchedulesLock MutexLock;
704 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
709 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
713 log->log("Client", Log::DEBUG, "Got schedule!s! object");
715 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
719 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
723 log->log("Client", Log::DEBUG, "Got schedule object");
725 UCHAR* sendBuffer = (UCHAR*)malloc(100000);
726 ULONG sendBufferLength = 100000;
727 ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
731 // assign all the event info to temp vars then we know exactly what size they are
734 ULONG thisEventDuration;
735 const char* thisEventTitle;
736 const char* thisEventSubTitle;
737 const char* thisEventDescription;
739 ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
740 ULONG thisEventLength;
742 #if VDRVERSNUM < 10300
744 const cEventInfo *event;
745 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
747 event = Schedule->GetEventNumber(eventNumber);
749 thisEventID = event->GetEventID();
750 thisEventTime = event->GetTime();
751 thisEventDuration = event->GetDuration();
752 thisEventTitle = event->GetTitle();
753 thisEventSubTitle = event->GetSubtitle();
754 thisEventDescription = event->GetExtendedDescription();
758 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
760 thisEventID = event->EventID();
761 thisEventTime = event->StartTime();
762 thisEventDuration = event->Duration();
763 thisEventTitle = event->Title();
764 thisEventSubTitle = NULL;
765 thisEventDescription = event->Description();
769 log->log("Client", Log::DEBUG, "Got an event object %p", event);
772 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
775 if ((thisEventTime + thisEventDuration) <= startTime) continue;
778 if (thisEventTime >= (startTime + duration)) continue;
780 if (!thisEventTitle) thisEventTitle = empty;
781 if (!thisEventSubTitle) thisEventSubTitle = empty;
782 if (!thisEventDescription) thisEventDescription = empty;
784 thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
786 log->log("Client", Log::DEBUG, "Done s1");
788 // now extend the buffer if necessary
789 if ((sendBufferUsed + thisEventLength) > sendBufferLength)
791 log->log("Client", Log::DEBUG, "Extending buffer");
792 sendBufferLength += 100000;
793 UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
797 UCHAR sendBuffer2[8];
798 *(ULONG*)&sendBuffer2[0] = htonl(4);
799 *(ULONG*)&sendBuffer2[4] = htonl(0);
800 tcp.sendPacket(sendBuffer2, 8);
801 log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
807 log->log("Client", Log::DEBUG, "Done s2");
809 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID); sendBufferUsed += sizeof(ULONG);
810 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime); sendBufferUsed += sizeof(ULONG);
811 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
813 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle); sendBufferUsed += strlen(thisEventTitle) + 1;
814 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle); sendBufferUsed += strlen(thisEventSubTitle) + 1;
815 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
817 log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
820 log->log("Client", Log::DEBUG, "Got all event data");
822 if (sendBufferUsed == sizeof(ULONG))
826 log->log("Client", Log::DEBUG, "Written 0 because no data");
830 // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
831 *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
832 tcp.sendPacket(sendBuffer, sendBufferUsed);
833 log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
841 int MVPClient::processConfigSave(UCHAR* buffer, int length)
843 char* section = (char*)buffer;
847 for (int k = 0; k < length; k++)
849 if (buffer[k] == '\0')
853 key = (char*)&buffer[k+1];
857 value = (char*)&buffer[k+1];
863 // if the last string (value) doesnt have null terminator, give up
864 if (buffer[length - 1] != '\0') return 0;
866 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
867 if (config.setValueString(section, key, value))
879 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
881 char* section = (char*)buffer;
884 for (int k = 0; k < length; k++)
886 if (buffer[k] == '\0')
888 key = (char*)&buffer[k+1];
893 char* value = config.getValueString(section, key);
897 UCHAR sendBuffer[4 + strlen(value) + 1];
898 *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
899 strcpy((char*)&sendBuffer[4], value);
900 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
902 log->log("Client", Log::DEBUG, "Written config load packet");
908 *(ULONG*)&sendBuffer[0] = htonl(4);
909 *(ULONG*)&sendBuffer[4] = htonl(0);
910 tcp.sendPacket(sendBuffer, 8);
912 log->log("Client", Log::DEBUG, "Written config load failed packet");
918 void MVPClient::cleanConfig()
920 log->log("Client", Log::DEBUG, "Clean config");
922 cRecordings Recordings;
927 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
928 char* position = resumes;
929 for(int k = 0; k < numReturns; k++)
931 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
933 cRecording* recording = Recordings.GetByName(position);
936 // doesn't exist anymore
937 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
938 config.deleteValue("ResumeData", position);
942 log->log("Client", Log::DEBUG, "This recording still exists");
945 position += strlen(position) + 1;
957 event = Schedule->GetPresentEvent();
959 fprintf(f, "\n\nCurrent event\n\n");
961 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
962 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
963 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
964 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
966 event = Schedule->GetFollowingEvent();
968 fprintf(f, "\n\nFollowing event\n\n");
970 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
971 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
972 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
973 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
979 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
980 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
981 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
982 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
990 void MVPClient::test2()
992 FILE* f = fopen("/tmp/s.txt", "w");
994 #if VDRVERSNUM < 10300
995 cMutexLock MutexLock;
996 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
998 cSchedulesLock MutexLock;
999 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1004 fprintf(f, "Schedules = NULL\n");
1009 fprintf(f, "Schedules dump:\n");
1013 const cSchedule *Schedule;
1014 int scheduleNumber = 0;
1017 cChannel *thisChannel;
1019 #if VDRVERSNUM < 10300
1020 const cEventInfo *event;
1021 int eventNumber = 0;
1023 const cEvent *event;
1026 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1027 // Schedule = Schedules->GetSchedule();
1028 Schedule = Schedules->First();
1031 fprintf(f, "First Schedule = NULL\n");
1038 fprintf(f, "Schedule #%i\n", scheduleNumber);
1039 fprintf(f, "-------------\n\n");
1041 #if VDRVERSNUM < 10300
1042 tchid = Schedule->GetChannelID();
1044 tchid = Schedule->ChannelID();
1047 #if VDRVERSNUM < 10300
1048 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1049 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1051 // put the count at the end.
1054 thisChannel = Channels.GetByChannelID(tchid, true);
1057 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1061 fprintf(f, "thisChannel = NULL for tchid\n");
1064 #if VDRVERSNUM < 10300
1065 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1067 event = Schedule->GetEventNumber(eventNumber);
1068 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1069 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1070 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1071 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1072 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1073 fprintf(f, "Event %u dump:\n", eventNumber);
1078 // This whole section needs rewriting to walk the list.
1079 event = Schedule->Events()->First();
1081 event = Schedule->Events()->Next(event);
1086 fprintf(f, "\nDump from object:\n");
1088 fprintf(f, "\nEND\n");
1098 fprintf(f, "End of current Schedule\n\n\n");
1100 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1114 const cEventInfo *GetPresentEvent(void) const;
1115 const cEventInfo *GetFollowingEvent(void) const;
1116 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1117 const cEventInfo *GetEventAround(time_t tTime) const;
1118 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1121 const unsigned char GetTableID(void) const;
1122 const char *GetTimeString(void) const;
1123 const char *GetEndTimeString(void) const;
1124 const char *GetDate(void) const;
1125 bool IsFollowing(void) const;
1126 bool IsPresent(void) const;
1127 const char *GetExtendedDescription(void) const;
1128 const char *GetSubtitle(void) const;
1129 const char *GetTitle(void) const;
1130 unsigned short GetEventID(void) const;
1131 long GetDuration(void) const;
1132 time_t GetTime(void) const;
1133 tChannelID GetChannelID(void) const;
1134 int GetChannelNumber(void) const { return nChannelNumber; }
1135 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1136 void Dump(FILE *f, const char *Prefix = "") const;
1142 void MVPClient::test(int channelNumber)
1144 FILE* f = fopen("/tmp/test.txt", "w");
1146 cMutexLock MutexLock;
1147 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1151 fprintf(f, "Schedules = NULL\n");
1156 fprintf(f, "Schedules dump:\n");
1157 // Schedules->Dump(f);
1159 const cSchedule *Schedule;
1160 cChannel *thisChannel;
1161 const cEventInfo *event;
1163 thisChannel = channelFromNumber(channelNumber);
1166 fprintf(f, "thisChannel = NULL\n");
1171 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1172 // Schedule = Schedules->GetSchedule();
1173 // Schedule = Schedules->First();
1176 fprintf(f, "First Schedule = NULL\n");
1181 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1183 // For some channels VDR seems to pick a random point in time to
1184 // start dishing out events, but they are in order
1185 // at some point in the list the time snaps to the current event
1190 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1192 event = Schedule->GetEventNumber(eventNumber);
1193 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1194 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1195 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1199 fprintf(f, "\nEND\n");
1213 Schedules = the collection of all the Schedule objects
1214 Schedule = One schedule, contants all the events for a channel
1215 Event = One programme
1224 Subtitle (used for "Programmes resume at ...")
1227 IsPresent ? easy to work out tho. Oh it doesn't always work
1232 void MVPClient::test2()
1234 log->log("-", Log::DEBUG, "Timers List");
1236 for (int i = 0; i < Timers.Count(); i++)
1238 cTimer *timer = Timers.Get(i);
1239 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1240 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1241 #if VDRVERSNUM < 10300
1242 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());
1244 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());
1246 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1247 log->log("-", Log::DEBUG, "");
1250 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
1251 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1252 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1257 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1258 recording is a bool, 0 for not currently recording, 1 for currently recording
1259 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
1263 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1265 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1266 int count = 4; // leave space for the packet length
1268 const char* fileName;
1270 int numTimers = Timers.Count();
1272 *(ULONG*)&sendBuffer[count] = htonl(numTimers); count += 4;
1274 for (int i = 0; i < numTimers; i++)
1276 if (count > 49000) break;
1278 timer = Timers.Get(i);
1280 #if VDRVERSNUM < 10300
1281 *(ULONG*)&sendBuffer[count] = htonl(timer->Active()); count += 4;
1283 *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive)); count += 4;
1285 *(ULONG*)&sendBuffer[count] = htonl(timer->Recording()); count += 4;
1286 *(ULONG*)&sendBuffer[count] = htonl(timer->Pending()); count += 4;
1287 *(ULONG*)&sendBuffer[count] = htonl(timer->Priority()); count += 4;
1288 *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime()); count += 4;
1289 *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number()); count += 4;
1290 *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime()); count += 4;
1291 *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime()); count += 4;
1293 fileName = timer->File();
1294 strcpy((char*)&sendBuffer[count], fileName);
1295 count += strlen(fileName) + 1;
1298 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1300 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1302 tcp.sendPacket(sendBuffer, count);
1303 delete[] sendBuffer;
1304 log->log("Client", Log::DEBUG, "Written timers list");
1309 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1311 char* timerString = new char[strlen((char*)buffer) + 1];
1312 strcpy(timerString, (char*)buffer);
1314 #if VDRVERSNUM < 10300
1316 // If this is VDR 1.2 the date part of the timer string must be reduced
1317 // to just DD rather than YYYY-MM-DD
1319 int s = 0; // source
1320 int d = 0; // destination
1322 while(c != 2) // copy up to date section, including the second ':'
1324 timerString[d] = buffer[s];
1325 if (buffer[s] == ':') c++;
1329 // now it has copied up to the date section
1331 while(c != 2) // waste YYYY-MM-
1333 if (buffer[s] == '-') c++;
1336 // now source is at the DD
1337 memcpy(&timerString[d], &buffer[s], length - s);
1339 timerString[d] = '\0';
1341 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1342 log->log("Client", Log::DEBUG, "%s", timerString);
1346 cTimer *timer = new cTimer;
1347 if (timer->Parse((char*)timerString))
1349 cTimer *t = Timers.GetTimer(timer);
1353 #if VDRVERSNUM < 10300
1356 Timers.SetModified();