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