]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Protocol changes
[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   log->log("Client", Log::DEBUG, "length = %i", length);
799   ULONG channelNumber = ntohl(*(ULONG*)data);
800
801   cChannel* channel = channelFromNumber(channelNumber);
802   if (!channel)
803   {
804     rp->addULONG(0);
805     rp->finalise();
806     tcp.sendPacket(rp->getPtr(), rp->getLen());
807     delete rp;
808     return 1;
809   }
810
811   // get the priority we should use
812   int fail = 1;
813   int priority = config.getValueLong("General", "Live priority", &fail);
814   if (!fail)
815   {
816     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
817   }
818   else
819   {
820     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
821     priority = 0;
822   }
823
824   // a bit of sanity..
825   if (priority < 0) priority = 0;
826   if (priority > 99) priority = 99;
827
828   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
829   lp = MVPReceiver::create(channel, priority);
830
831   if (!lp)
832   {
833     rp->addULONG(0);
834     rp->finalise();
835     tcp.sendPacket(rp->getPtr(), rp->getLen());
836     delete rp;
837     return 1;
838   }
839
840   if (!lp->init(&tcp, streamID))
841   {
842     delete lp;
843     lp = NULL;
844     rp->addULONG(0);
845     rp->finalise();
846     tcp.sendPacket(rp->getPtr(), rp->getLen());
847     delete rp;
848     return 1;
849   }
850
851   rp->addULONG(1);
852   rp->finalise();
853   tcp.sendPacket(rp->getPtr(), rp->getLen());
854   delete rp;
855   return 1;
856 }
857
858 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
859 {
860   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
861   if (lp)
862   {
863     delete lp;
864     lp = NULL;
865   }
866   else if (recplayer)
867   {
868     writeResumeData();
869
870     delete recplayer;
871     delete recordingManager;
872     recplayer = NULL;
873     recordingManager = NULL;
874   }
875
876   rp->addULONG(1);
877   rp->finalise();
878   tcp.sendPacket(rp->getPtr(), rp->getLen());
879   delete rp;
880   return 1;
881 }
882
883 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
884 {
885   if (!lp && !recplayer)
886   {
887     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
888     return 0;
889   }
890
891   ULLONG position = ntohll(*(ULLONG*)data);
892   data += sizeof(ULLONG);
893   ULONG amount = ntohl(*(ULONG*)data);
894
895   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
896
897   UCHAR sendBuffer[amount];
898   ULONG amountReceived = 0; // compiler moan.
899   if (lp)
900   {
901     log->log("Client", Log::DEBUG, "getting from live");
902     amountReceived = lp->getBlock(&sendBuffer[0], amount);
903
904     if (!amountReceived)
905     {
906       // vdr has possibly disconnected the receiver
907       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
908       delete lp;
909       lp = NULL;
910     }
911   }
912   else if (recplayer)
913   {
914     log->log("Client", Log::DEBUG, "getting from recording");
915     amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
916   }
917
918   if (!amountReceived)
919   {
920     rp->addULONG(0);
921     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
922   }
923   else
924   {
925     rp->copyin(sendBuffer, amountReceived);
926     log->log("Client", Log::DEBUG, "written %lu", amountReceived);
927   }
928
929   rp->finalise();
930   tcp.sendPacket(rp->getPtr(), rp->getLen());
931   log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
932   delete rp;
933   return 1;
934 }
935
936 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
937 {
938   // data is a pointer to the fileName string
939
940   recordingManager = new cRecordings;
941   recordingManager->Load();
942
943   cRecording* recording = recordingManager->GetByName((char*)data);
944
945   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
946
947   if (recording)
948   {
949     recplayer = new RecPlayer(recording);
950
951     rp->addULONG(recplayer->getLengthBytes());
952     rp->addULONG(recplayer->getLengthFrames());
953     rp->finalise();
954     tcp.sendPacket(rp->getPtr(), rp->getLen());
955     delete rp;
956     
957     log->log("Client", Log::DEBUG, "written totalLength");
958   }
959   else
960   {
961     delete recordingManager;
962     recordingManager = NULL;
963   }
964   return 1;
965 }
966
967 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
968 {
969   ULLONG retval = 0;
970
971   ULONG frameNumber = ntohl(*(ULONG*)data);
972   data += 4;
973
974   if (!recplayer)
975   {
976     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
977   }
978   else
979   {
980     retval = recplayer->positionFromFrameNumber(frameNumber);
981   }
982
983   rp->addULONG(retval);
984   rp->finalise();
985   tcp.sendPacket(rp->getPtr(), rp->getLen());
986   delete rp;
987
988   log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
989   return 1;
990 }
991
992 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
993 {
994   ULONG retval = 0;
995
996   ULLONG position = ntohll(*(ULLONG*)data);
997   data += 8;
998
999   if (!recplayer)
1000   {
1001     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1002   }
1003   else
1004   {
1005     retval = recplayer->frameNumberFromPosition(position);
1006   }
1007
1008   rp->addULONG(retval);
1009   rp->finalise();
1010   tcp.sendPacket(rp->getPtr(), rp->getLen());
1011   delete rp;
1012
1013   log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1014   return 1;
1015 }
1016
1017 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1018 {
1019   bool success = false;
1020
1021   ULONG frameNumber = ntohl(*(ULONG*)data);
1022   data += 4;
1023   ULONG direction = ntohl(*(ULONG*)data);
1024   data += 4;
1025
1026   ULLONG rfilePosition = 0;
1027   ULONG rframeNumber = 0;
1028   ULONG rframeLength = 0;
1029
1030   if (!recplayer)
1031   {
1032     log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1033   }
1034   else
1035   {
1036     success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1037   }
1038
1039   // returns file position, frame number, length
1040
1041   if (success)
1042   {
1043     rp->addULLONG(rfilePosition);
1044     rp->addULONG(rframeNumber);
1045     rp->addULONG(rframeLength);
1046   }
1047   else
1048   {
1049     rp->addULONG(0);
1050   }
1051
1052   rp->finalise();
1053   tcp.sendPacket(rp->getPtr(), rp->getLen());
1054   delete rp;
1055   
1056   log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1057   return 1;
1058 }
1059
1060 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1061 {
1062   ULONG channelNumber = ntohl(*(ULONG*)data);
1063   data += 4;
1064   ULONG startTime = ntohl(*(ULONG*)data);
1065   data += 4;
1066   ULONG duration = ntohl(*(ULONG*)data);
1067
1068   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1069
1070   cChannel* channel = channelFromNumber(channelNumber);
1071   if (!channel)
1072   {
1073     rp->addULONG(0);
1074     rp->finalise();
1075     tcp.sendPacket(rp->getPtr(), rp->getLen());
1076     delete rp;
1077   
1078     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1079     return 1;
1080   }
1081
1082   log->log("Client", Log::DEBUG, "Got channel");
1083
1084 #if VDRVERSNUM < 10300
1085   cMutexLock MutexLock;
1086   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1087 #else
1088   cSchedulesLock MutexLock;
1089   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1090 #endif
1091   if (!Schedules)
1092   {
1093     rp->addULONG(0);
1094     rp->finalise();
1095     tcp.sendPacket(rp->getPtr(), rp->getLen());
1096     delete rp;
1097     
1098     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1099     return 1;
1100   }
1101
1102   log->log("Client", Log::DEBUG, "Got schedule!s! object");
1103
1104   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1105   if (!Schedule)
1106   {
1107     rp->addULONG(0);
1108     rp->finalise();
1109     tcp.sendPacket(rp->getPtr(), rp->getLen());
1110     delete rp;
1111     
1112     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1113     return 1;
1114   }
1115
1116   log->log("Client", Log::DEBUG, "Got schedule object");
1117
1118   const char* empty = "";
1119   bool atLeastOneEvent = false;
1120
1121   ULONG thisEventID;
1122   ULONG thisEventTime;
1123   ULONG thisEventDuration;
1124   const char* thisEventTitle;
1125   const char* thisEventSubTitle;
1126   const char* thisEventDescription;
1127
1128 #if VDRVERSNUM < 10300
1129
1130   const cEventInfo *event;
1131   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1132   {
1133     event = Schedule->GetEventNumber(eventNumber);
1134
1135     thisEventID = event->GetEventID();
1136     thisEventTime = event->GetTime();
1137     thisEventDuration = event->GetDuration();
1138     thisEventTitle = event->GetTitle();
1139     thisEventSubTitle = event->GetSubtitle();
1140     thisEventDescription = event->GetExtendedDescription();
1141
1142 #else
1143
1144   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1145   {
1146     thisEventID = event->EventID();
1147     thisEventTime = event->StartTime();
1148     thisEventDuration = event->Duration();
1149     thisEventTitle = event->Title();
1150     thisEventSubTitle = NULL;
1151     thisEventDescription = event->Description();
1152
1153 #endif
1154
1155     log->log("Client", Log::DEBUG, "Got an event object %p", event);
1156
1157     //in the past filter
1158     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1159
1160     //start time filter
1161     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1162
1163     //duration filter
1164     if (thisEventTime >= (startTime + duration)) continue;
1165
1166     if (!thisEventTitle) thisEventTitle = empty;
1167     if (!thisEventSubTitle) thisEventSubTitle = empty;
1168     if (!thisEventDescription) thisEventDescription = empty;
1169
1170     rp->addULONG(thisEventID);
1171     rp->addULONG(thisEventTime);
1172     rp->addULONG(thisEventDuration);
1173
1174     rp->addString(thisEventTitle);
1175     rp->addString(thisEventSubTitle);
1176     rp->addString(thisEventDescription);
1177
1178     atLeastOneEvent = true;
1179     log->log("Client", Log::DEBUG, "Done s3");
1180   }
1181
1182   log->log("Client", Log::DEBUG, "Got all event data");
1183
1184   if (!atLeastOneEvent)
1185   {
1186     rp->addULONG(0);
1187     log->log("Client", Log::DEBUG, "Written 0 because no data");
1188   }
1189   
1190   rp->finalise();
1191   tcp.sendPacket(rp->getPtr(), rp->getLen());
1192   delete rp;
1193     
1194   log->log("Client", Log::DEBUG, "written schedules packet");
1195
1196   return 1;
1197 }
1198
1199 #endif //VOMPSTANDALONE
1200
1201 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1202 {
1203   char* section = (char*)buffer;
1204   char* key = NULL;
1205   char* value = NULL;
1206
1207   for (int k = 0; k < length; k++)
1208   {
1209     if (buffer[k] == '\0')
1210     {
1211       if (!key)
1212       {
1213         key = (char*)&buffer[k+1];
1214       }
1215       else
1216       {
1217         value = (char*)&buffer[k+1];
1218         break;
1219       }
1220     }
1221   }
1222
1223   // if the last string (value) doesnt have null terminator, give up
1224   if (buffer[length - 1] != '\0') return 0;
1225
1226   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1227   if (config.setValueString(section, key, value))
1228   {
1229     rp->addULONG(1);
1230   }
1231   else
1232   {
1233     rp->addULONG(0);
1234   }
1235
1236   rp->finalise();
1237   tcp.sendPacket(rp->getPtr(), rp->getLen());
1238   delete rp;
1239   
1240   return 1;
1241 }
1242
1243 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1244 {
1245   char* section = (char*)buffer;
1246   char* key = NULL;
1247
1248   for (int k = 0; k < length; k++)
1249   {
1250     if (buffer[k] == '\0')
1251     {
1252       key = (char*)&buffer[k+1];
1253       break;
1254     }
1255   }
1256
1257   char* value = config.getValueString(section, key);
1258
1259   if (value)
1260   {
1261     rp->addString(value);
1262     log->log("Client", Log::DEBUG, "Written config load packet");
1263     delete[] value;
1264   }
1265   else
1266   {
1267     rp->addULONG(0);
1268     log->log("Client", Log::DEBUG, "Written config load failed packet");
1269   }
1270
1271   rp->finalise();
1272   tcp.sendPacket(rp->getPtr(), rp->getLen());
1273   delete rp;
1274   
1275   return 1;
1276 }
1277
1278 #ifndef VOMPSTANDALONE
1279
1280 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1281 {
1282   cTimer *timer;
1283   int numTimers = Timers.Count();
1284
1285   rp->addULONG(numTimers);
1286
1287   for (int i = 0; i < numTimers; i++)
1288   {
1289     timer = Timers.Get(i);
1290
1291 #if VDRVERSNUM < 10300
1292     rp->addULONG(timer->Active());
1293 #else
1294     rp->addULONG(timer->HasFlags(tfActive));
1295 #endif
1296     rp->addULONG(timer->Recording());
1297     rp->addULONG(timer->Pending());
1298     rp->addULONG(timer->Priority());
1299     rp->addULONG(timer->Lifetime());
1300     rp->addULONG(timer->Channel()->Number());
1301     rp->addULONG(timer->StartTime());
1302     rp->addULONG(timer->StopTime());
1303     rp->addULONG(timer->Day());
1304     rp->addULONG(timer->WeekDays());
1305     rp->addString(timer->File());
1306   }
1307
1308   rp->finalise();
1309   tcp.sendPacket(rp->getPtr(), rp->getLen());
1310   delete rp;
1311   
1312   log->log("Client", Log::DEBUG, "Written timers list");
1313
1314   return 1;
1315 }
1316
1317 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1318 {
1319   char* timerString = new char[strlen((char*)buffer) + 1];
1320   strcpy(timerString, (char*)buffer);
1321
1322 #if VDRVERSNUM < 10300
1323
1324   // If this is VDR 1.2 the date part of the timer string must be reduced
1325   // to just DD rather than YYYY-MM-DD
1326
1327   int s = 0; // source
1328   int d = 0; // destination
1329   int c = 0; // count
1330   while(c != 2) // copy up to date section, including the second ':'
1331   {
1332     timerString[d] = buffer[s];
1333     if (buffer[s] == ':') c++;
1334     ++s;
1335     ++d;
1336   }
1337   // now it has copied up to the date section
1338   c = 0;
1339   while(c != 2) // waste YYYY-MM-
1340   {
1341     if (buffer[s] == '-') c++;
1342     ++s;
1343   }
1344   // now source is at the DD
1345   memcpy(&timerString[d], &buffer[s], length - s);
1346   d += length - s;
1347   timerString[d] = '\0';
1348
1349   log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1350   log->log("Client", Log::DEBUG, "%s", timerString);
1351
1352 #endif
1353
1354   cTimer *timer = new cTimer;
1355   if (timer->Parse((char*)timerString))
1356   {
1357     cTimer *t = Timers.GetTimer(timer);
1358     if (!t)
1359     {
1360       Timers.Add(timer);
1361 #if VDRVERSNUM < 10300
1362       Timers.Save();
1363 #else
1364       Timers.SetModified();
1365 #endif
1366       rp->addULONG(0);
1367       rp->finalise();
1368       tcp.sendPacket(rp->getPtr(), rp->getLen());
1369       delete rp;
1370       return 1;
1371     }
1372     else
1373     {
1374       rp->addULONG(1);
1375       rp->finalise();
1376       tcp.sendPacket(rp->getPtr(), rp->getLen());
1377       delete rp;
1378     }
1379   }
1380   else
1381   {
1382     rp->addULONG(2);
1383     rp->finalise();
1384     tcp.sendPacket(rp->getPtr(), rp->getLen());
1385     delete rp;
1386   }
1387   delete timer;
1388   return 1;
1389 }
1390
1391 int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1392 {
1393   log->log("Client", Log::DEBUG, "Delete timer called");
1394   // get timer
1395   
1396   int position = 0;
1397   
1398   INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
1399   INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
1400   INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;  
1401   INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;  
1402   INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
1403     
1404   cTimer* ti = NULL;
1405   for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1406   {
1407     if  ( (ti->Channel()->Number() == delChannel)
1408      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1409      &&   (ti->StartTime() == delStart)
1410      &&   (ti->StopTime() == delStop) )
1411        break;
1412   }
1413   
1414   if (!ti)
1415   {
1416     rp->addULONG(4);
1417     rp->finalise();
1418     tcp.sendPacket(rp->getPtr(), rp->getLen());
1419     delete rp;
1420     return 1;
1421   }
1422           
1423   if (!Timers.BeingEdited())
1424   {
1425     if (!ti->Recording())
1426     {
1427       Timers.Del(ti);
1428       Timers.SetModified();
1429       rp->addULONG(10);
1430       rp->finalise();
1431       tcp.sendPacket(rp->getPtr(), rp->getLen());
1432       delete rp;
1433       return 1;
1434     }
1435     else
1436     {
1437       log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1438       rp->addULONG(3);
1439       rp->finalise();
1440       tcp.sendPacket(rp->getPtr(), rp->getLen());
1441       delete rp;
1442       return 1;
1443     }  
1444   }
1445   else
1446   {
1447     log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1448     rp->addULONG(1);
1449     rp->finalise();
1450     tcp.sendPacket(rp->getPtr(), rp->getLen());
1451     delete rp;
1452     return 1;
1453   }  
1454 }
1455
1456 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1457 {
1458   // data is a pointer to the fileName string
1459
1460   cRecordings Recordings;
1461   Recordings.Load(); // probably have to do this
1462
1463   cRecording *recording = Recordings.GetByName((char*)data);
1464
1465   time_t timerStart = 0;
1466   time_t timerStop = 0;
1467   char* summary = NULL;
1468   ULONG resumePoint = 0;
1469
1470   if (!recording)
1471   {
1472     log->log("Client", Log::ERR, "GetRecInfo found no recording");
1473     rp->addULONG(0);
1474     rp->finalise();
1475     tcp.sendPacket(rp->getPtr(), rp->getLen());
1476     delete rp;
1477     return 1;
1478   }
1479
1480   /* Return packet:
1481   4 bytes: start time for timer
1482   4 bytes: end time for timer
1483   4 bytes: resume point
1484   string: summary
1485   4 bytes: num components
1486   {
1487     1 byte: stream
1488     1 byte: type
1489     string: language
1490     string: description
1491   }
1492
1493   */
1494
1495   // Get current timer
1496
1497   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1498   if (rc)
1499   {
1500     timerStart = rc->Timer()->StartTime();
1501     timerStop = rc->Timer()->StopTime();
1502     log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1503   }
1504
1505   rp->addULONG(timerStart);
1506   rp->addULONG(timerStop);
1507
1508   // Get resume point
1509
1510   char* value = config.getValueString("ResumeData", (char*)data);
1511   if (value)
1512   {
1513     resumePoint = strtoul(value, NULL, 10);
1514     delete[] value;
1515   }
1516   log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1517
1518   rp->addULONG(resumePoint);
1519
1520
1521   // Get summary
1522
1523 #if VDRVERSNUM < 10300
1524   summary = (char*)recording->Summary();
1525 #else
1526   const cRecordingInfo *Info = recording->Info();
1527   summary = (char*)Info->ShortText();
1528   if (isempty(summary)) summary = (char*)Info->Description();
1529 #endif
1530   log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1531   if (summary)
1532   {
1533     rp->addString(summary);
1534   }
1535   else
1536   {
1537     rp->addString("");
1538   }
1539
1540   // Get channels
1541
1542 #if VDRVERSNUM < 10300
1543
1544   // Send 0 for numchannels - this signals the client this info is not available
1545   rp->addULONG(0);
1546
1547 #else
1548   const cComponents* components = Info->Components();
1549
1550   log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1551
1552   if (!components)
1553   {
1554     rp->addULONG(0);
1555   }
1556   else
1557   {
1558     rp->addULONG(components->NumComponents());
1559   
1560     tComponent* component;
1561     for (int i = 0; i < components->NumComponents(); i++)
1562     {
1563       component = components->Component(i);
1564
1565       log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1566       
1567       rp->addUCHAR(component->stream);
1568       rp->addUCHAR(component->type);
1569
1570       if (component->language)
1571       {
1572         rp->addString(component->language);
1573       }
1574       else
1575       {
1576         rp->addString("");
1577       }
1578       if (component->description)
1579       {
1580         rp->addString(component->description);
1581       }
1582       else
1583       {
1584         rp->addString("");
1585       }
1586     }
1587   }
1588
1589 #endif
1590
1591   // Done. send it
1592
1593   rp->finalise();
1594   tcp.sendPacket(rp->getPtr(), rp->getLen());
1595   delete rp;
1596
1597   log->log("Client", Log::DEBUG, "Written getrecinfo");
1598
1599   return 1;
1600 }
1601
1602
1603
1604
1605 // FIXME obselete
1606
1607 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1608 {
1609   if (!recplayer)
1610   {
1611     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1612     return 0;
1613   }
1614
1615   recplayer->scan();
1616
1617   rp->addULLONG(recplayer->getLengthBytes());
1618   rp->addULONG(recplayer->getLengthFrames());
1619   rp->finalise();
1620   tcp.sendPacket(rp->getPtr(), rp->getLen());
1621   delete rp;
1622   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1623   return 1;
1624 }
1625
1626 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1627
1628
1629 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1630 {
1631   // data is a pointer to the fileName string
1632
1633   cMarks Marks;
1634   cRecordings Recordings;
1635   Recordings.Load(); // probably have to do this
1636
1637   cRecording *recording = Recordings.GetByName((char*)data);
1638
1639   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1640
1641   if (recording)
1642   {
1643     Marks.Load(recording->FileName());
1644     if (Marks.Count())
1645     {
1646       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1647       {
1648         log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1649
1650         rp->addULONG(m->position);
1651       }
1652     }
1653     else
1654     {
1655       log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1656       rp->addULONG(0);
1657     }
1658   }
1659
1660   rp->finalise();
1661   tcp.sendPacket(rp->getPtr(), rp->getLen());
1662   delete rp;
1663   
1664   log->log("Client", Log::DEBUG, "Written Marks list");
1665
1666   return 1;
1667 }
1668
1669 #endif //VOMPSTANDALONE
1670
1671 /**
1672   * media List Request:
1673   * 4 length
1674   * 4 VDR_GETMEDIALIST
1675   * 4 flags (currently unused)
1676   * n dirname
1677   * n+1 0
1678   * Media List response:
1679   * 4 length
1680   * 4 VDR_
1681   * 4 numentries
1682   * per entry:
1683   * 4 media type
1684   * 4 time stamp
1685   * 4 flags
1686   * 4 strlen (incl. 0 Byte)
1687   * string
1688   * 0
1689 */
1690
1691 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1692 {
1693   if (length < 4) {
1694     log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1695     return 0;
1696   }
1697   char * dirname=NULL;
1698   if (length > 4) {
1699     //we have a dirname provided
1700     dirname=(char *)&data[4];
1701     log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1702   }
1703
1704   MediaList * ml=MediaList::readList(baseConfig,dirname);
1705   if (ml == NULL) {
1706      log->log("Client", Log::ERR, "getMediaList returned NULL");
1707      return 0;
1708   }
1709
1710   //response code (not yet set)
1711   rp->addULONG(0);
1712
1713   //numentries
1714   rp->addULONG(ml->size());
1715
1716   for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1717   {
1718     Media *m=*nm;
1719     log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1720     rp->addULONG(m->getType());
1721     //time stamp
1722     rp->addULONG(m->getTime());
1723     //flags
1724     rp->addULONG(0);
1725     int len=strlen(m->getFilename());
1726     //strlen
1727     rp->addULONG(len+1);
1728     rp->addString(m->getFilename());
1729   }
1730   delete ml;
1731
1732   rp->finalise();
1733   tcp.sendPacket(rp->getPtr(), rp->getLen());
1734   delete rp;
1735   
1736   log->log("Client", Log::DEBUG, "Written Media list");
1737   return 1;
1738 }
1739
1740 /**
1741   * get image Request:
1742   * 4 flags (currently unused)
1743   * 4 x size
1744   * 4 y size
1745   * n filename
1746   * n+1 0
1747   * get image response:
1748   * 4 length
1749   * 4 VDR_GETIMAGE
1750   * 4 len of image
1751 */
1752
1753 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1754 {
1755   if (length < 12) {
1756     log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1757     return 0;
1758   }
1759   if (imageFile) {
1760     fclose(imageFile);
1761     imageFile=NULL;
1762   }
1763   char * filename=NULL;
1764   if (length > 12) {
1765     //we have a dirname provided
1766     filename=(char *)&data[12];
1767     log->log("Client", Log::DEBUG, "getPicture  %s", filename);
1768   }
1769   else {
1770     log->log("Client", Log::ERR, "getPicture  empty filename");
1771   }
1772   if (filename) {
1773     imageFile=fopen(filename,"r");
1774     if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1775   }
1776   int size=0;
1777   if (imageFile) {
1778     struct stat st;
1779     if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1780   }
1781   //response code (not yet set)
1782   rp->addULONG(31);
1783   //size
1784   rp->addULONG(size);
1785
1786   log->log("Client", Log::DEBUG, "getPicture size  %u", size);
1787
1788   rp->finalise();
1789   tcp.sendPacket(rp->getPtr(), rp->getLen());
1790   delete rp;
1791
1792   log->log("Client", Log::DEBUG, "Written getPicture");
1793
1794   return 1;
1795 }
1796
1797
1798 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1799 {
1800   if (!imageFile)
1801   {
1802     log->log("Client", Log::DEBUG, "Get image block called when no image active");
1803     return 0;
1804   }
1805
1806   ULLONG position = ntohll(*(ULLONG*)data);
1807   data += sizeof(ULLONG);
1808   ULONG amount = ntohl(*(ULONG*)data);
1809
1810   log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1811
1812   UCHAR sendBuffer[amount];
1813   ULONG amountReceived = 0; // compiler moan.
1814   ULLONG cpos=ftell(imageFile);
1815   if (position != cpos) {
1816     fseek(imageFile,position-cpos,SEEK_CUR);
1817   }
1818   if (position != (ULLONG)ftell(imageFile)) {
1819     log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1820   }
1821   else {
1822     amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1823   }
1824
1825   if (!amountReceived)
1826   {
1827     rp->addULONG(0);
1828     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1829   }
1830   else
1831   {
1832     rp->copyin(sendBuffer, amount);
1833     log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1834   }
1835   
1836   rp->finalise();
1837   tcp.sendPacket(rp->getPtr(), rp->getLen());
1838   delete rp;
1839
1840   return 1;
1841 }
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853 /*
1854     event = Schedule->GetPresentEvent();
1855
1856     fprintf(f, "\n\nCurrent event\n\n");
1857
1858     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1859     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1860     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1861     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1862
1863     event = Schedule->GetFollowingEvent();
1864
1865     fprintf(f, "\n\nFollowing event\n\n");
1866
1867     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1868     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1869     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1870     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1871
1872     fprintf(f, "\n\n");
1873
1874     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1875     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1876     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1877     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1878
1879     fprintf(f, "\n\n");
1880
1881
1882
1883 void MVPClient::test2()
1884 {
1885   FILE* f = fopen("/tmp/s.txt", "w");
1886
1887 #if VDRVERSNUM < 10300
1888   cMutexLock MutexLock;
1889   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1890 #else
1891   cSchedulesLock MutexLock;
1892   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1893 #endif
1894
1895   if (!Schedules)
1896   {
1897     fprintf(f, "Schedules = NULL\n");
1898     fclose(f);
1899     return;
1900   }
1901
1902   fprintf(f, "Schedules dump:\n");
1903   Schedules->Dump(f);
1904
1905
1906   const cSchedule *Schedule;
1907   int scheduleNumber = 0;
1908
1909   tChannelID tchid;
1910   cChannel *thisChannel;
1911
1912 #if VDRVERSNUM < 10300
1913   const cEventInfo *event;
1914   int eventNumber = 0;
1915 #else
1916   const cEvent *event;
1917 #endif
1918
1919 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
1920 //    Schedule = Schedules->GetSchedule();
1921   Schedule = Schedules->First();
1922   if (!Schedule)
1923   {
1924     fprintf(f, "First Schedule = NULL\n");
1925     fclose(f);
1926     return;
1927   }
1928
1929   while (Schedule)
1930   {
1931     fprintf(f, "Schedule #%i\n", scheduleNumber);
1932     fprintf(f, "-------------\n\n");
1933
1934 #if VDRVERSNUM < 10300
1935     tchid = Schedule->GetChannelID();
1936 #else
1937     tchid = Schedule->ChannelID();
1938 #endif
1939
1940 #if VDRVERSNUM < 10300
1941     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1942     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1943 #else
1944 //  put the count at the end.
1945 #endif
1946
1947     thisChannel = Channels.GetByChannelID(tchid, true);
1948     if (thisChannel)
1949     {
1950       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1951     }
1952     else
1953     {
1954       fprintf(f, "thisChannel = NULL for tchid\n");
1955     }
1956
1957 #if VDRVERSNUM < 10300
1958     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1959     {
1960       event = Schedule->GetEventNumber(eventNumber);
1961       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1962       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1963       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1964       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1965       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1966       fprintf(f, "Event %u dump:\n", eventNumber);
1967       event->Dump(f);
1968       fprintf(f, "\n\n");
1969     }
1970 #else
1971 //  This whole section needs rewriting to walk the list.
1972     event = Schedule->Events()->First();
1973     while (event) {
1974       event = Schedule->Events()->Next(event);
1975     }
1976 #endif
1977
1978
1979     fprintf(f, "\nDump from object:\n");
1980     Schedule->Dump(f);
1981     fprintf(f, "\nEND\n");
1982
1983
1984
1985
1986     fprintf(f, "End of current Schedule\n\n\n");
1987
1988     Schedule = (const cSchedule *)Schedules->Next(Schedule);
1989     scheduleNumber++;
1990   }
1991
1992   fclose(f);
1993 }
1994
1995
1996   const cEventInfo *GetPresentEvent(void) const;
1997   const cEventInfo *GetFollowingEvent(void) const;
1998   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1999   const cEventInfo *GetEventAround(time_t tTime) const;
2000   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2001
2002
2003   const unsigned char GetTableID(void) const;
2004   const char *GetTimeString(void) const;
2005   const char *GetEndTimeString(void) const;
2006   const char *GetDate(void) const;
2007   bool IsFollowing(void) const;
2008   bool IsPresent(void) const;
2009   const char *GetExtendedDescription(void) const;
2010   const char *GetSubtitle(void) const;
2011   const char *GetTitle(void) const;
2012   unsigned short GetEventID(void) const;
2013   long GetDuration(void) const;
2014   time_t GetTime(void) const;
2015   tChannelID GetChannelID(void) const;
2016   int GetChannelNumber(void) const { return nChannelNumber; }
2017   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2018   void Dump(FILE *f, const char *Prefix = "") const;
2019
2020
2021
2022 void MVPClient::test(int channelNumber)
2023 {
2024   FILE* f = fopen("/tmp/test.txt", "w");
2025
2026   cMutexLock MutexLock;
2027   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2028
2029   if (!Schedules)
2030   {
2031     fprintf(f, "Schedules = NULL\n");
2032     fclose(f);
2033     return;
2034   }
2035
2036   fprintf(f, "Schedules dump:\n");
2037 //  Schedules->Dump(f);
2038
2039   const cSchedule *Schedule;
2040   cChannel *thisChannel;
2041   const cEventInfo *event;
2042
2043   thisChannel = channelFromNumber(channelNumber);
2044   if (!thisChannel)
2045   {
2046     fprintf(f, "thisChannel = NULL\n");
2047     fclose(f);
2048     return;
2049   }
2050
2051   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2052 //    Schedule = Schedules->GetSchedule();
2053 //  Schedule = Schedules->First();
2054   if (!Schedule)
2055   {
2056     fprintf(f, "First Schedule = NULL\n");
2057     fclose(f);
2058     return;
2059   }
2060
2061   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2062
2063   // For some channels VDR seems to pick a random point in time to
2064   // start dishing out events, but they are in order
2065   // at some point in the list the time snaps to the current event
2066
2067
2068
2069
2070   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2071   {
2072     event = Schedule->GetEventNumber(eventNumber);
2073     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2074     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2075     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2076     fprintf(f, "\n\n");
2077   }
2078
2079   fprintf(f, "\nEND\n");
2080
2081   fclose(f);
2082 }
2083
2084
2085
2086 Right, so
2087
2088 Schedules = the collection of all the Schedule objects
2089 Schedule  = One schedule, contants all the events for a channel
2090 Event     = One programme
2091
2092
2093 Want:
2094
2095 Event ID
2096 Time
2097 Duration
2098 Title
2099 Subtitle (used for "Programmes resume at ...")
2100 Description
2101
2102 IsPresent ? easy to work out tho. Oh it doesn't always work
2103
2104
2105 void MVPClient::test2()
2106 {
2107   log->log("-", Log::DEBUG, "Timers List");
2108
2109   for (int i = 0; i < Timers.Count(); i++)
2110   {
2111     cTimer *timer = Timers.Get(i);
2112     //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2113     log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2114 #if VDRVERSNUM < 10300
2115     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());
2116 #else
2117     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());
2118 #endif
2119     log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2120     log->log("-", Log::DEBUG, "");
2121   }
2122
2123   // asprintf(&buffer, "%d:%s:%s  :%04d:%04d:%d:%d:%s:%s\n",
2124 //            active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2125 //            PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2126 }
2127
2128
2129 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2130 recording is a bool, 0 for not currently recording, 1 for currently recording
2131 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
2132 */
2133