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