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(char* tconfigDirExtra, int tsocket)
35 recordingManager = NULL;
36 log = Log::getInstance();
38 configDirExtra = tconfigDirExtra;
42 MVPClient::~MVPClient()
44 log->log("Client", Log::DEBUG, "MVP client destructor");
55 delete recordingManager;
57 recordingManager = NULL;
60 if (loggedIn) cleanConfig();
64 ULLONG MVPClient::ntohll(ULLONG a)
69 ULLONG MVPClient::htonll(ULLONG a)
71 #if BYTE_ORDER == BIG_ENDIAN
76 b = ((a << 56) & 0xFF00000000000000ULL)
77 | ((a << 40) & 0x00FF000000000000ULL)
78 | ((a << 24) & 0x0000FF0000000000ULL)
79 | ((a << 8) & 0x000000FF00000000ULL)
80 | ((a >> 8) & 0x00000000FF000000ULL)
81 | ((a >> 24) & 0x0000000000FF0000ULL)
82 | ((a >> 40) & 0x000000000000FF00ULL)
83 | ((a >> 56) & 0x00000000000000FFULL) ;
89 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
91 cChannel* channel = NULL;
93 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
95 if (!channel->GroupSep())
97 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
99 if (channel->Number() == (int)channelNumber)
101 int vpid = channel->Vpid();
102 #if VDRVERSNUM < 10300
103 int apid1 = channel->Apid1();
105 int apid1 = channel->Apid(0);
107 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
115 log->log("Client", Log::DEBUG, "Channel not found");
121 void MVPClient::writeResumeData()
123 config.setValueLong("ResumeData",
124 (char*)rp->getCurrentRecording()->FileName(),
125 rp->frameNumberFromPosition(rp->getLastPosition()) );
128 void MVPClient::sendULONG(ULONG ul)
131 *(ULONG*)&sendBuffer[0] = htonl(4);
132 *(ULONG*)&sendBuffer[4] = htonl(ul);
134 tcp.sendPacket(sendBuffer, 8);
135 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
138 void MVPClientStartThread(void* arg)
140 MVPClient* m = (MVPClient*)arg;
142 // Nothing external to this class has a reference to it
143 // This is the end of the thread.. so delete m
150 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
151 log->log("Client", Log::DEBUG, "MVPClient run success");
155 void MVPClient::run2()
160 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
161 pthread_detach(runThread); // Detach
163 tcp.disableReadTimeout();
165 tcp.setSoKeepTime(3);
166 tcp.setNonBlocking();
176 log->log("Client", Log::DEBUG, "Waiting");
177 buffer = (UCHAR*)tcp.receivePacket();
178 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
181 log->log("Client", Log::DEBUG, "Detected connection closed");
185 packetLength = tcp.getDataLength() - 4;
186 opcode = ntohl(*(ULONG*)buffer);
189 if (!loggedIn && (opcode != 1))
195 log->log("Client", Log::DEBUG, "SwitchOp");
199 result = processLogin(data, packetLength);
202 result = processGetRecordingsList(data, packetLength);
205 result = processDeleteRecording(data, packetLength);
208 result = processGetChannelsList(data, packetLength);
211 result = processStartStreamingChannel(data, packetLength);
214 result = processGetBlock(data, packetLength);
217 result = processStopStreaming(data, packetLength);
220 result = processStartStreamingRecording(data, packetLength);
223 result = processGetChannelSchedule(data, packetLength);
226 result = processConfigSave(data, packetLength);
229 result = processConfigLoad(data, packetLength);
232 result = processReScanRecording(data, packetLength); // FIXME obselete
235 result = processGetTimers(data, packetLength);
238 result = processSetTimer(data, packetLength);
241 result = processPositionFromFrameNumber(data, packetLength);
244 result = processFrameNumberFromPosition(data, packetLength);
247 result = processMoveRecording(data, packetLength);
250 result = processGetIFrame(data, packetLength);
253 result = processGetRecInfo(data, packetLength);
256 result = processGetMarks(data, packetLength);
259 result = processGetChannelPids(data, packetLength);
268 int MVPClient::processLogin(UCHAR* buffer, int length)
270 if (length != 6) return 0;
274 const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
277 log->log("Client", Log::DEBUG, "No config dir!");
281 char configFileName[PATH_MAX];
282 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]);
283 config.init(configFileName);
285 // Send the login reply
287 time_t timeNow = time(NULL);
288 struct tm* timeStruct = localtime(&timeNow);
289 int timeOffset = timeStruct->tm_gmtoff;
291 UCHAR sendBuffer[12];
292 *(ULONG*)&sendBuffer[0] = htonl(8);
293 *(ULONG*)&sendBuffer[4] = htonl(timeNow);
294 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
296 tcp.sendPacket(sendBuffer, 12);
297 log->log("Client", Log::DEBUG, "written login reply");
303 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
305 UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
306 int count = 4; // leave space for the packet length
311 int Percent = VideoDiskSpace(&FreeMB);
312 int Total = (FreeMB / (100 - Percent)) * 100;
314 *(ULONG*)&sendBuffer[count] = htonl(Total);
315 count += sizeof(ULONG);
316 *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
317 count += sizeof(ULONG);
318 *(ULONG*)&sendBuffer[count] = htonl(Percent);
319 count += sizeof(ULONG);
322 cRecordings Recordings;
325 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
327 if (count > 49000) break; // just how big is that hard disk?!
328 *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
331 point = (char*)recording->Name();
332 strcpy((char*)&sendBuffer[count], point);
333 count += strlen(point) + 1;
335 point = (char*)recording->FileName();
336 strcpy((char*)&sendBuffer[count], point);
337 count += strlen(point) + 1;
340 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
342 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
344 tcp.sendPacket(sendBuffer, count);
346 log->log("Client", Log::DEBUG, "Written list");
351 int MVPClient::processDeleteRecording(UCHAR* data, int length)
353 // data is a pointer to the fileName string
355 cRecordings Recordings;
356 Recordings.Load(); // probably have to do this
358 cRecording* recording = Recordings.GetByName((char*)data);
360 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
364 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
366 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
369 if (recording->Delete())
371 // Copy svdrp's way of doing this, see if it works
372 #if VDRVERSNUM > 10300
373 ::Recordings.DelByName(recording->FileName());
395 int MVPClient::processMoveRecording(UCHAR* data, int length)
397 log->log("Client", Log::DEBUG, "Process move recording");
398 char* fileName = (char*)data;
399 char* newPath = NULL;
401 for (int k = 0; k < length; k++)
405 newPath = (char*)&data[k+1];
409 if (!newPath) return 0;
411 cRecordings Recordings;
412 Recordings.Load(); // probably have to do this
414 cRecording* recording = Recordings.GetByName((char*)fileName);
416 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
420 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
423 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
424 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
425 log->log("Client", Log::DEBUG, "to: %s", newPath);
427 const char* t = recording->FileName();
429 char* dateDirName = NULL; int k;
430 char* titleDirName = NULL; int j;
432 // Find the datedirname
433 for(k = strlen(t) - 1; k >= 0; k--)
437 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
438 dateDirName = new char[strlen(&t[k+1]) + 1];
439 strcpy(dateDirName, &t[k+1]);
444 // Find the titledirname
446 for(j = k-1; j >= 0; j--)
450 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
451 titleDirName = new char[(k - j - 1) + 1];
452 memcpy(titleDirName, &t[j+1], k - j - 1);
453 titleDirName[k - j - 1] = '\0';
458 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
459 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
461 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
463 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
464 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
465 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
467 // FIXME Check whether this already exists before mkdiring it
469 log->log("Client", Log::DEBUG, "%s", newContainer);
473 int statret = stat(newContainer, &dstat);
474 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
476 log->log("Client", Log::DEBUG, "new dir does not exist");
477 int mkdirret = mkdir(newContainer, 0755);
480 delete[] dateDirName;
481 delete[] titleDirName;
482 delete[] newContainer;
487 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
489 delete[] dateDirName;
490 delete[] titleDirName;
491 delete[] newContainer;
496 // Ok, the directory container has been made, or it pre-existed.
498 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
499 sprintf(newDir, "%s/%s", newContainer, dateDirName);
501 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
502 int renameret = rename(t, newDir);
505 // Success. Test for remove old dir containter
506 char* oldTitleDir = new char[k+1];
507 memcpy(oldTitleDir, t, k);
508 oldTitleDir[k] = '\0';
509 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
510 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
511 delete[] oldTitleDir;
516 #if VDRVERSNUM > 10311
518 ::Recordings.Update();
520 // Success. Send a different packet from just a ulong
521 int totalLength = 4 + 4 + strlen(newDir) + 1;
522 UCHAR* sendBuffer = new UCHAR[totalLength];
523 *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
524 *(ULONG*)&sendBuffer[4] = htonl(1); // success
525 strcpy((char*)&sendBuffer[8], newDir);
526 tcp.sendPacket(sendBuffer, totalLength);
534 delete[] dateDirName;
535 delete[] titleDirName;
536 delete[] newContainer;
552 int MVPClient::processGetChannelsList(UCHAR* data, int length)
554 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
555 int count = 4; // leave space for the packet length
559 char* chanConfig = config.getValueString("General", "Channels");
561 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
563 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
565 #if VDRVERSNUM < 10300
566 if (!channel->GroupSep() && (!channel->Ca() || allChans))
568 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
571 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
573 if (channel->Vpid()) type = 1;
574 #if VDRVERSNUM < 10300
577 else if (channel->Apid(0)) type = 2;
581 if (count > 49000) break;
582 *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
585 *(ULONG*)&sendBuffer[count] = htonl(type);
588 point = (char*)channel->Name();
589 strcpy((char*)&sendBuffer[count], point);
590 count += strlen(point) + 1;
594 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
596 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
598 tcp.sendPacket(sendBuffer, count);
600 log->log("Client", Log::DEBUG, "Written channels list");
605 int MVPClient::processGetChannelPids(UCHAR* data, int length)
607 ULONG channelNumber = ntohl(*(ULONG*)data);
609 cChannel* channel = channelFromNumber(channelNumber);
617 ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
618 // Work out space required and number of Apids
620 #if VDRVERSNUM < 10300
622 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
623 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
625 if (channel->Apid2())
628 spaceRequired += 10; // 8 + 2 nulls
630 else if (channel->Apid1())
633 spaceRequired += 5; // 4 + 1 null
642 for (const int *Apid = channel->Apids(); *Apid; Apid++)
644 spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
650 // Format of response
658 UCHAR* sendBuffer = new UCHAR[spaceRequired];
660 *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4); point += 4; // take off first 4 bytes
661 *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid()); point += 4;
662 *(ULONG*)&sendBuffer[point] = htonl(numApids); point += 4;
664 #if VDRVERSNUM < 10300
667 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid1()); point += 4;
668 sendBuffer[point] = '\0'; point += 1;
672 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid2()); point += 4;
673 sendBuffer[point] = '\0'; point += 1;
676 for (ULONG i = 0; i < numApids; i++)
678 *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i)); point += 4;
679 strcpy((char*)&sendBuffer[point], channel->Alang(i)); point += strlen(channel->Alang(i)) + 1;
683 // printf("About to send getchannelpids response. length = %u\n", spaceRequired);
684 tcp.dump(sendBuffer, spaceRequired);
686 tcp.sendPacket(sendBuffer, spaceRequired);
688 log->log("Client", Log::DEBUG, "Written channels pids");
693 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
695 log->log("Client", Log::DEBUG, "length = %i", length);
696 ULONG channelNumber = ntohl(*(ULONG*)data);
698 cChannel* channel = channelFromNumber(channelNumber);
705 // get the priority we should use
707 int priority = config.getValueLong("General", "Live priority", &fail);
710 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
714 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
719 if (priority < 0) priority = 0;
720 if (priority > 99) priority = 99;
722 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
723 lp = MVPReceiver::create(channel, priority);
743 int MVPClient::processStopStreaming(UCHAR* data, int length)
745 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
756 delete recordingManager;
758 recordingManager = NULL;
765 int MVPClient::processGetBlock(UCHAR* data, int length)
769 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
773 ULLONG position = ntohll(*(ULLONG*)data);
774 data += sizeof(ULLONG);
775 ULONG amount = ntohl(*(ULONG*)data);
777 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
779 UCHAR sendBuffer[amount + 4];
780 ULONG amountReceived = 0; // compiler moan.
783 log->log("Client", Log::DEBUG, "getting from live");
784 amountReceived = lp->getBlock(&sendBuffer[4], amount);
788 // vdr has possibly disconnected the receiver
789 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
796 log->log("Client", Log::DEBUG, "getting from recording");
797 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
803 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
807 *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
808 tcp.sendPacket(sendBuffer, amountReceived + 4);
809 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
815 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
817 // data is a pointer to the fileName string
819 recordingManager = new cRecordings;
820 recordingManager->Load();
822 cRecording* recording = recordingManager->GetByName((char*)data);
824 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
828 rp = new RecPlayer(recording);
830 UCHAR sendBuffer[16];
831 *(ULONG*)&sendBuffer[0] = htonl(12);
832 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
833 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
835 tcp.sendPacket(sendBuffer, 16);
836 log->log("Client", Log::DEBUG, "written totalLength");
840 delete recordingManager;
841 recordingManager = NULL;
846 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
850 ULONG frameNumber = ntohl(*(ULONG*)data);
855 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
859 retval = rp->positionFromFrameNumber(frameNumber);
862 UCHAR sendBuffer[12];
863 *(ULONG*)&sendBuffer[0] = htonl(8);
864 *(ULLONG*)&sendBuffer[4] = htonll(retval);
866 tcp.sendPacket(sendBuffer, 12);
867 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
871 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
875 ULLONG position = ntohll(*(ULLONG*)data);
880 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
884 retval = rp->frameNumberFromPosition(position);
888 *(ULONG*)&sendBuffer[0] = htonl(4);
889 *(ULONG*)&sendBuffer[4] = htonl(retval);
891 tcp.sendPacket(sendBuffer, 8);
892 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
896 int MVPClient::processGetIFrame(UCHAR* data, int length)
898 bool success = false;
900 ULONG frameNumber = ntohl(*(ULONG*)data);
902 ULONG direction = ntohl(*(ULONG*)data);
905 ULLONG rfilePosition = 0;
906 ULONG rframeNumber = 0;
907 ULONG rframeLength = 0;
911 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
915 success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
918 // returns file position, frame number, length
920 UCHAR sendBuffer[20];
926 *(ULONG*)&sendBuffer[0] = htonl(16);
927 *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
928 *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
929 *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
934 *(ULONG*)&sendBuffer[0] = htonl(4);
935 *(ULONG*)&sendBuffer[4] = 0;
938 log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
940 tcp.sendPacket(sendBuffer, packetLength);
941 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
945 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
947 ULONG channelNumber = ntohl(*(ULONG*)data);
949 ULONG startTime = ntohl(*(ULONG*)data);
951 ULONG duration = ntohl(*(ULONG*)data);
953 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
955 cChannel* channel = channelFromNumber(channelNumber);
959 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
963 log->log("Client", Log::DEBUG, "Got channel");
965 #if VDRVERSNUM < 10300
966 cMutexLock MutexLock;
967 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
969 cSchedulesLock MutexLock;
970 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
975 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
979 log->log("Client", Log::DEBUG, "Got schedule!s! object");
981 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
985 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
989 log->log("Client", Log::DEBUG, "Got schedule object");
991 UCHAR* sendBuffer = (UCHAR*)malloc(100000);
992 ULONG sendBufferLength = 100000;
993 ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
997 // assign all the event info to temp vars then we know exactly what size they are
1000 ULONG thisEventDuration;
1001 const char* thisEventTitle;
1002 const char* thisEventSubTitle;
1003 const char* thisEventDescription;
1005 ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
1006 ULONG thisEventLength;
1008 #if VDRVERSNUM < 10300
1010 const cEventInfo *event;
1011 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1013 event = Schedule->GetEventNumber(eventNumber);
1015 thisEventID = event->GetEventID();
1016 thisEventTime = event->GetTime();
1017 thisEventDuration = event->GetDuration();
1018 thisEventTitle = event->GetTitle();
1019 thisEventSubTitle = event->GetSubtitle();
1020 thisEventDescription = event->GetExtendedDescription();
1024 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1026 thisEventID = event->EventID();
1027 thisEventTime = event->StartTime();
1028 thisEventDuration = event->Duration();
1029 thisEventTitle = event->Title();
1030 thisEventSubTitle = NULL;
1031 thisEventDescription = event->Description();
1035 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1037 //in the past filter
1038 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1041 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1044 if (thisEventTime >= (startTime + duration)) continue;
1046 if (!thisEventTitle) thisEventTitle = empty;
1047 if (!thisEventSubTitle) thisEventSubTitle = empty;
1048 if (!thisEventDescription) thisEventDescription = empty;
1050 thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
1052 log->log("Client", Log::DEBUG, "Done s1");
1054 // now extend the buffer if necessary
1055 if ((sendBufferUsed + thisEventLength) > sendBufferLength)
1057 log->log("Client", Log::DEBUG, "Extending buffer");
1058 sendBufferLength += 100000;
1059 UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
1063 UCHAR sendBuffer2[8];
1064 *(ULONG*)&sendBuffer2[0] = htonl(4);
1065 *(ULONG*)&sendBuffer2[4] = htonl(0);
1066 tcp.sendPacket(sendBuffer2, 8);
1067 log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
1073 log->log("Client", Log::DEBUG, "Done s2");
1075 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID); sendBufferUsed += sizeof(ULONG);
1076 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime); sendBufferUsed += sizeof(ULONG);
1077 *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1079 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle); sendBufferUsed += strlen(thisEventTitle) + 1;
1080 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle); sendBufferUsed += strlen(thisEventSubTitle) + 1;
1081 strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1083 log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1086 log->log("Client", Log::DEBUG, "Got all event data");
1088 if (sendBufferUsed == sizeof(ULONG))
1092 log->log("Client", Log::DEBUG, "Written 0 because no data");
1096 // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1097 *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1098 tcp.sendPacket(sendBuffer, sendBufferUsed);
1099 log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1107 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1109 char* section = (char*)buffer;
1113 for (int k = 0; k < length; k++)
1115 if (buffer[k] == '\0')
1119 key = (char*)&buffer[k+1];
1123 value = (char*)&buffer[k+1];
1129 // if the last string (value) doesnt have null terminator, give up
1130 if (buffer[length - 1] != '\0') return 0;
1132 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1133 if (config.setValueString(section, key, value))
1145 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1147 char* section = (char*)buffer;
1150 for (int k = 0; k < length; k++)
1152 if (buffer[k] == '\0')
1154 key = (char*)&buffer[k+1];
1159 char* value = config.getValueString(section, key);
1163 UCHAR sendBuffer[4 + strlen(value) + 1];
1164 *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1165 strcpy((char*)&sendBuffer[4], value);
1166 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1168 log->log("Client", Log::DEBUG, "Written config load packet");
1173 UCHAR sendBuffer[8];
1174 *(ULONG*)&sendBuffer[0] = htonl(4);
1175 *(ULONG*)&sendBuffer[4] = htonl(0);
1176 tcp.sendPacket(sendBuffer, 8);
1178 log->log("Client", Log::DEBUG, "Written config load failed packet");
1184 void MVPClient::cleanConfig()
1186 log->log("Client", Log::DEBUG, "Clean config");
1188 cRecordings Recordings;
1193 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1194 char* position = resumes;
1195 for(int k = 0; k < numReturns; k++)
1197 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1199 cRecording* recording = Recordings.GetByName(position);
1202 // doesn't exist anymore
1203 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1204 config.deleteValue("ResumeData", position);
1208 log->log("Client", Log::DEBUG, "This recording still exists");
1211 position += strlen(position) + 1;
1223 event = Schedule->GetPresentEvent();
1225 fprintf(f, "\n\nCurrent event\n\n");
1227 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1228 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1229 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1230 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1232 event = Schedule->GetFollowingEvent();
1234 fprintf(f, "\n\nFollowing event\n\n");
1236 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1237 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1238 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1239 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1245 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1246 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1247 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1248 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1256 void MVPClient::test2()
1258 FILE* f = fopen("/tmp/s.txt", "w");
1260 #if VDRVERSNUM < 10300
1261 cMutexLock MutexLock;
1262 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1264 cSchedulesLock MutexLock;
1265 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1270 fprintf(f, "Schedules = NULL\n");
1275 fprintf(f, "Schedules dump:\n");
1279 const cSchedule *Schedule;
1280 int scheduleNumber = 0;
1283 cChannel *thisChannel;
1285 #if VDRVERSNUM < 10300
1286 const cEventInfo *event;
1287 int eventNumber = 0;
1289 const cEvent *event;
1292 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1293 // Schedule = Schedules->GetSchedule();
1294 Schedule = Schedules->First();
1297 fprintf(f, "First Schedule = NULL\n");
1304 fprintf(f, "Schedule #%i\n", scheduleNumber);
1305 fprintf(f, "-------------\n\n");
1307 #if VDRVERSNUM < 10300
1308 tchid = Schedule->GetChannelID();
1310 tchid = Schedule->ChannelID();
1313 #if VDRVERSNUM < 10300
1314 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1315 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1317 // put the count at the end.
1320 thisChannel = Channels.GetByChannelID(tchid, true);
1323 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1327 fprintf(f, "thisChannel = NULL for tchid\n");
1330 #if VDRVERSNUM < 10300
1331 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1333 event = Schedule->GetEventNumber(eventNumber);
1334 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1335 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1336 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1337 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1338 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1339 fprintf(f, "Event %u dump:\n", eventNumber);
1344 // This whole section needs rewriting to walk the list.
1345 event = Schedule->Events()->First();
1347 event = Schedule->Events()->Next(event);
1352 fprintf(f, "\nDump from object:\n");
1354 fprintf(f, "\nEND\n");
1364 fprintf(f, "End of current Schedule\n\n\n");
1366 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1380 const cEventInfo *GetPresentEvent(void) const;
1381 const cEventInfo *GetFollowingEvent(void) const;
1382 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1383 const cEventInfo *GetEventAround(time_t tTime) const;
1384 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1387 const unsigned char GetTableID(void) const;
1388 const char *GetTimeString(void) const;
1389 const char *GetEndTimeString(void) const;
1390 const char *GetDate(void) const;
1391 bool IsFollowing(void) const;
1392 bool IsPresent(void) const;
1393 const char *GetExtendedDescription(void) const;
1394 const char *GetSubtitle(void) const;
1395 const char *GetTitle(void) const;
1396 unsigned short GetEventID(void) const;
1397 long GetDuration(void) const;
1398 time_t GetTime(void) const;
1399 tChannelID GetChannelID(void) const;
1400 int GetChannelNumber(void) const { return nChannelNumber; }
1401 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1402 void Dump(FILE *f, const char *Prefix = "") const;
1408 void MVPClient::test(int channelNumber)
1410 FILE* f = fopen("/tmp/test.txt", "w");
1412 cMutexLock MutexLock;
1413 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1417 fprintf(f, "Schedules = NULL\n");
1422 fprintf(f, "Schedules dump:\n");
1423 // Schedules->Dump(f);
1425 const cSchedule *Schedule;
1426 cChannel *thisChannel;
1427 const cEventInfo *event;
1429 thisChannel = channelFromNumber(channelNumber);
1432 fprintf(f, "thisChannel = NULL\n");
1437 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1438 // Schedule = Schedules->GetSchedule();
1439 // Schedule = Schedules->First();
1442 fprintf(f, "First Schedule = NULL\n");
1447 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1449 // For some channels VDR seems to pick a random point in time to
1450 // start dishing out events, but they are in order
1451 // at some point in the list the time snaps to the current event
1456 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1458 event = Schedule->GetEventNumber(eventNumber);
1459 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1460 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1461 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1465 fprintf(f, "\nEND\n");
1479 Schedules = the collection of all the Schedule objects
1480 Schedule = One schedule, contants all the events for a channel
1481 Event = One programme
1490 Subtitle (used for "Programmes resume at ...")
1493 IsPresent ? easy to work out tho. Oh it doesn't always work
1498 void MVPClient::test2()
1500 log->log("-", Log::DEBUG, "Timers List");
1502 for (int i = 0; i < Timers.Count(); i++)
1504 cTimer *timer = Timers.Get(i);
1505 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1506 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1507 #if VDRVERSNUM < 10300
1508 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());
1510 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());
1512 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1513 log->log("-", Log::DEBUG, "");
1516 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
1517 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1518 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1523 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1524 recording is a bool, 0 for not currently recording, 1 for currently recording
1525 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
1529 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1531 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1532 int count = 4; // leave space for the packet length
1534 const char* fileName;
1536 int numTimers = Timers.Count();
1538 *(ULONG*)&sendBuffer[count] = htonl(numTimers); count += 4;
1540 for (int i = 0; i < numTimers; i++)
1542 if (count > 49000) break;
1544 timer = Timers.Get(i);
1546 #if VDRVERSNUM < 10300
1547 *(ULONG*)&sendBuffer[count] = htonl(timer->Active()); count += 4;
1549 *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive)); count += 4;
1551 *(ULONG*)&sendBuffer[count] = htonl(timer->Recording()); count += 4;
1552 *(ULONG*)&sendBuffer[count] = htonl(timer->Pending()); count += 4;
1553 *(ULONG*)&sendBuffer[count] = htonl(timer->Priority()); count += 4;
1554 *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime()); count += 4;
1555 *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number()); count += 4;
1556 *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime()); count += 4;
1557 *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime()); count += 4;
1559 fileName = timer->File();
1560 strcpy((char*)&sendBuffer[count], fileName);
1561 count += strlen(fileName) + 1;
1564 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1566 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1568 tcp.sendPacket(sendBuffer, count);
1569 delete[] sendBuffer;
1570 log->log("Client", Log::DEBUG, "Written timers list");
1575 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1577 char* timerString = new char[strlen((char*)buffer) + 1];
1578 strcpy(timerString, (char*)buffer);
1580 #if VDRVERSNUM < 10300
1582 // If this is VDR 1.2 the date part of the timer string must be reduced
1583 // to just DD rather than YYYY-MM-DD
1585 int s = 0; // source
1586 int d = 0; // destination
1588 while(c != 2) // copy up to date section, including the second ':'
1590 timerString[d] = buffer[s];
1591 if (buffer[s] == ':') c++;
1595 // now it has copied up to the date section
1597 while(c != 2) // waste YYYY-MM-
1599 if (buffer[s] == '-') c++;
1602 // now source is at the DD
1603 memcpy(&timerString[d], &buffer[s], length - s);
1605 timerString[d] = '\0';
1607 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1608 log->log("Client", Log::DEBUG, "%s", timerString);
1612 cTimer *timer = new cTimer;
1613 if (timer->Parse((char*)timerString))
1615 cTimer *t = Timers.GetTimer(timer);
1619 #if VDRVERSNUM < 10300
1622 Timers.SetModified();
1640 void MVPClient::incClients()
1642 pthread_mutex_lock(&threadClientMutex);
1643 MVPClient::nr_clients++;
1644 pthread_mutex_unlock(&threadClientMutex);
1647 void MVPClient::decClients()
1649 pthread_mutex_lock(&threadClientMutex);
1650 MVPClient::nr_clients--;
1651 pthread_mutex_unlock(&threadClientMutex);
1654 int MVPClient::getNrClients()
1657 pthread_mutex_lock(&threadClientMutex);
1658 nrClients = MVPClient::nr_clients;
1659 pthread_mutex_unlock(&threadClientMutex);
1663 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1665 // data is a pointer to the fileName string
1667 cRecordings Recordings;
1668 Recordings.Load(); // probably have to do this
1670 cRecording *recording = Recordings.GetByName((char*)data);
1672 time_t timerStart = 0;
1673 time_t timerStop = 0;
1674 char* summary = NULL;
1675 ULONG resumePoint = 0;
1679 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1684 ULONG sendBufferSize = 10000;
1685 UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1686 ULONG pos = 4; // leave first 4 bytes for size field
1690 4 bytes: start time for timer
1691 4 bytes: end time for timer
1692 4 bytes: resume point
1694 4 bytes: num components
1704 // Get current timer
1706 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1709 timerStart = rc->Timer()->StartTime();
1710 timerStop = rc->Timer()->StopTime();
1711 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1714 *(time_t*)&sendBuffer[pos] = htonl(timerStart); pos += 4;
1715 *(time_t*)&sendBuffer[pos] = htonl(timerStop); pos += 4;
1719 char* value = config.getValueString("ResumeData", (char*)data);
1722 resumePoint = strtoul(value, NULL, 10);
1725 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1727 *(ULONG*)&sendBuffer[pos] = htonl(resumePoint); pos += 4;
1732 #if VDRVERSNUM < 10300
1733 summary = (char*)recording->Summary();
1735 const cRecordingInfo *Info = recording->Info();
1736 summary = (char*)Info->ShortText();
1737 if (isempty(summary)) summary = (char*)Info->Description();
1739 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1742 // memory insanity...
1743 if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1745 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1748 sendBuffer = newBuffer;
1749 sendBufferSize += strlen(summary) + 10000;
1759 strcpy((char*)&sendBuffer[pos], summary);
1760 pos += strlen(summary) + 1;
1764 strcpy((char*)&sendBuffer[pos], "");
1771 #if VDRVERSNUM < 10300
1773 // Send 0 for numchannels - this signals the client this info is not available
1774 *(ULONG*)&sendBuffer[pos] = 0; pos += 4;
1777 const cComponents* components = Info->Components();
1779 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1783 *(ULONG*)&sendBuffer[pos] = htonl(0); pos += 4;
1787 *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents()); pos += 4;
1789 tComponent* component;
1790 for (int i = 0; i < components->NumComponents(); i++)
1792 component = components->Component(i);
1794 // memory insanity...
1795 ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1796 + (component->description ? strlen(component->description) : 0) + 2;
1798 if ((sendBufferSize - pos) < extraNeeded)
1800 UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1803 sendBuffer = newBuffer;
1804 sendBufferSize += extraNeeded + 10000;
1814 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1815 sendBuffer[pos] = component->stream; pos += 1;
1816 sendBuffer[pos] = component->type; pos += 1;
1817 if (component->language)
1819 strcpy((char*)&sendBuffer[pos], component->language);
1820 pos += strlen(component->language) + 1;
1824 strcpy((char*)&sendBuffer[pos], "");
1827 if (component->description)
1829 strcpy((char*)&sendBuffer[pos], component->description);
1830 pos += strlen(component->description) + 1;
1834 strcpy((char*)&sendBuffer[pos], "");
1845 *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 : take off the size field
1847 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1849 tcp.sendPacket(sendBuffer, pos);
1850 delete[] sendBuffer;
1851 log->log("Client", Log::DEBUG, "Written getrecinfo");
1861 int MVPClient::processReScanRecording(UCHAR* data, int length)
1865 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1871 UCHAR sendBuffer[16];
1872 *(ULONG*)&sendBuffer[0] = htonl(12);
1873 *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1874 *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1876 tcp.sendPacket(sendBuffer, 16);
1877 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1881 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1884 int MVPClient::processGetMarks(UCHAR* data, int length)
1886 // data is a pointer to the fileName string
1888 UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1889 int count = 4; // leave space for the packet length
1893 cRecordings Recordings;
1894 Recordings.Load(); // probably have to do this
1896 cRecording *recording = Recordings.GetByName((char*)data);
1898 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1902 Marks.Load(recording->FileName());
1905 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1907 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1909 if (count > 49000) break;
1910 *(ULONG*)&sendBuffer[count] = htonl(m->position);
1916 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1917 *(ULONG*)&sendBuffer[count] = htonl(0);
1922 *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
1924 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1926 tcp.sendPacket(sendBuffer, count);
1927 delete[] sendBuffer;
1928 log->log("Client", Log::DEBUG, "Written Marks list");