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