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
24 //#include <vdr/menu.h>
26 pthread_mutex_t threadClientMutex;
27 int MVPClient::nr_clients = 0;
30 MVPClient::MVPClient(Config* cfgBase, char* tconfigDirExtra, int tsocket)
33 #ifndef VOMPSTANDALONE
36 recordingManager = NULL;
39 log = Log::getInstance();
41 configDirExtra = tconfigDirExtra;
46 MVPClient::~MVPClient()
48 log->log("Client", Log::DEBUG, "MVP client destructor");
49 #ifndef VOMPSTANDALONE
60 delete recordingManager;
62 recordingManager = NULL;
65 if (loggedIn) cleanConfig();
69 ULLONG MVPClient::ntohll(ULLONG a)
74 ULLONG MVPClient::htonll(ULLONG a)
76 #if BYTE_ORDER == BIG_ENDIAN
81 b = ((a << 56) & 0xFF00000000000000ULL)
82 | ((a << 40) & 0x00FF000000000000ULL)
83 | ((a << 24) & 0x0000FF0000000000ULL)
84 | ((a << 8) & 0x000000FF00000000ULL)
85 | ((a >> 8) & 0x00000000FF000000ULL)
86 | ((a >> 24) & 0x0000000000FF0000ULL)
87 | ((a >> 40) & 0x000000000000FF00ULL)
88 | ((a >> 56) & 0x00000000000000FFULL) ;
94 #ifndef VOMPSTANDALONE
95 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
97 cChannel* channel = NULL;
99 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
101 if (!channel->GroupSep())
103 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
105 if (channel->Number() == (int)channelNumber)
107 int vpid = channel->Vpid();
108 #if VDRVERSNUM < 10300
109 int apid1 = channel->Apid1();
111 int apid1 = channel->Apid(0);
113 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
121 log->log("Client", Log::DEBUG, "Channel not found");
127 void MVPClient::writeResumeData()
129 config.setValueLong("ResumeData",
130 (char*)rp->getCurrentRecording()->FileName(),
131 rp->frameNumberFromPosition(rp->getLastPosition()) );
135 void MVPClient::sendULONG(ULONG ul)
138 *(ULONG*)&sendBuffer[0] = htonl(4);
139 *(ULONG*)&sendBuffer[4] = htonl(ul);
141 tcp.sendPacket(sendBuffer, 8);
142 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
145 void MVPClientStartThread(void* arg)
147 MVPClient* m = (MVPClient*)arg;
149 // Nothing external to this class has a reference to it
150 // This is the end of the thread.. so delete m
157 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
158 log->log("Client", Log::DEBUG, "MVPClient run success");
162 void MVPClient::run2()
167 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
168 pthread_detach(runThread); // Detach
170 tcp.disableReadTimeout();
172 tcp.setSoKeepTime(3);
173 tcp.setNonBlocking();
178 ULONG extraDataLength;
185 log->log("Client", Log::DEBUG, "Waiting");
187 if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break;
188 channelID = ntohl(channelID);
191 log->log("Client", Log::ERR, "Incoming channel number not 1!");
195 log->log("Client", Log::DEBUG, "Got chan");
197 if (!tcp.readData((UCHAR*)&serialNumber, sizeof(ULONG))) break;
198 serialNumber = ntohl(serialNumber);
200 log->log("Client", Log::DEBUG, "Got ser");
202 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
203 opcode = ntohl(opcode);
205 log->log("Client", Log::DEBUG, "Got op %lu", opcode);
207 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
208 extraDataLength = ntohl(extraDataLength);
209 if (extraDataLength > 200000)
211 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
215 log->log("Client", Log::DEBUG, "Got edl %lu", extraDataLength);
219 data = (UCHAR*)malloc(extraDataLength);
222 log->log("Client", Log::ERR, "Extra data buffer malloc error");
226 if (!tcp.readData(data, extraDataLength))
228 log->log("Client", Log::ERR, "Could not read extradata");
238 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, serialNumber, opcode, extraDataLength);
240 if (!loggedIn && (opcode != 1))
242 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
243 if (data) free(data);
250 result = processLogin(data, extraDataLength);
252 #ifndef VOMPSTANDALONE
254 result = processGetRecordingsList(data, extraDataLength);
257 result = processDeleteRecording(data, extraDataLength);
260 result = processGetChannelsList(data, extraDataLength);
263 result = processStartStreamingChannel(data, extraDataLength);
266 result = processGetBlock(data, extraDataLength);
269 result = processStopStreaming(data, extraDataLength);
272 result = processStartStreamingRecording(data, extraDataLength);
275 result = processGetChannelSchedule(data, extraDataLength);
279 result = processConfigSave(data, extraDataLength);
282 result = processConfigLoad(data, extraDataLength);
284 #ifndef VOMPSTANDALONE
286 result = processReScanRecording(data, extraDataLength); // FIXME obselete
289 result = processGetTimers(data, extraDataLength);
292 result = processSetTimer(data, extraDataLength);
295 result = processPositionFromFrameNumber(data, extraDataLength);
298 result = processFrameNumberFromPosition(data, extraDataLength);
301 result = processMoveRecording(data, extraDataLength);
304 result = processGetIFrame(data, extraDataLength);
307 result = processGetRecInfo(data, extraDataLength);
310 result = processGetMarks(data, extraDataLength);
313 result = processGetChannelPids(data, extraDataLength);
316 result = processDeleteTimer(data, extraDataLength);
320 result = processGetMediaList(data, extraDataLength);
323 result = processGetPicture(data, extraDataLength);
326 result = processGetImageBlock(data, extraDataLength);
330 if (data) free(data);
335 int MVPClient::processLogin(UCHAR* buffer, int length)
337 if (length != 6) return 0;
341 #ifndef VOMPSTANDALONE
342 const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
344 const char* configDir = ".";
348 log->log("Client", Log::DEBUG, "No config dir!");
352 char configFileName[PATH_MAX];
353 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]);
354 config.init(configFileName);
356 // Send the login reply
358 time_t timeNow = time(NULL);
359 struct tm* timeStruct = localtime(&timeNow);
360 int timeOffset = timeStruct->tm_gmtoff;
362 UCHAR sendBuffer[12];
363 *(ULONG*)&sendBuffer[0] = htonl(8);
364 *(ULONG*)&sendBuffer[4] = htonl(timeNow);
365 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
367 tcp.sendPacket(sendBuffer, 12);
368 log->log("Client", Log::DEBUG, "written login reply");
374 #ifndef VOMPSTANDALONE
375 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
377 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
378 int count = 4; // leave space for the packet length
383 int Percent = VideoDiskSpace(&FreeMB);
384 int Total = (FreeMB / (100 - Percent)) * 100;
386 *(ULONG*)&sendBuffer[count] = htonl(Total);
387 count += sizeof(ULONG);
388 *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
389 count += sizeof(ULONG);
390 *(ULONG*)&sendBuffer[count] = htonl(Percent);
391 count += sizeof(ULONG);
394 cRecordings Recordings;
397 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
399 if (count > 49000) break; // just how big is that hard disk?!
400 *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
403 point = (char*)recording->Name();
404 strcpy((char*)&sendBuffer[count], point);
405 count += strlen(point) + 1;
407 point = (char*)recording->FileName();
408 strcpy((char*)&sendBuffer[count], point);
409 count += strlen(point) + 1;
412 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
414 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
416 tcp.sendPacket(sendBuffer, count);
418 log->log("Client", Log::DEBUG, "Written list");
423 int MVPClient::processDeleteRecording(UCHAR* data, int length)
425 // data is a pointer to the fileName string
427 cRecordings Recordings;
428 Recordings.Load(); // probably have to do this
430 cRecording* recording = Recordings.GetByName((char*)data);
432 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
436 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
438 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
441 if (recording->Delete())
443 // Copy svdrp's way of doing this, see if it works
444 #if VDRVERSNUM > 10300
445 ::Recordings.DelByName(recording->FileName());
467 int MVPClient::processMoveRecording(UCHAR* data, int length)
469 log->log("Client", Log::DEBUG, "Process move recording");
470 char* fileName = (char*)data;
471 char* newPath = NULL;
473 for (int k = 0; k < length; k++)
477 newPath = (char*)&data[k+1];
481 if (!newPath) return 0;
483 cRecordings Recordings;
484 Recordings.Load(); // probably have to do this
486 cRecording* recording = Recordings.GetByName((char*)fileName);
488 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
492 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
495 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
496 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
497 log->log("Client", Log::DEBUG, "to: %s", newPath);
499 const char* t = recording->FileName();
501 char* dateDirName = NULL; int k;
502 char* titleDirName = NULL; int j;
504 // Find the datedirname
505 for(k = strlen(t) - 1; k >= 0; k--)
509 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
510 dateDirName = new char[strlen(&t[k+1]) + 1];
511 strcpy(dateDirName, &t[k+1]);
516 // Find the titledirname
518 for(j = k-1; j >= 0; j--)
522 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
523 titleDirName = new char[(k - j - 1) + 1];
524 memcpy(titleDirName, &t[j+1], k - j - 1);
525 titleDirName[k - j - 1] = '\0';
530 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
531 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
533 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
535 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
536 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
537 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
539 // FIXME Check whether this already exists before mkdiring it
541 log->log("Client", Log::DEBUG, "%s", newContainer);
545 int statret = stat(newContainer, &dstat);
546 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
548 log->log("Client", Log::DEBUG, "new dir does not exist");
549 int mkdirret = mkdir(newContainer, 0755);
552 delete[] dateDirName;
553 delete[] titleDirName;
554 delete[] newContainer;
559 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
561 delete[] dateDirName;
562 delete[] titleDirName;
563 delete[] newContainer;
568 // Ok, the directory container has been made, or it pre-existed.
570 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
571 sprintf(newDir, "%s/%s", newContainer, dateDirName);
573 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
574 int renameret = rename(t, newDir);
577 // Success. Test for remove old dir containter
578 char* oldTitleDir = new char[k+1];
579 memcpy(oldTitleDir, t, k);
580 oldTitleDir[k] = '\0';
581 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
582 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
583 delete[] oldTitleDir;
588 #if VDRVERSNUM > 10311
590 ::Recordings.Update();
592 // Success. Send a different packet from just a ulong
593 int totalLength = 4 + 4 + strlen(newDir) + 1;
594 UCHAR* sendBuffer = new UCHAR[totalLength];
595 *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
596 *(ULONG*)&sendBuffer[4] = htonl(1); // success
597 strcpy((char*)&sendBuffer[8], newDir);
598 tcp.sendPacket(sendBuffer, totalLength);
606 delete[] dateDirName;
607 delete[] titleDirName;
608 delete[] newContainer;
624 int MVPClient::processGetChannelsList(UCHAR* data, int length)
626 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
627 int count = 4; // leave space for the packet length
631 char* chanConfig = config.getValueString("General", "Channels");
633 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
635 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
637 #if VDRVERSNUM < 10300
638 if (!channel->GroupSep() && (!channel->Ca() || allChans))
640 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
643 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
645 if (channel->Vpid()) type = 1;
646 #if VDRVERSNUM < 10300
649 else if (channel->Apid(0)) type = 2;
653 if (count > 49000) break;
654 *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
657 *(ULONG*)&sendBuffer[count] = htonl(type);
660 point = (char*)channel->Name();
661 strcpy((char*)&sendBuffer[count], point);
662 count += strlen(point) + 1;
666 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
668 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
670 tcp.sendPacket(sendBuffer, count);
672 log->log("Client", Log::DEBUG, "Written channels list");
677 int MVPClient::processGetChannelPids(UCHAR* data, int length)
679 ULONG channelNumber = ntohl(*(ULONG*)data);
681 cChannel* channel = channelFromNumber(channelNumber);
689 ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
690 // Work out space required and number of Apids
692 #if VDRVERSNUM < 10300
694 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
695 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
697 if (channel->Apid2())
700 spaceRequired += 10; // 8 + 2 nulls
702 else if (channel->Apid1())
705 spaceRequired += 5; // 4 + 1 null
714 for (const int *Apid = channel->Apids(); *Apid; Apid++)
716 spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
722 // Format of response
730 UCHAR* sendBuffer = new UCHAR[spaceRequired];
732 *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4); point += 4; // take off first 4 bytes
733 *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid()); point += 4;
734 *(ULONG*)&sendBuffer[point] = htonl(numApids); point += 4;
736 #if VDRVERSNUM < 10300
739 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid1()); point += 4;
740 sendBuffer[point] = '\0'; point += 1;
744 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid2()); point += 4;
745 sendBuffer[point] = '\0'; point += 1;
748 for (ULONG i = 0; i < numApids; i++)
750 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i)); point += 4;
751 strcpy((char*)&sendBuffer[point], channel->Alang(i)); point += strlen(channel->Alang(i)) + 1;
755 // printf("About to send getchannelpids response. length = %u\n", spaceRequired);
756 //tcp.dump(sendBuffer, spaceRequired);
758 tcp.sendPacket(sendBuffer, spaceRequired);
760 log->log("Client", Log::DEBUG, "Written channels pids");
765 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
767 log->log("Client", Log::DEBUG, "length = %i", length);
768 ULONG channelNumber = ntohl(*(ULONG*)data);
770 cChannel* channel = channelFromNumber(channelNumber);
777 // get the priority we should use
779 int priority = config.getValueLong("General", "Live priority", &fail);
782 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
786 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
791 if (priority < 0) priority = 0;
792 if (priority > 99) priority = 99;
794 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
795 lp = MVPReceiver::create(channel, priority);
815 int MVPClient::processStopStreaming(UCHAR* data, int length)
817 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
828 delete recordingManager;
830 recordingManager = NULL;
837 int MVPClient::processGetBlock(UCHAR* data, int length)
841 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
845 ULLONG position = ntohll(*(ULLONG*)data);
846 data += sizeof(ULLONG);
847 ULONG amount = ntohl(*(ULONG*)data);
849 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
851 UCHAR sendBuffer[amount + 4];
852 ULONG amountReceived = 0; // compiler moan.
855 log->log("Client", Log::DEBUG, "getting from live");
856 amountReceived = lp->getBlock(&sendBuffer[4], amount);
860 // vdr has possibly disconnected the receiver
861 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
868 log->log("Client", Log::DEBUG, "getting from recording");
869 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
875 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
879 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
880 tcp.sendPacket(sendBuffer, amountReceived + 4);
881 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
887 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
889 // data is a pointer to the fileName string
891 recordingManager = new cRecordings;
892 recordingManager->Load();
894 cRecording* recording = recordingManager->GetByName((char*)data);
896 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
900 rp = new RecPlayer(recording);
902 UCHAR sendBuffer[16];
903 *(ULONG*)&sendBuffer[0] = htonl(12);
904 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
905 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
907 tcp.sendPacket(sendBuffer, 16);
908 log->log("Client", Log::DEBUG, "written totalLength");
912 delete recordingManager;
913 recordingManager = NULL;
918 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
922 ULONG frameNumber = ntohl(*(ULONG*)data);
927 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
931 retval = rp->positionFromFrameNumber(frameNumber);
934 UCHAR sendBuffer[12];
935 *(ULONG*)&sendBuffer[0] = htonl(8);
936 *(ULLONG*)&sendBuffer[4] = htonll(retval);
938 tcp.sendPacket(sendBuffer, 12);
939 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
943 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
947 ULLONG position = ntohll(*(ULLONG*)data);
952 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
956 retval = rp->frameNumberFromPosition(position);
960 *(ULONG*)&sendBuffer[0] = htonl(4);
961 *(ULONG*)&sendBuffer[4] = htonl(retval);
963 tcp.sendPacket(sendBuffer, 8);
964 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
968 int MVPClient::processGetIFrame(UCHAR* data, int length)
970 bool success = false;
972 ULONG frameNumber = ntohl(*(ULONG*)data);
974 ULONG direction = ntohl(*(ULONG*)data);
977 ULLONG rfilePosition = 0;
978 ULONG rframeNumber = 0;
979 ULONG rframeLength = 0;
983 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
987 success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
990 // returns file position, frame number, length
992 UCHAR sendBuffer[20];
998 *(ULONG*)&sendBuffer[0] = htonl(16);
999 *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
1000 *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
1001 *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
1006 *(ULONG*)&sendBuffer[0] = htonl(4);
1007 *(ULONG*)&sendBuffer[4] = 0;
1010 log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1012 tcp.sendPacket(sendBuffer, packetLength);
1013 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
1017 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
1019 ULONG channelNumber = ntohl(*(ULONG*)data);
1021 ULONG startTime = ntohl(*(ULONG*)data);
1023 ULONG duration = ntohl(*(ULONG*)data);
1025 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1027 cChannel* channel = channelFromNumber(channelNumber);
1031 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1035 log->log("Client", Log::DEBUG, "Got channel");
1037 #if VDRVERSNUM < 10300
1038 cMutexLock MutexLock;
1039 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1041 cSchedulesLock MutexLock;
1042 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1047 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1051 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1053 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1057 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1061 log->log("Client", Log::DEBUG, "Got schedule object");
1063 UCHAR* sendBuffer = (UCHAR*)malloc(100000);
1064 ULONG sendBufferLength = 100000;
1065 ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
1067 const char* empty = "";
1069 // assign all the event info to temp vars then we know exactly what size they are
1071 ULONG thisEventTime;
1072 ULONG thisEventDuration;
1073 const char* thisEventTitle;
1074 const char* thisEventSubTitle;
1075 const char* thisEventDescription;
1077 ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
1078 ULONG thisEventLength;
1080 #if VDRVERSNUM < 10300
1082 const cEventInfo *event;
1083 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1085 event = Schedule->GetEventNumber(eventNumber);
1087 thisEventID = event->GetEventID();
1088 thisEventTime = event->GetTime();
1089 thisEventDuration = event->GetDuration();
1090 thisEventTitle = event->GetTitle();
1091 thisEventSubTitle = event->GetSubtitle();
1092 thisEventDescription = event->GetExtendedDescription();
1096 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1098 thisEventID = event->EventID();
1099 thisEventTime = event->StartTime();
1100 thisEventDuration = event->Duration();
1101 thisEventTitle = event->Title();
1102 thisEventSubTitle = NULL;
1103 thisEventDescription = event->Description();
1107 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1109 //in the past filter
1110 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1113 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1116 if (thisEventTime >= (startTime + duration)) continue;
1118 if (!thisEventTitle) thisEventTitle = empty;
1119 if (!thisEventSubTitle) thisEventSubTitle = empty;
1120 if (!thisEventDescription) thisEventDescription = empty;
1122 thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
1124 log->log("Client", Log::DEBUG, "Done s1");
1126 // now extend the buffer if necessary
1127 if ((sendBufferUsed + thisEventLength) > sendBufferLength)
1129 log->log("Client", Log::DEBUG, "Extending buffer");
1130 sendBufferLength += 100000;
1131 UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
1135 UCHAR sendBuffer2[8];
1136 *(ULONG*)&sendBuffer2[0] = htonl(4);
1137 *(ULONG*)&sendBuffer2[4] = htonl(0);
1138 tcp.sendPacket(sendBuffer2, 8);
1139 log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
1145 log->log("Client", Log::DEBUG, "Done s2");
1147 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID); sendBufferUsed += sizeof(ULONG);
1148 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime); sendBufferUsed += sizeof(ULONG);
1149 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1151 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle); sendBufferUsed += strlen(thisEventTitle) + 1;
1152 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle); sendBufferUsed += strlen(thisEventSubTitle) + 1;
1153 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1155 log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1158 log->log("Client", Log::DEBUG, "Got all event data");
1160 if (sendBufferUsed == sizeof(ULONG))
1164 log->log("Client", Log::DEBUG, "Written 0 because no data");
1168 // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1169 *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1170 tcp.sendPacket(sendBuffer, sendBufferUsed);
1171 log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1179 #endif //VOMPSTANDALONE
1181 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1183 char* section = (char*)buffer;
1187 for (int k = 0; k < length; k++)
1189 if (buffer[k] == '\0')
1193 key = (char*)&buffer[k+1];
1197 value = (char*)&buffer[k+1];
1203 // if the last string (value) doesnt have null terminator, give up
1204 if (buffer[length - 1] != '\0') return 0;
1206 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1207 if (config.setValueString(section, key, value))
1219 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1221 char* section = (char*)buffer;
1224 for (int k = 0; k < length; k++)
1226 if (buffer[k] == '\0')
1228 key = (char*)&buffer[k+1];
1233 char* value = config.getValueString(section, key);
1237 UCHAR sendBuffer[4 + strlen(value) + 1];
1238 *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1239 strcpy((char*)&sendBuffer[4], value);
1240 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1242 log->log("Client", Log::DEBUG, "Written config load packet");
1247 UCHAR sendBuffer[8];
1248 *(ULONG*)&sendBuffer[0] = htonl(4);
1249 *(ULONG*)&sendBuffer[4] = htonl(0);
1250 tcp.sendPacket(sendBuffer, 8);
1252 log->log("Client", Log::DEBUG, "Written config load failed packet");
1258 void MVPClient::cleanConfig()
1260 log->log("Client", Log::DEBUG, "Clean config");
1262 #ifndef VOMPSTANDALONE
1263 cRecordings Recordings;
1268 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1269 char* position = resumes;
1270 for(int k = 0; k < numReturns; k++)
1272 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1274 cRecording* recording = Recordings.GetByName(position);
1277 // doesn't exist anymore
1278 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1279 config.deleteValue("ResumeData", position);
1283 log->log("Client", Log::DEBUG, "This recording still exists");
1286 position += strlen(position) + 1;
1299 event = Schedule->GetPresentEvent();
1301 fprintf(f, "\n\nCurrent event\n\n");
1303 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1304 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1305 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1306 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1308 event = Schedule->GetFollowingEvent();
1310 fprintf(f, "\n\nFollowing event\n\n");
1312 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1313 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1314 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1315 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1321 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1322 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1323 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1324 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1332 void MVPClient::test2()
1334 FILE* f = fopen("/tmp/s.txt", "w");
1336 #if VDRVERSNUM < 10300
1337 cMutexLock MutexLock;
1338 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1340 cSchedulesLock MutexLock;
1341 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1346 fprintf(f, "Schedules = NULL\n");
1351 fprintf(f, "Schedules dump:\n");
1355 const cSchedule *Schedule;
1356 int scheduleNumber = 0;
1359 cChannel *thisChannel;
1361 #if VDRVERSNUM < 10300
1362 const cEventInfo *event;
1363 int eventNumber = 0;
1365 const cEvent *event;
1368 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1369 // Schedule = Schedules->GetSchedule();
1370 Schedule = Schedules->First();
1373 fprintf(f, "First Schedule = NULL\n");
1380 fprintf(f, "Schedule #%i\n", scheduleNumber);
1381 fprintf(f, "-------------\n\n");
1383 #if VDRVERSNUM < 10300
1384 tchid = Schedule->GetChannelID();
1386 tchid = Schedule->ChannelID();
1389 #if VDRVERSNUM < 10300
1390 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1391 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1393 // put the count at the end.
1396 thisChannel = Channels.GetByChannelID(tchid, true);
1399 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1403 fprintf(f, "thisChannel = NULL for tchid\n");
1406 #if VDRVERSNUM < 10300
1407 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1409 event = Schedule->GetEventNumber(eventNumber);
1410 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1411 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1412 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1413 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1414 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1415 fprintf(f, "Event %u dump:\n", eventNumber);
1420 // This whole section needs rewriting to walk the list.
1421 event = Schedule->Events()->First();
1423 event = Schedule->Events()->Next(event);
1428 fprintf(f, "\nDump from object:\n");
1430 fprintf(f, "\nEND\n");
1440 fprintf(f, "End of current Schedule\n\n\n");
1442 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1456 const cEventInfo *GetPresentEvent(void) const;
1457 const cEventInfo *GetFollowingEvent(void) const;
1458 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1459 const cEventInfo *GetEventAround(time_t tTime) const;
1460 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1463 const unsigned char GetTableID(void) const;
1464 const char *GetTimeString(void) const;
1465 const char *GetEndTimeString(void) const;
1466 const char *GetDate(void) const;
1467 bool IsFollowing(void) const;
1468 bool IsPresent(void) const;
1469 const char *GetExtendedDescription(void) const;
1470 const char *GetSubtitle(void) const;
1471 const char *GetTitle(void) const;
1472 unsigned short GetEventID(void) const;
1473 long GetDuration(void) const;
1474 time_t GetTime(void) const;
1475 tChannelID GetChannelID(void) const;
1476 int GetChannelNumber(void) const { return nChannelNumber; }
1477 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1478 void Dump(FILE *f, const char *Prefix = "") const;
1484 void MVPClient::test(int channelNumber)
1486 FILE* f = fopen("/tmp/test.txt", "w");
1488 cMutexLock MutexLock;
1489 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1493 fprintf(f, "Schedules = NULL\n");
1498 fprintf(f, "Schedules dump:\n");
1499 // Schedules->Dump(f);
1501 const cSchedule *Schedule;
1502 cChannel *thisChannel;
1503 const cEventInfo *event;
1505 thisChannel = channelFromNumber(channelNumber);
1508 fprintf(f, "thisChannel = NULL\n");
1513 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1514 // Schedule = Schedules->GetSchedule();
1515 // Schedule = Schedules->First();
1518 fprintf(f, "First Schedule = NULL\n");
1523 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1525 // For some channels VDR seems to pick a random point in time to
1526 // start dishing out events, but they are in order
1527 // at some point in the list the time snaps to the current event
1532 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1534 event = Schedule->GetEventNumber(eventNumber);
1535 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1536 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1537 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1541 fprintf(f, "\nEND\n");
1555 Schedules = the collection of all the Schedule objects
1556 Schedule = One schedule, contants all the events for a channel
1557 Event = One programme
1566 Subtitle (used for "Programmes resume at ...")
1569 IsPresent ? easy to work out tho. Oh it doesn't always work
1574 void MVPClient::test2()
1576 log->log("-", Log::DEBUG, "Timers List");
1578 for (int i = 0; i < Timers.Count(); i++)
1580 cTimer *timer = Timers.Get(i);
1581 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1582 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1583 #if VDRVERSNUM < 10300
1584 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());
1586 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());
1588 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1589 log->log("-", Log::DEBUG, "");
1592 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
1593 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1594 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1599 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1600 recording is a bool, 0 for not currently recording, 1 for currently recording
1601 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
1604 #ifndef VOMPSTANDALONE
1606 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1608 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1609 int count = 4; // leave space for the packet length
1611 const char* fileName;
1613 int numTimers = Timers.Count();
1615 *(ULONG*)&sendBuffer[count] = htonl(numTimers); count += 4;
1617 for (int i = 0; i < numTimers; i++)
1619 if (count > 49000) break;
1621 timer = Timers.Get(i);
1623 #if VDRVERSNUM < 10300
1624 *(ULONG*)&sendBuffer[count] = htonl(timer->Active()); count += 4;
1626 *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive)); count += 4;
1628 *(ULONG*)&sendBuffer[count] = htonl(timer->Recording()); count += 4;
1629 *(ULONG*)&sendBuffer[count] = htonl(timer->Pending()); count += 4;
1630 *(ULONG*)&sendBuffer[count] = htonl(timer->Priority()); count += 4;
1631 *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime()); count += 4;
1632 *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number()); count += 4;
1633 *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime()); count += 4;
1634 *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime()); count += 4;
1635 *(ULONG*)&sendBuffer[count] = htonl(timer->Day()); count += 4;
1636 *(ULONG*)&sendBuffer[count] = htonl(timer->WeekDays()); count += 4;
1638 fileName = timer->File();
1639 strcpy((char*)&sendBuffer[count], fileName);
1640 count += strlen(fileName) + 1;
1643 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1645 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1647 //tcp.dump(sendBuffer, count);
1649 tcp.sendPacket(sendBuffer, count);
1650 delete[] sendBuffer;
1651 log->log("Client", Log::DEBUG, "Written timers list");
1656 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1658 char* timerString = new char[strlen((char*)buffer) + 1];
1659 strcpy(timerString, (char*)buffer);
1661 #if VDRVERSNUM < 10300
1663 // If this is VDR 1.2 the date part of the timer string must be reduced
1664 // to just DD rather than YYYY-MM-DD
1666 int s = 0; // source
1667 int d = 0; // destination
1669 while(c != 2) // copy up to date section, including the second ':'
1671 timerString[d] = buffer[s];
1672 if (buffer[s] == ':') c++;
1676 // now it has copied up to the date section
1678 while(c != 2) // waste YYYY-MM-
1680 if (buffer[s] == '-') c++;
1683 // now source is at the DD
1684 memcpy(&timerString[d], &buffer[s], length - s);
1686 timerString[d] = '\0';
1688 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1689 log->log("Client", Log::DEBUG, "%s", timerString);
1693 cTimer *timer = new cTimer;
1694 if (timer->Parse((char*)timerString))
1696 cTimer *t = Timers.GetTimer(timer);
1700 #if VDRVERSNUM < 10300
1703 Timers.SetModified();
1721 #endif //VOMPSTANDALONE
1723 void MVPClient::incClients()
1725 pthread_mutex_lock(&threadClientMutex);
1726 MVPClient::nr_clients++;
1727 pthread_mutex_unlock(&threadClientMutex);
1730 void MVPClient::decClients()
1732 pthread_mutex_lock(&threadClientMutex);
1733 MVPClient::nr_clients--;
1734 pthread_mutex_unlock(&threadClientMutex);
1737 int MVPClient::getNrClients()
1740 pthread_mutex_lock(&threadClientMutex);
1741 nrClients = MVPClient::nr_clients;
1742 pthread_mutex_unlock(&threadClientMutex);
1746 #ifndef VOMPSTANDALONE
1748 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1750 // data is a pointer to the fileName string
1752 cRecordings Recordings;
1753 Recordings.Load(); // probably have to do this
1755 cRecording *recording = Recordings.GetByName((char*)data);
1757 time_t timerStart = 0;
1758 time_t timerStop = 0;
1759 char* summary = NULL;
1760 ULONG resumePoint = 0;
1764 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1769 ULONG sendBufferSize = 10000;
1770 UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1771 ULONG pos = 4; // leave first 4 bytes for size field
1775 4 bytes: start time for timer
1776 4 bytes: end time for timer
1777 4 bytes: resume point
1779 4 bytes: num components
1789 // Get current timer
1791 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1794 timerStart = rc->Timer()->StartTime();
1795 timerStop = rc->Timer()->StopTime();
1796 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1799 *(time_t*)&sendBuffer[pos] = htonl(timerStart); pos += 4;
1800 *(time_t*)&sendBuffer[pos] = htonl(timerStop); pos += 4;
1804 char* value = config.getValueString("ResumeData", (char*)data);
1807 resumePoint = strtoul(value, NULL, 10);
1810 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1812 *(ULONG*)&sendBuffer[pos] = htonl(resumePoint); pos += 4;
1817 #if VDRVERSNUM < 10300
1818 summary = (char*)recording->Summary();
1820 const cRecordingInfo *Info = recording->Info();
1821 summary = (char*)Info->ShortText();
1822 if (isempty(summary)) summary = (char*)Info->Description();
1824 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1827 // memory insanity...
1828 if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1830 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1833 sendBuffer = newBuffer;
1834 sendBufferSize += strlen(summary) + 10000;
1844 strcpy((char*)&sendBuffer[pos], summary);
1845 pos += strlen(summary) + 1;
1849 strcpy((char*)&sendBuffer[pos], "");
1856 #if VDRVERSNUM < 10300
1858 // Send 0 for numchannels - this signals the client this info is not available
1859 *(ULONG*)&sendBuffer[pos] = 0; pos += 4;
1862 const cComponents* components = Info->Components();
1864 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1868 *(ULONG*)&sendBuffer[pos] = htonl(0); pos += 4;
1872 *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents()); pos += 4;
1874 tComponent* component;
1875 for (int i = 0; i < components->NumComponents(); i++)
1877 component = components->Component(i);
1879 // memory insanity...
1880 ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1881 + (component->description ? strlen(component->description) : 0) + 2;
1883 if ((sendBufferSize - pos) < extraNeeded)
1885 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1888 sendBuffer = newBuffer;
1889 sendBufferSize += extraNeeded + 10000;
1899 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1900 sendBuffer[pos] = component->stream; pos += 1;
1901 sendBuffer[pos] = component->type; pos += 1;
1902 if (component->language)
1904 strcpy((char*)&sendBuffer[pos], component->language);
1905 pos += strlen(component->language) + 1;
1909 strcpy((char*)&sendBuffer[pos], "");
1912 if (component->description)
1914 strcpy((char*)&sendBuffer[pos], component->description);
1915 pos += strlen(component->description) + 1;
1919 strcpy((char*)&sendBuffer[pos], "");
1930 *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 : take off the size field
1932 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1934 tcp.sendPacket(sendBuffer, pos);
1935 delete[] sendBuffer;
1936 log->log("Client", Log::DEBUG, "Written getrecinfo");
1946 int MVPClient::processReScanRecording(UCHAR* data, int length)
1950 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1956 UCHAR sendBuffer[16];
1957 *(ULONG*)&sendBuffer[0] = htonl(12);
1958 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1959 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1961 tcp.sendPacket(sendBuffer, 16);
1962 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1966 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1969 int MVPClient::processGetMarks(UCHAR* data, int length)
1971 // data is a pointer to the fileName string
1973 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1974 int count = 4; // leave space for the packet length
1978 cRecordings Recordings;
1979 Recordings.Load(); // probably have to do this
1981 cRecording *recording = Recordings.GetByName((char*)data);
1983 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1987 Marks.Load(recording->FileName());
1990 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1992 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1994 if (count > 49000) break;
1995 *(ULONG*)&sendBuffer[count] = htonl(m->position);
2001 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
2002 *(ULONG*)&sendBuffer[count] = htonl(0);
2007 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
2009 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
2011 tcp.sendPacket(sendBuffer, count);
2012 delete[] sendBuffer;
2013 log->log("Client", Log::DEBUG, "Written Marks list");
2018 #endif //VOMPSTANDALONE
2022 * media List Request:
2024 * 4 VDR_GETMEDIALIST
2025 * 4 flags (currently unused)
2028 * Media List response:
2036 * 4 strlen (incl. 0 Byte)
2040 #define MLISTBUF 500000
2041 int MVPClient::processGetMediaList(UCHAR* data, int length)
2044 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
2047 char * dirname=NULL;
2049 //we have a dirname provided
2050 dirname=(char *)&data[4];
2051 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
2055 UCHAR* sendBuffer = new UCHAR[MLISTBUF]; // FIXME hope this is enough
2056 int count = 4; // leave space for the header
2058 MediaList * ml=MediaList::readList(baseConfig,dirname);
2060 log->log("Client", Log::ERR, "getMediaList returned NULL");
2063 //response code (not yet set)
2064 *(ULONG*)&sendBuffer[count] = htonl(0);
2067 *(ULONG*)&sendBuffer[count] = htonl(ml->size());
2069 for (MediaList::iterator nm=ml->begin();nm<ml->end() && count < (MLISTBUF-1000);nm++) {
2071 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
2072 *(ULONG*)&sendBuffer[count] = htonl(m->getType());
2075 *(ULONG*)&sendBuffer[count] = htonl(m->getTime());
2078 *(ULONG*)&sendBuffer[count] = htonl(0);
2080 int len=strlen(m->getFilename());
2082 *(ULONG*)&sendBuffer[count] = htonl(len+1);
2084 //should have a check for strlen > 1000...
2085 strcpy((char *)&sendBuffer[count],m->getFilename());
2090 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
2092 log->log("Client", Log::DEBUG, "getMediaList size %u", ntohl(*(ULONG*)&sendBuffer[0]));
2094 tcp.sendPacket(sendBuffer, count);
2095 delete[] sendBuffer;
2096 log->log("Client", Log::DEBUG, "Written Media list");
2102 * get image Request:
2103 * 4 flags (currently unused)
2108 * get image response:
2113 #define MLISTBUF 500000
2114 int MVPClient::processGetPicture(UCHAR* data, int length)
2117 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
2124 char * filename=NULL;
2126 //we have a dirname provided
2127 filename=(char *)&data[12];
2128 log->log("Client", Log::DEBUG, "getPicture %s", filename);
2131 log->log("Client", Log::ERR, "getPicture empty filename");
2134 imageFile=fopen(filename,"r");
2135 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
2140 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
2142 UCHAR* sendBuffer = new UCHAR[12];
2143 int count = 4; // leave space for the header
2144 //response code (not yet set)
2145 *(ULONG*)&sendBuffer[count] = htonl(31);
2148 *(ULONG*)&sendBuffer[count] = htonl(size);
2150 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
2151 log->log("Client", Log::DEBUG, "getPicture size %u", size);
2153 tcp.sendPacket(sendBuffer, count);
2154 delete[] sendBuffer;
2155 log->log("Client", Log::DEBUG, "Written Media list");
2161 int MVPClient::processGetImageBlock(UCHAR* data, int length)
2165 log->log("Client", Log::DEBUG, "Get image block called when no image active");
2169 ULLONG position = ntohll(*(ULLONG*)data);
2170 data += sizeof(ULLONG);
2171 ULONG amount = ntohl(*(ULONG*)data);
2173 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
2175 UCHAR sendBuffer[amount + 4];
2176 ULONG amountReceived = 0; // compiler moan.
2177 ULLONG cpos=ftell(imageFile);
2178 if (position != cpos) {
2179 fseek(imageFile,position-cpos,SEEK_CUR);
2181 if (position != (ULLONG)ftell(imageFile)) {
2182 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
2185 amountReceived=fread(&sendBuffer[4],1,amount,imageFile);
2188 if (!amountReceived)
2191 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
2195 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
2196 tcp.sendPacket(sendBuffer, amountReceived + 4);
2197 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
2203 #ifndef VOMPSTANDALONE
2205 int MVPClient::processDeleteTimer(UCHAR* buffer, int length)
2207 log->log("Client", Log::DEBUG, "Delete timer called");
2212 INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
2213 INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
2214 INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;
2215 INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;
2216 INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
2219 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
2221 if ( (ti->Channel()->Number() == delChannel)
2222 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
2223 && (ti->StartTime() == delStart)
2224 && (ti->StopTime() == delStop) )
2234 if (!Timers.BeingEdited())
2236 if (!ti->Recording())
2239 Timers.SetModified();
2245 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
2252 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");