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