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