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