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