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