]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Completion of move recording code
[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[12];
784     *(ULONG*)&sendBuffer[0] = htonl(8);
785     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
786
787     tcp.sendPacket(sendBuffer, 12);
788     log->log("Client", Log::DEBUG, "written totalLength");
789   }
790   else
791   {
792     delete recordingManager;
793     recordingManager = NULL;
794   }
795   return 1;
796 }
797
798 int MVPClient::processReScanRecording(UCHAR* data, int length)
799 {
800   ULLONG retval = 0;
801
802   if (!rp)
803   {
804     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
805   }
806   else
807   {
808     rp->scan();
809     retval = rp->getTotalLength();
810   }
811
812   UCHAR sendBuffer[12];
813   *(ULONG*)&sendBuffer[0] = htonl(8);
814   *(ULLONG*)&sendBuffer[4] = htonll(retval);
815
816   tcp.sendPacket(sendBuffer, 12);
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 }