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