]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Fix for chasing playback by allowing the client to rescan the length
[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 //  processGetChannelSchedule(NULL, 0);
60
61 //test();
62
63 }
64
65 MVPClient::~MVPClient()
66 {
67   log->log("Client", Log::DEBUG, "MVP client destructor");
68   if (lp)
69   {
70     delete lp;
71     lp = NULL;
72   }
73   else if (rp)
74   {
75     writeResumeData();
76
77     delete rp;
78     delete recordingManager;
79     rp = NULL;
80     recordingManager = NULL;
81   }
82
83   cleanConfig();
84 }
85
86 cChannel* MVPClient::channelFromNumber(unsigned long channelNumber)
87 {
88   cChannel* channel = NULL;
89
90   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
91   {
92     if (!channel->GroupSep())
93     {
94       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
95
96       if (channel->Number() == (int)channelNumber)
97       {
98         int vpid = channel->Vpid();
99 #if VDRVERSNUM < 10300
100         int apid1 = channel->Apid1();
101 #else
102         int apid1 = channel->Apid(0);
103 #endif
104         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
105         return channel;
106       }
107     }
108   }
109
110   if (!channel)
111   {
112     log->log("Client", Log::DEBUG, "Channel not found");
113   }
114
115   return channel;
116 }
117
118
119 void MVPClient::writeResumeData()
120 {
121   config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
122 }
123
124 void MVPClient::sendULONG(ULONG ul)
125 {
126   unsigned char sendBuffer[8];
127   *(unsigned long*)&sendBuffer[0] = htonl(4);
128   *(unsigned long*)&sendBuffer[4] = htonl(ul);
129
130   tcp.sendPacket(sendBuffer, 8);
131   log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
132 }
133
134 void MVPClientStartThread(void* arg)
135 {
136   MVPClient* m = (MVPClient*)arg;
137   m->run2();
138   // Nothing external to this class has a reference to it
139   // This is the end of the thread.. so delete m
140   delete m;
141   pthread_exit(NULL);
142 }
143
144 int MVPClient::run()
145 {
146   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
147   log->log("Client", Log::DEBUG, "MVPClient run success");
148   return 1;
149 }
150
151 void MVPClient::run2()
152 {
153   // Thread stuff
154   sigset_t sigset;
155   sigfillset(&sigset);
156   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
157   pthread_detach(runThread);  // Detach
158
159   tcp.disableReadTimeout();
160
161   tcp.setSoKeepTime(3);
162   tcp.setNonBlocking();
163
164   unsigned char* buffer;
165   unsigned char* data;
166   int packetLength;
167   unsigned long opcode;
168
169   while(1)
170   {
171     log->log("Client", Log::DEBUG, "Waiting");
172     buffer = (unsigned char*)tcp.receivePacket();
173     log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
174     if (buffer == NULL)
175     {
176       log->log("Client", Log::DEBUG, "Detected connection closed");
177       break;
178     }
179
180     packetLength = tcp.getDataLength() - 4;
181     opcode = ntohl(*(unsigned long*)buffer);
182     data = buffer + 4;
183
184
185     switch(opcode)
186     {
187       case 1:
188         processLogin(data, packetLength);
189         break;
190       case 2:
191         processGetRecordingsList(data, packetLength);
192         break;
193       case 3:
194         processDeleteRecording(data, packetLength);
195         break;
196       case 4:
197         processGetSummary(data, packetLength);
198         break;
199       case 5:
200         processGetChannelsList(data, packetLength);
201         break;
202       case 6:
203         processStartStreamingChannel(data, packetLength);
204         break;
205       case 7:
206         processGetBlock(data, packetLength);
207         break;
208       case 8:
209         processStopStreaming(data, packetLength);
210         break;
211       case 9:
212         processStartStreamingRecording(data, packetLength);
213         break;
214       case 10:
215         processGetChannelSchedule(data, packetLength);
216         break;
217       case 11:
218         processConfigSave(data, packetLength);
219         break;
220       case 12:
221         processConfigLoad(data, packetLength);
222         break;
223       case 13:
224         processReScanRecording(data, packetLength);
225         break;
226     }
227
228     free(buffer);
229   }
230 }
231
232 void MVPClient::processLogin(unsigned char* buffer, int length)
233 {
234   time_t timeNow = time(NULL);
235   struct tm* timeStruct = localtime(&timeNow);
236   int timeOffset = timeStruct->tm_gmtoff;
237
238   unsigned char sendBuffer[12];
239   *(unsigned long*)&sendBuffer[0] = htonl(8);
240   *(unsigned long*)&sendBuffer[4] = htonl(timeNow);
241   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
242
243   tcp.sendPacket(sendBuffer, 12);
244   log->log("Client", Log::DEBUG, "written login reply");
245 }
246
247 void MVPClient::processGetRecordingsList(unsigned char* data, int length)
248 {
249   unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
250   int count = 4; // leave space for the packet length
251   char* point;
252
253
254   int FreeMB;
255   int Percent = VideoDiskSpace(&FreeMB);
256   int Total = (FreeMB / (100 - Percent)) * 100;
257
258   *(unsigned long*)&sendBuffer[count] = htonl(Total);
259   count += sizeof(unsigned long);
260   *(unsigned long*)&sendBuffer[count] = htonl(FreeMB);
261   count += sizeof(unsigned long);
262   *(unsigned long*)&sendBuffer[count] = htonl(Percent);
263   count += sizeof(unsigned long);
264
265
266   cRecordings Recordings;
267   Recordings.Load();
268
269   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
270   {
271     if (count > 49000) break; // just how big is that hard disk?!
272     *(unsigned long*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
273     count += 4;
274
275     point = (char*)recording->Name();
276     strcpy((char*)&sendBuffer[count], point);
277     count += strlen(point) + 1;
278
279     point = (char*)recording->FileName();
280     strcpy((char*)&sendBuffer[count], point);
281     count += strlen(point) + 1;
282   }
283
284   *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
285
286   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
287
288   tcp.sendPacket(sendBuffer, count);
289   delete[] sendBuffer;
290   log->log("Client", Log::DEBUG, "Written list");
291 }
292
293 void MVPClient::processDeleteRecording(unsigned char* data, int length)
294 {
295   // data is a pointer to the fileName string
296
297   cRecordings Recordings;
298   Recordings.Load(); // probably have to do this
299
300   cRecording* recording = Recordings.GetByName((char*)data);
301
302   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
303
304   if (recording)
305   {
306     log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
307     recording->Delete();
308     sendULONG(1);
309   }
310   else
311   {
312     sendULONG(0);
313   }
314 }
315
316 void MVPClient::processGetSummary(unsigned char* data, int length)
317 {
318   // data is a pointer to the fileName string
319
320   cRecordings Recordings;
321   Recordings.Load(); // probably have to do this
322
323   cRecording *recording = Recordings.GetByName((char*)data);
324
325   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
326
327   if (recording)
328   {
329     unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
330     int count = 4; // leave space for the packet length
331
332     char* point;
333 #if VDRVERSNUM < 10300
334     point = (char*)recording->Summary();
335 #else
336     const cRecordingInfo *Info = recording->Info();
337     point = (char*)Info->ShortText();
338     log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
339     if (isempty(point))
340     {
341       point = (char*)Info->Description();
342       log->log("Client", Log::DEBUG, "description pointer %p", point);
343     }
344 #endif
345     strcpy((char*)&sendBuffer[count], point);
346     count += strlen(point) + 1;
347     *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
348
349     log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
350
351     tcp.sendPacket(sendBuffer, count);
352     delete[] sendBuffer;
353     log->log("Client", Log::DEBUG, "Written summary");
354
355
356   }
357   else
358   {
359     sendULONG(0);
360   }
361 }
362
363 void MVPClient::processGetChannelsList(unsigned char* data, int length)
364 {
365   unsigned char* sendBuffer = new unsigned char[50000]; // FIXME hope this is enough
366   int count = 4; // leave space for the packet length
367   char* point;
368   unsigned long type;
369
370   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
371   {
372 #if VDRVERSNUM < 10300
373     if (!channel->GroupSep() && !channel->Ca())
374 #else
375     if (!channel->GroupSep() && !channel->Ca(0))
376 #endif
377     {
378       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
379
380       if (channel->Vpid()) type = 1;
381 #if VDRVERSNUM < 10300
382       else type = 2;
383 #else
384       else if (channel->Apid(0)) type = 2;
385       else continue;
386 #endif
387
388       if (count > 49000) break;
389       *(unsigned long*)&sendBuffer[count] = htonl(channel->Number());
390       count += 4;
391
392       *(unsigned long*)&sendBuffer[count] = htonl(type);
393       count += 4;
394
395       point = (char*)channel->Name();
396       strcpy((char*)&sendBuffer[count], point);
397       count += strlen(point) + 1;
398     }
399   }
400
401   *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
402
403   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
404
405   tcp.sendPacket(sendBuffer, count);
406   delete[] sendBuffer;
407   log->log("Client", Log::DEBUG, "Written channels list");
408 }
409
410 void MVPClient::processStartStreamingChannel(unsigned char* data, int length)
411 {
412   log->log("Client", Log::DEBUG, "length = %i", length);
413   unsigned long channelNumber = ntohl(*(unsigned long*)data);
414
415   cChannel* channel = channelFromNumber(channelNumber);
416   if (!channel)
417   {
418     sendULONG(0);
419     return;
420   }
421
422   lp = MVPReceiver::create(channel);
423
424   if (!lp)
425   {
426     sendULONG(0);
427     return;
428   }
429
430   if (!lp->init())
431   {
432     delete lp;
433     lp = NULL;
434     sendULONG(0);
435     return;
436   }
437
438   sendULONG(1);
439 }
440
441 void MVPClient::processStopStreaming(unsigned char* data, int length)
442 {
443   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
444   if (lp)
445   {
446     delete lp;
447     lp = NULL;
448   }
449   else if (rp)
450   {
451     writeResumeData();
452
453     delete rp;
454     delete recordingManager;
455     rp = NULL;
456     recordingManager = NULL;
457   }
458
459   sendULONG(1);
460 }
461
462 void MVPClient::processGetBlock(unsigned char* data, int length)
463 {
464   if (!lp && !rp)
465   {
466     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
467     return;
468   }
469
470   ULLONG position = ntohll(*(ULLONG*)data);
471   data += sizeof(ULLONG);
472   unsigned long amount = ntohl(*(unsigned long*)data);
473
474   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
475
476   unsigned char sendBuffer[amount + 4];
477   unsigned long amountReceived = 0; // compiler moan.
478   if (lp)
479   {
480     log->log("Client", Log::DEBUG, "getting from live");
481     amountReceived = lp->getBlock(&sendBuffer[4], amount);
482
483     if (!amountReceived)
484     {
485       // vdr has possibly disconnected the receiver
486       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
487       delete lp;
488       lp = NULL;
489     }
490   }
491   else if (rp)
492   {
493     log->log("Client", Log::DEBUG, "getting from recording");
494     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
495   }
496
497   *(unsigned long*)&sendBuffer[0] = htonl(amountReceived);
498   tcp.sendPacket(sendBuffer, amountReceived + 4);
499   log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
500 }
501
502 void MVPClient::processStartStreamingRecording(unsigned char* data, int length)
503 {
504   // data is a pointer to the fileName string
505
506   recordingManager = new cRecordings;
507   recordingManager->Load();
508
509   cRecording* recording = recordingManager->GetByName((char*)data);
510
511   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
512
513   if (recording)
514   {
515     rp = new RecPlayer(recording);
516
517     unsigned char sendBuffer[12];
518     *(unsigned long*)&sendBuffer[0] = htonl(8);
519     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
520
521     tcp.sendPacket(sendBuffer, 12);
522     log->log("Client", Log::DEBUG, "written totalLength");
523   }
524   else
525   {
526     delete recordingManager;
527     recordingManager = NULL;
528   }
529 }
530
531 void MVPClient::processReScanRecording(unsigned char* data, int length)
532 {
533   ULLONG retval = 0;
534
535   if (!rp)
536   {
537     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
538   }
539   else
540   {
541     rp->scan();
542     retval = rp->getTotalLength();
543   }
544
545   unsigned char sendBuffer[12];
546   *(unsigned long*)&sendBuffer[0] = htonl(8);
547   *(ULLONG*)&sendBuffer[4] = htonll(retval);
548
549   tcp.sendPacket(sendBuffer, 12);
550   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
551 }
552
553 void MVPClient::processGetChannelSchedule(unsigned char* data, int length)
554 {
555   ULONG channelNumber = ntohl(*(ULLONG*)data);
556   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
557
558   cChannel* channel = channelFromNumber(channelNumber);
559   if (!channel)
560   {
561     unsigned char sendBuffer[4];
562     *(unsigned long*)&sendBuffer[0] = htonl(0);
563     tcp.sendPacket(sendBuffer, 4);
564     log->log("Client", Log::DEBUG, "written null");
565     return;
566   }
567
568 #if VDRVERSNUM < 10300
569   cMutexLock MutexLock;
570   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
571 #else
572   cSchedulesLock MutexLock;
573   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
574 #endif
575   if (!Schedules)
576   {
577     unsigned char sendBuffer[8];
578     *(unsigned long*)&sendBuffer[0] = htonl(4);
579     *(unsigned long*)&sendBuffer[4] = htonl(0);
580     tcp.sendPacket(sendBuffer, 8);
581     log->log("Client", Log::DEBUG, "written 0");
582     return;
583   }
584
585   unsigned char sendBuffer[8];
586   *(unsigned long*)&sendBuffer[0] = htonl(4);
587   *(unsigned long*)&sendBuffer[4] = htonl(1);
588   tcp.sendPacket(sendBuffer, 8);
589   log->log("Client", Log::DEBUG, "written 1");
590
591
592 }
593
594 void MVPClient::testChannelSchedule(unsigned char* data, int length)
595 {
596   FILE* f = fopen("/tmp/s.txt", "w");
597
598 #if VDRVERSNUM < 10300
599   cMutexLock MutexLock;
600   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
601 #else
602   cSchedulesLock MutexLock;
603   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
604 #endif
605   if (!Schedules)
606   {
607     fprintf(f, "Schedules = NULL\n");
608     fclose(f);
609     return;
610   }
611
612   fprintf(f, "Schedules dump:\n");
613   Schedules->Dump(f);
614
615
616   const cSchedule *Schedule;
617   int scheduleNumber = 0;
618
619   tChannelID tchid;
620   cChannel *thisChannel;
621
622 #if VDRVERSNUM < 10300
623   const cEventInfo *event;
624   int eventNumber = 0;
625 #else
626   const cEvent *event;
627 #endif
628
629 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
630 //    Schedule = Schedules->GetSchedule();
631   Schedule = Schedules->First();
632   if (!Schedule)
633   {
634     fprintf(f, "First Schedule = NULL\n");
635     fclose(f);
636     return;
637   }
638
639   while (Schedule)
640   {
641     fprintf(f, "Schedule #%i\n", scheduleNumber);
642     fprintf(f, "-------------\n\n");
643
644 #if VDRVERSNUM < 10300
645     tchid = Schedule->GetChannelID();
646 #else
647     tchid = Schedule->ChannelID();
648 #endif
649 #if VDRVERSNUM < 10300
650     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
651     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
652 #else
653 //  put the count at the end.
654 #endif
655     thisChannel = Channels.GetByChannelID(tchid, true);
656     if (thisChannel)
657     {
658       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
659     }
660     else
661     {
662       fprintf(f, "thisChannel = NULL for tchid\n");
663     }
664
665 #if VDRVERSNUM < 10300
666     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
667     {
668       event = Schedule->GetEventNumber(eventNumber);
669       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
670       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
671       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
672       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
673       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
674       fprintf(f, "Event %u dump:\n", eventNumber);
675       event->Dump(f);
676       fprintf(f, "\n\n");
677     }
678 #else
679 //  This whole section needs rewriting to walk the list.
680     event = Schedule->Events()->First();
681     while (event) {
682       event = Schedule->Events()->Next(event);
683     }
684 #endif
685
686
687     fprintf(f, "\nDump from object:\n");
688     Schedule->Dump(f);
689     fprintf(f, "\nEND\n");
690
691
692
693
694 /*
695   const cEventInfo *GetPresentEvent(void) const;
696   const cEventInfo *GetFollowingEvent(void) const;
697   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
698   const cEventInfo *GetEventAround(time_t tTime) const;
699   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
700
701
702   const unsigned char GetTableID(void) const;
703   const char *GetTimeString(void) const;
704   const char *GetEndTimeString(void) const;
705   const char *GetDate(void) const;
706   bool IsFollowing(void) const;
707   bool IsPresent(void) const;
708   const char *GetExtendedDescription(void) const;
709   const char *GetSubtitle(void) const;
710   const char *GetTitle(void) const;
711   unsigned short GetEventID(void) const;
712   long GetDuration(void) const;
713   time_t GetTime(void) const;
714   tChannelID GetChannelID(void) const;
715   int GetChannelNumber(void) const { return nChannelNumber; }
716   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
717   void Dump(FILE *f, const char *Prefix = "") const;
718
719 */
720
721
722
723
724
725     fprintf(f, "End of current Schedule\n\n\n");
726
727     Schedule = (const cSchedule *)Schedules->Next(Schedule);
728     scheduleNumber++;
729   }
730
731   fclose(f);
732 }
733
734 void MVPClient::processConfigSave(unsigned char* buffer, int length)
735 {
736   char* section = (char*)buffer;
737   char* key = NULL;
738   char* value = NULL;
739
740   for (int k = 0; k < length; k++)
741   {
742     if (buffer[k] == '\0')
743     {
744       if (!key)
745       {
746         key = (char*)&buffer[k+1];
747       }
748       else
749       {
750         value = (char*)&buffer[k+1];
751         break;
752       }
753     }
754   }
755
756   // if the last string (value) doesnt have null terminator, give up
757   if (buffer[length - 1] != '\0') return;
758
759   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
760   if (config.setValueString(section, key, value))
761   {
762     sendULONG(1);
763   }
764   else
765   {
766     sendULONG(0);
767   }
768 }
769
770 void MVPClient::processConfigLoad(unsigned char* buffer, int length)
771 {
772   char* section = (char*)buffer;
773   char* key = NULL;
774
775   for (int k = 0; k < length; k++)
776   {
777     if (buffer[k] == '\0')
778     {
779       key = (char*)&buffer[k+1];
780       break;
781     }
782   }
783
784   char* value = config.getValueString(section, key);
785
786   if (value)
787   {
788     unsigned char sendBuffer[4 + strlen(value) + 1];
789     *(unsigned long*)&sendBuffer[0] = htonl(strlen(value) + 1);
790     strcpy((char*)&sendBuffer[4], value);
791     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
792
793     log->log("Client", Log::DEBUG, "Written config load packet");
794     delete[] value;
795   }
796   else
797   {
798     unsigned char sendBuffer[8];
799     *(unsigned long*)&sendBuffer[0] = htonl(4);
800     *(unsigned long*)&sendBuffer[4] = htonl(0);
801     tcp.sendPacket(sendBuffer, 8);
802
803     log->log("Client", Log::DEBUG, "Written config load failed packet");
804   }
805 }
806
807 void MVPClient::cleanConfig()
808 {
809   log->log("Client", Log::DEBUG, "Clean config");
810
811   cRecordings Recordings;
812   Recordings.Load();
813
814   int numReturns;
815   int length;
816   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
817   char* position = resumes;
818   for(int k = 0; k < numReturns; k++)
819   {
820     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
821
822     cRecording* recording = Recordings.GetByName(position);
823     if (!recording)
824     {
825       // doesn't exist anymore
826       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
827       config.deleteValue("ResumeData", position);
828     }
829     else
830     {
831       log->log("Client", Log::DEBUG, "This recording still exists");
832     }
833
834     position += strlen(position) + 1;
835   }
836
837   delete[] resumes;
838 }
839
840