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