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