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