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