]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Fix compat with vdr 1.2
[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 #if VDRVERSNUM > 10300
349         ::Recordings.DelByName(recording->FileName());
350 #endif
351         sendULONG(1);
352       }
353       else
354       {
355         sendULONG(2);
356       }
357     }
358     else
359     {
360       sendULONG(3);
361     }
362   }
363   else
364   {
365     sendULONG(4);
366   }
367
368   return 1;
369 }
370
371 int MVPClient::processGetSummary(UCHAR* data, int length)
372 {
373   // data is a pointer to the fileName string
374
375   cRecordings Recordings;
376   Recordings.Load(); // probably have to do this
377
378   cRecording *recording = Recordings.GetByName((char*)data);
379
380   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
381
382   if (recording)
383   {
384     UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
385     int count = 4; // leave space for the packet length
386     char* point;
387
388 #if VDRVERSNUM < 10300
389     point = (char*)recording->Summary();
390 #else
391     const cRecordingInfo *Info = recording->Info();
392     point = (char*)Info->ShortText();
393     log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
394     if (isempty(point))
395     {
396       point = (char*)Info->Description();
397       log->log("Client", Log::DEBUG, "description pointer %p", point);
398     }
399 #endif
400
401     if (point)
402     {
403       strcpy((char*)&sendBuffer[count], point);
404       count += strlen(point) + 1;
405     }
406     else
407     {
408       strcpy((char*)&sendBuffer[count], "");
409       count += 1;
410     }
411
412     *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
413
414     log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
415
416     tcp.sendPacket(sendBuffer, count);
417     delete[] sendBuffer;
418     log->log("Client", Log::DEBUG, "Written summary");
419
420
421   }
422   else
423   {
424     sendULONG(0);
425   }
426
427   return 1;
428 }
429
430 int MVPClient::processGetChannelsList(UCHAR* data, int length)
431 {
432   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
433   int count = 4; // leave space for the packet length
434   char* point;
435   ULONG type;
436
437   char* chanConfig = config.getValueString("General", "Channels");
438   int allChans = 1;
439   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
440
441   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
442   {
443 #if VDRVERSNUM < 10300
444     if (!channel->GroupSep() && (!channel->Ca() || allChans))
445 #else
446     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
447 #endif
448     {
449       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
450
451       if (channel->Vpid()) type = 1;
452 #if VDRVERSNUM < 10300
453       else type = 2;
454 #else
455       else if (channel->Apid(0)) type = 2;
456       else continue;
457 #endif
458
459       if (count > 49000) break;
460       *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
461       count += 4;
462
463       *(ULONG*)&sendBuffer[count] = htonl(type);
464       count += 4;
465
466       point = (char*)channel->Name();
467       strcpy((char*)&sendBuffer[count], point);
468       count += strlen(point) + 1;
469     }
470   }
471
472   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
473
474   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
475
476   tcp.sendPacket(sendBuffer, count);
477   delete[] sendBuffer;
478   log->log("Client", Log::DEBUG, "Written channels list");
479
480   return 1;
481 }
482
483 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
484 {
485   log->log("Client", Log::DEBUG, "length = %i", length);
486   ULONG channelNumber = ntohl(*(ULONG*)data);
487
488   cChannel* channel = channelFromNumber(channelNumber);
489   if (!channel)
490   {
491     sendULONG(0);
492     return 1;
493   }
494
495   // get the priority we should use
496   int fail = 1;
497   int priority = config.getValueLong("General", "Live priority", &fail);
498   if (!fail)
499   {
500     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
501   }
502   else
503   {
504     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
505     priority = 0;
506   }
507
508   // a bit of sanity..
509   if (priority < 0) priority = 0;
510   if (priority > 99) priority = 99;
511
512   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
513   lp = MVPReceiver::create(channel, priority);
514
515   if (!lp)
516   {
517     sendULONG(0);
518     return 1;
519   }
520
521   if (!lp->init())
522   {
523     delete lp;
524     lp = NULL;
525     sendULONG(0);
526     return 1;
527   }
528
529   sendULONG(1);
530   return 1;
531 }
532
533 int MVPClient::processStopStreaming(UCHAR* data, int length)
534 {
535   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
536   if (lp)
537   {
538     delete lp;
539     lp = NULL;
540   }
541   else if (rp)
542   {
543     writeResumeData();
544
545     delete rp;
546     delete recordingManager;
547     rp = NULL;
548     recordingManager = NULL;
549   }
550
551   sendULONG(1);
552   return 1;
553 }
554
555 int MVPClient::processGetBlock(UCHAR* data, int length)
556 {
557   if (!lp && !rp)
558   {
559     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
560     return 0;
561   }
562
563   ULLONG position = ntohll(*(ULLONG*)data);
564   data += sizeof(ULLONG);
565   ULONG amount = ntohl(*(ULONG*)data);
566
567   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
568
569   UCHAR sendBuffer[amount + 4];
570   ULONG amountReceived = 0; // compiler moan.
571   if (lp)
572   {
573     log->log("Client", Log::DEBUG, "getting from live");
574     amountReceived = lp->getBlock(&sendBuffer[4], amount);
575
576     if (!amountReceived)
577     {
578       // vdr has possibly disconnected the receiver
579       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
580       delete lp;
581       lp = NULL;
582     }
583   }
584   else if (rp)
585   {
586     log->log("Client", Log::DEBUG, "getting from recording");
587     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
588   }
589
590   if (!amountReceived)
591   {
592     sendULONG(0);
593     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
594   }
595   else
596   {
597     *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
598     tcp.sendPacket(sendBuffer, amountReceived + 4);
599     log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
600   }
601
602   return 1;
603 }
604
605 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
606 {
607   // data is a pointer to the fileName string
608
609   recordingManager = new cRecordings;
610   recordingManager->Load();
611
612   cRecording* recording = recordingManager->GetByName((char*)data);
613
614   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
615
616   if (recording)
617   {
618     rp = new RecPlayer(recording);
619
620     UCHAR sendBuffer[12];
621     *(ULONG*)&sendBuffer[0] = htonl(8);
622     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
623
624     tcp.sendPacket(sendBuffer, 12);
625     log->log("Client", Log::DEBUG, "written totalLength");
626   }
627   else
628   {
629     delete recordingManager;
630     recordingManager = NULL;
631   }
632   return 1;
633 }
634
635 int MVPClient::processReScanRecording(UCHAR* data, int length)
636 {
637   ULLONG retval = 0;
638
639   if (!rp)
640   {
641     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
642   }
643   else
644   {
645     rp->scan();
646     retval = rp->getTotalLength();
647   }
648
649   UCHAR sendBuffer[12];
650   *(ULONG*)&sendBuffer[0] = htonl(8);
651   *(ULLONG*)&sendBuffer[4] = htonll(retval);
652
653   tcp.sendPacket(sendBuffer, 12);
654   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
655   return 1;
656 }
657
658 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
659 {
660   ULLONG retval = 0;
661
662   ULONG frameNumber = ntohl(*(ULONG*)data);
663   data += 4;
664
665   if (!rp)
666   {
667     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
668   }
669   else
670   {
671     retval = rp->positionFromFrameNumber(frameNumber);
672   }
673
674   UCHAR sendBuffer[12];
675   *(ULONG*)&sendBuffer[0] = htonl(8);
676   *(ULLONG*)&sendBuffer[4] = htonll(retval);
677
678   tcp.sendPacket(sendBuffer, 12);
679   log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
680   return 1;
681 }
682
683 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
684 {
685   ULONG channelNumber = ntohl(*(ULONG*)data);
686   data += 4;
687   ULONG startTime = ntohl(*(ULONG*)data);
688   data += 4;
689   ULONG duration = ntohl(*(ULONG*)data);
690
691   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
692
693   cChannel* channel = channelFromNumber(channelNumber);
694   if (!channel)
695   {
696     sendULONG(0);
697     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
698     return 1;
699   }
700
701   log->log("Client", Log::DEBUG, "Got channel");
702
703 #if VDRVERSNUM < 10300
704   cMutexLock MutexLock;
705   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
706 #else
707   cSchedulesLock MutexLock;
708   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
709 #endif
710   if (!Schedules)
711   {
712     sendULONG(0);
713     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
714     return 1;
715   }
716
717   log->log("Client", Log::DEBUG, "Got schedule!s! object");
718
719   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
720   if (!Schedule)
721   {
722     sendULONG(0);
723     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
724     return 1;
725   }
726
727   log->log("Client", Log::DEBUG, "Got schedule object");
728
729   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
730   ULONG sendBufferLength = 100000;
731   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
732
733   char* empty = "";
734
735   // assign all the event info to temp vars then we know exactly what size they are
736   ULONG thisEventID;
737   ULONG thisEventTime;
738   ULONG thisEventDuration;
739   const char* thisEventTitle;
740   const char* thisEventSubTitle;
741   const char* thisEventDescription;
742
743   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
744   ULONG thisEventLength;
745
746 #if VDRVERSNUM < 10300
747
748   const cEventInfo *event;
749   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
750   {
751     event = Schedule->GetEventNumber(eventNumber);
752
753     thisEventID = event->GetEventID();
754     thisEventTime = event->GetTime();
755     thisEventDuration = event->GetDuration();
756     thisEventTitle = event->GetTitle();
757     thisEventSubTitle = event->GetSubtitle();
758     thisEventDescription = event->GetExtendedDescription();
759
760 #else
761
762   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
763   {
764     thisEventID = event->EventID();
765     thisEventTime = event->StartTime();
766     thisEventDuration = event->Duration();
767     thisEventTitle = event->Title();
768     thisEventSubTitle = NULL;
769     thisEventDescription = event->Description();
770
771 #endif
772
773     log->log("Client", Log::DEBUG, "Got an event object %p", event);
774
775     //in the past filter
776     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
777
778     //start time filter
779     if ((thisEventTime + thisEventDuration) <= startTime) continue;
780
781     //duration filter
782     if (thisEventTime >= (startTime + duration)) continue;
783
784     if (!thisEventTitle) thisEventTitle = empty;
785     if (!thisEventSubTitle) thisEventSubTitle = empty;
786     if (!thisEventDescription) thisEventDescription = empty;
787
788     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
789
790     log->log("Client", Log::DEBUG, "Done s1");
791
792     // now extend the buffer if necessary
793     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
794     {
795       log->log("Client", Log::DEBUG, "Extending buffer");
796       sendBufferLength += 100000;
797       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
798       if (temp == NULL)
799       {
800         free(sendBuffer);
801         UCHAR sendBuffer2[8];
802         *(ULONG*)&sendBuffer2[0] = htonl(4);
803         *(ULONG*)&sendBuffer2[4] = htonl(0);
804         tcp.sendPacket(sendBuffer2, 8);
805         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
806         return 1;
807       }
808       sendBuffer = temp;
809     }
810
811     log->log("Client", Log::DEBUG, "Done s2");
812
813     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
814     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
815     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
816
817     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
818     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
819     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
820
821     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
822   }
823
824   log->log("Client", Log::DEBUG, "Got all event data");
825
826   if (sendBufferUsed == sizeof(ULONG))
827   {
828     // No data
829     sendULONG(0);
830     log->log("Client", Log::DEBUG, "Written 0 because no data");
831   }
832   else
833   {
834     // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
835     *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
836     tcp.sendPacket(sendBuffer, sendBufferUsed);
837     log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
838   }
839
840   free(sendBuffer);
841
842   return 1;
843 }
844
845 int MVPClient::processConfigSave(UCHAR* buffer, int length)
846 {
847   char* section = (char*)buffer;
848   char* key = NULL;
849   char* value = NULL;
850
851   for (int k = 0; k < length; k++)
852   {
853     if (buffer[k] == '\0')
854     {
855       if (!key)
856       {
857         key = (char*)&buffer[k+1];
858       }
859       else
860       {
861         value = (char*)&buffer[k+1];
862         break;
863       }
864     }
865   }
866
867   // if the last string (value) doesnt have null terminator, give up
868   if (buffer[length - 1] != '\0') return 0;
869
870   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
871   if (config.setValueString(section, key, value))
872   {
873     sendULONG(1);
874   }
875   else
876   {
877     sendULONG(0);
878   }
879
880   return 1;
881 }
882
883 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
884 {
885   char* section = (char*)buffer;
886   char* key = NULL;
887
888   for (int k = 0; k < length; k++)
889   {
890     if (buffer[k] == '\0')
891     {
892       key = (char*)&buffer[k+1];
893       break;
894     }
895   }
896
897   char* value = config.getValueString(section, key);
898
899   if (value)
900   {
901     UCHAR sendBuffer[4 + strlen(value) + 1];
902     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
903     strcpy((char*)&sendBuffer[4], value);
904     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
905
906     log->log("Client", Log::DEBUG, "Written config load packet");
907     delete[] value;
908   }
909   else
910   {
911     UCHAR sendBuffer[8];
912     *(ULONG*)&sendBuffer[0] = htonl(4);
913     *(ULONG*)&sendBuffer[4] = htonl(0);
914     tcp.sendPacket(sendBuffer, 8);
915
916     log->log("Client", Log::DEBUG, "Written config load failed packet");
917   }
918
919   return 1;
920 }
921
922 void MVPClient::cleanConfig()
923 {
924   log->log("Client", Log::DEBUG, "Clean config");
925
926   cRecordings Recordings;
927   Recordings.Load();
928
929   int numReturns;
930   int length;
931   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
932   char* position = resumes;
933   for(int k = 0; k < numReturns; k++)
934   {
935     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
936
937     cRecording* recording = Recordings.GetByName(position);
938     if (!recording)
939     {
940       // doesn't exist anymore
941       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
942       config.deleteValue("ResumeData", position);
943     }
944     else
945     {
946       log->log("Client", Log::DEBUG, "This recording still exists");
947     }
948
949     position += strlen(position) + 1;
950   }
951
952   delete[] resumes;
953 }
954
955
956
957
958
959
960 /*
961     event = Schedule->GetPresentEvent();
962
963     fprintf(f, "\n\nCurrent event\n\n");
964
965     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
966     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
967     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
968     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
969
970     event = Schedule->GetFollowingEvent();
971
972     fprintf(f, "\n\nFollowing event\n\n");
973
974     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
975     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
976     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
977     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
978
979     fprintf(f, "\n\n");
980 */
981
982 /*
983     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
984     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
985     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
986     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
987
988     fprintf(f, "\n\n");
989 */
990
991 /*
992
993
994 void MVPClient::test2()
995 {
996   FILE* f = fopen("/tmp/s.txt", "w");
997
998 #if VDRVERSNUM < 10300
999   cMutexLock MutexLock;
1000   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1001 #else
1002   cSchedulesLock MutexLock;
1003   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1004 #endif
1005
1006   if (!Schedules)
1007   {
1008     fprintf(f, "Schedules = NULL\n");
1009     fclose(f);
1010     return;
1011   }
1012
1013   fprintf(f, "Schedules dump:\n");
1014   Schedules->Dump(f);
1015
1016
1017   const cSchedule *Schedule;
1018   int scheduleNumber = 0;
1019
1020   tChannelID tchid;
1021   cChannel *thisChannel;
1022
1023 #if VDRVERSNUM < 10300
1024   const cEventInfo *event;
1025   int eventNumber = 0;
1026 #else
1027   const cEvent *event;
1028 #endif
1029
1030 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
1031 //    Schedule = Schedules->GetSchedule();
1032   Schedule = Schedules->First();
1033   if (!Schedule)
1034   {
1035     fprintf(f, "First Schedule = NULL\n");
1036     fclose(f);
1037     return;
1038   }
1039
1040   while (Schedule)
1041   {
1042     fprintf(f, "Schedule #%i\n", scheduleNumber);
1043     fprintf(f, "-------------\n\n");
1044
1045 #if VDRVERSNUM < 10300
1046     tchid = Schedule->GetChannelID();
1047 #else
1048     tchid = Schedule->ChannelID();
1049 #endif
1050
1051 #if VDRVERSNUM < 10300
1052     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1053     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1054 #else
1055 //  put the count at the end.
1056 #endif
1057
1058     thisChannel = Channels.GetByChannelID(tchid, true);
1059     if (thisChannel)
1060     {
1061       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1062     }
1063     else
1064     {
1065       fprintf(f, "thisChannel = NULL for tchid\n");
1066     }
1067
1068 #if VDRVERSNUM < 10300
1069     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1070     {
1071       event = Schedule->GetEventNumber(eventNumber);
1072       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1073       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1074       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1075       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1076       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1077       fprintf(f, "Event %u dump:\n", eventNumber);
1078       event->Dump(f);
1079       fprintf(f, "\n\n");
1080     }
1081 #else
1082 //  This whole section needs rewriting to walk the list.
1083     event = Schedule->Events()->First();
1084     while (event) {
1085       event = Schedule->Events()->Next(event);
1086     }
1087 #endif
1088
1089
1090     fprintf(f, "\nDump from object:\n");
1091     Schedule->Dump(f);
1092     fprintf(f, "\nEND\n");
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102     fprintf(f, "End of current Schedule\n\n\n");
1103
1104     Schedule = (const cSchedule *)Schedules->Next(Schedule);
1105     scheduleNumber++;
1106   }
1107
1108   fclose(f);
1109 }
1110
1111
1112
1113 */
1114
1115
1116
1117 /*
1118   const cEventInfo *GetPresentEvent(void) const;
1119   const cEventInfo *GetFollowingEvent(void) const;
1120   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1121   const cEventInfo *GetEventAround(time_t tTime) const;
1122   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1123
1124
1125   const unsigned char GetTableID(void) const;
1126   const char *GetTimeString(void) const;
1127   const char *GetEndTimeString(void) const;
1128   const char *GetDate(void) const;
1129   bool IsFollowing(void) const;
1130   bool IsPresent(void) const;
1131   const char *GetExtendedDescription(void) const;
1132   const char *GetSubtitle(void) const;
1133   const char *GetTitle(void) const;
1134   unsigned short GetEventID(void) const;
1135   long GetDuration(void) const;
1136   time_t GetTime(void) const;
1137   tChannelID GetChannelID(void) const;
1138   int GetChannelNumber(void) const { return nChannelNumber; }
1139   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1140   void Dump(FILE *f, const char *Prefix = "") const;
1141
1142 */
1143
1144
1145 /*
1146 void MVPClient::test(int channelNumber)
1147 {
1148   FILE* f = fopen("/tmp/test.txt", "w");
1149
1150   cMutexLock MutexLock;
1151   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1152
1153   if (!Schedules)
1154   {
1155     fprintf(f, "Schedules = NULL\n");
1156     fclose(f);
1157     return;
1158   }
1159
1160   fprintf(f, "Schedules dump:\n");
1161 //  Schedules->Dump(f);
1162
1163   const cSchedule *Schedule;
1164   cChannel *thisChannel;
1165   const cEventInfo *event;
1166
1167   thisChannel = channelFromNumber(channelNumber);
1168   if (!thisChannel)
1169   {
1170     fprintf(f, "thisChannel = NULL\n");
1171     fclose(f);
1172     return;
1173   }
1174
1175   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1176 //    Schedule = Schedules->GetSchedule();
1177 //  Schedule = Schedules->First();
1178   if (!Schedule)
1179   {
1180     fprintf(f, "First Schedule = NULL\n");
1181     fclose(f);
1182     return;
1183   }
1184
1185   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1186
1187   // For some channels VDR seems to pick a random point in time to
1188   // start dishing out events, but they are in order
1189   // at some point in the list the time snaps to the current event
1190
1191
1192
1193
1194   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1195   {
1196     event = Schedule->GetEventNumber(eventNumber);
1197     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1198     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1199     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1200     fprintf(f, "\n\n");
1201   }
1202
1203   fprintf(f, "\nEND\n");
1204
1205   fclose(f);
1206 }
1207
1208 */
1209
1210
1211
1212 /*
1213
1214
1215 Right, so
1216
1217 Schedules = the collection of all the Schedule objects
1218 Schedule  = One schedule, contants all the events for a channel
1219 Event     = One programme
1220
1221
1222 Want:
1223
1224 Event ID
1225 Time
1226 Duration
1227 Title
1228 Subtitle (used for "Programmes resume at ...")
1229 Description
1230
1231 IsPresent ? easy to work out tho. Oh it doesn't always work
1232
1233 */
1234
1235 /*
1236 void MVPClient::test2()
1237 {
1238   log->log("-", Log::DEBUG, "Timers List");
1239
1240   for (int i = 0; i < Timers.Count(); i++)
1241   {
1242     cTimer *timer = Timers.Get(i);
1243     //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1244     log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1245 #if VDRVERSNUM < 10300
1246     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());
1247 #else
1248     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());
1249 #endif
1250     log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1251     log->log("-", Log::DEBUG, "");
1252   }
1253
1254   // asprintf(&buffer, "%d:%s:%s  :%04d:%04d:%d:%d:%s:%s\n",
1255 //            active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1256 //            PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1257 }
1258 */
1259
1260 /*
1261 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1262 recording is a bool, 0 for not currently recording, 1 for currently recording
1263 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
1264 */
1265
1266
1267 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1268 {
1269   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1270   int count = 4; // leave space for the packet length
1271
1272   const char* fileName;
1273   cTimer *timer;
1274   int numTimers = Timers.Count();
1275
1276   *(ULONG*)&sendBuffer[count] = htonl(numTimers);    count += 4;
1277
1278   for (int i = 0; i < numTimers; i++)
1279   {
1280     if (count > 49000) break;
1281
1282     timer = Timers.Get(i);
1283
1284 #if VDRVERSNUM < 10300
1285     *(ULONG*)&sendBuffer[count] = htonl(timer->Active());                 count += 4;
1286 #else
1287     *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive));       count += 4;
1288 #endif
1289     *(ULONG*)&sendBuffer[count] = htonl(timer->Recording());              count += 4;
1290     *(ULONG*)&sendBuffer[count] = htonl(timer->Pending());                count += 4;
1291     *(ULONG*)&sendBuffer[count] = htonl(timer->Priority());               count += 4;
1292     *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime());               count += 4;
1293     *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number());      count += 4;
1294     *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime());              count += 4;
1295     *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime());               count += 4;
1296
1297     fileName = timer->File();
1298     strcpy((char*)&sendBuffer[count], fileName);
1299     count += strlen(fileName) + 1;
1300   }
1301
1302   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1303
1304   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1305
1306   tcp.sendPacket(sendBuffer, count);
1307   delete[] sendBuffer;
1308   log->log("Client", Log::DEBUG, "Written timers list");
1309
1310   return 1;
1311 }
1312
1313 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1314 {
1315   char* timerString = new char[strlen((char*)buffer) + 1];
1316   strcpy(timerString, (char*)buffer);
1317
1318 #if VDRVERSNUM < 10300
1319
1320   // If this is VDR 1.2 the date part of the timer string must be reduced
1321   // to just DD rather than YYYY-MM-DD
1322
1323   int s = 0; // source
1324   int d = 0; // destination
1325   int c = 0; // count
1326   while(c != 2) // copy up to date section, including the second ':'
1327   {
1328     timerString[d] = buffer[s];
1329     if (buffer[s] == ':') c++;
1330     ++s;
1331     ++d;
1332   }
1333   // now it has copied up to the date section
1334   c = 0;
1335   while(c != 2) // waste YYYY-MM-
1336   {
1337     if (buffer[s] == '-') c++;
1338     ++s;
1339   }
1340   // now source is at the DD
1341   memcpy(&timerString[d], &buffer[s], length - s);
1342   d += length - s;
1343   timerString[d] = '\0';
1344
1345   log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1346   log->log("Client", Log::DEBUG, "%s", timerString);
1347
1348 #endif
1349
1350   cTimer *timer = new cTimer;
1351   if (timer->Parse((char*)timerString))
1352   {
1353     cTimer *t = Timers.GetTimer(timer);
1354     if (!t)
1355     {
1356       Timers.Add(timer);
1357 #if VDRVERSNUM < 10300
1358       Timers.Save();
1359 #else
1360       Timers.SetModified();
1361 #endif
1362       sendULONG(0);
1363       return 1;
1364     }
1365     else
1366     {
1367       sendULONG(1);
1368     }
1369   }
1370   else
1371   {
1372      sendULONG(2);
1373   }
1374   delete timer;
1375   return 1;
1376 }