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