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