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