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