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