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