]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
*** empty log message ***
[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   // get the priority we should use
426   int fail = 1;
427   int priority = config.getValueLong("General", "Live priority", &fail);
428   if (!fail)
429   {
430     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
431   }
432   else
433   {
434     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
435     priority = 0;
436   }
437
438   // a bit of sanity..
439   if (priority < 0) priority = 0;
440   if (priority > 99) priority = 99;
441
442   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
443   lp = MVPReceiver::create(channel, priority);
444
445   if (!lp)
446   {
447     sendULONG(0);
448     return;
449   }
450
451   if (!lp->init())
452   {
453     delete lp;
454     lp = NULL;
455     sendULONG(0);
456     return;
457   }
458
459   sendULONG(1);
460 }
461
462 void MVPClient::processStopStreaming(UCHAR* data, int length)
463 {
464   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
465   if (lp)
466   {
467     delete lp;
468     lp = NULL;
469   }
470   else if (rp)
471   {
472     writeResumeData();
473
474     delete rp;
475     delete recordingManager;
476     rp = NULL;
477     recordingManager = NULL;
478   }
479
480   sendULONG(1);
481 }
482
483 void MVPClient::processGetBlock(UCHAR* data, int length)
484 {
485   if (!lp && !rp)
486   {
487     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
488     return;
489   }
490
491   ULLONG position = ntohll(*(ULLONG*)data);
492   data += sizeof(ULLONG);
493   ULONG amount = ntohl(*(ULONG*)data);
494
495   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
496
497   UCHAR sendBuffer[amount + 4];
498   ULONG amountReceived = 0; // compiler moan.
499   if (lp)
500   {
501     log->log("Client", Log::DEBUG, "getting from live");
502     amountReceived = lp->getBlock(&sendBuffer[4], amount);
503
504     if (!amountReceived)
505     {
506       // vdr has possibly disconnected the receiver
507       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
508       delete lp;
509       lp = NULL;
510     }
511   }
512   else if (rp)
513   {
514     log->log("Client", Log::DEBUG, "getting from recording");
515     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
516   }
517
518   *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
519   tcp.sendPacket(sendBuffer, amountReceived + 4);
520   log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
521 }
522
523 void MVPClient::processStartStreamingRecording(UCHAR* data, int length)
524 {
525   // data is a pointer to the fileName string
526
527   recordingManager = new cRecordings;
528   recordingManager->Load();
529
530   cRecording* recording = recordingManager->GetByName((char*)data);
531
532   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
533
534   if (recording)
535   {
536     rp = new RecPlayer(recording);
537
538     UCHAR sendBuffer[12];
539     *(ULONG*)&sendBuffer[0] = htonl(8);
540     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
541
542     tcp.sendPacket(sendBuffer, 12);
543     log->log("Client", Log::DEBUG, "written totalLength");
544   }
545   else
546   {
547     delete recordingManager;
548     recordingManager = NULL;
549   }
550 }
551
552 void MVPClient::processReScanRecording(UCHAR* data, int length)
553 {
554   ULLONG retval = 0;
555
556   if (!rp)
557   {
558     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
559   }
560   else
561   {
562     rp->scan();
563     retval = rp->getTotalLength();
564   }
565
566   UCHAR sendBuffer[12];
567   *(ULONG*)&sendBuffer[0] = htonl(8);
568   *(ULLONG*)&sendBuffer[4] = htonll(retval);
569
570   tcp.sendPacket(sendBuffer, 12);
571   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
572 }
573
574 void MVPClient::processGetChannelSchedule(UCHAR* data, int length)
575 {
576   ULONG channelNumber = ntohl(*(ULLONG*)data);
577   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
578
579   cChannel* channel = channelFromNumber(channelNumber);
580   if (!channel)
581   {
582     UCHAR sendBuffer[4];
583     *(ULONG*)&sendBuffer[0] = htonl(4);
584     *(ULONG*)&sendBuffer[4] = htonl(0);
585     tcp.sendPacket(sendBuffer, 8);
586     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
587     return;
588   }
589
590   log->log("Client", Log::DEBUG, "Got channel");
591
592 #if VDRVERSNUM < 10300
593   cMutexLock MutexLock;
594   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
595 #else
596   cSchedulesLock MutexLock;
597   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
598 #endif
599   if (!Schedules)
600   {
601     UCHAR sendBuffer[8];
602     *(ULONG*)&sendBuffer[0] = htonl(4);
603     *(ULONG*)&sendBuffer[4] = htonl(0);
604     tcp.sendPacket(sendBuffer, 8);
605     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
606     return;
607   }
608
609   log->log("Client", Log::DEBUG, "Got schedule!s! object");
610
611   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
612   if (!Schedule)
613   {
614     UCHAR sendBuffer[8];
615     *(ULONG*)&sendBuffer[0] = htonl(4);
616     *(ULONG*)&sendBuffer[4] = htonl(0);
617     tcp.sendPacket(sendBuffer, 8);
618     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
619     return;
620   }
621
622   log->log("Client", Log::DEBUG, "Got schedule object");
623
624   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
625   ULONG sendBufferLength = 100000;
626   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
627
628   char* empty = "";
629
630   // assign all the event info to temp vars then we know exactly what size they are
631   ULONG thisEventID;
632   ULONG thisEventTime;
633   ULONG thisEventDuration;
634   const char* thisEventTitle;
635   const char* thisEventSubTitle;
636   const char* thisEventDescription;
637
638   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
639   ULONG thisEventLength;
640
641 #if VDRVERSNUM < 10300
642
643   const cEventInfo *event;
644   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
645   {
646     event = Schedule->GetEventNumber(eventNumber);
647
648     thisEventID = event->GetEventID();
649     thisEventTime = event->GetTime();
650     thisEventDuration = event->GetDuration();
651     thisEventTitle = event->GetTitle();
652     thisEventSubTitle = event->GetSubtitle();
653     thisEventDescription = event->GetExtendedDescription();
654
655 #else
656
657   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
658   {
659     thisEventID = event->EventID();
660     thisEventTime = event->StartTime();
661     thisEventDuration = event->Duration();
662     thisEventTitle = event->Title();
663     thisEventSubTitle = NULL;
664     thisEventDescription = event->Description();
665
666 #endif
667
668     log->log("Client", Log::DEBUG, "Got an event object %p", event);
669
670     //in the past filter
671     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
672
673     //24 hour filter
674     if (thisEventTime > ((ULONG)time(NULL) + 86400)) continue;
675
676     if (!thisEventTitle) thisEventTitle = empty;
677     if (!thisEventSubTitle) thisEventSubTitle = empty;
678     if (!thisEventDescription) thisEventDescription = empty;
679
680     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
681
682     log->log("Client", Log::DEBUG, "Done s1");
683
684     // now extend the buffer if necessary
685     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
686     {
687       log->log("Client", Log::DEBUG, "Extending buffer");
688       sendBufferLength += 100000;
689       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
690       if (temp == NULL)
691       {
692         free(sendBuffer);
693         UCHAR sendBuffer2[8];
694         *(ULONG*)&sendBuffer2[0] = htonl(4);
695         *(ULONG*)&sendBuffer2[4] = htonl(0);
696         tcp.sendPacket(sendBuffer2, 8);
697         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
698         return;
699       }
700       sendBuffer = temp;
701     }
702
703     log->log("Client", Log::DEBUG, "Done s2");
704
705     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
706     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
707     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
708
709     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
710     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
711     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
712
713     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
714   }
715
716   log->log("Client", Log::DEBUG, "Got all event data");
717
718   // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
719   *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
720
721   tcp.sendPacket(sendBuffer, sendBufferUsed);
722   log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
723
724   free(sendBuffer);
725
726   return;
727 }
728
729 void MVPClient::processConfigSave(UCHAR* buffer, int length)
730 {
731   char* section = (char*)buffer;
732   char* key = NULL;
733   char* value = NULL;
734
735   for (int k = 0; k < length; k++)
736   {
737     if (buffer[k] == '\0')
738     {
739       if (!key)
740       {
741         key = (char*)&buffer[k+1];
742       }
743       else
744       {
745         value = (char*)&buffer[k+1];
746         break;
747       }
748     }
749   }
750
751   // if the last string (value) doesnt have null terminator, give up
752   if (buffer[length - 1] != '\0') return;
753
754   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
755   if (config.setValueString(section, key, value))
756   {
757     sendULONG(1);
758   }
759   else
760   {
761     sendULONG(0);
762   }
763 }
764
765 void MVPClient::processConfigLoad(UCHAR* buffer, int length)
766 {
767   char* section = (char*)buffer;
768   char* key = NULL;
769
770   for (int k = 0; k < length; k++)
771   {
772     if (buffer[k] == '\0')
773     {
774       key = (char*)&buffer[k+1];
775       break;
776     }
777   }
778
779   char* value = config.getValueString(section, key);
780
781   if (value)
782   {
783     UCHAR sendBuffer[4 + strlen(value) + 1];
784     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
785     strcpy((char*)&sendBuffer[4], value);
786     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
787
788     log->log("Client", Log::DEBUG, "Written config load packet");
789     delete[] value;
790   }
791   else
792   {
793     UCHAR sendBuffer[8];
794     *(ULONG*)&sendBuffer[0] = htonl(4);
795     *(ULONG*)&sendBuffer[4] = htonl(0);
796     tcp.sendPacket(sendBuffer, 8);
797
798     log->log("Client", Log::DEBUG, "Written config load failed packet");
799   }
800 }
801
802 void MVPClient::cleanConfig()
803 {
804   log->log("Client", Log::DEBUG, "Clean config");
805
806   cRecordings Recordings;
807   Recordings.Load();
808
809   int numReturns;
810   int length;
811   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
812   char* position = resumes;
813   for(int k = 0; k < numReturns; k++)
814   {
815     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
816
817     cRecording* recording = Recordings.GetByName(position);
818     if (!recording)
819     {
820       // doesn't exist anymore
821       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
822       config.deleteValue("ResumeData", position);
823     }
824     else
825     {
826       log->log("Client", Log::DEBUG, "This recording still exists");
827     }
828
829     position += strlen(position) + 1;
830   }
831
832   delete[] resumes;
833 }
834
835
836
837
838
839
840 /*
841     event = Schedule->GetPresentEvent();
842
843     fprintf(f, "\n\nCurrent event\n\n");
844
845     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
846     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
847     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
848     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
849
850     event = Schedule->GetFollowingEvent();
851
852     fprintf(f, "\n\nFollowing event\n\n");
853
854     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
855     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
856     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
857     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
858
859     fprintf(f, "\n\n");
860 */
861
862 /*
863     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
864     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
865     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
866     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
867
868     fprintf(f, "\n\n");
869 */
870
871 /*
872
873
874 void MVPClient::test2()
875 {
876   FILE* f = fopen("/tmp/s.txt", "w");
877
878 #if VDRVERSNUM < 10300
879   cMutexLock MutexLock;
880   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
881 #else
882   cSchedulesLock MutexLock;
883   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
884 #endif
885
886   if (!Schedules)
887   {
888     fprintf(f, "Schedules = NULL\n");
889     fclose(f);
890     return;
891   }
892
893   fprintf(f, "Schedules dump:\n");
894   Schedules->Dump(f);
895
896
897   const cSchedule *Schedule;
898   int scheduleNumber = 0;
899
900   tChannelID tchid;
901   cChannel *thisChannel;
902
903 #if VDRVERSNUM < 10300
904   const cEventInfo *event;
905   int eventNumber = 0;
906 #else
907   const cEvent *event;
908 #endif
909
910 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
911 //    Schedule = Schedules->GetSchedule();
912   Schedule = Schedules->First();
913   if (!Schedule)
914   {
915     fprintf(f, "First Schedule = NULL\n");
916     fclose(f);
917     return;
918   }
919
920   while (Schedule)
921   {
922     fprintf(f, "Schedule #%i\n", scheduleNumber);
923     fprintf(f, "-------------\n\n");
924
925 #if VDRVERSNUM < 10300
926     tchid = Schedule->GetChannelID();
927 #else
928     tchid = Schedule->ChannelID();
929 #endif
930
931 #if VDRVERSNUM < 10300
932     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
933     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
934 #else
935 //  put the count at the end.
936 #endif
937
938     thisChannel = Channels.GetByChannelID(tchid, true);
939     if (thisChannel)
940     {
941       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
942     }
943     else
944     {
945       fprintf(f, "thisChannel = NULL for tchid\n");
946     }
947
948 #if VDRVERSNUM < 10300
949     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
950     {
951       event = Schedule->GetEventNumber(eventNumber);
952       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
953       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
954       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
955       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
956       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
957       fprintf(f, "Event %u dump:\n", eventNumber);
958       event->Dump(f);
959       fprintf(f, "\n\n");
960     }
961 #else
962 //  This whole section needs rewriting to walk the list.
963     event = Schedule->Events()->First();
964     while (event) {
965       event = Schedule->Events()->Next(event);
966     }
967 #endif
968
969
970     fprintf(f, "\nDump from object:\n");
971     Schedule->Dump(f);
972     fprintf(f, "\nEND\n");
973
974
975
976
977
978
979
980
981
982     fprintf(f, "End of current Schedule\n\n\n");
983
984     Schedule = (const cSchedule *)Schedules->Next(Schedule);
985     scheduleNumber++;
986   }
987
988   fclose(f);
989 }
990
991
992
993 */
994
995
996
997 /*
998   const cEventInfo *GetPresentEvent(void) const;
999   const cEventInfo *GetFollowingEvent(void) const;
1000   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1001   const cEventInfo *GetEventAround(time_t tTime) const;
1002   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1003
1004
1005   const unsigned char GetTableID(void) const;
1006   const char *GetTimeString(void) const;
1007   const char *GetEndTimeString(void) const;
1008   const char *GetDate(void) const;
1009   bool IsFollowing(void) const;
1010   bool IsPresent(void) const;
1011   const char *GetExtendedDescription(void) const;
1012   const char *GetSubtitle(void) const;
1013   const char *GetTitle(void) const;
1014   unsigned short GetEventID(void) const;
1015   long GetDuration(void) const;
1016   time_t GetTime(void) const;
1017   tChannelID GetChannelID(void) const;
1018   int GetChannelNumber(void) const { return nChannelNumber; }
1019   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1020   void Dump(FILE *f, const char *Prefix = "") const;
1021
1022 */
1023
1024
1025 /*
1026 void MVPClient::test(int channelNumber)
1027 {
1028   FILE* f = fopen("/tmp/test.txt", "w");
1029
1030   cMutexLock MutexLock;
1031   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1032
1033   if (!Schedules)
1034   {
1035     fprintf(f, "Schedules = NULL\n");
1036     fclose(f);
1037     return;
1038   }
1039
1040   fprintf(f, "Schedules dump:\n");
1041 //  Schedules->Dump(f);
1042
1043   const cSchedule *Schedule;
1044   cChannel *thisChannel;
1045   const cEventInfo *event;
1046
1047   thisChannel = channelFromNumber(channelNumber);
1048   if (!thisChannel)
1049   {
1050     fprintf(f, "thisChannel = NULL\n");
1051     fclose(f);
1052     return;
1053   }
1054
1055   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1056 //    Schedule = Schedules->GetSchedule();
1057 //  Schedule = Schedules->First();
1058   if (!Schedule)
1059   {
1060     fprintf(f, "First Schedule = NULL\n");
1061     fclose(f);
1062     return;
1063   }
1064
1065   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1066
1067   // For some channels VDR seems to pick a random point in time to
1068   // start dishing out events, but they are in order
1069   // at some point in the list the time snaps to the current event
1070
1071
1072
1073
1074   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1075   {
1076     event = Schedule->GetEventNumber(eventNumber);
1077     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1078     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1079     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1080     fprintf(f, "\n\n");
1081   }
1082
1083   fprintf(f, "\nEND\n");
1084
1085   fclose(f);
1086 }
1087
1088 */
1089
1090
1091
1092 /*
1093
1094
1095 Right, so
1096
1097 Schedules = the collection of all the Schedule objects
1098 Schedule  = One schedule, contants all the events for a channel
1099 Event     = One programme
1100
1101
1102 Want:
1103
1104 Event ID
1105 Time
1106 Duration
1107 Title
1108 Subtitle (used for "Programmes resume at ...")
1109 Description
1110
1111 IsPresent ? easy to work out tho. Oh it doesn't always work
1112
1113 */