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