]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
FTA options patch by davep
[vompserver.git] / mvpclient.c
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
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
19 */
20
21 #include "mvpclient.h"
22
23 MVPClient::MVPClient(int tsocket)
24  : tcp(tsocket)
25 {
26   lp = NULL;
27   rp = NULL;
28   recordingManager = NULL;
29   log = Log::getInstance();
30
31   // Get IP address of client for config module
32
33   char ipa[20];
34   struct sockaddr_in peer;
35   socklen_t salen = sizeof(struct sockaddr);
36   if(getpeername(tsocket, (struct sockaddr*)&peer, &salen) == 0)
37   {
38     strcpy(ipa, inet_ntoa(peer.sin_addr));
39   }
40   else
41   {
42     ipa[0] = '\0';
43     log->log("Client", Log::DEBUG, "Cannot get peer name!");
44   }
45
46   const char* configDir = cPlugin::ConfigDirectory();
47   if (!configDir)
48   {
49     log->log("Client", Log::DEBUG, "No config dir!");
50     return;
51   }
52
53   char configFileName[PATH_MAX];
54   snprintf(configFileName, PATH_MAX - strlen(configDir) - strlen(ipa) - 20, "%s/vomp-%s.conf", configDir, ipa);
55   config.init(configFileName);
56
57   log->log("Client", Log::DEBUG, "Config file name: %s", configFileName);
58
59
60 //test(14);
61
62 }
63
64 MVPClient::~MVPClient()
65 {
66   log->log("Client", Log::DEBUG, "MVP client destructor");
67   if (lp)
68   {
69     delete lp;
70     lp = NULL;
71   }
72   else if (rp)
73   {
74     writeResumeData();
75
76     delete rp;
77     delete recordingManager;
78     rp = NULL;
79     recordingManager = NULL;
80   }
81
82   cleanConfig();
83 }
84
85 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
86 {
87   cChannel* channel = NULL;
88
89   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
90   {
91     if (!channel->GroupSep())
92     {
93       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
94
95       if (channel->Number() == (int)channelNumber)
96       {
97         int vpid = channel->Vpid();
98 #if VDRVERSNUM < 10300
99         int apid1 = channel->Apid1();
100 #else
101         int apid1 = channel->Apid(0);
102 #endif
103         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
104         return channel;
105       }
106     }
107   }
108
109   if (!channel)
110   {
111     log->log("Client", Log::DEBUG, "Channel not found");
112   }
113
114   return channel;
115 }
116
117
118 void MVPClient::writeResumeData()
119 {
120   config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
121 }
122
123 void MVPClient::sendULONG(ULONG ul)
124 {
125   UCHAR sendBuffer[8];
126   *(ULONG*)&sendBuffer[0] = htonl(4);
127   *(ULONG*)&sendBuffer[4] = htonl(ul);
128
129   tcp.sendPacket(sendBuffer, 8);
130   log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
131 }
132
133 void MVPClientStartThread(void* arg)
134 {
135   MVPClient* m = (MVPClient*)arg;
136   m->run2();
137   // Nothing external to this class has a reference to it
138   // This is the end of the thread.. so delete m
139   delete m;
140   pthread_exit(NULL);
141 }
142
143 int MVPClient::run()
144 {
145   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
146   log->log("Client", Log::DEBUG, "MVPClient run success");
147   return 1;
148 }
149
150 void MVPClient::run2()
151 {
152   // Thread stuff
153   sigset_t sigset;
154   sigfillset(&sigset);
155   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
156   pthread_detach(runThread);  // Detach
157
158   tcp.disableReadTimeout();
159
160   tcp.setSoKeepTime(3);
161   tcp.setNonBlocking();
162
163   UCHAR* buffer;
164   UCHAR* data;
165   int packetLength;
166   ULONG opcode;
167
168   while(1)
169   {
170     log->log("Client", Log::DEBUG, "Waiting");
171     buffer = (UCHAR*)tcp.receivePacket();
172     log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
173     if (buffer == NULL)
174     {
175       log->log("Client", Log::DEBUG, "Detected connection closed");
176       break;
177     }
178
179     packetLength = tcp.getDataLength() - 4;
180     opcode = ntohl(*(ULONG*)buffer);
181     data = buffer + 4;
182
183
184     switch(opcode)
185     {
186       case 1:
187         processLogin(data, packetLength);
188         break;
189       case 2:
190         processGetRecordingsList(data, packetLength);
191         break;
192       case 3:
193         processDeleteRecording(data, packetLength);
194         break;
195       case 4:
196         processGetSummary(data, packetLength);
197         break;
198       case 5:
199         processGetChannelsList(data, packetLength);
200         break;
201       case 6:
202         processStartStreamingChannel(data, packetLength);
203         break;
204       case 7:
205         processGetBlock(data, packetLength);
206         break;
207       case 8:
208         processStopStreaming(data, packetLength);
209         break;
210       case 9:
211         processStartStreamingRecording(data, packetLength);
212         break;
213       case 10:
214         processGetChannelSchedule(data, packetLength);
215         break;
216       case 11:
217         processConfigSave(data, packetLength);
218         break;
219       case 12:
220         processConfigLoad(data, packetLength);
221         break;
222       case 13:
223         processReScanRecording(data, packetLength);
224         break;
225     }
226
227     free(buffer);
228   }
229 }
230
231 void MVPClient::processLogin(UCHAR* buffer, int length)
232 {
233   time_t timeNow = time(NULL);
234   struct tm* timeStruct = localtime(&timeNow);
235   int timeOffset = timeStruct->tm_gmtoff;
236
237   UCHAR sendBuffer[12];
238   *(ULONG*)&sendBuffer[0] = htonl(8);
239   *(ULONG*)&sendBuffer[4] = htonl(timeNow);
240   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
241
242   tcp.sendPacket(sendBuffer, 12);
243   log->log("Client", Log::DEBUG, "written login reply");
244 }
245
246 void MVPClient::processGetRecordingsList(UCHAR* data, int length)
247 {
248   UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
249   int count = 4; // leave space for the packet length
250   char* point;
251
252
253   int FreeMB;
254   int Percent = VideoDiskSpace(&FreeMB);
255   int Total = (FreeMB / (100 - Percent)) * 100;
256
257   *(ULONG*)&sendBuffer[count] = htonl(Total);
258   count += sizeof(ULONG);
259   *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
260   count += sizeof(ULONG);
261   *(ULONG*)&sendBuffer[count] = htonl(Percent);
262   count += sizeof(ULONG);
263
264
265   cRecordings Recordings;
266   Recordings.Load();
267
268   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
269   {
270     if (count > 49000) break; // just how big is that hard disk?!
271     *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
272     count += 4;
273
274     point = (char*)recording->Name();
275     strcpy((char*)&sendBuffer[count], point);
276     count += strlen(point) + 1;
277
278     point = (char*)recording->FileName();
279     strcpy((char*)&sendBuffer[count], point);
280     count += strlen(point) + 1;
281   }
282
283   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
284
285   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
286
287   tcp.sendPacket(sendBuffer, count);
288   delete[] sendBuffer;
289   log->log("Client", Log::DEBUG, "Written list");
290 }
291
292 void MVPClient::processDeleteRecording(UCHAR* data, int length)
293 {
294   // data is a pointer to the fileName string
295
296   cRecordings Recordings;
297   Recordings.Load(); // probably have to do this
298
299   cRecording* recording = Recordings.GetByName((char*)data);
300
301   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
302
303   if (recording)
304   {
305     log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
306     recording->Delete();
307     sendULONG(1);
308   }
309   else
310   {
311     sendULONG(0);
312   }
313 }
314
315 void MVPClient::processGetSummary(UCHAR* data, int length)
316 {
317   // data is a pointer to the fileName string
318
319   cRecordings Recordings;
320   Recordings.Load(); // probably have to do this
321
322   cRecording *recording = Recordings.GetByName((char*)data);
323
324   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
325
326   if (recording)
327   {
328     UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
329     int count = 4; // leave space for the packet length
330
331     char* point;
332 #if VDRVERSNUM < 10300
333     point = (char*)recording->Summary();
334 #else
335     const cRecordingInfo *Info = recording->Info();
336     point = (char*)Info->ShortText();
337     log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
338     if (isempty(point))
339     {
340       point = (char*)Info->Description();
341       log->log("Client", Log::DEBUG, "description pointer %p", point);
342     }
343 #endif
344     strcpy((char*)&sendBuffer[count], point);
345     count += strlen(point) + 1;
346     *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
347
348     log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
349
350     tcp.sendPacket(sendBuffer, count);
351     delete[] sendBuffer;
352     log->log("Client", Log::DEBUG, "Written summary");
353
354
355   }
356   else
357   {
358     sendULONG(0);
359   }
360 }
361
362 void MVPClient::processGetChannelsList(UCHAR* data, int length)
363 {
364   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
365   int count = 4; // leave space for the packet length
366   char* point;
367   ULONG type;
368
369   char* chanConfig = config.getValueString("General", "Channels");
370   int allChans = 1;
371   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
372
373   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
374   {
375 #if VDRVERSNUM < 10300
376     if (!channel->GroupSep() && (!channel->Ca() || allChans))
377 #else
378     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
379 #endif
380     {
381       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
382
383       if (channel->Vpid()) type = 1;
384 #if VDRVERSNUM < 10300
385       else type = 2;
386 #else
387       else if (channel->Apid(0)) type = 2;
388       else continue;
389 #endif
390
391       if (count > 49000) break;
392       *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
393       count += 4;
394
395       *(ULONG*)&sendBuffer[count] = htonl(type);
396       count += 4;
397
398       point = (char*)channel->Name();
399       strcpy((char*)&sendBuffer[count], point);
400       count += strlen(point) + 1;
401     }
402   }
403
404   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
405
406   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
407
408   tcp.sendPacket(sendBuffer, count);
409   delete[] sendBuffer;
410   log->log("Client", Log::DEBUG, "Written channels list");
411 }
412
413 void MVPClient::processStartStreamingChannel(UCHAR* data, int length)
414 {
415   log->log("Client", Log::DEBUG, "length = %i", length);
416   ULONG channelNumber = ntohl(*(ULONG*)data);
417
418   cChannel* channel = channelFromNumber(channelNumber);
419   if (!channel)
420   {
421     sendULONG(0);
422     return;
423   }
424
425   lp = MVPReceiver::create(channel);
426
427   if (!lp)
428   {
429     sendULONG(0);
430     return;
431   }
432
433   if (!lp->init())
434   {
435     delete lp;
436     lp = NULL;
437     sendULONG(0);
438     return;
439   }
440
441   sendULONG(1);
442 }
443
444 void MVPClient::processStopStreaming(UCHAR* data, int length)
445 {
446   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
447   if (lp)
448   {
449     delete lp;
450     lp = NULL;
451   }
452   else if (rp)
453   {
454     writeResumeData();
455
456     delete rp;
457     delete recordingManager;
458     rp = NULL;
459     recordingManager = NULL;
460   }
461
462   sendULONG(1);
463 }
464
465 void MVPClient::processGetBlock(UCHAR* data, int length)
466 {
467   if (!lp && !rp)
468   {
469     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
470     return;
471   }
472
473   ULLONG position = ntohll(*(ULLONG*)data);
474   data += sizeof(ULLONG);
475   ULONG amount = ntohl(*(ULONG*)data);
476
477   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
478
479   UCHAR sendBuffer[amount + 4];
480   ULONG amountReceived = 0; // compiler moan.
481   if (lp)
482   {
483     log->log("Client", Log::DEBUG, "getting from live");
484     amountReceived = lp->getBlock(&sendBuffer[4], amount);
485
486     if (!amountReceived)
487     {
488       // vdr has possibly disconnected the receiver
489       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
490       delete lp;
491       lp = NULL;
492     }
493   }
494   else if (rp)
495   {
496     log->log("Client", Log::DEBUG, "getting from recording");
497     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
498   }
499
500   *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
501   tcp.sendPacket(sendBuffer, amountReceived + 4);
502   log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
503 }
504
505 void MVPClient::processStartStreamingRecording(UCHAR* data, int length)
506 {
507   // data is a pointer to the fileName string
508
509   recordingManager = new cRecordings;
510   recordingManager->Load();
511
512   cRecording* recording = recordingManager->GetByName((char*)data);
513
514   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
515
516   if (recording)
517   {
518     rp = new RecPlayer(recording);
519
520     UCHAR sendBuffer[12];
521     *(ULONG*)&sendBuffer[0] = htonl(8);
522     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
523
524     tcp.sendPacket(sendBuffer, 12);
525     log->log("Client", Log::DEBUG, "written totalLength");
526   }
527   else
528   {
529     delete recordingManager;
530     recordingManager = NULL;
531   }
532 }
533
534 void MVPClient::processReScanRecording(UCHAR* data, int length)
535 {
536   ULLONG retval = 0;
537
538   if (!rp)
539   {
540     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
541   }
542   else
543   {
544     rp->scan();
545     retval = rp->getTotalLength();
546   }
547
548   UCHAR sendBuffer[12];
549   *(ULONG*)&sendBuffer[0] = htonl(8);
550   *(ULLONG*)&sendBuffer[4] = htonll(retval);
551
552   tcp.sendPacket(sendBuffer, 12);
553   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
554 }
555
556 void MVPClient::processGetChannelSchedule(UCHAR* data, int length)
557 {
558   ULONG channelNumber = ntohl(*(ULLONG*)data);
559   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
560
561   cChannel* channel = channelFromNumber(channelNumber);
562   if (!channel)
563   {
564     UCHAR sendBuffer[4];
565     *(ULONG*)&sendBuffer[0] = htonl(4);
566     *(ULONG*)&sendBuffer[4] = htonl(0);
567     tcp.sendPacket(sendBuffer, 8);
568     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
569     return;
570   }
571
572   log->log("Client", Log::DEBUG, "Got channel");
573
574 #if VDRVERSNUM < 10300
575   cMutexLock MutexLock;
576   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
577 #else
578   cSchedulesLock MutexLock;
579   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
580 #endif
581   if (!Schedules)
582   {
583     UCHAR sendBuffer[8];
584     *(ULONG*)&sendBuffer[0] = htonl(4);
585     *(ULONG*)&sendBuffer[4] = htonl(0);
586     tcp.sendPacket(sendBuffer, 8);
587     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
588     return;
589   }
590
591   log->log("Client", Log::DEBUG, "Got schedule!s! object");
592
593   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
594   if (!Schedule)
595   {
596     UCHAR sendBuffer[8];
597     *(ULONG*)&sendBuffer[0] = htonl(4);
598     *(ULONG*)&sendBuffer[4] = htonl(0);
599     tcp.sendPacket(sendBuffer, 8);
600     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
601     return;
602   }
603
604   log->log("Client", Log::DEBUG, "Got schedule object");
605
606   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
607   ULONG sendBufferLength = 100000;
608   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
609
610   char* empty = "";
611
612   // assign all the event info to temp vars then we know exactly what size they are
613   ULONG thisEventID;
614   ULONG thisEventTime;
615   ULONG thisEventDuration;
616   const char* thisEventTitle;
617   const char* thisEventSubTitle;
618   const char* thisEventDescription;
619
620   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
621   ULONG thisEventLength;
622
623 #if VDRVERSNUM < 10300
624
625   const cEventInfo *event;
626   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
627   {
628     event = Schedule->GetEventNumber(eventNumber);
629
630     thisEventID = event->GetEventID();
631     thisEventTime = event->GetTime();
632     thisEventDuration = event->GetDuration();
633     thisEventTitle = event->GetTitle();
634     thisEventSubTitle = event->GetSubtitle();
635     thisEventDescription = event->GetExtendedDescription();
636
637 #else
638
639   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
640   {
641     thisEventID = event->EventID();
642     thisEventTime = event->StartTime();
643     thisEventDuration = event->Duration();
644     thisEventTitle = event->Title();
645     thisEventSubTitle = NULL;
646     thisEventDescription = event->Description();
647
648 #endif
649
650     log->log("Client", Log::DEBUG, "Got an event object %p", event);
651
652     //in the past filter
653     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
654
655     //24 hour filter
656     if (thisEventTime > ((ULONG)time(NULL) + 86400)) continue;
657
658     if (!thisEventTitle) thisEventTitle = empty;
659     if (!thisEventSubTitle) thisEventSubTitle = empty;
660     if (!thisEventDescription) thisEventDescription = empty;
661
662     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
663
664     log->log("Client", Log::DEBUG, "Done s1");
665
666     // now extend the buffer if necessary
667     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
668     {
669       log->log("Client", Log::DEBUG, "Extending buffer");
670       sendBufferLength += 100000;
671       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
672       if (temp == NULL)
673       {
674         free(sendBuffer);
675         UCHAR sendBuffer2[8];
676         *(ULONG*)&sendBuffer2[0] = htonl(4);
677         *(ULONG*)&sendBuffer2[4] = htonl(0);
678         tcp.sendPacket(sendBuffer2, 8);
679         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
680         return;
681       }
682       sendBuffer = temp;
683     }
684
685     log->log("Client", Log::DEBUG, "Done s2");
686
687     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
688     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
689     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
690
691     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
692     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
693     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
694
695     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
696   }
697
698   log->log("Client", Log::DEBUG, "Got all event data");
699
700   // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
701   *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
702
703   tcp.sendPacket(sendBuffer, sendBufferUsed);
704   log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
705
706   free(sendBuffer);
707
708   return;
709 }
710
711 void MVPClient::processConfigSave(UCHAR* buffer, int length)
712 {
713   char* section = (char*)buffer;
714   char* key = NULL;
715   char* value = NULL;
716
717   for (int k = 0; k < length; k++)
718   {
719     if (buffer[k] == '\0')
720     {
721       if (!key)
722       {
723         key = (char*)&buffer[k+1];
724       }
725       else
726       {
727         value = (char*)&buffer[k+1];
728         break;
729       }
730     }
731   }
732
733   // if the last string (value) doesnt have null terminator, give up
734   if (buffer[length - 1] != '\0') return;
735
736   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
737   if (config.setValueString(section, key, value))
738   {
739     sendULONG(1);
740   }
741   else
742   {
743     sendULONG(0);
744   }
745 }
746
747 void MVPClient::processConfigLoad(UCHAR* buffer, int length)
748 {
749   char* section = (char*)buffer;
750   char* key = NULL;
751
752   for (int k = 0; k < length; k++)
753   {
754     if (buffer[k] == '\0')
755     {
756       key = (char*)&buffer[k+1];
757       break;
758     }
759   }
760
761   char* value = config.getValueString(section, key);
762
763   if (value)
764   {
765     UCHAR sendBuffer[4 + strlen(value) + 1];
766     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
767     strcpy((char*)&sendBuffer[4], value);
768     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
769
770     log->log("Client", Log::DEBUG, "Written config load packet");
771     delete[] value;
772   }
773   else
774   {
775     UCHAR sendBuffer[8];
776     *(ULONG*)&sendBuffer[0] = htonl(4);
777     *(ULONG*)&sendBuffer[4] = htonl(0);
778     tcp.sendPacket(sendBuffer, 8);
779
780     log->log("Client", Log::DEBUG, "Written config load failed packet");
781   }
782 }
783
784 void MVPClient::cleanConfig()
785 {
786   log->log("Client", Log::DEBUG, "Clean config");
787
788   cRecordings Recordings;
789   Recordings.Load();
790
791   int numReturns;
792   int length;
793   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
794   char* position = resumes;
795   for(int k = 0; k < numReturns; k++)
796   {
797     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
798
799     cRecording* recording = Recordings.GetByName(position);
800     if (!recording)
801     {
802       // doesn't exist anymore
803       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
804       config.deleteValue("ResumeData", position);
805     }
806     else
807     {
808       log->log("Client", Log::DEBUG, "This recording still exists");
809     }
810
811     position += strlen(position) + 1;
812   }
813
814   delete[] resumes;
815 }
816
817
818
819
820
821
822 /*
823     event = Schedule->GetPresentEvent();
824
825     fprintf(f, "\n\nCurrent event\n\n");
826
827     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
828     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
829     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
830     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
831
832     event = Schedule->GetFollowingEvent();
833
834     fprintf(f, "\n\nFollowing event\n\n");
835
836     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
837     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
838     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
839     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
840
841     fprintf(f, "\n\n");
842 */
843
844 /*
845     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
846     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
847     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
848     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
849
850     fprintf(f, "\n\n");
851 */
852
853 /*
854
855
856 void MVPClient::test2()
857 {
858   FILE* f = fopen("/tmp/s.txt", "w");
859
860 #if VDRVERSNUM < 10300
861   cMutexLock MutexLock;
862   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
863 #else
864   cSchedulesLock MutexLock;
865   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
866 #endif
867
868   if (!Schedules)
869   {
870     fprintf(f, "Schedules = NULL\n");
871     fclose(f);
872     return;
873   }
874
875   fprintf(f, "Schedules dump:\n");
876   Schedules->Dump(f);
877
878
879   const cSchedule *Schedule;
880   int scheduleNumber = 0;
881
882   tChannelID tchid;
883   cChannel *thisChannel;
884
885 #if VDRVERSNUM < 10300
886   const cEventInfo *event;
887   int eventNumber = 0;
888 #else
889   const cEvent *event;
890 #endif
891
892 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
893 //    Schedule = Schedules->GetSchedule();
894   Schedule = Schedules->First();
895   if (!Schedule)
896   {
897     fprintf(f, "First Schedule = NULL\n");
898     fclose(f);
899     return;
900   }
901
902   while (Schedule)
903   {
904     fprintf(f, "Schedule #%i\n", scheduleNumber);
905     fprintf(f, "-------------\n\n");
906
907 #if VDRVERSNUM < 10300
908     tchid = Schedule->GetChannelID();
909 #else
910     tchid = Schedule->ChannelID();
911 #endif
912
913 #if VDRVERSNUM < 10300
914     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
915     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
916 #else
917 //  put the count at the end.
918 #endif
919
920     thisChannel = Channels.GetByChannelID(tchid, true);
921     if (thisChannel)
922     {
923       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
924     }
925     else
926     {
927       fprintf(f, "thisChannel = NULL for tchid\n");
928     }
929
930 #if VDRVERSNUM < 10300
931     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
932     {
933       event = Schedule->GetEventNumber(eventNumber);
934       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
935       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
936       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
937       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
938       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
939       fprintf(f, "Event %u dump:\n", eventNumber);
940       event->Dump(f);
941       fprintf(f, "\n\n");
942     }
943 #else
944 //  This whole section needs rewriting to walk the list.
945     event = Schedule->Events()->First();
946     while (event) {
947       event = Schedule->Events()->Next(event);
948     }
949 #endif
950
951
952     fprintf(f, "\nDump from object:\n");
953     Schedule->Dump(f);
954     fprintf(f, "\nEND\n");
955
956
957
958
959
960
961
962
963
964     fprintf(f, "End of current Schedule\n\n\n");
965
966     Schedule = (const cSchedule *)Schedules->Next(Schedule);
967     scheduleNumber++;
968   }
969
970   fclose(f);
971 }
972
973
974
975 */
976
977
978
979 /*
980   const cEventInfo *GetPresentEvent(void) const;
981   const cEventInfo *GetFollowingEvent(void) const;
982   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
983   const cEventInfo *GetEventAround(time_t tTime) const;
984   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
985
986
987   const unsigned char GetTableID(void) const;
988   const char *GetTimeString(void) const;
989   const char *GetEndTimeString(void) const;
990   const char *GetDate(void) const;
991   bool IsFollowing(void) const;
992   bool IsPresent(void) const;
993   const char *GetExtendedDescription(void) const;
994   const char *GetSubtitle(void) const;
995   const char *GetTitle(void) const;
996   unsigned short GetEventID(void) const;
997   long GetDuration(void) const;
998   time_t GetTime(void) const;
999   tChannelID GetChannelID(void) const;
1000   int GetChannelNumber(void) const { return nChannelNumber; }
1001   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1002   void Dump(FILE *f, const char *Prefix = "") const;
1003
1004 */
1005
1006
1007 /*
1008 void MVPClient::test(int channelNumber)
1009 {
1010   FILE* f = fopen("/tmp/test.txt", "w");
1011
1012   cMutexLock MutexLock;
1013   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1014
1015   if (!Schedules)
1016   {
1017     fprintf(f, "Schedules = NULL\n");
1018     fclose(f);
1019     return;
1020   }
1021
1022   fprintf(f, "Schedules dump:\n");
1023 //  Schedules->Dump(f);
1024
1025   const cSchedule *Schedule;
1026   cChannel *thisChannel;
1027   const cEventInfo *event;
1028
1029   thisChannel = channelFromNumber(channelNumber);
1030   if (!thisChannel)
1031   {
1032     fprintf(f, "thisChannel = NULL\n");
1033     fclose(f);
1034     return;
1035   }
1036
1037   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1038 //    Schedule = Schedules->GetSchedule();
1039 //  Schedule = Schedules->First();
1040   if (!Schedule)
1041   {
1042     fprintf(f, "First Schedule = NULL\n");
1043     fclose(f);
1044     return;
1045   }
1046
1047   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1048
1049   // For some channels VDR seems to pick a random point in time to
1050   // start dishing out events, but they are in order
1051   // at some point in the list the time snaps to the current event
1052
1053
1054
1055
1056   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1057   {
1058     event = Schedule->GetEventNumber(eventNumber);
1059     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1060     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1061     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1062     fprintf(f, "\n\n");
1063   }
1064
1065   fprintf(f, "\nEND\n");
1066
1067   fclose(f);
1068 }
1069
1070 */
1071
1072
1073
1074 /*
1075
1076
1077 Right, so
1078
1079 Schedules = the collection of all the Schedule objects
1080 Schedule  = One schedule, contants all the events for a channel
1081 Event     = One programme
1082
1083
1084 Want:
1085
1086 Event ID
1087 Time
1088 Duration
1089 Title
1090 Subtitle (used for "Programmes resume at ...")
1091 Description
1092
1093 IsPresent ? easy to work out tho. Oh it doesn't always work
1094
1095 */