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