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