]> git.vomp.tv Git - vompserver.git/blob - vompclientrrproc.c
Add handling for recording minuatures
[vompserver.git] / vompclientrrproc.c
1 /*
2     Copyright 2008 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 <stdlib.h>
22
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
29 #include <vdr/menu.h>
30 #include <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
33 #include "services/scraper2vdr.h"
34 #endif
35
36 #include "vompclientrrproc.h"
37 #include "vompclient.h"
38 #include "log.h"
39 #include "media.h"
40 #include "mediaplayer.h"
41 #include "servermediafile.h"
42 #include "i18n.h"
43 #include "vdrcommand.h"
44 #include "picturereader.h"
45
46 bool ResumeIDLock;
47
48 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MIN = 0x00000301;
49 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000302;
50 // format is aabbccdd
51 // cc is release protocol version, increase with every release, that changes protocol
52 // dd is development protocol version, set to zero at every release, 
53 // increase for every protocol change in git
54 // bb not equal zero should indicate a non loggytronic protocol
55 // aa is reserved for future use
56 // VOMP_PROTOCOL_VERSION_MIN is the protocol version minimal supported by the server
57 // VOMP_PROTOCOL_VERSION_MAX is the protocol version maximal supported by the server
58 // This allows to run older clients from a new server
59 // Increase the minimal protocol version everytime you break compatibility for a certain 
60 // command. 
61
62 ULONG VompClientRRProc::getProtocolVersionMin()
63 {
64   return VOMP_PROTOCOL_VERSION_MIN;
65 }
66
67 ULONG VompClientRRProc::getProtocolVersionMax()
68 {
69   return VOMP_PROTOCOL_VERSION_MAX;
70 }
71
72 VompClientRRProc::VompClientRRProc(VompClient& x)
73  : x(x)
74 {
75   log = Log::getInstance();
76   req = NULL;
77   resp = NULL;
78 }
79
80 VompClientRRProc::~VompClientRRProc()
81 {
82   threadStop();
83 }
84
85 bool VompClientRRProc::init()
86 {
87   int a = threadStart();
88   sleep(1);
89   return a;
90 }
91
92 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
93 {
94   /*
95      Accept a new request
96      Now we have a queue system is used,
97      since on rare occasion the client fire two request at once
98      e.g. heavily channel switching 
99      then processing only a single request would cause a deadlock in the client
100      Marten
101   */
102
103   log->log("RRProc", Log::DEBUG, "recvReq");
104   threadLock();
105   req_queue.push(newRequest);
106   threadSignalNoLock();
107   log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");     
108   threadUnlock();
109
110   return true;
111 }
112
113 void VompClientRRProc::threadMethod()
114 {
115   threadLock();
116   log->log("RRProc", Log::DEBUG, "threadMethod startup");     
117
118   if (req_queue.size() != 0)
119   {
120 /*
121 -    log->log("RRProc", Log::ERR, "threadMethod err 1");     
122 -    threadUnlock();
123 -    return;
124
125 That was how the code used to be.
126
127 TODO: Work out why this happens.
128 */  
129   
130     log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");     
131     while (req_queue.size()) 
132     {
133       //log->log("RRProc", Log::DEBUG, "thread while");
134       req = req_queue.front();
135       req_queue.pop();
136       
137       threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
138       
139       if (!processPacket())
140       {
141         log->log("RRProc", Log::ERR, "processPacket exited with fail");     
142         return;
143       }
144       
145       threadLock();
146     } 
147     log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");     
148
149   }
150     
151   while(1)  
152   {
153     log->log("RRProc", Log::DEBUG, "threadMethod waiting");     
154     threadWaitForSignal();  // unlocks, waits, relocks
155     if (req_queue.size() == 0)
156     {
157       log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");     
158       threadUnlock();
159       return;
160     }
161     
162     // signalled with something in queue
163     
164     log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
165
166     while (req_queue.size()) 
167     {
168       //log->log("RRProc", Log::DEBUG, "thread while");
169       req = req_queue.front();
170       req_queue.pop();
171       
172       threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
173       
174       if (!processPacket())
175       {
176         log->log("RRProc", Log::ERR, "processPacket exited with fail");     
177         return;
178       }
179       
180       threadLock();
181     } 
182     
183     // locked and run out of packets to process
184   }  
185 }
186
187 bool VompClientRRProc::processPacket()
188 {
189   resp = new ResponsePacket();
190   if (!resp->init(req->requestID))
191   {
192     log->log("RRProc", Log::ERR, "response packet init fail");     
193     delete resp; 
194     
195     if (req->data) free(req->data);
196     delete req;
197     req = NULL;
198
199     return false;
200   }
201     
202   int result = 0;
203
204   switch(req->opcode)
205   {
206     case 1:
207       result = processLogin();
208       break;
209 #ifndef VOMPSTANDALONE        
210     case 2:
211       result = processGetRecordingsList();
212       break;
213     case 3:
214       result = processDeleteRecording();
215       break;
216     case 5:
217       result = processGetChannelsList();
218       break;
219     case 6:
220       result = processStartStreamingChannel();
221       break;
222     case 7:
223       result = processGetBlock();
224       break;
225     case 8:
226       result = processStopStreaming();
227       break;
228     case 9:
229       result = processStartStreamingRecording();
230       break;
231     case 10:
232       result = processGetChannelSchedule();
233       break;
234 #endif     
235     case 11:
236       result = processConfigSave();
237       break;
238     case 12:
239       result = processConfigLoad();
240       break;
241 #ifndef VOMPSTANDALONE        
242     case 13:
243       result = processReScanRecording();         // FIXME obselete
244       break;
245     case 14:
246       result = processGetTimers();
247       break;
248     case 15:
249       result = processSetTimer();
250       break;
251     case 16:
252       result = processPositionFromFrameNumber();
253       break;
254     case 17:
255       result = processFrameNumberFromPosition();
256       break;
257     case 18:
258       result = processMoveRecording();
259       break;
260     case 19:
261       result = processGetIFrame();
262       break;
263     case 20:
264       result = processGetRecInfo();
265       break;
266     case 21:
267       result = processGetMarks();
268       break;
269     case 22:
270       result = processGetChannelPids();
271       break;
272     case 23:
273       result = processDeleteTimer();
274       break;
275     case 666:
276       result = processVDRShutdown();
277       break;
278     case VDR_GETRECSCRAPEREVENTTYPE:
279       result = processGetRecScraperEventType();
280     break;
281     case VDR_GETSCRAPERMOVIEINFO:
282       result = processGetScraperMovieInfo();
283     break;
284     case VDR_GETSCRAPERSERIESINFO:
285       result = processGetScraperSeriesInfo();
286     break;
287     case VDR_LOADTVMEDIA:
288       result = processLoadTvMedia();
289     break;
290     case VDR_LOADTVMEDIARECTHUMB:
291       result = processLoadTvMediaRecThumb();
292     break;
293 #endif
294     case VDR_GETMEDIALIST:
295       result = processGetMediaList();
296       break;
297     case VDR_OPENMEDIA:
298       result = processOpenMedia();
299       break;
300     case VDR_GETMEDIABLOCK:
301       result = processGetMediaBlock();
302       break;
303     case 33:
304       result = processGetLanguageList();
305       break;
306     case 34:
307       result = processGetLanguageContent();
308       break;
309     case VDR_GETMEDIAINFO:
310             result = processGetMediaInfo();
311             break;
312     case VDR_CLOSECHANNEL:
313             result = processCloseMediaChannel();
314             break;
315     case 37:
316       result = processSetCharset();
317       break;
318   }
319
320   delete resp;
321   resp = NULL;
322   
323   if (req->data) free(req->data);
324   delete req;
325   req = NULL;
326   
327   if (result) return true;
328   return false;
329 }
330
331 int VompClientRRProc::processLogin()
332 {
333   if (req->dataLength != 6) return 0;
334
335   // Open the config
336
337   char configFileName[PATH_MAX];
338   snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]);
339   x.config.init(configFileName);
340
341   // Send the login reply
342
343   time_t timeNow = time(NULL);
344   struct tm* timeStruct = localtime(&timeNow);
345   int timeOffset = timeStruct->tm_gmtoff;
346
347   resp->addULONG(timeNow);
348   resp->addLONG(timeOffset);
349   resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
350   resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
351   resp->finalise();
352   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
353   log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
354     
355   x.loggedIn = true;
356   x.netLog(); // safe to run here since the client won't start net logging for a while yet
357   
358   return 1;
359 }
360
361 int VompClientRRProc::processSetCharset()
362 {
363   int charset = ntohl(*(ULONG*)req->data);
364   if (charset>0 && charset<3)
365   {
366     log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
367     x.setCharset(charset);
368     resp->addULONG(1);
369   }
370   else
371   {
372     log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
373     resp->addULONG(0);
374   }
375   resp->finalise();
376   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
377   return 1;
378 }
379
380 int VompClientRRProc::processConfigSave()
381 {
382   char* section = (char*)req->data;
383   char* key = NULL;
384   char* value = NULL;
385
386   for (UINT k = 0; k < req->dataLength; k++)
387   {
388     if (req->data[k] == '\0')
389     {
390       if (!key)
391       {
392         key = (char*)&req->data[k+1];
393       }
394       else
395       {
396         value = (char*)&req->data[k+1];
397         break;
398       }
399     }
400   }
401
402   // if the last string (value) doesnt have null terminator, give up
403   if (req->data[req->dataLength - 1] != '\0') return 0;
404
405   log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
406   if (x.config.setValueString(section, key, value))
407   {
408     resp->addULONG(1);
409   }
410   else
411   {
412     resp->addULONG(0);
413   }
414
415   resp->finalise();
416   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
417   
418   return 1;
419 }
420
421 int VompClientRRProc::processConfigLoad()
422 {
423   char* section = (char*)req->data;
424   char* key = NULL;
425
426   for (UINT k = 0; k < req->dataLength; k++)
427   {
428     if (req->data[k] == '\0')
429     {
430       key = (char*)&req->data[k+1];
431       break;
432     }
433   }
434
435   char* value = x.config.getValueString(section, key);
436
437   if (value)
438   {
439     resp->addString(value);//client coding, do not touch
440     log->log("RRProc", Log::DEBUG, "Written config load packet");
441     delete[] value;
442   }
443   else
444   {
445     resp->addULONG(0);
446     log->log("RRProc", Log::DEBUG, "Written config load failed packet");
447   }
448
449   resp->finalise();
450   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
451   
452   return 1;
453 }
454
455
456 //helper for sending from a serialize buffer
457 //insert the used len into the first 4 Bytes of the buffer
458 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
459   resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
460   resp->finalise();
461   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
462 }
463
464 /**
465   * media List Request:
466   * Media List response:
467   * flags, mediaList
468 */
469 #define MLISTBUF 500000
470 int VompClientRRProc::processGetMediaList()
471 {
472   SerializeBuffer buffer(req->data,req->dataLength);
473   MediaURI uri(0,NULL,NULL);
474   VDR_GetMediaListRequest request(&uri);
475   if (request.deserialize(&buffer) != 0) {
476     log->log("Client", Log::ERR, "getMediaList unable to deserialize");
477     return 0;
478   }
479   const char *dirname=uri.getName();
480   log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
481
482   MediaList * ml=NULL;
483   if (dirname == NULL) {
484     ml=x.media->getRootList();
485   } else {
486     ml=x.media->getMediaList(&uri);
487   }
488   if (ml == NULL) {
489      log->log("Client", Log::ERR, "getMediaList returned NULL");
490      return 0;
491   }
492   SerializeBuffer rbuf(MLISTBUF,false,true);
493   ULONG flags=0; //TODO: real error handling by setting flags
494   VDR_GetMediaListResponse response(&flags,ml);
495   if (response.serialize(&rbuf) != 0) {
496      log->log("Client", Log::ERR, "getMediaList returned NULL");
497      delete ml;
498      return 0;
499   }
500   log->log("Client", Log::DEBUG, "getMediaList size  %u", ml->size());
501   delete ml;
502   
503
504   sendPacket(&rbuf);
505   log->log("Client", Log::DEBUG, "Written Media list");
506   return 1;
507 }
508 /**
509   * openMedia Request:
510   * openMedia response:
511 */
512 int VompClientRRProc::processOpenMedia()
513 {
514   SerializeBuffer buffer(req->data,req->dataLength);
515   MediaURI uri(0,NULL,NULL);
516   ULONG channel=0;
517   ULONG xs=0;
518   ULONG ys=0;
519   VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
520   if (request.deserialize(&buffer) != 0) {
521     log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
522     return 0;
523   }
524   const char *name=uri.getName();
525   log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
526   ULLONG size=0;
527   int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
528   ULONG flags=0;
529   if (rt != 0) {
530     size=0;
531     flags=1;
532     log->log("Client", Log::ERR, "openMediaRequest unable to open");
533   }
534   VDR_OpenMediumResponse response(&flags,&size);
535   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
536   if (response.serialize(&rbuf) != 0) {
537      log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
538      return 0;
539   }
540   log->log("Client", Log::DEBUG, "openMediaRequest size  %llu", size);
541   sendPacket(&rbuf);
542   return 1;
543 }
544 /**
545   * VDR_GETMEDIABLOCK
546   * resp
547   * packet - no serialized response!
548   */
549 int VompClientRRProc::processGetMediaBlock()
550 {
551   SerializeBuffer buffer(req->data,req->dataLength);
552   ULLONG position = 0;
553   ULONG amount = 0;
554   ULONG channel = 0;
555   VDR_GetMediaBlockRequest request(&channel,&position,&amount);
556   if (request.deserialize(&buffer) != 0) {
557     log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
558     return 0;
559   }
560   log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
561
562   UCHAR sendBuffer[amount ];
563   ULONG amountReceived = 0; 
564   UCHAR *rbuf=sendBuffer;
565   int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
566   if (!amountReceived || rt != 0)
567   {
568     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
569   }
570   else
571   {
572     if (rbuf != sendBuffer) {
573       //the provider did not use the optimized handling with using my buffer
574       resp->copyin(rbuf,amountReceived);
575       free(rbuf);
576     } else {
577       // the provider did not allocate a new buffer
578       resp->copyin(sendBuffer,amountReceived);
579     }
580   }
581   resp->finalise();
582   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
583   log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
584   return 1;
585 }
586 /**
587   * VDR_GETMEDIAINFO
588   */
589
590 int VompClientRRProc::processGetMediaInfo()
591 {
592   SerializeBuffer buffer(req->data,req->dataLength);
593   ULONG channel=0;
594   VDR_GetMediaInfoRequest request(&channel);
595   if (request.deserialize(&buffer) != 0) {
596     log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
597     return 0;
598   }
599   log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
600   ULONG flags=0;
601   MediaInfo mi;
602   int rt=x.media->getMediaInfo(channel,&mi);
603   if (rt != 0) {
604     flags=1;
605     log->log("Client", Log::ERR, "getMediaInfo unable to get");
606   }
607   VDR_GetMediaInfoResponse response(&flags,&mi);
608   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
609   if (response.serialize(&rbuf) != 0) {
610      log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
611      return 0;
612   }
613   sendPacket(&rbuf);
614   return 1;
615 }
616
617 /**
618   * VDR_CLOSECHANNEL
619   */
620
621
622 int VompClientRRProc::processCloseMediaChannel()
623 {
624   SerializeBuffer buffer(req->data,req->dataLength);
625   ULONG channel=0;
626   VDR_CloseMediaChannelRequest request(&channel);
627   if (request.deserialize(&buffer) != 0) {
628     log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
629     return 0;
630   }
631   ULONG flags=0;
632   log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
633   int rt=x.media->closeMediaChannel(channel);
634   if (rt != 0) {
635     flags=1;
636     log->log("Client", Log::ERR, "closeMediaChannel unable to get");
637   }
638   VDR_CloseMediaChannelResponse response(&flags);
639   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
640   if (response.serialize(&rbuf) != 0) {
641      log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
642      return 0;
643   }
644   sendPacket(&rbuf);
645   return 1;
646 }
647
648
649
650 int VompClientRRProc::processGetLanguageList()
651 {
652   x.i18n.findLanguages();
653   const I18n::lang_code_list& languages = x.i18n.getLanguageList();
654   std::string result;
655   I18n::lang_code_list::const_iterator iter;
656   for (iter = languages.begin(); iter != languages.end(); ++iter)
657   {
658     resp->addString(iter->first.c_str()); // Source code is acsii
659     resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
660   }
661   resp->finalise();
662   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
663   return 1;
664 }
665
666 int VompClientRRProc::processGetLanguageContent()
667 {
668   if (req->dataLength <= 0) return 0;
669   std::string code, result;
670   code.assign((char*)req->data, req->dataLength - 1);
671   x.i18n.findLanguages();
672   I18n::trans_table texts = x.i18n.getLanguageContent(code);
673   I18n::trans_table::const_iterator iter;
674   for (iter = texts.begin(); iter != texts.end(); ++iter)
675   {
676     resp->addString(iter->first.c_str());// source code is acsii since it is english
677     resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
678   }
679   resp->finalise();
680   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
681   return 1;
682 }
683
684 #ifndef VOMPSTANDALONE
685
686 int VompClientRRProc::processGetRecordingsList()
687 {
688   int FreeMB;
689 #if APIVERSNUM > 20101
690   int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
691 #else
692   int Percent = VideoDiskSpace(&FreeMB);
693 #endif
694   int Total = (FreeMB / (100 - Percent)) * 100;
695   
696   resp->addULONG(Total);
697   resp->addULONG(FreeMB);
698   resp->addULONG(Percent);
699   
700   cRecordings Recordings;
701   Recordings.Load();
702
703   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
704   {
705 #if VDRVERSNUM < 10721
706     resp->addULONG(recording->start);
707 #else
708     resp->addULONG(recording->Start());
709 #endif
710     resp->addUCHAR(recording->IsNew() ? 1 : 0);
711     resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
712     resp->addString(recording->FileName());//file name are not  visible by user do not touch
713   }
714
715   resp->finalise();
716   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
717   
718   log->log("RRProc", Log::DEBUG, "Written recordings list");
719
720   return 1;
721 }
722
723 int VompClientRRProc::processDeleteRecording()
724 {
725   // data is a pointer to the fileName string
726
727   cRecordings Recordings;
728   Recordings.Load(); // probably have to do this
729
730   cRecording* recording = Recordings.GetByName((char*)req->data);
731
732   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
733
734   if (recording)
735   {
736     log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
737
738     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
739     if (!rc)
740     {
741       if (recording->Delete())
742       {
743         // Copy svdrp's way of doing this, see if it works
744 #if VDRVERSNUM > 10300
745         ::Recordings.DelByName(recording->FileName());
746 #endif
747         resp->addULONG(1);
748       }
749       else
750       {
751         resp->addULONG(2);
752       }
753     }
754     else
755     {
756       resp->addULONG(3);
757     }
758   }
759   else
760   {
761     resp->addULONG(4);
762   }
763
764   resp->finalise();
765   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
766   
767   return 1;
768 }
769
770 int VompClientRRProc::processMoveRecording()
771 {
772   log->log("RRProc", Log::DEBUG, "Process move recording");
773   char* fileName = (char*)req->data;
774   char* newPath = NULL;
775
776   for (UINT k = 0; k < req->dataLength; k++)
777   {
778     if (req->data[k] == '\0')
779     {
780       newPath = (char*)&req->data[k+1];
781       break;
782     }
783   }
784   if (!newPath) return 0;
785
786   cRecordings Recordings;
787   Recordings.Load(); // probably have to do this
788
789   cRecording* recording = Recordings.GetByName((char*)fileName);
790
791   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
792
793   if (recording)
794   {
795     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
796     if (!rc)
797     {
798       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
799       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
800       log->log("RRProc", Log::DEBUG, "to: %s", newPath);
801
802       const char* t = recording->FileName();
803
804       char* dateDirName = NULL;   int k;
805       char* titleDirName = NULL;  int j;
806
807       // Find the datedirname
808       for(k = strlen(t) - 1; k >= 0; k--)
809       {
810         if (t[k] == '/')
811         {
812           log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
813           dateDirName = new char[strlen(&t[k+1]) + 1];
814           strcpy(dateDirName, &t[k+1]);
815           break;
816         }
817       }
818
819       // Find the titledirname
820
821       for(j = k-1; j >= 0; j--)
822       {
823         if (t[j] == '/')
824         {
825           log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
826           titleDirName = new char[(k - j - 1) + 1];
827           memcpy(titleDirName, &t[j+1], k - j - 1);
828           titleDirName[k - j - 1] = '\0';
829           break;
830         }
831       }
832
833       log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
834       log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
835 #if APIVERSNUM > 20101
836       log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
837 #else
838       log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
839 #endif
840
841       char* newPathConv = new char[strlen(newPath)+1];
842       strcpy(newPathConv, newPath);
843       ExchangeChars(newPathConv, true);
844       log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
845
846 #if APIVERSNUM > 20101
847       char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
848       log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
849       sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
850 #else
851       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
852       log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
853       sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
854 #endif
855       delete[] newPathConv;
856
857       log->log("RRProc", Log::DEBUG, "%s", newContainer);
858
859       struct stat dstat;
860       int statret = stat(newContainer, &dstat);
861       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
862       {
863         log->log("RRProc", Log::DEBUG, "new dir does not exist");
864         int mkdirret = mkdir(newContainer, 0755);
865         if (mkdirret != 0)
866         {
867           delete[] dateDirName;
868           delete[] titleDirName;
869           delete[] newContainer;
870
871           resp->addULONG(5);          
872           resp->finalise();
873           x.tcp.sendPacket(resp->getPtr(), resp->getLen());
874           return 1;
875         }
876       }
877       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
878       {
879         delete[] dateDirName;
880         delete[] titleDirName;
881         delete[] newContainer;
882
883         resp->addULONG(5);          
884         resp->finalise();
885         x.tcp.sendPacket(resp->getPtr(), resp->getLen());
886         return 1;
887       }
888
889       // Ok, the directory container has been made, or it pre-existed.
890
891       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
892       sprintf(newDir, "%s/%s", newContainer, dateDirName);
893
894       log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
895       int renameret = rename(t, newDir);
896       if (renameret == 0)
897       {
898         // Success. Test for remove old dir containter
899         char* oldTitleDir = new char[k+1];
900         memcpy(oldTitleDir, t, k);
901         oldTitleDir[k] = '\0';
902         log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
903         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
904         delete[] oldTitleDir;
905       }
906
907       if (renameret == 0)
908       {
909 #if VDRVERSNUM > 10311
910         // Tell VDR
911         ::Recordings.Update();
912 #endif
913         // Success. Send a different packet from just a ulong
914         resp->addULONG(1); // success
915         resp->addString(newDir); //system depent do not convert
916       }
917       else
918       {
919         resp->addULONG(5);          
920       }
921
922       resp->finalise();
923       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
924
925       delete[] dateDirName;
926       delete[] titleDirName;
927       delete[] newContainer;
928       delete[] newDir;
929     }
930     else
931     {
932       resp->addULONG(3);          
933       resp->finalise();
934       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
935     }
936   }
937   else
938   {
939     resp->addULONG(4);          
940     resp->finalise();
941     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
942   }
943
944   return 1;
945 }
946
947 int VompClientRRProc::processGetChannelsList()
948 {
949   ULONG type;
950
951   char* chanConfig = x.config.getValueString("General", "Channels");
952   int allChans = 1;
953   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
954
955   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
956   {
957 #if VDRVERSNUM < 10300
958     if (!channel->GroupSep() && (!channel->Ca() || allChans))
959 #else
960     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
961 #endif
962     {
963       log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
964
965       if (channel->Vpid()) type = 1;
966 #if VDRVERSNUM < 10300
967       else type = 2;
968 #else
969       else if (channel->Apid(0)) type = 2;
970       else continue;
971 #endif
972
973       resp->addULONG(channel->Number());
974       resp->addULONG(type);      
975       resp->addString(x.charconvsys->Convert(channel->Name()));
976 #if VDRVERSNUM < 10703
977       resp->addULONG(2);
978 #else
979       resp->addULONG(channel->Vtype());
980 #endif      
981     }
982   }
983
984   resp->finalise();
985   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
986
987   log->log("RRProc", Log::DEBUG, "Written channels list");
988
989   return 1;
990 }
991
992 int VompClientRRProc::processGetChannelPids()
993 {
994   ULONG channelNumber = ntohl(*(ULONG*)req->data);
995
996   cChannel* channel = x.channelFromNumber(channelNumber);
997   if (!channel)
998   {
999     resp->addULONG(0);
1000     resp->finalise();
1001     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1002     return 1;
1003   }
1004
1005   ULONG numApids = 0;
1006   ULONG numDpids = 0;
1007   ULONG numSpids = 0;
1008
1009
1010 #if VDRVERSNUM < 10300
1011
1012   log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1013   log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1014
1015   if (channel->Apid2())
1016     numApids = 2;
1017   else if (channel->Apid1())
1018     numApids = 1;
1019   else
1020     numApids = 0;
1021
1022 #else
1023
1024   for (const int *Apid = channel->Apids(); *Apid; Apid++)
1025   {
1026     numApids++;
1027   }
1028   for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1029   {
1030     numDpids++;
1031   }
1032   for (const int *Spid = channel->Spids(); *Spid; Spid++)
1033   {
1034     numSpids++;
1035   }
1036 #endif
1037
1038
1039   // Format of response
1040   // vpid
1041   // number of apids
1042   // {
1043   //    apid
1044   //    lang string
1045   // }
1046   // number of dpids
1047   // {
1048   //    dpid
1049   //    lang string
1050   // }
1051   // number of spids
1052   // {
1053   //    spid
1054   //    lang string
1055   // }
1056   // tpid
1057
1058   resp->addULONG(channel->Vpid());
1059 #if VDRVERSNUM < 10703
1060   resp->addULONG(2);
1061 #else
1062   resp->addULONG(channel->Vtype());
1063 #endif
1064   resp->addULONG(numApids);
1065
1066 #if VDRVERSNUM < 10300
1067   if (numApids >= 1)
1068   {
1069     resp->addULONG(channel->Apid1());
1070     resp->addString("");
1071   }
1072   if (numApids == 2)
1073   {
1074     resp->addULONG(channel->Apid2());
1075     resp->addString("");
1076   }
1077   resp->addULONG(0);
1078   resp->addULONG(0); 
1079 #else
1080   for (ULONG i = 0; i < numApids; i++)
1081   {
1082     resp->addULONG(channel->Apid(i));
1083     resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1084   }
1085   resp->addULONG(numDpids);
1086   for (ULONG i = 0; i < numDpids; i++)
1087   {
1088     resp->addULONG(channel->Dpid(i));
1089     resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1090   }
1091   resp->addULONG(numSpids);
1092   for (ULONG i = 0; i < numSpids; i++)
1093   {
1094     resp->addULONG(channel->Spid(i));
1095     resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1096   }
1097 #endif
1098   resp->addULONG(channel->Tpid());
1099   // Format of extended response, for compatibility with older client at the end
1100   // {
1101   //    atypes
1102   // }
1103   // {
1104   //    dtypes
1105   // }
1106   // {
1107   //    stypes
1108   //    comppageid
1109   //    ancpageids
1110   // }
1111 #if VDRVERSNUM < 10300
1112   if (numApids >= 1)
1113   {
1114     resp->addULONG(4);
1115   }
1116   if (numApids == 2)
1117   {
1118     resp->addULONG(4);
1119   }
1120 #else
1121   for (ULONG i = 0; i < numApids; i++)
1122   {
1123 #if VDRVERSNUM < 10715
1124     resp->addULONG(4);
1125 #else
1126     resp->addULONG(channel->Atype(i));
1127 #endif
1128   }
1129   for (ULONG i = 0; i < numDpids; i++)
1130   {
1131 #if VDRVERSNUM < 10715
1132     resp->addULONG(0x6A /*AC3*/);
1133 #else
1134     resp->addULONG(channel->Dtype(i));
1135 #endif
1136   }
1137   for (ULONG i = 0; i < numSpids; i++)
1138   {
1139 #if VDRVERSNUM < 10715
1140     resp->addULONG(0);
1141     resp->addULONG(0);
1142     resp->addULONG(0);
1143 #else
1144     resp->addULONG(channel->SubtitlingType(i));
1145     resp->addULONG(channel->CompositionPageId(i));
1146     resp->addULONG(channel->AncillaryPageId(i));
1147 #endif
1148   }
1149 #endif
1150
1151
1152   resp->finalise();
1153   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1154   
1155   log->log("RRProc", Log::DEBUG, "Written channels pids");
1156
1157   return 1;
1158 }
1159
1160 int VompClientRRProc::processStartStreamingChannel()
1161 {
1162   if (x.lp)
1163   {
1164     log->log("RRProc", Log::ERR, "Client called start streaming twice");
1165     return 0;
1166   }
1167   
1168   log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1169   ULONG channelNumber = ntohl(*(ULONG*)req->data);
1170
1171   cChannel* channel = x.channelFromNumber(channelNumber);
1172   if (!channel)
1173   {
1174     resp->addULONG(0);
1175     resp->finalise();
1176     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1177     return 1;
1178   }
1179
1180   // get the priority we should use
1181   int fail = 1;
1182   int priority = x.config.getValueLong("General", "Live priority", &fail);
1183   if (!fail)
1184   {
1185     log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1186   }
1187   else
1188   {
1189     log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1190     priority = 0;
1191   }
1192
1193   // a bit of sanity..
1194 #if VDRVERSNUM < 10725
1195   if (priority < 0) priority = 0;
1196 #else 
1197   if (priority < -99) priority = -99;
1198 #endif
1199   if (priority > 99) priority = 99;
1200
1201   log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1202   x.lp = MVPReceiver::create(channel, priority);
1203
1204   if (!x.lp)
1205   {
1206     resp->addULONG(0);
1207     resp->finalise();
1208     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1209     return 1;
1210   }
1211
1212   if (!x.lp->init(&x.tcp, req->requestID))
1213   {
1214     delete x.lp;
1215     x.lp = NULL;
1216     resp->addULONG(0);
1217     resp->finalise();
1218     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1219     return 1;
1220   }
1221
1222   resp->addULONG(1);
1223   resp->finalise();
1224   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1225   return 1;
1226 }
1227
1228 int VompClientRRProc::processStopStreaming()
1229 {
1230   log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1231   if (x.lp)
1232   {
1233     x.lp->detachMVPReceiver();
1234     delete x.lp;
1235     x.lp = NULL;
1236   }
1237   else if (x.recplayer)
1238   {
1239     x.writeResumeData();
1240
1241     delete x.recplayer;
1242     delete x.recordingManager;
1243     x.recplayer = NULL;
1244     x.recordingManager = NULL;
1245   }
1246
1247   resp->addULONG(1);
1248   resp->finalise();
1249   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1250   return 1;
1251 }
1252
1253 int VompClientRRProc::processGetBlock()
1254 {
1255   if (x.lp)
1256   {
1257     log->log("RRProc", Log::ERR, "Get block called during live streaming");
1258     return 0;
1259   }
1260
1261   if (!x.recplayer)
1262   {
1263     log->log("RRProc", Log::ERR, "Get block called when no recording open");
1264     return 0;
1265   }
1266
1267   UCHAR* data = req->data;
1268
1269   ULLONG position = x.ntohll(*(ULLONG*)data);
1270   data += sizeof(ULLONG);
1271   ULONG amount = ntohl(*(ULONG*)data);
1272
1273   log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1274
1275   UCHAR sendBuffer[amount];
1276   ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1277
1278   if (!amountReceived)
1279   {
1280     resp->addULONG(0);
1281     log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1282   }
1283   else
1284   {
1285     resp->copyin(sendBuffer, amountReceived);
1286     log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1287   }
1288
1289   resp->finalise();
1290   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1291   log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1292   return 1;
1293 }
1294
1295 int VompClientRRProc::processStartStreamingRecording()
1296 {
1297   // data is a pointer to the fileName string
1298
1299   x.recordingManager = new cRecordings;
1300   x.recordingManager->Load();
1301
1302   cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1303
1304   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1305
1306   if (recording)
1307   {
1308     x.recplayer = new RecPlayer(recording);
1309
1310     resp->addULLONG(x.recplayer->getLengthBytes());
1311     resp->addULONG(x.recplayer->getLengthFrames());
1312     
1313 #if VDRVERSNUM < 10703
1314     resp->addUCHAR(true);//added for TS    
1315 #else
1316     resp->addUCHAR(recording->IsPesRecording());//added for TS
1317 #endif
1318
1319     resp->finalise();
1320     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1321     
1322     log->log("RRProc", Log::DEBUG, "written totalLength");
1323   }
1324   else
1325   {
1326     delete x.recordingManager;
1327     x.recordingManager = NULL;
1328   }
1329   return 1;
1330 }
1331
1332 int VompClientRRProc::processPositionFromFrameNumber()
1333 {
1334   ULLONG retval = 0;
1335
1336   ULONG frameNumber = ntohl(*(ULONG*)req->data);
1337
1338   if (!x.recplayer)
1339   {
1340     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1341   }
1342   else
1343   {
1344     retval = x.recplayer->positionFromFrameNumber(frameNumber);
1345   }
1346
1347   resp->addULLONG(retval);
1348   resp->finalise();
1349   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1350
1351   log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1352   return 1;
1353 }
1354
1355 int VompClientRRProc::processFrameNumberFromPosition()
1356 {
1357   ULONG retval = 0;
1358
1359   ULLONG position = x.ntohll(*(ULLONG*)req->data);
1360
1361   if (!x.recplayer)
1362   {
1363     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1364   }
1365   else
1366   {
1367     retval = x.recplayer->frameNumberFromPosition(position);
1368   }
1369
1370   resp->addULONG(retval);
1371   resp->finalise();
1372   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1373
1374   log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1375   return 1;
1376 }
1377
1378 int VompClientRRProc::processGetIFrame()
1379 {
1380   bool success = false;
1381
1382   ULONG* data = (ULONG*)req->data;
1383
1384   ULONG frameNumber = ntohl(*data);
1385   data++;
1386   ULONG direction = ntohl(*data);
1387
1388   ULLONG rfilePosition = 0;
1389   ULONG rframeNumber = 0;
1390   ULONG rframeLength = 0;
1391
1392   if (!x.recplayer)
1393   {
1394     log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1395   }
1396   else
1397   {
1398     success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1399   }
1400
1401   // returns file position, frame number, length
1402
1403   if (success)
1404   {
1405     resp->addULLONG(rfilePosition);
1406     resp->addULONG(rframeNumber);
1407     resp->addULONG(rframeLength);
1408   }
1409   else
1410   {
1411     resp->addULONG(0);
1412   }
1413
1414   resp->finalise();
1415   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1416   
1417   log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1418   return 1;
1419 }
1420
1421 int VompClientRRProc::processGetChannelSchedule()
1422 {
1423   ULONG* data = (ULONG*)req->data;
1424
1425   ULONG channelNumber = ntohl(*data);
1426   data++;
1427   ULONG startTime = ntohl(*data);
1428   data++;
1429   ULONG duration = ntohl(*data);
1430
1431   log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1432
1433   cChannel* channel = x.channelFromNumber(channelNumber);
1434   if (!channel)
1435   {
1436     resp->addULONG(0);
1437     resp->finalise();
1438     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1439   
1440     log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1441     return 1;
1442   }
1443
1444   log->log("RRProc", Log::DEBUG, "Got channel");
1445
1446 #if VDRVERSNUM < 10300
1447   cMutexLock MutexLock;
1448   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1449 #else
1450   cSchedulesLock MutexLock;
1451   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1452 #endif
1453   if (!Schedules)
1454   {
1455     resp->addULONG(0);
1456     resp->finalise();
1457     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1458     
1459     log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1460     return 1;
1461   }
1462
1463   log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1464
1465   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1466   if (!Schedule)
1467   {
1468     resp->addULONG(0);
1469     resp->finalise();
1470     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1471     
1472     log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1473     return 1;
1474   }
1475
1476   log->log("RRProc", Log::DEBUG, "Got schedule object");
1477
1478   const char* empty = "";
1479   bool atLeastOneEvent = false;
1480
1481   ULONG thisEventID;
1482   ULONG thisEventTime;
1483   ULONG thisEventDuration;
1484   const char* thisEventTitle;
1485   const char* thisEventSubTitle;
1486   const char* thisEventDescription;
1487
1488 #if VDRVERSNUM < 10300
1489
1490   const cEventInfo *event;
1491   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1492   {
1493     event = Schedule->GetEventNumber(eventNumber);
1494
1495     thisEventID = event->GetEventID();
1496     thisEventTime = event->GetTime();
1497     thisEventDuration = event->GetDuration();
1498     thisEventTitle = event->GetTitle();
1499     thisEventSubTitle = event->GetSubtitle();
1500     thisEventDescription = event->GetExtendedDescription();
1501
1502 #else
1503
1504   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1505   {
1506     thisEventID = event->EventID();
1507     thisEventTime = event->StartTime();
1508     thisEventDuration = event->Duration();
1509     thisEventTitle = event->Title();
1510     thisEventSubTitle = NULL;
1511     thisEventDescription = event->Description();
1512
1513 #endif
1514
1515     //in the past filter
1516     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1517
1518     //start time filter
1519     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1520
1521     //duration filter
1522     if (thisEventTime >= (startTime + duration)) continue;
1523
1524     if (!thisEventTitle) thisEventTitle = empty;
1525     if (!thisEventSubTitle) thisEventSubTitle = empty;
1526     if (!thisEventDescription) thisEventDescription = empty;
1527
1528     resp->addULONG(thisEventID);
1529     resp->addULONG(thisEventTime);
1530     resp->addULONG(thisEventDuration);
1531
1532     resp->addString(x.charconvsys->Convert(thisEventTitle));
1533     resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1534     resp->addString(x.charconvsys->Convert(thisEventDescription));
1535
1536     atLeastOneEvent = true;
1537   }
1538
1539   log->log("RRProc", Log::DEBUG, "Got all event data");
1540
1541   if (!atLeastOneEvent)
1542   {
1543     resp->addULONG(0);
1544     log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1545   }
1546   
1547   resp->finalise();
1548   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1549     
1550   log->log("RRProc", Log::DEBUG, "written schedules packet");
1551
1552   return 1;
1553 }
1554
1555 int VompClientRRProc::processGetTimers()
1556 {
1557   cTimer *timer;
1558   int numTimers = Timers.Count();
1559
1560   resp->addULONG(numTimers);
1561
1562   for (int i = 0; i < numTimers; i++)
1563   {
1564     timer = Timers.Get(i);
1565
1566 #if VDRVERSNUM < 10300
1567     resp->addULONG(timer->Active());
1568 #else
1569     resp->addULONG(timer->HasFlags(tfActive));
1570 #endif
1571     resp->addULONG(timer->Recording());
1572     resp->addULONG(timer->Pending());
1573     resp->addULONG(timer->Priority());
1574     resp->addULONG(timer->Lifetime());
1575     resp->addULONG(timer->Channel()->Number());
1576     resp->addULONG(timer->StartTime());
1577     resp->addULONG(timer->StopTime());
1578     resp->addULONG(timer->Day());
1579     resp->addULONG(timer->WeekDays());
1580     resp->addString(timer->File()); //Filename is system specific and not visible by user
1581   }
1582
1583   resp->finalise();
1584   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1585   
1586   log->log("RRProc", Log::DEBUG, "Written timers list");
1587
1588   return 1;
1589 }
1590
1591 int VompClientRRProc::processSetTimer()
1592 {
1593   char* timerString = new char[strlen((char*)req->data) + 1];
1594   strcpy(timerString, (char*)req->data);
1595
1596 #if VDRVERSNUM < 10300
1597
1598   // If this is VDR 1.2 the date part of the timer string must be reduced
1599   // to just DD rather than YYYY-MM-DD
1600
1601   int s = 0; // source
1602   int d = 0; // destination
1603   int c = 0; // count
1604   while(c != 2) // copy up to date section, including the second ':'
1605   {
1606     timerString[d] = req->data[s];
1607     if (req->data[s] == ':') c++;
1608     ++s;
1609     ++d;
1610   }
1611   // now it has copied up to the date section
1612   c = 0;
1613   while(c != 2) // waste YYYY-MM-
1614   {
1615     if (req->data[s] == '-') c++;
1616     ++s;
1617   }
1618   // now source is at the DD
1619   memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1620   d += req->dataLength - s;
1621   timerString[d] = '\0';
1622
1623   log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1624
1625 #endif
1626   log->log("RRProc", Log::DEBUG, "%s", timerString);
1627
1628   cTimer *timer = new cTimer;
1629   if (timer->Parse((char*)timerString))
1630   {
1631     cTimer *t = Timers.GetTimer(timer);
1632     if (!t)
1633     {
1634       Timers.Add(timer);
1635 #if VDRVERSNUM < 10300
1636       Timers.Save();
1637 #else
1638       Timers.SetModified();
1639 #endif
1640       resp->addULONG(0);
1641       resp->finalise();
1642       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1643       return 1; // FIXME - cTimer* timer is leaked here!
1644     }
1645     else
1646     {
1647       resp->addULONG(1);
1648       resp->finalise();
1649       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1650     }
1651   }
1652   else
1653   {
1654     resp->addULONG(2);
1655     resp->finalise();
1656     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1657   }
1658   delete timer;
1659   return 1;
1660 }
1661
1662 int VompClientRRProc::processDeleteTimer()
1663 {
1664   log->log("RRProc", Log::DEBUG, "Delete timer called");
1665   // get timer
1666   
1667   int position = 0;
1668   
1669   INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1670   INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1671   INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1672   INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1673   INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1674     
1675   cTimer* ti = NULL;
1676   for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1677   {
1678     if  ( (ti->Channel()->Number() == delChannel)
1679      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1680      &&   (ti->StartTime() == delStart)
1681      &&   (ti->StopTime() == delStop) )
1682        break;
1683   }
1684   
1685   if (!ti)
1686   {
1687     resp->addULONG(4);
1688     resp->finalise();
1689     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1690     return 1;
1691   }
1692           
1693   if (!Timers.BeingEdited())
1694   {
1695     if (!ti->Recording())
1696     {
1697       Timers.Del(ti);
1698       Timers.SetModified();
1699       resp->addULONG(10);
1700       resp->finalise();
1701       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1702       return 1;
1703     }
1704     else
1705     {
1706       log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1707       resp->addULONG(3);
1708       resp->finalise();
1709       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1710       return 1;
1711     }  
1712   }
1713   else
1714   {
1715     log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1716     resp->addULONG(1);
1717     resp->finalise();
1718     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1719     return 1;
1720   }  
1721 }
1722
1723 int VompClientRRProc::processGetRecInfo()
1724 {
1725   // data is a pointer to the fileName string
1726   
1727   cRecordings Recordings;
1728   Recordings.Load(); // probably have to do this
1729
1730   cRecording *recording = Recordings.GetByName((char*)req->data);
1731
1732   time_t timerStart = 0;
1733   time_t timerStop = 0;
1734   char* summary = NULL;
1735   char* shorttext = NULL;
1736   char* description = NULL;
1737   char* title = NULL;
1738   bool newsummary=false;
1739   ULONG resumePoint = 0;
1740
1741   if (!recording)
1742   {
1743     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1744     resp->addULONG(0);
1745     resp->finalise();
1746     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1747     return 1;
1748   }
1749
1750   /* Return packet:
1751   4 bytes: start time for timer
1752   4 bytes: end time for timer
1753   4 bytes: resume point
1754   string: summary
1755   4 bytes: num components
1756   {
1757     1 byte: stream
1758     1 byte: type
1759     string: language
1760     string: description
1761   }
1762   8 bytes: frames per second
1763   */
1764
1765   // Get current timer
1766
1767   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1768   if (rc)
1769   {
1770     timerStart = rc->Timer()->StartTime();
1771     timerStop = rc->Timer()->StopTime();
1772     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1773   }
1774
1775   resp->addULONG(timerStart);
1776   resp->addULONG(timerStop);
1777
1778   // Get resume point
1779
1780 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
1781   if (value)
1782   {
1783     resumePoint = strtoul(value, NULL, 10);
1784     delete[] value;
1785   }*/
1786
1787   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1788   int ResumeId;
1789   if (ResumeIdC) {
1790     ResumeId = atoi(ResumeIdC);
1791     delete[] ResumeIdC;
1792   }
1793   else
1794     ResumeId = 0;  //default if not defined in vomp-MAC.conf
1795
1796   while (ResumeIDLock)
1797     cCondWait::SleepMs(100);
1798   ResumeIDLock = true;
1799   int OldSetupResumeID = Setup.ResumeID;
1800   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
1801 #if VDRVERSNUM < 10703
1802   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
1803 #else
1804   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1805 #endif
1806   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
1807   ResumeIDLock = false;
1808
1809   int resume = ResumeFile.Read();
1810   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1811   if (resume >= 0) 
1812     resumePoint = ResumeFile.Read();
1813
1814   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1815
1816   resp->addULONG(resumePoint);
1817
1818   // Get summary
1819
1820 #if VDRVERSNUM < 10300
1821   summary = (char*)recording->Summary();
1822 #else
1823   const cRecordingInfo *Info = recording->Info();
1824   shorttext = (char*)Info->ShortText();
1825   description = (char*) (char*)Info->Description();
1826   if (isempty(shorttext)) summary=description;
1827   else if (isempty(description)) summary=shorttext;
1828   else {
1829      int length=strlen(description)+strlen(shorttext)+4;
1830      summary=new char[length];
1831      snprintf(summary,length,"%s\n\n%s",shorttext,description);
1832      newsummary=true;
1833   }
1834   
1835   if (isempty(summary)) summary = (char*)Info->Description();
1836 #endif
1837   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1838   if (summary)
1839   {
1840     resp->addString(x.charconvsys->Convert(summary));
1841     if (newsummary) delete [] summary;
1842   }
1843   else
1844   {
1845     resp->addString("");
1846   }
1847
1848   // Get channels
1849
1850 #if VDRVERSNUM < 10300
1851
1852   // Send 0 for numchannels - this signals the client this info is not available
1853   resp->addULONG(0);
1854
1855 #else
1856   const cComponents* components = Info->Components();
1857
1858   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1859
1860   if (!components)
1861   {
1862     resp->addULONG(0);
1863   }
1864   else
1865   {
1866     resp->addULONG(components->NumComponents());
1867   
1868     tComponent* component;
1869     for (int i = 0; i < components->NumComponents(); i++)
1870     {
1871       component = components->Component(i);
1872
1873       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1874       
1875       resp->addUCHAR(component->stream);
1876       resp->addUCHAR(component->type);
1877
1878       if (component->language)
1879       {
1880         resp->addString(x.charconvsys->Convert(component->language));
1881       }
1882       else
1883       {
1884         resp->addString("");
1885       }
1886       if (component->description)
1887       {
1888         resp->addString(x.charconvsys->Convert(component->description));
1889       }
1890       else
1891       {
1892         resp->addString("");
1893       }
1894     }
1895   }
1896
1897 #endif
1898   double framespersec;
1899 #if VDRVERSNUM < 10703
1900   framespersec = FRAMESPERSEC;
1901 #else
1902   framespersec = Info->FramesPerSecond();
1903 #endif
1904   resp->adddouble(framespersec);
1905   title = (char*)Info->Title();
1906   if (title) 
1907   {
1908     resp->addString(x.charconvsys->Convert(title));
1909   }
1910   else
1911   {
1912       resp->addString(x.charconvsys->Convert(recording->Name()));
1913   }
1914   
1915   // Done. send it
1916
1917   resp->finalise();
1918   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1919
1920   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1921
1922   return 1;
1923 }
1924
1925
1926
1927
1928 // FIXME obselete
1929
1930 int VompClientRRProc::processReScanRecording()
1931 {
1932   if (!x.recplayer)
1933   {
1934     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1935     return 0;
1936   }
1937
1938   x.recplayer->scan();
1939
1940   resp->addULLONG(x.recplayer->getLengthBytes());
1941   resp->addULONG(x.recplayer->getLengthFrames());
1942   resp->finalise();
1943   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1944   log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1945   return 1;
1946 }
1947
1948 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1949
1950 int VompClientRRProc::processGetMarks()
1951 {
1952   // data is a pointer to the fileName string
1953
1954   cMarks Marks;
1955   cRecordings Recordings;
1956   Recordings.Load(); // probably have to do this
1957
1958   cRecording *recording = Recordings.GetByName((char*)req->data);
1959
1960   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1961
1962   if (recording)
1963   {
1964 #if VDRVERSNUM < 10703
1965     Marks.Load(recording->FileName());
1966 #else
1967     Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1968 #endif
1969     if (Marks.Count())
1970     {
1971       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1972       {
1973 #if VDRVERSNUM < 10721
1974         ULLONG mposition = m->position;
1975 #else
1976         ULLONG mposition = m->Position();
1977 #endif
1978         log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1979
1980         resp->addULONG(mposition);
1981       }
1982     }
1983     else
1984     {
1985       log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1986       resp->addULONG(0);
1987     }
1988   }
1989
1990   resp->finalise();
1991   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1992   
1993   log->log("RRProc", Log::DEBUG, "Written Marks list");
1994
1995   return 1;
1996 }
1997
1998 int VompClientRRProc::processVDRShutdown()
1999 {
2000   log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2001   VompClient::decClients(); // Temporarily make this client disappear
2002   cRemote::Put(kPower);
2003   VompClient::incClients();
2004   resp->finalise();
2005   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2006   return 1;
2007 }
2008
2009 int VompClientRRProc::processGetRecScraperEventType()
2010 {
2011   Recordings.Load(); // probably have to do this
2012
2013   cRecording *recording = Recordings.GetByName((char*)req->data);
2014   ScraperGetEventType call;
2015   call.type = tNone;
2016
2017   if (recording && x.scraper) 
2018   {
2019      call.recording = recording;
2020      x.scraper->Service("GetEventType",&call);
2021   }
2022   resp->addUCHAR(call.type);
2023   if (call.type == tMovie)
2024   {
2025      resp->addLONG(call.movieId);
2026   } else if (call.type == tSeries){
2027      resp->addLONG(call.seriesId);
2028      resp->addLONG(call.episodeId);
2029   }
2030   resp->finalise();
2031   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2032
2033   return 1;
2034 }
2035
2036 #define ADDSTRING_TO_PAKET(y) if ((y)!=0)  resp->addString(x.charconvutf8->Convert(y)); else resp->addString(""); 
2037
2038 int VompClientRRProc::processGetScraperMovieInfo()
2039 {
2040    
2041    cMovie movie;
2042    movie.movieId = ntohl(*(ULONG*)req->data);
2043    if (!x.scraper) {
2044       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2045       return 0; //stupid, I have no scraper why are you still asking
2046    }
2047    x.scraper->Service("GetMovie",&movie);
2048    
2049
2050    ADDSTRING_TO_PAKET(movie.title.c_str());
2051    ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2052    ADDSTRING_TO_PAKET(movie.tagline.c_str());
2053    ADDSTRING_TO_PAKET(movie.overview.c_str());
2054    resp->addUCHAR(movie.adult);
2055    ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2056
2057    resp->addLONG(movie.budget);
2058    resp->addLONG(movie.revenue);
2059    ADDSTRING_TO_PAKET(movie.genres.c_str());
2060    ADDSTRING_TO_PAKET(movie.homepage.c_str());
2061    ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2062    resp->addLONG(movie.runtime);
2063    resp->adddouble(movie.popularity);
2064    resp->adddouble(movie.voteAverage);
2065    resp->addULONG(movie.poster.width);
2066    resp->addULONG(movie.poster.height);
2067    resp->addULONG(movie.fanart.width);
2068    resp->addULONG(movie.fanart.height);
2069    resp->addULONG(movie.collectionPoster.width);
2070    resp->addULONG(movie.collectionPoster.height);
2071    resp->addULONG(movie.collectionFanart.width);
2072    resp->addULONG(movie.collectionFanart.height);
2073    resp->addULONG(movie.actors.size());
2074    for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2075        ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2076        ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2077        resp->addULONG(movie.actors[acty].actorThumb.width);
2078        resp->addULONG(movie.actors[acty].actorThumb.height);
2079    }
2080   resp->finalise();
2081   
2082   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2083
2084   
2085   return 1;
2086
2087 }
2088
2089 int VompClientRRProc::processGetScraperSeriesInfo()
2090 {
2091    cSeries series;
2092    series.seriesId = ntohl(*(ULONG*)req->data);
2093    series.episodeId = ntohl(*(ULONG*)(req->data+4));
2094    if (!x.scraper) {
2095       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2096       return 0; //stupid, I have no scraper why are you still asking
2097    }
2098    x.scraper->Service("GetSeries",&series);
2099    
2100    ADDSTRING_TO_PAKET(series.name.c_str());
2101    ADDSTRING_TO_PAKET(series.overview.c_str());
2102    ADDSTRING_TO_PAKET(series.firstAired.c_str());
2103    ADDSTRING_TO_PAKET(series.network.c_str());
2104    ADDSTRING_TO_PAKET(series.genre.c_str());
2105    resp->adddouble(series.rating);
2106    ADDSTRING_TO_PAKET(series.status.c_str());
2107    
2108    resp->addLONG(series.episode.number);
2109    resp->addLONG(series.episode.season);
2110    ADDSTRING_TO_PAKET(series.episode.name.c_str());
2111    ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2112    ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2113    ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2114    resp->adddouble(series.episode.rating);
2115    resp->addULONG(series.episode.episodeImage.width);
2116    resp->addULONG(series.episode.episodeImage.height);
2117    
2118    ULONG num_actors = series.actors.size();
2119    resp->addULONG(num_actors);
2120    for (ULONG acty=0; acty < num_actors; acty++) {
2121        ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2122        ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2123        resp->addULONG(series.actors[acty].actorThumb.width);
2124        resp->addULONG(series.actors[acty].actorThumb.height);
2125    }
2126    ULONG num_posters = series.posters.size();
2127    resp->addULONG(num_posters);
2128    for (ULONG medias = 0; medias < num_posters; medias++ ) {
2129        cTvMedia media=series.posters[medias];
2130        resp->addULONG(media.width);
2131        resp->addULONG(media.height);
2132    }
2133
2134    ULONG num_banners = series.banners.size();
2135    resp->addULONG(num_banners);
2136    for (ULONG medias = 0; medias < num_banners; medias++ ) {
2137        cTvMedia media=series.banners[medias];
2138        resp->addULONG(media.width);
2139        resp->addULONG(media.height);
2140    }
2141    ULONG num_fanarts = series.fanarts.size();
2142    resp->addULONG(num_fanarts);
2143    for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2144        cTvMedia media=series.fanarts[medias];
2145        resp->addULONG(media.width);
2146        resp->addULONG(media.height);
2147    }
2148    resp->addULONG(series.seasonPoster.width);
2149    resp->addULONG(series.seasonPoster.height);
2150    
2151    resp->finalise();
2152
2153    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2154    
2155    return 1;
2156 }
2157
2158 int VompClientRRProc::processLoadTvMedia()
2159 {
2160    TVMediaRequest tvreq;
2161    tvreq.streamID = req->requestID;
2162    tvreq.type = ntohl(*(ULONG*)req->data);
2163    tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2164    tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2165    tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2166    tvreq.container = ntohl(*(ULONG*)(req->data+16));
2167    tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2168    log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2169    x.pict->addTVMediaRequest(tvreq);
2170
2171    
2172    resp->finalise();
2173
2174    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2175    
2176    return 1;
2177 }
2178
2179 int VompClientRRProc::processLoadTvMediaRecThumb()
2180 {
2181    TVMediaRequest tvreq;
2182    tvreq.streamID = req->requestID;
2183    tvreq.type = 3; // unknown but primary_name is set
2184    tvreq.primary_id = 0;
2185    tvreq.primary_name = std::string((const char*) req->data);
2186    tvreq.secondary_id = 0;
2187    tvreq.type_pict = 1;
2188    tvreq.container = 0;
2189    tvreq.container_member = 0;
2190    log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2191    x.pict->addTVMediaRequest(tvreq);
2192
2193    
2194    resp->finalise();
2195
2196    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2197    
2198    return 1;
2199 }
2200
2201  
2202
2203
2204   
2205 #endif // !VOMPSTANDALONE
2206