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