]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Upgrade to protocol
[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 pthread_mutex_t threadClientMutex;
27 int MVPClient::nr_clients = 0;
28
29
30 MVPClient::MVPClient(Config* cfgBase, char* tconfigDirExtra, int tsocket)
31  : tcp(tsocket)
32 {
33   lp = NULL;
34   rp = NULL;
35   imageFile = 0;
36   recordingManager = NULL;
37   log = Log::getInstance();
38   loggedIn = false;
39   configDirExtra = tconfigDirExtra;
40   baseConfig = cfgBase;
41   incClients();
42 }
43
44 MVPClient::~MVPClient()
45 {
46   log->log("Client", Log::DEBUG, "MVP client destructor");
47   if (lp)
48   {
49     delete lp;
50     lp = NULL;
51   }
52   else if (rp)
53   {
54     writeResumeData();
55
56     delete rp;
57     delete recordingManager;
58     rp = NULL;
59     recordingManager = NULL;
60   }
61
62   if (loggedIn) cleanConfig();
63   decClients();
64 }
65
66 ULLONG MVPClient::ntohll(ULLONG a)
67 {
68   return htonll(a);
69 }
70
71 ULLONG MVPClient::htonll(ULLONG a)
72 {
73   #if BYTE_ORDER == BIG_ENDIAN
74     return a;
75   #else
76     ULLONG b = 0;
77
78     b = ((a << 56) & 0xFF00000000000000ULL)
79       | ((a << 40) & 0x00FF000000000000ULL)
80       | ((a << 24) & 0x0000FF0000000000ULL)
81       | ((a <<  8) & 0x000000FF00000000ULL)
82       | ((a >>  8) & 0x00000000FF000000ULL)
83       | ((a >> 24) & 0x0000000000FF0000ULL)
84       | ((a >> 40) & 0x000000000000FF00ULL)
85       | ((a >> 56) & 0x00000000000000FFULL) ;
86
87     return b;
88   #endif
89 }
90
91 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
92 {
93   cChannel* channel = NULL;
94
95   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
96   {
97     if (!channel->GroupSep())
98     {
99       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
100
101       if (channel->Number() == (int)channelNumber)
102       {
103         int vpid = channel->Vpid();
104 #if VDRVERSNUM < 10300
105         int apid1 = channel->Apid1();
106 #else
107         int apid1 = channel->Apid(0);
108 #endif
109         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
110         return channel;
111       }
112     }
113   }
114
115   if (!channel)
116   {
117     log->log("Client", Log::DEBUG, "Channel not found");
118   }
119
120   return channel;
121 }
122
123 void MVPClient::writeResumeData()
124 {
125   config.setValueLong("ResumeData",
126                           (char*)rp->getCurrentRecording()->FileName(),
127                           rp->frameNumberFromPosition(rp->getLastPosition()) );
128 }
129
130 void MVPClient::sendULONG(ULONG ul)
131 {
132   UCHAR sendBuffer[8];
133   *(ULONG*)&sendBuffer[0] = htonl(4);
134   *(ULONG*)&sendBuffer[4] = htonl(ul);
135
136   tcp.sendPacket(sendBuffer, 8);
137   log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
138 }
139
140 void MVPClientStartThread(void* arg)
141 {
142   MVPClient* m = (MVPClient*)arg;
143   m->run2();
144   // Nothing external to this class has a reference to it
145   // This is the end of the thread.. so delete m
146   delete m;
147   pthread_exit(NULL);
148 }
149
150 int MVPClient::run()
151 {
152   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
153   log->log("Client", Log::DEBUG, "MVPClient run success");
154   return 1;
155 }
156
157 void MVPClient::run2()
158 {
159   // Thread stuff
160   sigset_t sigset;
161   sigfillset(&sigset);
162   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
163   pthread_detach(runThread);  // Detach
164
165   tcp.disableReadTimeout();
166
167   tcp.setSoKeepTime(3);
168   tcp.setNonBlocking();
169
170   ULONG channelID;
171   ULONG serialNumber;
172   ULONG opcode;
173   ULONG extraDataLength;
174   UCHAR* data;
175     
176   int result = 0;
177
178   while(1)
179   {
180     log->log("Client", Log::DEBUG, "Waiting");
181     
182     if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break;
183     channelID = ntohl(channelID);
184     if (channelID != 1)
185     {
186       log->log("Client", Log::ERR, "Incoming channel number not 1!");
187       break;
188     }
189
190     log->log("Client", Log::DEBUG, "Got chan");
191     
192     if (!tcp.readData((UCHAR*)&serialNumber, sizeof(ULONG))) break;
193     serialNumber = ntohl(serialNumber);
194
195     log->log("Client", Log::DEBUG, "Got ser");
196
197     if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
198     opcode = ntohl(opcode);
199
200     log->log("Client", Log::DEBUG, "Got op %lu", opcode);
201
202     if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
203     extraDataLength = ntohl(extraDataLength);
204     if (extraDataLength > 200000)
205     {
206       log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
207       break;
208     }
209
210     log->log("Client", Log::DEBUG, "Got edl %lu", extraDataLength);
211
212     if (extraDataLength)
213     {
214       data = (UCHAR*)malloc(extraDataLength);
215       if (!data)
216       {
217         log->log("Client", Log::ERR, "Extra data buffer malloc error");
218         break;
219       }
220       
221       if (!tcp.readData(data, extraDataLength))
222       {
223         log->log("Client", Log::ERR, "Could not read extradata");
224         free(data);
225         break;
226       }      
227     }
228     else
229     {
230       data = NULL;
231     }
232
233     log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, serialNumber, opcode, extraDataLength);
234
235     if (!loggedIn && (opcode != 1))
236     {
237       log->log("Client", Log::ERR, "Not logged in and opcode != 1");
238       if (data) free(data);
239       break;
240     }
241
242     switch(opcode)
243     {
244       case 1:
245         result = processLogin(data, extraDataLength);
246         break;
247       case 2:
248         result = processGetRecordingsList(data, extraDataLength);
249         break;
250       case 3:
251         result = processDeleteRecording(data, extraDataLength);
252         break;
253       case 5:
254         result = processGetChannelsList(data, extraDataLength);
255         break;
256       case 6:
257         result = processStartStreamingChannel(data, extraDataLength);
258         break;
259       case 7:
260         result = processGetBlock(data, extraDataLength);
261         break;
262       case 8:
263         result = processStopStreaming(data, extraDataLength);
264         break;
265       case 9:
266         result = processStartStreamingRecording(data, extraDataLength);
267         break;
268       case 10:
269         result = processGetChannelSchedule(data, extraDataLength);
270         break;
271       case 11:
272         result = processConfigSave(data, extraDataLength);
273         break;
274       case 12:
275         result = processConfigLoad(data, extraDataLength);
276         break;
277       case 13:
278         result = processReScanRecording(data, extraDataLength);         // FIXME obselete
279         break;
280       case 14:
281         result = processGetTimers(data, extraDataLength);
282         break;
283       case 15:
284         result = processSetTimer(data, extraDataLength);
285         break;
286       case 16:
287         result = processPositionFromFrameNumber(data, extraDataLength);
288         break;
289       case 17:
290         result = processFrameNumberFromPosition(data, extraDataLength);
291         break;
292       case 18:
293         result = processMoveRecording(data, extraDataLength);
294         break;
295       case 19:
296         result = processGetIFrame(data, extraDataLength);
297         break;
298       case 20:
299         result = processGetRecInfo(data, extraDataLength);
300         break;
301       case 21:
302         result = processGetMarks(data, extraDataLength);
303         break;
304       case 22:
305         result = processGetChannelPids(data, extraDataLength);
306         break;
307       case 23:
308         result = processDeleteTimer(data, extraDataLength);
309         break;
310       case 30:
311         result = processGetMediaList(data, extraDataLength);
312         break;
313       case 31:
314         result = processGetPicture(data, extraDataLength);
315         break;
316       case 32:
317         result = processGetImageBlock(data, extraDataLength);
318         break;
319     }
320
321     if (data) free(data);
322     if (!result) break;
323   }
324 }
325
326 int MVPClient::processLogin(UCHAR* buffer, int length)
327 {
328   if (length != 6) return 0;
329
330   // Open the config
331
332   const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
333   if (!configDir)
334   {
335     log->log("Client", Log::DEBUG, "No config dir!");
336     return 0;
337   }
338
339   char configFileName[PATH_MAX];
340   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]);
341   config.init(configFileName);
342
343   // Send the login reply
344
345   time_t timeNow = time(NULL);
346   struct tm* timeStruct = localtime(&timeNow);
347   int timeOffset = timeStruct->tm_gmtoff;
348
349   UCHAR sendBuffer[12];
350   *(ULONG*)&sendBuffer[0] = htonl(8);
351   *(ULONG*)&sendBuffer[4] = htonl(timeNow);
352   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
353
354   tcp.sendPacket(sendBuffer, 12);
355   log->log("Client", Log::DEBUG, "written login reply");
356
357   loggedIn = true;
358   return 1;
359 }
360
361 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
362 {
363   UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
364   int count = 4; // leave space for the packet length
365   char* point;
366
367
368   int FreeMB;
369   int Percent = VideoDiskSpace(&FreeMB);
370   int Total = (FreeMB / (100 - Percent)) * 100;
371
372   *(ULONG*)&sendBuffer[count] = htonl(Total);
373   count += sizeof(ULONG);
374   *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
375   count += sizeof(ULONG);
376   *(ULONG*)&sendBuffer[count] = htonl(Percent);
377   count += sizeof(ULONG);
378
379
380   cRecordings Recordings;
381   Recordings.Load();
382
383   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
384   {
385     if (count > 49000) break; // just how big is that hard disk?!
386     *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
387     count += 4;
388
389     point = (char*)recording->Name();
390     strcpy((char*)&sendBuffer[count], point);
391     count += strlen(point) + 1;
392
393     point = (char*)recording->FileName();
394     strcpy((char*)&sendBuffer[count], point);
395     count += strlen(point) + 1;
396   }
397
398   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
399
400   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
401
402   tcp.sendPacket(sendBuffer, count);
403   delete[] sendBuffer;
404   log->log("Client", Log::DEBUG, "Written list");
405
406   return 1;
407 }
408
409 int MVPClient::processDeleteRecording(UCHAR* data, int length)
410 {
411   // data is a pointer to the fileName string
412
413   cRecordings Recordings;
414   Recordings.Load(); // probably have to do this
415
416   cRecording* recording = Recordings.GetByName((char*)data);
417
418   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
419
420   if (recording)
421   {
422     log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
423
424     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
425     if (!rc)
426     {
427       if (recording->Delete())
428       {
429         // Copy svdrp's way of doing this, see if it works
430 #if VDRVERSNUM > 10300
431         ::Recordings.DelByName(recording->FileName());
432 #endif
433         sendULONG(1);
434       }
435       else
436       {
437         sendULONG(2);
438       }
439     }
440     else
441     {
442       sendULONG(3);
443     }
444   }
445   else
446   {
447     sendULONG(4);
448   }
449
450   return 1;
451 }
452
453 int MVPClient::processMoveRecording(UCHAR* data, int length)
454 {
455   log->log("Client", Log::DEBUG, "Process move recording");
456   char* fileName = (char*)data;
457   char* newPath = NULL;
458
459   for (int k = 0; k < length; k++)
460   {
461     if (data[k] == '\0')
462     {
463       newPath = (char*)&data[k+1];
464       break;
465     }
466   }
467   if (!newPath) return 0;
468
469   cRecordings Recordings;
470   Recordings.Load(); // probably have to do this
471
472   cRecording* recording = Recordings.GetByName((char*)fileName);
473
474   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
475
476   if (recording)
477   {
478     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
479     if (!rc)
480     {
481       log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
482       log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
483       log->log("Client", Log::DEBUG, "to: %s", newPath);
484
485       const char* t = recording->FileName();
486
487       char* dateDirName = NULL;   int k;
488       char* titleDirName = NULL;  int j;
489
490       // Find the datedirname
491       for(k = strlen(t) - 1; k >= 0; k--)
492       {
493         if (t[k] == '/')
494         {
495           log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
496           dateDirName = new char[strlen(&t[k+1]) + 1];
497           strcpy(dateDirName, &t[k+1]);
498           break;
499         }
500       }
501
502       // Find the titledirname
503
504       for(j = k-1; j >= 0; j--)
505       {
506         if (t[j] == '/')
507         {
508           log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
509           titleDirName = new char[(k - j - 1) + 1];
510           memcpy(titleDirName, &t[j+1], k - j - 1);
511           titleDirName[k - j - 1] = '\0';
512           break;
513         }
514       }
515
516       log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
517       log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
518
519       log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
520
521       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
522       log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
523       sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
524
525       // FIXME Check whether this already exists before mkdiring it
526
527       log->log("Client", Log::DEBUG, "%s", newContainer);
528
529
530       struct stat dstat;
531       int statret = stat(newContainer, &dstat);
532       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
533       {
534         log->log("Client", Log::DEBUG, "new dir does not exist");
535         int mkdirret = mkdir(newContainer, 0755);
536         if (mkdirret != 0)
537         {
538           delete[] dateDirName;
539           delete[] titleDirName;
540           delete[] newContainer;
541           sendULONG(5);
542           return 1;
543         }
544       }
545       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
546       {
547         delete[] dateDirName;
548         delete[] titleDirName;
549         delete[] newContainer;
550         sendULONG(5);
551         return 1;
552       }
553
554       // Ok, the directory container has been made, or it pre-existed.
555
556       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
557       sprintf(newDir, "%s/%s", newContainer, dateDirName);
558
559       log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
560       int renameret = rename(t, newDir);
561       if (renameret == 0)
562       {
563         // Success. Test for remove old dir containter
564         char* oldTitleDir = new char[k+1];
565         memcpy(oldTitleDir, t, k);
566         oldTitleDir[k] = '\0';
567         log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
568         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
569         delete[] oldTitleDir;
570       }
571
572       if (renameret == 0)
573       {
574 #if VDRVERSNUM > 10311
575         // Tell VDR
576         ::Recordings.Update();
577 #endif
578         // Success. Send a different packet from just a ulong
579         int totalLength = 4 + 4 + strlen(newDir) + 1;
580         UCHAR* sendBuffer = new UCHAR[totalLength];
581         *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
582         *(ULONG*)&sendBuffer[4] = htonl(1); // success
583         strcpy((char*)&sendBuffer[8], newDir);
584         tcp.sendPacket(sendBuffer, totalLength);
585         delete[] sendBuffer;
586       }
587       else
588       {
589         sendULONG(5);
590       }
591
592       delete[] dateDirName;
593       delete[] titleDirName;
594       delete[] newContainer;
595       delete[] newDir;
596     }
597     else
598     {
599       sendULONG(3);
600     }
601   }
602   else
603   {
604     sendULONG(4);
605   }
606
607   return 1;
608 }
609
610 int MVPClient::processGetChannelsList(UCHAR* data, int length)
611 {
612   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
613   int count = 4; // leave space for the packet length
614   char* point;
615   ULONG type;
616
617   char* chanConfig = config.getValueString("General", "Channels");
618   int allChans = 1;
619   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
620
621   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
622   {
623 #if VDRVERSNUM < 10300
624     if (!channel->GroupSep() && (!channel->Ca() || allChans))
625 #else
626     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
627 #endif
628     {
629       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
630
631       if (channel->Vpid()) type = 1;
632 #if VDRVERSNUM < 10300
633       else type = 2;
634 #else
635       else if (channel->Apid(0)) type = 2;
636       else continue;
637 #endif
638
639       if (count > 49000) break;
640       *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
641       count += 4;
642
643       *(ULONG*)&sendBuffer[count] = htonl(type);
644       count += 4;
645
646       point = (char*)channel->Name();
647       strcpy((char*)&sendBuffer[count], point);
648       count += strlen(point) + 1;
649     }
650   }
651
652   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
653
654   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
655
656   tcp.sendPacket(sendBuffer, count);
657   delete[] sendBuffer;
658   log->log("Client", Log::DEBUG, "Written channels list");
659
660   return 1;
661 }
662
663 int MVPClient::processGetChannelPids(UCHAR* data, int length)
664 {
665   ULONG channelNumber = ntohl(*(ULONG*)data);
666
667   cChannel* channel = channelFromNumber(channelNumber);
668   if (!channel)
669   {
670     sendULONG(0);
671     return 1;
672   }
673
674   ULONG numApids = 0;
675   ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
676   // Work out space required and number of Apids
677
678 #if VDRVERSNUM < 10300
679
680   log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
681   log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
682
683   if (channel->Apid2())
684   {
685     numApids = 2;
686     spaceRequired += 10; // 8 + 2 nulls
687   }
688   else if (channel->Apid1())
689   {
690     numApids = 1;
691     spaceRequired += 5; // 4 + 1 null
692   }
693   else
694   {
695     numApids = 0;
696   }
697
698 #else
699
700   for (const int *Apid = channel->Apids(); *Apid; Apid++)
701   {
702     spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
703     numApids++;
704   }
705 #endif
706
707
708   // Format of response
709   // vpid
710   // number of apids
711   // {
712   //    apid
713   //    lang string
714   // }
715
716   UCHAR* sendBuffer = new UCHAR[spaceRequired];
717   ULONG point = 0;
718   *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4);   point += 4;   // take off first 4 bytes
719   *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid());     point += 4;
720   *(ULONG*)&sendBuffer[point] = htonl(numApids);            point += 4;
721
722 #if VDRVERSNUM < 10300
723   if (numApids >= 1)
724   {
725     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid1());  point += 4;
726     sendBuffer[point] = '\0';                               point += 1;
727   }
728   if (numApids == 2)
729   {
730     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid2());  point += 4;
731     sendBuffer[point] = '\0';                               point += 1;
732   }
733 #else
734   for (ULONG i = 0; i < numApids; i++)
735   {
736     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i));  point += 4;
737     strcpy((char*)&sendBuffer[point], channel->Alang(i));   point += strlen(channel->Alang(i)) + 1;
738   }
739 #endif
740
741 //  printf("About to send getchannelpids response. length = %u\n", spaceRequired);
742   //tcp.dump(sendBuffer, spaceRequired);
743
744   tcp.sendPacket(sendBuffer, spaceRequired);
745   delete[] sendBuffer;
746   log->log("Client", Log::DEBUG, "Written channels pids");
747
748   return 1;
749 }
750
751 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
752 {
753   log->log("Client", Log::DEBUG, "length = %i", length);
754   ULONG channelNumber = ntohl(*(ULONG*)data);
755
756   cChannel* channel = channelFromNumber(channelNumber);
757   if (!channel)
758   {
759     sendULONG(0);
760     return 1;
761   }
762
763   // get the priority we should use
764   int fail = 1;
765   int priority = config.getValueLong("General", "Live priority", &fail);
766   if (!fail)
767   {
768     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
769   }
770   else
771   {
772     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
773     priority = 0;
774   }
775
776   // a bit of sanity..
777   if (priority < 0) priority = 0;
778   if (priority > 99) priority = 99;
779
780   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
781   lp = MVPReceiver::create(channel, priority);
782
783   if (!lp)
784   {
785     sendULONG(0);
786     return 1;
787   }
788
789   if (!lp->init())
790   {
791     delete lp;
792     lp = NULL;
793     sendULONG(0);
794     return 1;
795   }
796
797   sendULONG(1);
798   return 1;
799 }
800
801 int MVPClient::processStopStreaming(UCHAR* data, int length)
802 {
803   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
804   if (lp)
805   {
806     delete lp;
807     lp = NULL;
808   }
809   else if (rp)
810   {
811     writeResumeData();
812
813     delete rp;
814     delete recordingManager;
815     rp = NULL;
816     recordingManager = NULL;
817   }
818
819   sendULONG(1);
820   return 1;
821 }
822
823 int MVPClient::processGetBlock(UCHAR* data, int length)
824 {
825   if (!lp && !rp)
826   {
827     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
828     return 0;
829   }
830
831   ULLONG position = ntohll(*(ULLONG*)data);
832   data += sizeof(ULLONG);
833   ULONG amount = ntohl(*(ULONG*)data);
834
835   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
836
837   UCHAR sendBuffer[amount + 4];
838   ULONG amountReceived = 0; // compiler moan.
839   if (lp)
840   {
841     log->log("Client", Log::DEBUG, "getting from live");
842     amountReceived = lp->getBlock(&sendBuffer[4], amount);
843
844     if (!amountReceived)
845     {
846       // vdr has possibly disconnected the receiver
847       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
848       delete lp;
849       lp = NULL;
850     }
851   }
852   else if (rp)
853   {
854     log->log("Client", Log::DEBUG, "getting from recording");
855     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
856   }
857
858   if (!amountReceived)
859   {
860     sendULONG(0);
861     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
862   }
863   else
864   {
865     *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
866     tcp.sendPacket(sendBuffer, amountReceived + 4);
867     log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
868   }
869
870   return 1;
871 }
872
873 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
874 {
875   // data is a pointer to the fileName string
876
877   recordingManager = new cRecordings;
878   recordingManager->Load();
879
880   cRecording* recording = recordingManager->GetByName((char*)data);
881
882   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
883
884   if (recording)
885   {
886     rp = new RecPlayer(recording);
887
888     UCHAR sendBuffer[16];
889     *(ULONG*)&sendBuffer[0] = htonl(12);
890     *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
891     *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
892
893     tcp.sendPacket(sendBuffer, 16);
894     log->log("Client", Log::DEBUG, "written totalLength");
895   }
896   else
897   {
898     delete recordingManager;
899     recordingManager = NULL;
900   }
901   return 1;
902 }
903
904 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
905 {
906   ULLONG retval = 0;
907
908   ULONG frameNumber = ntohl(*(ULONG*)data);
909   data += 4;
910
911   if (!rp)
912   {
913     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
914   }
915   else
916   {
917     retval = rp->positionFromFrameNumber(frameNumber);
918   }
919
920   UCHAR sendBuffer[12];
921   *(ULONG*)&sendBuffer[0] = htonl(8);
922   *(ULLONG*)&sendBuffer[4] = htonll(retval);
923
924   tcp.sendPacket(sendBuffer, 12);
925   log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
926   return 1;
927 }
928
929 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
930 {
931   ULONG retval = 0;
932
933   ULLONG position = ntohll(*(ULLONG*)data);
934   data += 8;
935
936   if (!rp)
937   {
938     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
939   }
940   else
941   {
942     retval = rp->frameNumberFromPosition(position);
943   }
944
945   UCHAR sendBuffer[8];
946   *(ULONG*)&sendBuffer[0] = htonl(4);
947   *(ULONG*)&sendBuffer[4] = htonl(retval);
948
949   tcp.sendPacket(sendBuffer, 8);
950   log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
951   return 1;
952 }
953
954 int MVPClient::processGetIFrame(UCHAR* data, int length)
955 {
956   bool success = false;
957
958   ULONG frameNumber = ntohl(*(ULONG*)data);
959   data += 4;
960   ULONG direction = ntohl(*(ULONG*)data);
961   data += 4;
962
963   ULLONG rfilePosition = 0;
964   ULONG rframeNumber = 0;
965   ULONG rframeLength = 0;
966
967   if (!rp)
968   {
969     log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
970   }
971   else
972   {
973     success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
974   }
975
976   // returns file position, frame number, length
977
978   UCHAR sendBuffer[20];
979   int packetLength;
980
981   if (success)
982   {
983     packetLength = 20;
984     *(ULONG*)&sendBuffer[0] = htonl(16);
985     *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
986     *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
987     *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
988   }
989   else
990   {
991     packetLength = 8;
992     *(ULONG*)&sendBuffer[0] = htonl(4);
993     *(ULONG*)&sendBuffer[4] = 0;
994   }
995
996   log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
997
998   tcp.sendPacket(sendBuffer, packetLength);
999   log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
1000   return 1;
1001 }
1002
1003 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
1004 {
1005   ULONG channelNumber = ntohl(*(ULONG*)data);
1006   data += 4;
1007   ULONG startTime = ntohl(*(ULONG*)data);
1008   data += 4;
1009   ULONG duration = ntohl(*(ULONG*)data);
1010
1011   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1012
1013   cChannel* channel = channelFromNumber(channelNumber);
1014   if (!channel)
1015   {
1016     sendULONG(0);
1017     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1018     return 1;
1019   }
1020
1021   log->log("Client", Log::DEBUG, "Got channel");
1022
1023 #if VDRVERSNUM < 10300
1024   cMutexLock MutexLock;
1025   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1026 #else
1027   cSchedulesLock MutexLock;
1028   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1029 #endif
1030   if (!Schedules)
1031   {
1032     sendULONG(0);
1033     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1034     return 1;
1035   }
1036
1037   log->log("Client", Log::DEBUG, "Got schedule!s! object");
1038
1039   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1040   if (!Schedule)
1041   {
1042     sendULONG(0);
1043     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1044     return 1;
1045   }
1046
1047   log->log("Client", Log::DEBUG, "Got schedule object");
1048
1049   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
1050   ULONG sendBufferLength = 100000;
1051   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
1052
1053   const char* empty = "";
1054
1055   // assign all the event info to temp vars then we know exactly what size they are
1056   ULONG thisEventID;
1057   ULONG thisEventTime;
1058   ULONG thisEventDuration;
1059   const char* thisEventTitle;
1060   const char* thisEventSubTitle;
1061   const char* thisEventDescription;
1062
1063   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
1064   ULONG thisEventLength;
1065
1066 #if VDRVERSNUM < 10300
1067
1068   const cEventInfo *event;
1069   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1070   {
1071     event = Schedule->GetEventNumber(eventNumber);
1072
1073     thisEventID = event->GetEventID();
1074     thisEventTime = event->GetTime();
1075     thisEventDuration = event->GetDuration();
1076     thisEventTitle = event->GetTitle();
1077     thisEventSubTitle = event->GetSubtitle();
1078     thisEventDescription = event->GetExtendedDescription();
1079
1080 #else
1081
1082   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1083   {
1084     thisEventID = event->EventID();
1085     thisEventTime = event->StartTime();
1086     thisEventDuration = event->Duration();
1087     thisEventTitle = event->Title();
1088     thisEventSubTitle = NULL;
1089     thisEventDescription = event->Description();
1090
1091 #endif
1092
1093     log->log("Client", Log::DEBUG, "Got an event object %p", event);
1094
1095     //in the past filter
1096     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1097
1098     //start time filter
1099     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1100
1101     //duration filter
1102     if (thisEventTime >= (startTime + duration)) continue;
1103
1104     if (!thisEventTitle) thisEventTitle = empty;
1105     if (!thisEventSubTitle) thisEventSubTitle = empty;
1106     if (!thisEventDescription) thisEventDescription = empty;
1107
1108     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
1109
1110     log->log("Client", Log::DEBUG, "Done s1");
1111
1112     // now extend the buffer if necessary
1113     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
1114     {
1115       log->log("Client", Log::DEBUG, "Extending buffer");
1116       sendBufferLength += 100000;
1117       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
1118       if (temp == NULL)
1119       {
1120         free(sendBuffer);
1121         UCHAR sendBuffer2[8];
1122         *(ULONG*)&sendBuffer2[0] = htonl(4);
1123         *(ULONG*)&sendBuffer2[4] = htonl(0);
1124         tcp.sendPacket(sendBuffer2, 8);
1125         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
1126         return 1;
1127       }
1128       sendBuffer = temp;
1129     }
1130
1131     log->log("Client", Log::DEBUG, "Done s2");
1132
1133     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
1134     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
1135     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1136
1137     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
1138     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
1139     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1140
1141     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1142   }
1143
1144   log->log("Client", Log::DEBUG, "Got all event data");
1145
1146   if (sendBufferUsed == sizeof(ULONG))
1147   {
1148     // No data
1149     sendULONG(0);
1150     log->log("Client", Log::DEBUG, "Written 0 because no data");
1151   }
1152   else
1153   {
1154     // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1155     *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1156     tcp.sendPacket(sendBuffer, sendBufferUsed);
1157     log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1158   }
1159
1160   free(sendBuffer);
1161
1162   return 1;
1163 }
1164
1165 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1166 {
1167   char* section = (char*)buffer;
1168   char* key = NULL;
1169   char* value = NULL;
1170
1171   for (int k = 0; k < length; k++)
1172   {
1173     if (buffer[k] == '\0')
1174     {
1175       if (!key)
1176       {
1177         key = (char*)&buffer[k+1];
1178       }
1179       else
1180       {
1181         value = (char*)&buffer[k+1];
1182         break;
1183       }
1184     }
1185   }
1186
1187   // if the last string (value) doesnt have null terminator, give up
1188   if (buffer[length - 1] != '\0') return 0;
1189
1190   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1191   if (config.setValueString(section, key, value))
1192   {
1193     sendULONG(1);
1194   }
1195   else
1196   {
1197     sendULONG(0);
1198   }
1199
1200   return 1;
1201 }
1202
1203 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1204 {
1205   char* section = (char*)buffer;
1206   char* key = NULL;
1207
1208   for (int k = 0; k < length; k++)
1209   {
1210     if (buffer[k] == '\0')
1211     {
1212       key = (char*)&buffer[k+1];
1213       break;
1214     }
1215   }
1216
1217   char* value = config.getValueString(section, key);
1218
1219   if (value)
1220   {
1221     UCHAR sendBuffer[4 + strlen(value) + 1];
1222     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1223     strcpy((char*)&sendBuffer[4], value);
1224     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1225
1226     log->log("Client", Log::DEBUG, "Written config load packet");
1227     delete[] value;
1228   }
1229   else
1230   {
1231     UCHAR sendBuffer[8];
1232     *(ULONG*)&sendBuffer[0] = htonl(4);
1233     *(ULONG*)&sendBuffer[4] = htonl(0);
1234     tcp.sendPacket(sendBuffer, 8);
1235
1236     log->log("Client", Log::DEBUG, "Written config load failed packet");
1237   }
1238
1239   return 1;
1240 }
1241
1242 void MVPClient::cleanConfig()
1243 {
1244   log->log("Client", Log::DEBUG, "Clean config");
1245
1246   cRecordings Recordings;
1247   Recordings.Load();
1248
1249   int numReturns;
1250   int length;
1251   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1252   char* position = resumes;
1253   for(int k = 0; k < numReturns; k++)
1254   {
1255     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1256
1257     cRecording* recording = Recordings.GetByName(position);
1258     if (!recording)
1259     {
1260       // doesn't exist anymore
1261       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1262       config.deleteValue("ResumeData", position);
1263     }
1264     else
1265     {
1266       log->log("Client", Log::DEBUG, "This recording still exists");
1267     }
1268
1269     position += strlen(position) + 1;
1270   }
1271
1272   delete[] resumes;
1273 }
1274
1275
1276
1277
1278
1279
1280 /*
1281     event = Schedule->GetPresentEvent();
1282
1283     fprintf(f, "\n\nCurrent event\n\n");
1284
1285     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1286     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1287     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1288     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1289
1290     event = Schedule->GetFollowingEvent();
1291
1292     fprintf(f, "\n\nFollowing event\n\n");
1293
1294     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1295     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1296     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1297     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1298
1299     fprintf(f, "\n\n");
1300 */
1301
1302 /*
1303     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1304     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1305     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1306     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1307
1308     fprintf(f, "\n\n");
1309 */
1310
1311 /*
1312
1313
1314 void MVPClient::test2()
1315 {
1316   FILE* f = fopen("/tmp/s.txt", "w");
1317
1318 #if VDRVERSNUM < 10300
1319   cMutexLock MutexLock;
1320   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1321 #else
1322   cSchedulesLock MutexLock;
1323   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1324 #endif
1325
1326   if (!Schedules)
1327   {
1328     fprintf(f, "Schedules = NULL\n");
1329     fclose(f);
1330     return;
1331   }
1332
1333   fprintf(f, "Schedules dump:\n");
1334   Schedules->Dump(f);
1335
1336
1337   const cSchedule *Schedule;
1338   int scheduleNumber = 0;
1339
1340   tChannelID tchid;
1341   cChannel *thisChannel;
1342
1343 #if VDRVERSNUM < 10300
1344   const cEventInfo *event;
1345   int eventNumber = 0;
1346 #else
1347   const cEvent *event;
1348 #endif
1349
1350 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
1351 //    Schedule = Schedules->GetSchedule();
1352   Schedule = Schedules->First();
1353   if (!Schedule)
1354   {
1355     fprintf(f, "First Schedule = NULL\n");
1356     fclose(f);
1357     return;
1358   }
1359
1360   while (Schedule)
1361   {
1362     fprintf(f, "Schedule #%i\n", scheduleNumber);
1363     fprintf(f, "-------------\n\n");
1364
1365 #if VDRVERSNUM < 10300
1366     tchid = Schedule->GetChannelID();
1367 #else
1368     tchid = Schedule->ChannelID();
1369 #endif
1370
1371 #if VDRVERSNUM < 10300
1372     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1373     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1374 #else
1375 //  put the count at the end.
1376 #endif
1377
1378     thisChannel = Channels.GetByChannelID(tchid, true);
1379     if (thisChannel)
1380     {
1381       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1382     }
1383     else
1384     {
1385       fprintf(f, "thisChannel = NULL for tchid\n");
1386     }
1387
1388 #if VDRVERSNUM < 10300
1389     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1390     {
1391       event = Schedule->GetEventNumber(eventNumber);
1392       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1393       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1394       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1395       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1396       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1397       fprintf(f, "Event %u dump:\n", eventNumber);
1398       event->Dump(f);
1399       fprintf(f, "\n\n");
1400     }
1401 #else
1402 //  This whole section needs rewriting to walk the list.
1403     event = Schedule->Events()->First();
1404     while (event) {
1405       event = Schedule->Events()->Next(event);
1406     }
1407 #endif
1408
1409
1410     fprintf(f, "\nDump from object:\n");
1411     Schedule->Dump(f);
1412     fprintf(f, "\nEND\n");
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422     fprintf(f, "End of current Schedule\n\n\n");
1423
1424     Schedule = (const cSchedule *)Schedules->Next(Schedule);
1425     scheduleNumber++;
1426   }
1427
1428   fclose(f);
1429 }
1430
1431
1432
1433 */
1434
1435
1436
1437 /*
1438   const cEventInfo *GetPresentEvent(void) const;
1439   const cEventInfo *GetFollowingEvent(void) const;
1440   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1441   const cEventInfo *GetEventAround(time_t tTime) const;
1442   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1443
1444
1445   const unsigned char GetTableID(void) const;
1446   const char *GetTimeString(void) const;
1447   const char *GetEndTimeString(void) const;
1448   const char *GetDate(void) const;
1449   bool IsFollowing(void) const;
1450   bool IsPresent(void) const;
1451   const char *GetExtendedDescription(void) const;
1452   const char *GetSubtitle(void) const;
1453   const char *GetTitle(void) const;
1454   unsigned short GetEventID(void) const;
1455   long GetDuration(void) const;
1456   time_t GetTime(void) const;
1457   tChannelID GetChannelID(void) const;
1458   int GetChannelNumber(void) const { return nChannelNumber; }
1459   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1460   void Dump(FILE *f, const char *Prefix = "") const;
1461
1462 */
1463
1464
1465 /*
1466 void MVPClient::test(int channelNumber)
1467 {
1468   FILE* f = fopen("/tmp/test.txt", "w");
1469
1470   cMutexLock MutexLock;
1471   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1472
1473   if (!Schedules)
1474   {
1475     fprintf(f, "Schedules = NULL\n");
1476     fclose(f);
1477     return;
1478   }
1479
1480   fprintf(f, "Schedules dump:\n");
1481 //  Schedules->Dump(f);
1482
1483   const cSchedule *Schedule;
1484   cChannel *thisChannel;
1485   const cEventInfo *event;
1486
1487   thisChannel = channelFromNumber(channelNumber);
1488   if (!thisChannel)
1489   {
1490     fprintf(f, "thisChannel = NULL\n");
1491     fclose(f);
1492     return;
1493   }
1494
1495   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1496 //    Schedule = Schedules->GetSchedule();
1497 //  Schedule = Schedules->First();
1498   if (!Schedule)
1499   {
1500     fprintf(f, "First Schedule = NULL\n");
1501     fclose(f);
1502     return;
1503   }
1504
1505   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1506
1507   // For some channels VDR seems to pick a random point in time to
1508   // start dishing out events, but they are in order
1509   // at some point in the list the time snaps to the current event
1510
1511
1512
1513
1514   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1515   {
1516     event = Schedule->GetEventNumber(eventNumber);
1517     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1518     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1519     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1520     fprintf(f, "\n\n");
1521   }
1522
1523   fprintf(f, "\nEND\n");
1524
1525   fclose(f);
1526 }
1527
1528 */
1529
1530
1531
1532 /*
1533
1534
1535 Right, so
1536
1537 Schedules = the collection of all the Schedule objects
1538 Schedule  = One schedule, contants all the events for a channel
1539 Event     = One programme
1540
1541
1542 Want:
1543
1544 Event ID
1545 Time
1546 Duration
1547 Title
1548 Subtitle (used for "Programmes resume at ...")
1549 Description
1550
1551 IsPresent ? easy to work out tho. Oh it doesn't always work
1552
1553 */
1554
1555 /*
1556 void MVPClient::test2()
1557 {
1558   log->log("-", Log::DEBUG, "Timers List");
1559
1560   for (int i = 0; i < Timers.Count(); i++)
1561   {
1562     cTimer *timer = Timers.Get(i);
1563     //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1564     log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1565 #if VDRVERSNUM < 10300
1566     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());
1567 #else
1568     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());
1569 #endif
1570     log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1571     log->log("-", Log::DEBUG, "");
1572   }
1573
1574   // asprintf(&buffer, "%d:%s:%s  :%04d:%04d:%d:%d:%s:%s\n",
1575 //            active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1576 //            PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1577 }
1578 */
1579
1580 /*
1581 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1582 recording is a bool, 0 for not currently recording, 1 for currently recording
1583 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
1584 */
1585
1586
1587 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1588 {
1589   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1590   int count = 4; // leave space for the packet length
1591
1592   const char* fileName;
1593   cTimer *timer;
1594   int numTimers = Timers.Count();
1595
1596   *(ULONG*)&sendBuffer[count] = htonl(numTimers);    count += 4;
1597
1598   for (int i = 0; i < numTimers; i++)
1599   {
1600     if (count > 49000) break;
1601
1602     timer = Timers.Get(i);
1603
1604 #if VDRVERSNUM < 10300
1605     *(ULONG*)&sendBuffer[count] = htonl(timer->Active());                 count += 4;
1606 #else
1607     *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive));       count += 4;
1608 #endif
1609     *(ULONG*)&sendBuffer[count] = htonl(timer->Recording());              count += 4;
1610     *(ULONG*)&sendBuffer[count] = htonl(timer->Pending());                count += 4;
1611     *(ULONG*)&sendBuffer[count] = htonl(timer->Priority());               count += 4;
1612     *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime());               count += 4;
1613     *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number());      count += 4;
1614     *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime());              count += 4;
1615     *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime());               count += 4;
1616     *(ULONG*)&sendBuffer[count] = htonl(timer->Day());                    count += 4;
1617     *(ULONG*)&sendBuffer[count] = htonl(timer->WeekDays());               count += 4;
1618
1619     fileName = timer->File();
1620     strcpy((char*)&sendBuffer[count], fileName);
1621     count += strlen(fileName) + 1;
1622   }
1623
1624   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1625
1626   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1627
1628 //tcp.dump(sendBuffer, count);
1629
1630   tcp.sendPacket(sendBuffer, count);
1631   delete[] sendBuffer;
1632   log->log("Client", Log::DEBUG, "Written timers list");
1633
1634   return 1;
1635 }
1636
1637 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1638 {
1639   char* timerString = new char[strlen((char*)buffer) + 1];
1640   strcpy(timerString, (char*)buffer);
1641
1642 #if VDRVERSNUM < 10300
1643
1644   // If this is VDR 1.2 the date part of the timer string must be reduced
1645   // to just DD rather than YYYY-MM-DD
1646
1647   int s = 0; // source
1648   int d = 0; // destination
1649   int c = 0; // count
1650   while(c != 2) // copy up to date section, including the second ':'
1651   {
1652     timerString[d] = buffer[s];
1653     if (buffer[s] == ':') c++;
1654     ++s;
1655     ++d;
1656   }
1657   // now it has copied up to the date section
1658   c = 0;
1659   while(c != 2) // waste YYYY-MM-
1660   {
1661     if (buffer[s] == '-') c++;
1662     ++s;
1663   }
1664   // now source is at the DD
1665   memcpy(&timerString[d], &buffer[s], length - s);
1666   d += length - s;
1667   timerString[d] = '\0';
1668
1669   log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1670   log->log("Client", Log::DEBUG, "%s", timerString);
1671
1672 #endif
1673
1674   cTimer *timer = new cTimer;
1675   if (timer->Parse((char*)timerString))
1676   {
1677     cTimer *t = Timers.GetTimer(timer);
1678     if (!t)
1679     {
1680       Timers.Add(timer);
1681 #if VDRVERSNUM < 10300
1682       Timers.Save();
1683 #else
1684       Timers.SetModified();
1685 #endif
1686       sendULONG(0);
1687       return 1;
1688     }
1689     else
1690     {
1691       sendULONG(1);
1692     }
1693   }
1694   else
1695   {
1696      sendULONG(2);
1697   }
1698   delete timer;
1699   return 1;
1700 }
1701
1702 void MVPClient::incClients()
1703 {
1704   pthread_mutex_lock(&threadClientMutex);
1705   MVPClient::nr_clients++;
1706   pthread_mutex_unlock(&threadClientMutex);
1707 }
1708
1709 void MVPClient::decClients()
1710 {
1711   pthread_mutex_lock(&threadClientMutex);
1712   MVPClient::nr_clients--;
1713   pthread_mutex_unlock(&threadClientMutex);
1714 }
1715
1716 int MVPClient::getNrClients()
1717 {
1718   int nrClients;
1719   pthread_mutex_lock(&threadClientMutex);
1720   nrClients = MVPClient::nr_clients;
1721   pthread_mutex_unlock(&threadClientMutex);
1722   return nrClients;
1723 }
1724
1725 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1726 {
1727   // data is a pointer to the fileName string
1728
1729   cRecordings Recordings;
1730   Recordings.Load(); // probably have to do this
1731
1732   cRecording *recording = Recordings.GetByName((char*)data);
1733
1734   time_t timerStart = 0;
1735   time_t timerStop = 0;
1736   char* summary = NULL;
1737   ULONG resumePoint = 0;
1738
1739   if (!recording)
1740   {
1741     log->log("Client", Log::ERR, "GetRecInfo found no recording");
1742     sendULONG(0);
1743     return 1;
1744   }
1745
1746   ULONG sendBufferSize = 10000;
1747   UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1748   ULONG pos = 4; // leave first 4 bytes for size field
1749
1750
1751   /* Return packet:
1752   4 bytes: start time for timer
1753   4 bytes: end time for timer
1754   4 bytes: resume point
1755   string: summary
1756   4 bytes: num components
1757   {
1758     1 byte: stream
1759     1 byte: type
1760     string: language
1761     string: description
1762   }
1763
1764   */
1765
1766   // Get current timer
1767
1768   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1769   if (rc)
1770   {
1771     timerStart = rc->Timer()->StartTime();
1772     timerStop = rc->Timer()->StopTime();
1773     log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1774   }
1775
1776   *(time_t*)&sendBuffer[pos] = htonl(timerStart);    pos += 4;
1777   *(time_t*)&sendBuffer[pos] = htonl(timerStop);     pos += 4;
1778
1779   // Get resume point
1780
1781   char* value = config.getValueString("ResumeData", (char*)data);
1782   if (value)
1783   {
1784     resumePoint = strtoul(value, NULL, 10);
1785     delete[] value;
1786   }
1787   log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1788
1789   *(ULONG*)&sendBuffer[pos] = htonl(resumePoint);    pos += 4;
1790
1791
1792   // Get summary
1793
1794 #if VDRVERSNUM < 10300
1795   summary = (char*)recording->Summary();
1796 #else
1797   const cRecordingInfo *Info = recording->Info();
1798   summary = (char*)Info->ShortText();
1799   if (isempty(summary)) summary = (char*)Info->Description();
1800 #endif
1801   log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1802   if (summary)
1803   {
1804     // memory insanity...
1805     if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1806     {
1807       UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1808       if (newBuffer)
1809       {
1810         sendBuffer = newBuffer;
1811         sendBufferSize += strlen(summary) + 10000;
1812       }
1813       else
1814       {
1815         free(sendBuffer);
1816         sendULONG(0);
1817         return 1;
1818       }
1819     }
1820
1821     strcpy((char*)&sendBuffer[pos], summary);
1822     pos += strlen(summary) + 1;
1823   }
1824   else
1825   {
1826     strcpy((char*)&sendBuffer[pos], "");
1827     pos += 1;
1828   }
1829
1830
1831   // Get channels
1832
1833 #if VDRVERSNUM < 10300
1834
1835   // Send 0 for numchannels - this signals the client this info is not available
1836   *(ULONG*)&sendBuffer[pos] = 0;    pos += 4;
1837
1838 #else
1839   const cComponents* components = Info->Components();
1840
1841   log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1842
1843   if (!components)
1844   {
1845     *(ULONG*)&sendBuffer[pos] = htonl(0);    pos += 4;
1846   }
1847   else
1848   {
1849     *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents());    pos += 4;
1850
1851     tComponent* component;
1852     for (int i = 0; i < components->NumComponents(); i++)
1853     {
1854       component = components->Component(i);
1855
1856       // memory insanity...
1857       ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1858                             + (component->description ? strlen(component->description) : 0) + 2;
1859
1860       if ((sendBufferSize - pos) < extraNeeded)
1861       {
1862         UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1863         if (newBuffer)
1864         {
1865           sendBuffer = newBuffer;
1866           sendBufferSize += extraNeeded + 10000;
1867         }
1868         else
1869         {
1870           free(sendBuffer);
1871           sendULONG(0);
1872           return 1;
1873         }
1874       }
1875
1876       log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1877       sendBuffer[pos] = component->stream;  pos += 1;
1878       sendBuffer[pos] = component->type;    pos += 1;
1879       if (component->language)
1880       {
1881         strcpy((char*)&sendBuffer[pos], component->language);
1882         pos += strlen(component->language) + 1;
1883       }
1884       else
1885       {
1886         strcpy((char*)&sendBuffer[pos], "");
1887         pos += 1;
1888       }
1889       if (component->description)
1890       {
1891         strcpy((char*)&sendBuffer[pos], component->description);
1892         pos += strlen(component->description) + 1;
1893       }
1894       else
1895       {
1896         strcpy((char*)&sendBuffer[pos], "");
1897         pos += 1;
1898       }
1899
1900     }
1901   }
1902
1903 #endif
1904
1905   // Done. send it
1906
1907   *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 :  take off the size field
1908
1909   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1910
1911   tcp.sendPacket(sendBuffer, pos);
1912   delete[] sendBuffer;
1913   log->log("Client", Log::DEBUG, "Written getrecinfo");
1914
1915   return 1;
1916 }
1917
1918
1919
1920
1921 // FIXME obselete
1922
1923 int MVPClient::processReScanRecording(UCHAR* data, int length)
1924 {
1925   if (!rp)
1926   {
1927     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1928     return 0;
1929   }
1930
1931   rp->scan();
1932
1933   UCHAR sendBuffer[16];
1934   *(ULONG*)&sendBuffer[0] = htonl(12);
1935   *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1936   *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1937
1938   tcp.sendPacket(sendBuffer, 16);
1939   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1940   return 1;
1941 }
1942
1943 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1944
1945
1946 int MVPClient::processGetMarks(UCHAR* data, int length)
1947 {
1948   // data is a pointer to the fileName string
1949
1950   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1951   int count = 4; // leave space for the packet length
1952
1953
1954   cMarks Marks;
1955   cRecordings Recordings;
1956   Recordings.Load(); // probably have to do this
1957
1958   cRecording *recording = Recordings.GetByName((char*)data);
1959
1960   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1961
1962   if (recording)
1963   {
1964     Marks.Load(recording->FileName());
1965     if (Marks.Count())
1966     {
1967       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1968       {
1969         log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1970
1971         if (count > 49000) break;
1972         *(ULONG*)&sendBuffer[count] = htonl(m->position);
1973         count += 4;
1974       }
1975     }
1976     else
1977     {
1978       log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1979       *(ULONG*)&sendBuffer[count] = htonl(0);
1980       count += 4;
1981     }
1982   }
1983
1984   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1985
1986   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1987
1988   tcp.sendPacket(sendBuffer, count);
1989   delete[] sendBuffer;
1990   log->log("Client", Log::DEBUG, "Written Marks list");
1991
1992   return 1;
1993 }
1994
1995
1996 /**
1997   * media List Request:
1998   * 4 length
1999   * 4 VDR_GETMEDIALIST
2000   * 4 flags (currently unused)
2001   * n dirname
2002   * n+1 0
2003   * Media List response:
2004   * 4 length
2005   * 4 VDR_
2006   * 4 numentries
2007   * per entry:
2008   * 4 media type
2009   * 4 time stamp
2010   * 4 flags
2011   * 4 strlen (incl. 0 Byte)
2012   * string
2013   * 0
2014 */
2015 #define MLISTBUF 500000
2016 int MVPClient::processGetMediaList(UCHAR* data, int length)
2017 {
2018   if (length < 4) {
2019     log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
2020     return 0;
2021   }
2022   char * dirname=NULL;
2023   if (length > 4) {
2024     //we have a dirname provided
2025     dirname=(char *)&data[4];
2026     log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
2027   }
2028
2029
2030   UCHAR* sendBuffer = new UCHAR[MLISTBUF]; // FIXME hope this is enough
2031   int count = 4; // leave space for the header
2032
2033   MediaList * ml=MediaList::readList(baseConfig,dirname);
2034   if (ml == NULL) {
2035      log->log("Client", Log::ERR, "getMediaList returned NULL");
2036      return 0;
2037   }
2038   //response code (not yet set)
2039   *(ULONG*)&sendBuffer[count] = htonl(0);
2040   count += 4;
2041   //numentries
2042   *(ULONG*)&sendBuffer[count] = htonl(ml->Count());
2043   count += 4;
2044   for (int nm=0;nm<ml->Count() && count < (MLISTBUF-1000);nm++) {
2045       Media *m=ml->Get(nm);
2046       log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
2047       *(ULONG*)&sendBuffer[count] = htonl(m->getType());
2048       count += 4;
2049       //time stamp
2050       *(ULONG*)&sendBuffer[count] = htonl(m->getTime());
2051       count += 4;
2052       //flags
2053       *(ULONG*)&sendBuffer[count] = htonl(0);
2054       count += 4;
2055       int len=strlen(m->getFilename());
2056       //strlen
2057       *(ULONG*)&sendBuffer[count] = htonl(len+1);
2058       count += 4;
2059       //should have a check for strlen > 1000...
2060       strcpy((char *)&sendBuffer[count],m->getFilename());
2061       count+=len+1;
2062   }
2063   delete ml;
2064
2065   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
2066
2067   log->log("Client", Log::DEBUG, "getMediaList size  %u", ntohl(*(ULONG*)&sendBuffer[0]));
2068
2069   tcp.sendPacket(sendBuffer, count);
2070   delete[] sendBuffer;
2071   log->log("Client", Log::DEBUG, "Written Media list");
2072
2073   return 1;
2074 }
2075
2076 /**
2077   * get image Request:
2078   * 4 length
2079   * 4 VDR_GETIMAGE
2080   * 4 flags (currently unused)
2081   * 4 x size
2082   * 4 y size
2083   * n filename
2084   * n+1 0
2085   * get image response:
2086   * 4 length
2087   * 4 VDR_GETIMAGE
2088   * 4 len of image
2089 */
2090 #define MLISTBUF 500000
2091 int MVPClient::processGetPicture(UCHAR* data, int length)
2092 {
2093   if (length < 12) {
2094     log->log("Client", Log::ERR, "getPicture packet too short %d", length);
2095     return 0;
2096   }
2097   if (imageFile) {
2098     fclose(imageFile);
2099     imageFile=NULL;
2100   }
2101   char * filename=NULL;
2102   if (length > 4) {
2103     //we have a dirname provided
2104     filename=(char *)&data[4];
2105     log->log("Client", Log::DEBUG, "getPicture  %s", filename);
2106   }
2107   else {
2108     log->log("Client", Log::ERR, "getPicture  empty filename");
2109   }
2110   if (filename) {
2111     imageFile=fopen(filename,"r");
2112     if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
2113   }
2114   int size=0;
2115   if (imageFile) {
2116     struct stat st;
2117     if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
2118   }
2119   UCHAR* sendBuffer = new UCHAR[12];
2120   int count = 4; // leave space for the header
2121   //response code (not yet set)
2122   *(ULONG*)&sendBuffer[count] = htonl(31);
2123   count += 4;
2124   //size
2125   *(ULONG*)&sendBuffer[count] = htonl(size);
2126   count += 4;
2127   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
2128   log->log("Client", Log::DEBUG, "getPicture size  %u", size);
2129
2130   tcp.sendPacket(sendBuffer, count);
2131   delete[] sendBuffer;
2132   log->log("Client", Log::DEBUG, "Written Media list");
2133
2134   return 1;
2135 }
2136
2137
2138 int MVPClient::processGetImageBlock(UCHAR* data, int length)
2139 {
2140   if (!imageFile)
2141   {
2142     log->log("Client", Log::DEBUG, "Get image block called when no image active");
2143     return 0;
2144   }
2145
2146   ULLONG position = ntohll(*(ULLONG*)data);
2147   data += sizeof(ULLONG);
2148   ULONG amount = ntohl(*(ULONG*)data);
2149
2150   log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
2151
2152   UCHAR sendBuffer[amount + 4];
2153   ULONG amountReceived = 0; // compiler moan.
2154   ULLONG cpos=ftell(imageFile);
2155   if (position != cpos) {
2156     fseek(imageFile,position-cpos,SEEK_CUR);
2157   }
2158   if (position != (ULLONG)ftell(imageFile)) {
2159     log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
2160   }
2161   else {
2162     amountReceived=fread(&sendBuffer[4],1,amount,imageFile);
2163   }
2164
2165   if (!amountReceived)
2166   {
2167     sendULONG(0);
2168     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
2169   }
2170   else
2171   {
2172     *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
2173     tcp.sendPacket(sendBuffer, amountReceived + 4);
2174     log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
2175   }
2176
2177   return 1;
2178 }
2179
2180 int MVPClient::processDeleteTimer(UCHAR* buffer, int length)
2181 {
2182   log->log("Client", Log::DEBUG, "Delete timer called");
2183   // get timer
2184   
2185   int position = 0;
2186   
2187   INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
2188   INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
2189   INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;  
2190   INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;  
2191   INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
2192     
2193   cTimer* ti = NULL;
2194   for (ti = Timers.First(); ti; ti = Timers.Next(ti))
2195   {
2196     if  ( (ti->Channel()->Number() == delChannel)
2197      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
2198      &&   (ti->StartTime() == delStart)
2199      &&   (ti->StopTime() == delStop) )
2200        break;
2201   }
2202   
2203   if (!ti)
2204   {
2205     sendULONG(4);
2206     return 1;
2207   }
2208           
2209   if (!Timers.BeingEdited())
2210   {
2211     if (!ti->Recording())
2212     {
2213       Timers.Del(ti);
2214       Timers.SetModified();
2215       sendULONG(10);
2216       return 1;
2217     }
2218     else
2219     {
2220       log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
2221       sendULONG(3);
2222       return 1;
2223     }  
2224   }
2225   else
2226   {
2227     log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
2228     sendULONG(1);
2229     return 1;
2230   }  
2231 }
2232