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