]> git.vomp.tv Git - vompserver.git/blob - vompclientrrproc.c
15 years that line of code has been waiting to crash
[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 = 0x00000301;
49 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000500;
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   int Total = 0; // This was never used, which explains why nobody noticed it's total rubbish anyway.
755   // Also it breaks with a lovely divide by 0 crash when your storage gets full.
756   
757   resp->addULONG(Total);
758   resp->addULONG(FreeMB);
759   resp->addULONG(Percent);
760
761 #if VDRVERSNUM >= 20301
762   LOCK_RECORDINGS_READ;
763   const cRecordings* tRecordings = Recordings;
764 #else
765   cThreadLock RecordingsLock(&Recordings);
766   const cRecordings* tRecordings = &Recordings;
767 #endif
768
769   for (const cRecording *recording = tRecordings->First(); recording; recording = tRecordings->Next(recording))
770   {
771 #if VDRVERSNUM < 10721
772     resp->addULONG(recording->start);
773 #else
774     resp->addULONG(recording->Start());
775 #endif
776     resp->addUCHAR(recording->IsNew() ? 1 : 0);
777     resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
778     resp->addString(recording->FileName());//file name are not  visible by user do not touch
779   }
780
781   resp->finalise();
782   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
783   
784   log->log("RRProc", Log::DEBUG, "Written recordings list");
785
786   return 1;
787 }
788
789 int VompClientRRProc::processDeleteRecording()
790 {
791   // data is a pointer to the fileName string
792
793 #if VDRVERSNUM >= 20301
794   LOCK_RECORDINGS_WRITE;
795   cRecordings* tRecordings = Recordings;
796 #else
797   cThreadLock RecordingsLock(&Recordings);
798   cRecordings* tRecordings = &Recordings;
799 #endif
800
801   cRecording* recording = tRecordings->GetByName((char*)req->data);
802
803   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
804
805   if (recording)
806   {
807     log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
808
809 // TODO: Switch to using: cRecording::IsInUse(void) const
810     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
811     if (!rc)
812     {
813       if (recording->Delete())
814       {
815 #if VDRVERSNUM >= 20301
816          tRecordings->DelByName(recording->FileName());
817          tRecordings->SetModified();
818 #elif VDRVERSNUM > 10300
819         // Copy svdrp's way of doing this, see if it works
820         ::Recordings.DelByName(recording->FileName());
821 #endif
822         resp->addULONG(1);
823       }
824       else
825       {
826         resp->addULONG(2);
827       }
828     }
829     else
830     {
831       resp->addULONG(3);
832     }
833   }
834   else
835   {
836     resp->addULONG(4);
837   }
838
839   resp->finalise();
840   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
841   
842   return 1;
843 }
844
845 int VompClientRRProc::processDeleteRecResume()
846 {
847   // data is a pointer to the fileName string
848
849 #if VDRVERSNUM < 20301
850   resp->addULONG(5);  // Not supported
851   resp->finalise();
852   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
853   return 1;
854 #else
855
856   cStateKey StateKey;
857   const cRecordings* Recordings = cRecordings::GetRecordingsRead(StateKey);
858   const cRecording* recording = Recordings->GetByName((char*)req->data);
859
860   if (recording)
861   {
862     log->log("RRProc", Log::DEBUG, "deleting recording resume : %s", recording->Name());
863
864     cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
865     StateKey.Remove();
866
867     if (ResumeFile.Read() >= 0)
868     {
869       ResumeFile.Delete();
870       resp->addULONG(1);  // success
871     }
872     else
873     {
874       resp->addULONG(2);  // failed, no resume point saved
875     }
876   }
877   else
878   {
879     StateKey.Remove();
880     resp->addULONG(4);  // failed to find recording
881   }
882
883   resp->finalise();
884   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
885
886   return 1;
887
888 #endif
889 }
890
891 int VompClientRRProc::processMoveRecording()
892 {
893   log->log("RRProc", Log::DEBUG, "Process move recording");
894   char* fileName = (char*)req->data;
895   char* newPath = NULL;
896
897   for (UINT k = 0; k < req->dataLength; k++)
898   {
899     if (req->data[k] == '\0')
900     {
901       newPath = (char*)&req->data[k+1];
902       break;
903     }
904   }
905   if (!newPath) return 0;
906
907
908 #if VDRVERSNUM >= 20301
909   LOCK_RECORDINGS_WRITE;
910   cRecordings* tRecordings = Recordings;
911 #else
912   cThreadLock RecordingsLock(&Recordings);
913   cRecordings* tRecordings = &Recordings;
914 #endif
915
916   cRecording* recording = tRecordings->GetByName((char*)fileName);
917
918   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
919
920   if (recording) // FIXME rewrite these error conditions!
921   {
922     // TODO: Switch to using: int cRecording::IsInUse(void) const
923     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
924     if (!rc)
925     {
926       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
927       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
928       log->log("RRProc", Log::DEBUG, "to: %s", newPath);
929
930       const char* t = recording->FileName();
931
932       char* dateDirName = NULL;   int k;
933       char* titleDirName = NULL;  int j;
934
935       // Find the datedirname
936       for(k = strlen(t) - 1; k >= 0; k--)
937       {
938         if (t[k] == '/')
939         {
940           log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
941           dateDirName = new char[strlen(&t[k+1]) + 1];
942           strcpy(dateDirName, &t[k+1]);
943           break;
944         }
945       }
946
947       // Find the titledirname
948
949       for(j = k-1; j >= 0; j--)
950       {
951         if (t[j] == '/')
952         {
953           log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
954           titleDirName = new char[(k - j - 1) + 1];
955           memcpy(titleDirName, &t[j+1], k - j - 1);
956           titleDirName[k - j - 1] = '\0';
957           break;
958         }
959       }
960
961       log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
962       log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
963 #if APIVERSNUM > 20101
964       log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
965 #else
966       log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
967 #endif
968
969       char* newPathConv = (char*)malloc(strlen(newPath)+1);
970       strcpy(newPathConv, newPath);
971       newPathConv = ExchangeChars(newPathConv, true);
972       log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
973
974 #if APIVERSNUM > 20101
975       char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
976       log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
977       sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
978 #else
979       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
980       log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
981       sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
982 #endif
983       free(newPathConv);
984
985       log->log("RRProc", Log::DEBUG, "%s", newContainer);
986
987       struct stat dstat;
988       int statret = stat(newContainer, &dstat);
989       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
990       {
991         log->log("RRProc", Log::DEBUG, "new dir does not exist");
992         int mkdirret = mkdir(newContainer, 0755);
993         if (mkdirret != 0)
994         {
995           delete[] dateDirName;
996           delete[] titleDirName;
997           delete[] newContainer;
998
999           resp->addULONG(5);          
1000           resp->finalise();
1001           x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1002           return 1;
1003         }
1004       }
1005       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
1006       {
1007         delete[] dateDirName;
1008         delete[] titleDirName;
1009         delete[] newContainer;
1010
1011         resp->addULONG(5);          
1012         resp->finalise();
1013         x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1014         return 1;
1015       }
1016
1017
1018       // Ok, the directory container has been made, or it pre-existed.
1019
1020       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
1021       sprintf(newDir, "%s/%s", newContainer, dateDirName);
1022
1023       log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
1024       int renameret = rename(t, newDir);
1025       if (renameret == 0)
1026       {
1027         // Success. Test for remove old dir containter
1028         char* oldTitleDir = new char[k+1];
1029         memcpy(oldTitleDir, t, k);
1030         oldTitleDir[k] = '\0';
1031         log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
1032         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
1033         delete[] oldTitleDir;
1034
1035 #if VDRVERSNUM >= 20301
1036         tRecordings->Update();
1037 #elif VDRVERSNUM > 10311
1038         ::Recordings.Update();
1039 #endif
1040         // Success. Send a different packet from just a ulong
1041         resp->addULONG(1); // success
1042         resp->addString(newDir); //system depent do not convert
1043       }
1044       else
1045       {
1046         resp->addULONG(5);          
1047       }
1048
1049       resp->finalise();
1050       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1051
1052       delete[] dateDirName;
1053       delete[] titleDirName;
1054       delete[] newContainer;
1055       delete[] newDir;
1056     }
1057     else
1058     {
1059       resp->addULONG(3);          
1060       resp->finalise();
1061       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1062     }
1063   }
1064   else
1065   {
1066     resp->addULONG(4);          
1067     resp->finalise();
1068     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1069   }
1070
1071   return 1;
1072 }
1073
1074 int VompClientRRProc::processGetChannelsList()
1075 {
1076   ULONG type;
1077
1078   char* chanConfig = x.config.getValueString("General", "Channels");
1079   int allChans = 1;
1080   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
1081
1082 #if VDRVERSNUM >= 20301
1083   LOCK_CHANNELS_READ;
1084   const cChannels* tChannels = Channels;
1085 #else
1086   const cChannels* tChannels = &Channels;
1087 #endif
1088
1089   for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
1090   {
1091 #if VDRVERSNUM < 10300
1092     if (!channel->GroupSep() && (!channel->Ca() || allChans))
1093 #else
1094     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
1095 #endif
1096     {
1097       log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
1098
1099       if (channel->Vpid()) type = 1;
1100 #if VDRVERSNUM < 10300
1101       else type = 2;
1102 #else
1103       else if (channel->Apid(0)) type = 2;
1104       else continue;
1105 #endif
1106
1107       resp->addULONG(channel->Number());
1108       resp->addULONG(type);      
1109       resp->addString(x.charconvsys->Convert(channel->Name()));
1110 #if VDRVERSNUM < 10703
1111       resp->addULONG(2);
1112 #else
1113       resp->addULONG(channel->Vtype());
1114 #endif      
1115     }
1116   }
1117
1118   resp->finalise();
1119   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1120
1121   log->log("RRProc", Log::DEBUG, "Written channels list");
1122
1123   return 1;
1124 }
1125
1126 int VompClientRRProc::processGetChannelPids()
1127 {
1128   ULONG channelNumber = ntohl(*(ULONG*)req->data);
1129
1130 #if VDRVERSNUM >= 20301
1131   LOCK_CHANNELS_READ;
1132   const cChannels* tChannels = Channels;
1133 #else
1134   cChannels* tChannels = &Channels;
1135 #endif
1136
1137   const cChannel* channel = tChannels->GetByNumber(channelNumber);
1138   if (!channel)
1139   {
1140     resp->addULONG(0);
1141     resp->finalise();
1142     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1143     return 1;
1144   }
1145
1146   ULONG numApids = 0;
1147   ULONG numDpids = 0;
1148   ULONG numSpids = 0;
1149
1150
1151 #if VDRVERSNUM < 10300
1152
1153   log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1154   log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1155
1156   if (channel->Apid2())
1157     numApids = 2;
1158   else if (channel->Apid1())
1159     numApids = 1;
1160   else
1161     numApids = 0;
1162
1163 #else
1164
1165   for (const int *Apid = channel->Apids(); *Apid; Apid++)
1166   {
1167     numApids++;
1168   }
1169   for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1170   {
1171     numDpids++;
1172   }
1173   for (const int *Spid = channel->Spids(); *Spid; Spid++)
1174   {
1175     numSpids++;
1176   }
1177 #endif
1178
1179
1180   // Format of response
1181   // vpid
1182   // number of apids
1183   // {
1184   //    apid
1185   //    lang string
1186   // }
1187   // number of dpids
1188   // {
1189   //    dpid
1190   //    lang string
1191   // }
1192   // number of spids
1193   // {
1194   //    spid
1195   //    lang string
1196   // }
1197   // tpid
1198
1199   resp->addULONG(channel->Vpid());
1200 #if VDRVERSNUM < 10703
1201   resp->addULONG(2);
1202 #else
1203   resp->addULONG(channel->Vtype());
1204 #endif
1205   resp->addULONG(numApids);
1206
1207 #if VDRVERSNUM < 10300
1208   if (numApids >= 1)
1209   {
1210     resp->addULONG(channel->Apid1());
1211     resp->addString("");
1212   }
1213   if (numApids == 2)
1214   {
1215     resp->addULONG(channel->Apid2());
1216     resp->addString("");
1217   }
1218   resp->addULONG(0);
1219   resp->addULONG(0); 
1220 #else
1221   for (ULONG i = 0; i < numApids; i++)
1222   {
1223     resp->addULONG(channel->Apid(i));
1224     resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1225   }
1226   resp->addULONG(numDpids);
1227   for (ULONG i = 0; i < numDpids; i++)
1228   {
1229     resp->addULONG(channel->Dpid(i));
1230     resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1231   }
1232   resp->addULONG(numSpids);
1233   for (ULONG i = 0; i < numSpids; i++)
1234   {
1235     resp->addULONG(channel->Spid(i));
1236     resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1237   }
1238 #endif
1239   resp->addULONG(channel->Tpid());
1240   // Format of extended response, for compatibility with older client at the end
1241   // {
1242   //    atypes
1243   // }
1244   // {
1245   //    dtypes
1246   // }
1247   // {
1248   //    stypes
1249   //    comppageid
1250   //    ancpageids
1251   // }
1252 #if VDRVERSNUM < 10300
1253   if (numApids >= 1)
1254   {
1255     resp->addULONG(4);
1256   }
1257   if (numApids == 2)
1258   {
1259     resp->addULONG(4);
1260   }
1261 #else
1262   for (ULONG i = 0; i < numApids; i++)
1263   {
1264 #if VDRVERSNUM < 10715
1265     resp->addULONG(4);
1266 #else
1267     resp->addULONG(channel->Atype(i));
1268 #endif
1269   }
1270   for (ULONG i = 0; i < numDpids; i++)
1271   {
1272 #if VDRVERSNUM < 10715
1273     resp->addULONG(0x6A /*AC3*/);
1274 #else
1275     resp->addULONG(channel->Dtype(i));
1276 #endif
1277   }
1278   for (ULONG i = 0; i < numSpids; i++)
1279   {
1280 #if VDRVERSNUM < 10715
1281     resp->addULONG(0);
1282     resp->addULONG(0);
1283     resp->addULONG(0);
1284 #else
1285     resp->addULONG(channel->SubtitlingType(i));
1286     resp->addULONG(channel->CompositionPageId(i));
1287     resp->addULONG(channel->AncillaryPageId(i));
1288 #endif
1289   }
1290 #endif
1291
1292
1293   resp->finalise();
1294   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1295   
1296   log->log("RRProc", Log::DEBUG, "Written channels pids");
1297
1298   return 1;
1299 }
1300
1301 int VompClientRRProc::processStartStreamingChannel()
1302 {
1303   if (x.lp)
1304   {
1305     log->log("RRProc", Log::ERR, "Client called start streaming twice");
1306     return 0;
1307   }
1308   
1309   log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1310   ULONG channelNumber = ntohl(*(ULONG*)req->data);
1311
1312 #if VDRVERSNUM >= 20301
1313   LOCK_CHANNELS_READ;
1314   const cChannels* tChannels = Channels;
1315 #else
1316   cChannels* tChannels = &Channels;
1317 #endif
1318
1319   const cChannel* channel = tChannels->GetByNumber(channelNumber);
1320   if (!channel)
1321   {
1322     resp->addULONG(0);
1323     resp->finalise();
1324     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1325     return 1;
1326   }
1327
1328   // get the priority we should use
1329   int fail = 1;
1330   int priority = x.config.getValueLong("General", "Live priority", &fail);
1331   if (!fail)
1332   {
1333     log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1334   }
1335   else
1336   {
1337     log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1338     priority = 0;
1339   }
1340
1341   // a bit of sanity..
1342 #if VDRVERSNUM < 10725
1343   if (priority < 0) priority = 0;
1344 #else 
1345   if (priority < -99) priority = -99;
1346 #endif
1347   if (priority > 99) priority = 99;
1348
1349   log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1350   x.lp = MVPReceiver::create(channel, priority);
1351
1352   if (!x.lp)
1353   {
1354     resp->addULONG(0);
1355     resp->finalise();
1356     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1357     return 1;
1358   }
1359
1360   if (!x.lp->init(&x.tcp, req->requestID))
1361   {
1362     delete x.lp;
1363     x.lp = NULL;
1364     resp->addULONG(0);
1365     resp->finalise();
1366     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1367     return 1;
1368   }
1369
1370   resp->addULONG(1);
1371   resp->finalise();
1372   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1373   return 1;
1374 }
1375
1376 int VompClientRRProc::processStopStreaming()
1377 {
1378   log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1379   if (x.lp)
1380   {
1381     x.lp->detachMVPReceiver();
1382     delete x.lp;
1383     x.lp = NULL;
1384   }
1385   else if (x.recplayer)
1386   {
1387     x.writeResumeData();
1388
1389     delete x.recplayer;
1390     x.recplayer = NULL;
1391   }
1392
1393   resp->addULONG(1);
1394   resp->finalise();
1395   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1396   return 1;
1397 }
1398
1399 int VompClientRRProc::processGetBlock()
1400 {
1401   if (x.lp)
1402   {
1403     log->log("RRProc", Log::ERR, "Get block called during live streaming");
1404     return 0;
1405   }
1406
1407   if (!x.recplayer)
1408   {
1409     log->log("RRProc", Log::ERR, "Get block called when no recording open");
1410     return 0;
1411   }
1412
1413   UCHAR* data = req->data;
1414
1415   ULLONG position = x.ntohll(*(ULLONG*)data);
1416   data += sizeof(ULLONG);
1417   ULONG amount = ntohl(*(ULONG*)data);
1418
1419   log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1420
1421   UCHAR sendBuffer[amount];
1422   ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1423
1424   if (!amountReceived)
1425   {
1426     resp->addULONG(0);
1427     log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1428   }
1429   else
1430   {
1431     resp->copyin(sendBuffer, amountReceived);
1432     log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1433   }
1434
1435   resp->finalise();
1436   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1437   log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1438   return 1;
1439 }
1440
1441 int VompClientRRProc::processStartStreamingRecording()
1442 {
1443   // data is a pointer to the fileName string
1444
1445 #if VDRVERSNUM >= 20301
1446   LOCK_RECORDINGS_READ;
1447   const cRecordings* tRecordings = Recordings;
1448 #else
1449   cThreadLock RecordingsLock(&Recordings);
1450   cRecordings* tRecordings = &Recordings;
1451 #endif
1452
1453   const cRecording* recording = tRecordings->GetByName((char*)req->data);
1454
1455   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1456
1457   if (recording)
1458   {
1459     x.recplayer = new RecPlayer(recording);
1460
1461     resp->addULLONG(x.recplayer->getLengthBytes());
1462     resp->addULONG(x.recplayer->getLengthFrames());
1463     
1464 #if VDRVERSNUM < 10703
1465     resp->addUCHAR(true);//added for TS    
1466 #else
1467     resp->addUCHAR(recording->IsPesRecording());//added for TS
1468 #endif
1469
1470     resp->finalise();
1471     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1472     
1473     log->log("RRProc", Log::DEBUG, "written totalLength");
1474   }
1475   else
1476   {
1477     // No such recording exists
1478     resp->addULLONG(0);
1479     resp->addULONG(0);
1480     resp->addUCHAR(false);
1481     resp->finalise();
1482     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1483     log->log("RRProc", Log::DEBUG, "start streaming recording failed");
1484   }
1485
1486   return 1;
1487 }
1488
1489 int VompClientRRProc::processPositionFromFrameNumber()
1490 {
1491   ULLONG retval = 0;
1492
1493   ULONG frameNumber = ntohl(*(ULONG*)req->data);
1494
1495   if (!x.recplayer)
1496   {
1497     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1498   }
1499   else
1500   {
1501     retval = x.recplayer->positionFromFrameNumber(frameNumber);
1502   }
1503
1504   resp->addULLONG(retval);
1505   resp->finalise();
1506   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1507
1508   log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1509   return 1;
1510 }
1511
1512 int VompClientRRProc::processFrameNumberFromPosition()
1513 {
1514   ULONG retval = 0;
1515
1516   ULLONG position = x.ntohll(*(ULLONG*)req->data);
1517
1518   if (!x.recplayer)
1519   {
1520     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1521   }
1522   else
1523   {
1524     retval = x.recplayer->frameNumberFromPosition(position);
1525   }
1526
1527   resp->addULONG(retval);
1528   resp->finalise();
1529   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1530
1531   log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1532   return 1;
1533 }
1534
1535 int VompClientRRProc::processGetIFrame()
1536 {
1537   bool success = false;
1538
1539   ULONG* data = (ULONG*)req->data;
1540
1541   ULONG frameNumber = ntohl(*data);
1542   data++;
1543   ULONG direction = ntohl(*data);
1544
1545   ULLONG rfilePosition = 0;
1546   ULONG rframeNumber = 0;
1547   ULONG rframeLength = 0;
1548
1549   if (!x.recplayer)
1550   {
1551     log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1552   }
1553   else
1554   {
1555     success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1556   }
1557
1558   // returns file position, frame number, length
1559
1560   if (success)
1561   {
1562     resp->addULLONG(rfilePosition);
1563     resp->addULONG(rframeNumber);
1564     resp->addULONG(rframeLength);
1565   }
1566   else
1567   {
1568     resp->addULONG(0);
1569   }
1570
1571   resp->finalise();
1572   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1573   
1574   log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1575   return 1;
1576 }
1577
1578 int VompClientRRProc::processGetChannelSchedule()
1579 {
1580   ULONG* data = (ULONG*)req->data;
1581
1582   ULONG channelNumber = ntohl(*data);
1583   data++;
1584   ULONG startTime = ntohl(*data);
1585   data++;
1586   ULONG duration = ntohl(*data);
1587
1588   log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1589
1590 #if VDRVERSNUM >= 20301
1591   LOCK_CHANNELS_READ;
1592   const cChannels* tChannels = Channels;
1593 #else
1594   cChannels* tChannels = &Channels;
1595 #endif
1596
1597   const cChannel* channel = tChannels->GetByNumber(channelNumber);
1598   if (!channel)
1599   {
1600     resp->addULONG(0);
1601     resp->finalise();
1602     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1603   
1604     log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1605     return 1;
1606   }
1607
1608   log->log("RRProc", Log::DEBUG, "Got channel");
1609
1610 #if VDRVERSNUM < 10300
1611   cMutexLock MutexLock;
1612   const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1613 #elif VDRVERSNUM < 20301
1614   cSchedulesLock MutexLock;
1615   const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1616 #else
1617   LOCK_SCHEDULES_READ;
1618   const cSchedules *tSchedules = Schedules;
1619 #endif
1620
1621   if (!tSchedules)
1622   {
1623     resp->addULONG(0);
1624     resp->finalise();
1625     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1626     
1627     log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1628     return 1;
1629   }
1630
1631   log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1632
1633   const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1634   if (!Schedule)
1635   {
1636     resp->addULONG(0);
1637     resp->finalise();
1638     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1639     
1640     log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1641     return 1;
1642   }
1643
1644   log->log("RRProc", Log::DEBUG, "Got schedule object");
1645
1646   const char* empty = "";
1647   bool atLeastOneEvent = false;
1648
1649   ULONG thisEventID;
1650   ULONG thisEventTime;
1651   ULONG thisEventDuration;
1652   const char* thisEventTitle;
1653   const char* thisEventSubTitle;
1654   const char* thisEventDescription;
1655
1656 #if VDRVERSNUM < 10300
1657
1658   const cEventInfo *event;
1659   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1660   {
1661     event = Schedule->GetEventNumber(eventNumber);
1662
1663     thisEventID = event->GetEventID();
1664     thisEventTime = event->GetTime();
1665     thisEventDuration = event->GetDuration();
1666     thisEventTitle = event->GetTitle();
1667     thisEventSubTitle = event->GetSubtitle();
1668     thisEventDescription = event->GetExtendedDescription();
1669
1670 #else
1671
1672   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1673   {
1674     thisEventID = event->EventID();
1675     thisEventTime = event->StartTime();
1676     thisEventDuration = event->Duration();
1677     thisEventTitle = event->Title();
1678     thisEventSubTitle = NULL;
1679     thisEventDescription = event->Description();
1680
1681 #endif
1682
1683     //in the past filter
1684     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1685
1686     //start time filter
1687     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1688
1689     //duration filter
1690     if (thisEventTime >= (startTime + duration)) continue;
1691
1692     if (!thisEventTitle) thisEventTitle = empty;
1693     if (!thisEventSubTitle) thisEventSubTitle = empty;
1694     if (!thisEventDescription) thisEventDescription = empty;
1695
1696     resp->addULONG(thisEventID);
1697     resp->addULONG(thisEventTime);
1698     resp->addULONG(thisEventDuration);
1699
1700     resp->addString(x.charconvsys->Convert(thisEventTitle));
1701     resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1702     resp->addString(x.charconvsys->Convert(thisEventDescription));
1703
1704     atLeastOneEvent = true;
1705   }
1706
1707   log->log("RRProc", Log::DEBUG, "Got all event data");
1708
1709   if (!atLeastOneEvent)
1710   {
1711     resp->addULONG(0);
1712     log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1713   }
1714   
1715   resp->finalise();
1716   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1717     
1718   log->log("RRProc", Log::DEBUG, "written schedules packet");
1719
1720   return 1;
1721 }
1722
1723 int VompClientRRProc::processGetTimers()
1724 {
1725 #if VDRVERSNUM >= 20301
1726   LOCK_TIMERS_READ;
1727   const cTimers* tTimers = Timers;
1728 #else
1729   const cTimers* tTimers = &Timers;
1730 #endif
1731
1732   const cTimer *timer;
1733   int numTimers = tTimers->Count();
1734
1735   resp->addULONG(numTimers);
1736
1737   for (int i = 0; i < numTimers; i++)
1738   {
1739     timer = tTimers->Get(i);
1740
1741 #if VDRVERSNUM < 10300
1742     resp->addULONG(timer->Active());
1743 #else
1744     resp->addULONG(timer->HasFlags(tfActive));
1745 #endif
1746     resp->addULONG(timer->Recording());
1747     resp->addULONG(timer->Pending());
1748     resp->addULONG(timer->Priority());
1749     resp->addULONG(timer->Lifetime());
1750     resp->addULONG(timer->Channel()->Number());
1751     resp->addULONG(timer->StartTime());
1752     resp->addULONG(timer->StopTime());
1753     resp->addULONG(timer->Day());
1754     resp->addULONG(timer->WeekDays());
1755     resp->addString(timer->File()); //Filename is system specific and not visible by user
1756   }
1757
1758   resp->finalise();
1759   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1760   
1761   log->log("RRProc", Log::DEBUG, "Written timers list");
1762
1763   return 1;
1764 }
1765
1766 int VompClientRRProc::processSetTimer()
1767 {
1768   char* timerString = new char[strlen((char*)req->data) + 1];
1769   strcpy(timerString, (char*)req->data);
1770
1771 #if VDRVERSNUM < 10300
1772
1773   // If this is VDR 1.2 the date part of the timer string must be reduced
1774   // to just DD rather than YYYY-MM-DD
1775
1776   int s = 0; // source
1777   int d = 0; // destination
1778   int c = 0; // count
1779   while(c != 2) // copy up to date section, including the second ':'
1780   {
1781     timerString[d] = req->data[s];
1782     if (req->data[s] == ':') c++;
1783     ++s;
1784     ++d;
1785   }
1786   // now it has copied up to the date section
1787   c = 0;
1788   while(c != 2) // waste YYYY-MM-
1789   {
1790     if (req->data[s] == '-') c++;
1791     ++s;
1792   }
1793   // now source is at the DD
1794   memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1795   d += req->dataLength - s;
1796   timerString[d] = '\0';
1797
1798   log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1799
1800 #endif
1801   log->log("RRProc", Log::DEBUG, "%s", timerString);
1802
1803   cTimer *timer = new cTimer;
1804   if (!timer->Parse((char*)timerString))
1805   {
1806     resp->addULONG(2);
1807     resp->finalise();
1808     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1809     delete timer;
1810     return 1;
1811   }
1812
1813 #if VDRVERSNUM >= 20301
1814   LOCK_TIMERS_WRITE;
1815   cTimers* tTimers = Timers;
1816 #else
1817   cTimers* tTimers = &Timers;
1818 #endif
1819
1820   cTimer *t = tTimers->GetTimer(timer);
1821   if (t)
1822   {
1823     resp->addULONG(1);
1824     resp->finalise();
1825     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1826     delete timer;
1827     return 1;
1828   }
1829
1830   timer->ClrFlags(tfRecording);
1831   tTimers->Add(timer);
1832 #if VDRVERSNUM < 10300
1833   tTimers->Save();
1834 #elif VDRVERSNUM < 20301
1835   tTimers->SetModified();
1836 #endif
1837
1838   resp->addULONG(0);
1839   resp->finalise();
1840   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1841   return 1;
1842 }
1843
1844 int VompClientRRProc::processDeleteTimer()
1845 {
1846   log->log("RRProc", Log::DEBUG, "Delete timer called");
1847   // get timer
1848   
1849   int position = 0;
1850   
1851   INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1852   INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1853   INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1854   INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1855   INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1856
1857 #if VDRVERSNUM >= 20301
1858   LOCK_TIMERS_WRITE;
1859   cTimers* tTimers = Timers;
1860 #else
1861   cTimers* tTimers = &Timers;
1862 #endif
1863
1864   cTimer* ti = NULL;
1865   for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1866   {
1867     if  ( (ti->Channel()->Number() == delChannel)
1868      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1869      &&   (ti->StartTime() == delStart)
1870      &&   (ti->StopTime() == delStop) )
1871        break;
1872   }
1873   
1874   if (!ti)
1875   {
1876     resp->addULONG(4);
1877     resp->finalise();
1878     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1879     return 1;
1880   }
1881
1882 #if VDRVERSNUM < 20301
1883 // I suppose with the new locking this just can't happen
1884   if (tTimers->BeingEdited())
1885   {
1886     log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1887     resp->addULONG(1);
1888     resp->finalise();
1889     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1890     return 1;
1891   }
1892 #endif
1893
1894   if (ti->Recording())
1895   {
1896     log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1897     resp->addULONG(3);
1898     resp->finalise();
1899     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1900     return 1;
1901   }
1902
1903   tTimers->Del(ti);
1904   tTimers->SetModified();
1905
1906   resp->addULONG(10);
1907   resp->finalise();
1908   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1909   return 1;
1910 }
1911
1912 int VompClientRRProc::processGetRecInfo()
1913 {
1914   // data is a pointer to the fileName string
1915 #if VDRVERSNUM >= 20301
1916   LOCK_RECORDINGS_READ;
1917   const cRecordings* tRecordings = Recordings;
1918 #else
1919   cThreadLock RecordingsLock(&Recordings);
1920   cRecordings* tRecordings = &Recordings;
1921 #endif
1922
1923   const cRecording *recording = tRecordings->GetByName((char*)req->data);
1924
1925   time_t timerStart = 0;
1926   time_t timerStop = 0;
1927   char* summary = NULL;
1928   char* shorttext = NULL;
1929   char* description = NULL;
1930   char* title = NULL;
1931   bool newsummary=false;
1932   ULONG resumePoint = 0;
1933
1934   if (!recording)
1935   {
1936     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1937     resp->addULONG(0);
1938     resp->finalise();
1939     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1940     return 1;
1941   }
1942
1943   /* Return packet:
1944   4 bytes: start time for timer
1945   4 bytes: end time for timer
1946   4 bytes: resume point
1947   string: summary
1948   4 bytes: num components
1949   {
1950     1 byte: stream
1951     1 byte: type
1952     string: language
1953     string: description
1954   }
1955   8 bytes: frames per second
1956   string: title/name
1957   */
1958
1959   // Get current timer
1960
1961   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1962   if (rc)
1963   {
1964     timerStart = rc->Timer()->StartTime();
1965     timerStop = rc->Timer()->StopTime();
1966     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1967   }
1968
1969   resp->addULONG(timerStart);
1970   resp->addULONG(timerStop);
1971
1972   // Get resume point
1973
1974 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
1975   if (value)
1976   {
1977     resumePoint = strtoul(value, NULL, 10);
1978     delete[] value;
1979   }*/
1980
1981   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1982   int ResumeId;
1983   if (ResumeIdC) {
1984     ResumeId = atoi(ResumeIdC);
1985     delete[] ResumeIdC;
1986   }
1987   else
1988     ResumeId = 0;  //default if not defined in vomp-MAC.conf
1989
1990   while (ResumeIDLock)
1991     cCondWait::SleepMs(100);
1992   ResumeIDLock = true;
1993   int OldSetupResumeID = Setup.ResumeID;
1994   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
1995 #if VDRVERSNUM < 10703
1996   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
1997 #else
1998   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1999 #endif
2000   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
2001   ResumeIDLock = false;
2002
2003   int resume = ResumeFile.Read();
2004   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2005   if (resume >= 0) 
2006     resumePoint = ResumeFile.Read();
2007
2008   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2009
2010   resp->addULONG(resumePoint);
2011
2012   // Get summary
2013
2014 #if VDRVERSNUM < 10300
2015   summary = (char*)recording->Summary();
2016 #else
2017   const cRecordingInfo *Info = recording->Info();
2018   shorttext = (char*)Info->ShortText();
2019   description = (char*) (char*)Info->Description();
2020   if (isempty(shorttext)) summary=description;
2021   else if (isempty(description)) summary=shorttext;
2022   else {
2023      int length=strlen(description)+strlen(shorttext)+4;
2024      summary=new char[length];
2025      snprintf(summary,length,"%s\n\n%s",shorttext,description);
2026      newsummary=true;
2027   }
2028   
2029   if (isempty(summary)) summary = (char*)Info->Description();
2030 #endif
2031   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2032   if (summary)
2033   {
2034     resp->addString(x.charconvsys->Convert(summary));
2035     if (newsummary) delete [] summary;
2036   }
2037   else
2038   {
2039     resp->addString("");
2040   }
2041
2042   // Get channels
2043
2044 #if VDRVERSNUM < 10300
2045
2046   // Send 0 for numchannels - this signals the client this info is not available
2047   resp->addULONG(0);
2048
2049 #else
2050   const cComponents* components = Info->Components();
2051
2052   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2053
2054   if (!components)
2055   {
2056     resp->addULONG(0);
2057   }
2058   else
2059   {
2060     resp->addULONG(components->NumComponents());
2061   
2062     tComponent* component;
2063     for (int i = 0; i < components->NumComponents(); i++)
2064     {
2065       component = components->Component(i);
2066
2067       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2068       
2069       resp->addUCHAR(component->stream);
2070       resp->addUCHAR(component->type);
2071
2072       if (component->language)
2073       {
2074         resp->addString(x.charconvsys->Convert(component->language));
2075       }
2076       else
2077       {
2078         resp->addString("");
2079       }
2080       if (component->description)
2081       {
2082         resp->addString(x.charconvsys->Convert(component->description));
2083       }
2084       else
2085       {
2086         resp->addString("");
2087       }
2088     }
2089   }
2090
2091 #endif
2092   double framespersec;
2093 #if VDRVERSNUM < 10703
2094   framespersec = FRAMESPERSEC;
2095 #else
2096   framespersec = Info->FramesPerSecond();
2097 #endif
2098   resp->adddouble(framespersec);
2099   title = (char*)Info->Title();
2100   if (title) 
2101   {
2102     resp->addString(x.charconvsys->Convert(title));
2103   }
2104   else
2105   {
2106       resp->addString(x.charconvsys->Convert(recording->Name()));
2107   }
2108   
2109   // Done. send it
2110
2111   resp->finalise();
2112   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2113
2114   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2115
2116   return 1;
2117 }
2118
2119 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2120
2121 int VompClientRRProc::processGetRecInfo2()
2122 {
2123   // data is a pointer to the fileName string
2124 #if VDRVERSNUM >= 20301
2125   LOCK_RECORDINGS_READ;
2126   const cRecordings* tRecordings = Recordings;
2127 #else
2128   cThreadLock RecordingsLock(&Recordings);
2129   cRecordings* tRecordings = &Recordings;
2130 #endif
2131
2132   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2133
2134   time_t timerStart = 0;
2135   time_t timerStop = 0;
2136   char* summary = NULL;
2137   char* shorttext = NULL;
2138   char* description = NULL;
2139   char* title = NULL;
2140   bool newsummary=false;
2141   ULONG resumePoint = 0;
2142
2143   if (!recording)
2144   {
2145     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2146     resp->addULONG(0);
2147     resp->finalise();
2148     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2149     return 1;
2150   }
2151
2152   /* Return packet:
2153   4 bytes: start time for timer
2154   4 bytes: end time for timer
2155   4 bytes: resume point
2156   string: summary
2157   4 bytes: num components
2158   {
2159     1 byte: stream
2160     1 byte: type
2161     string: language
2162     string: description
2163   }
2164   8 bytes: frames per second
2165   string: title/name
2166   // new stuff starts here
2167   string: channel name
2168   4 bytes: duration
2169   4 bytes : filesize
2170   4 bytes : priority
2171   4 bytes : lifetime
2172   */
2173
2174   // Get current timer
2175
2176   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2177   if (rc)
2178   {
2179     timerStart = rc->Timer()->StartTime();
2180     timerStop = rc->Timer()->StopTime();
2181     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2182   }
2183
2184   resp->addULONG(timerStart);
2185   resp->addULONG(timerStop);
2186
2187   // Get resume point
2188
2189 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
2190   if (value)
2191   {
2192     resumePoint = strtoul(value, NULL, 10);
2193     delete[] value;
2194   }*/
2195
2196   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2197   int ResumeId;
2198   if (ResumeIdC) {
2199     ResumeId = atoi(ResumeIdC);
2200     delete[] ResumeIdC;
2201   }
2202   else
2203     ResumeId = 0;  //default if not defined in vomp-MAC.conf
2204
2205   while (ResumeIDLock)
2206     cCondWait::SleepMs(100);
2207   ResumeIDLock = true;
2208   int OldSetupResumeID = Setup.ResumeID;
2209   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
2210 #if VDRVERSNUM < 10703
2211   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
2212 #else
2213   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2214 #endif
2215   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
2216   ResumeIDLock = false;
2217
2218   int resume = ResumeFile.Read();
2219   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2220   if (resume >= 0)
2221     resumePoint = ResumeFile.Read();
2222
2223   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2224
2225   resp->addULONG(resumePoint);
2226
2227   // Get summary
2228
2229 #if VDRVERSNUM < 10300
2230   summary = (char*)recording->Summary();
2231 #else
2232   const cRecordingInfo *Info = recording->Info();
2233   shorttext = (char*)Info->ShortText();
2234   description = (char*) (char*)Info->Description();
2235   if (isempty(shorttext)) summary=description;
2236   else if (isempty(description)) summary=shorttext;
2237   else {
2238      int length=strlen(description)+strlen(shorttext)+4;
2239      summary=new char[length];
2240      snprintf(summary,length,"%s\n\n%s",shorttext,description);
2241      newsummary=true;
2242   }
2243
2244   if (isempty(summary)) summary = (char*)Info->Description();
2245 #endif
2246   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2247   if (summary)
2248   {
2249     resp->addString(x.charconvsys->Convert(summary));
2250     if (newsummary) delete [] summary;
2251   }
2252   else
2253   {
2254     resp->addString("");
2255   }
2256
2257   // Get channels
2258
2259 #if VDRVERSNUM < 10300
2260
2261   // Send 0 for numchannels - this signals the client this info is not available
2262   resp->addULONG(0);
2263
2264 #else
2265   const cComponents* components = Info->Components();
2266
2267   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2268
2269   if (!components)
2270   {
2271     resp->addULONG(0);
2272   }
2273   else
2274   {
2275     resp->addULONG(components->NumComponents());
2276
2277     tComponent* component;
2278     for (int i = 0; i < components->NumComponents(); i++)
2279     {
2280       component = components->Component(i);
2281
2282       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2283
2284       resp->addUCHAR(component->stream);
2285       resp->addUCHAR(component->type);
2286
2287       if (component->language)
2288       {
2289         resp->addString(x.charconvsys->Convert(component->language));
2290       }
2291       else
2292       {
2293         resp->addString("");
2294       }
2295       if (component->description)
2296       {
2297         resp->addString(x.charconvsys->Convert(component->description));
2298       }
2299       else
2300       {
2301         resp->addString("");
2302       }
2303     }
2304   }
2305
2306 #endif
2307   double framespersec;
2308 #if VDRVERSNUM < 10703
2309   framespersec = FRAMESPERSEC;
2310 #else
2311   framespersec = Info->FramesPerSecond();
2312 #endif
2313   resp->adddouble(framespersec);
2314   title = (char*)Info->Title();
2315   if (title)
2316   {
2317     resp->addString(x.charconvsys->Convert(title));
2318   }
2319   else
2320   {
2321       resp->addString(x.charconvsys->Convert(recording->Name()));
2322   }
2323
2324   // New stuff
2325   if (Info->ChannelName())
2326   {
2327     resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2328   }
2329   else
2330   {
2331     resp->addString("");
2332   }
2333
2334   resp->addULONG(recording->LengthInSeconds());
2335   resp->addULONG(recording->FileSizeMB());
2336   resp->addULONG(recording->Priority());
2337   resp->addULONG(recording->Lifetime());
2338
2339   // Done. send it
2340
2341   resp->finalise();
2342   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2343
2344   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2345
2346   return 1;
2347 }
2348
2349 // FIXME obselete
2350
2351 int VompClientRRProc::processReScanRecording()
2352 {
2353   if (!x.recplayer)
2354   {
2355     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2356     return 0;
2357   }
2358
2359   x.recplayer->scan();
2360
2361   resp->addULLONG(x.recplayer->getLengthBytes());
2362   resp->addULONG(x.recplayer->getLengthFrames());
2363   resp->finalise();
2364   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2365   log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2366   return 1;
2367 }
2368
2369 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2370
2371 int VompClientRRProc::processGetMarks()
2372 {
2373   // data is a pointer to the fileName string
2374 #if VDRVERSNUM >= 20301
2375   LOCK_RECORDINGS_READ;
2376   const cRecordings* tRecordings = Recordings;
2377 #else
2378   cThreadLock RecordingsLock(&Recordings);
2379   cRecordings* tRecordings = &Recordings;
2380 #endif
2381
2382   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2383   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2384
2385   if (recording)
2386   {
2387     cMarks Marks;
2388 #if VDRVERSNUM < 10703
2389     Marks.Load(recording->FileName());
2390 #else
2391     Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2392 #endif
2393     if (Marks.Count())
2394     {
2395       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2396       {
2397 #if VDRVERSNUM < 10721
2398         ULLONG mposition = m->position;
2399 #else
2400         ULLONG mposition = m->Position();
2401 #endif
2402         log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2403
2404         resp->addULONG(mposition);
2405       }
2406     }
2407     else
2408     {
2409       log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2410       resp->addULONG(0);
2411     }
2412   }
2413
2414   resp->finalise();
2415   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2416   
2417   log->log("RRProc", Log::DEBUG, "Written Marks list");
2418
2419   return 1;
2420 }
2421
2422 int VompClientRRProc::processVDRShutdown()
2423 {
2424   log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2425   VompClient::decClients(); // Temporarily make this client disappear
2426   cRemote::Put(kPower);
2427   VompClient::incClients();
2428   resp->finalise();
2429   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2430   return 1;
2431 }
2432
2433 int VompClientRRProc::processGetRecScraperEventType()
2434 {
2435 #if VDRVERSNUM >= 20301
2436   LOCK_RECORDINGS_READ;
2437   const cRecordings* tRecordings = Recordings;
2438 #else
2439   cThreadLock RecordingsLock(&Recordings);
2440   cRecordings* tRecordings = &Recordings;
2441 #endif
2442
2443   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2444   ScraperGetEventType call;
2445   call.type = tNone;
2446
2447   if (recording && x.scrapQuery()) 
2448   {
2449      call.recording = recording;
2450      x.scraper->Service("GetEventType", &call);
2451   }
2452   resp->addUCHAR(call.type);
2453   if (call.type == tMovie)
2454   {
2455      resp->addLONG(call.movieId);
2456   }
2457   else if (call.type == tSeries)
2458   {
2459      resp->addLONG(call.seriesId);
2460      resp->addLONG(call.episodeId);
2461   }
2462   resp->finalise();
2463   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2464
2465   return 1;
2466 }
2467
2468 int VompClientRRProc::processGetEventScraperEventType()
2469 {
2470   ScraperGetEventType call;
2471   call.type = tNone;
2472   ULONG channelid = ntohl(*(ULONG*)req->data);
2473   ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2474   const cEvent *event = NULL; 
2475
2476 #if VDRVERSNUM >= 20301
2477   LOCK_CHANNELS_READ;
2478   const cChannels* tChannels = Channels;
2479 #else
2480   cChannels* tChannels = &Channels;
2481 #endif
2482
2483   const cChannel* channel = tChannels->GetByNumber(channelid);
2484
2485 #if VDRVERSNUM < 10300
2486   cMutexLock MutexLock;
2487   const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2488 #elif VDRVERSNUM < 20301
2489   cSchedulesLock MutexLock;
2490   const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2491 #else
2492   LOCK_SCHEDULES_READ;
2493   const cSchedules *tSchedules = Schedules;
2494 #endif
2495
2496   if (tSchedules && channel)
2497   {
2498      const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2499      if (Schedule) {
2500         event = Schedule->GetEvent(eventid);
2501     }
2502   }
2503     
2504   if (event && x.scrapQuery()) 
2505   {
2506      call.event = event;
2507      x.scraper->Service("GetEventType",&call);
2508   }
2509   resp->addUCHAR(call.type);
2510   if (call.type == tMovie)
2511   {
2512      resp->addLONG(call.movieId);
2513   } else if (call.type == tSeries){
2514      resp->addLONG(call.seriesId);
2515      resp->addLONG(call.episodeId);
2516   }
2517   if (x.pict->epgImageExists(eventid)) {
2518      resp->addLONG(1);
2519   } else {
2520      resp->addLONG(0);
2521   }
2522     
2523   resp->finalise();
2524   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2525
2526   return 1;
2527 }
2528
2529 #define ADDSTRING_TO_PAKET(y) if ((y)!=0)  resp->addString(x.charconvutf8->Convert(y)); else resp->addString(""); 
2530
2531 int VompClientRRProc::processGetScraperMovieInfo()
2532 {
2533    
2534    cMovie movie;
2535    movie.movieId = ntohl(*(ULONG*)req->data);
2536    if (!x.scrapQuery()) {
2537       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2538       return 0; //stupid, I have no scraper why are you still asking
2539    }
2540    x.scraper->Service("GetMovie",&movie);
2541    
2542
2543    ADDSTRING_TO_PAKET(movie.title.c_str());
2544    ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2545    ADDSTRING_TO_PAKET(movie.tagline.c_str());
2546    ADDSTRING_TO_PAKET(movie.overview.c_str());
2547    resp->addUCHAR(movie.adult);
2548    ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2549
2550    resp->addLONG(movie.budget);
2551    resp->addLONG(movie.revenue);
2552    ADDSTRING_TO_PAKET(movie.genres.c_str());
2553    ADDSTRING_TO_PAKET(movie.homepage.c_str());
2554    ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2555    resp->addLONG(movie.runtime);
2556    resp->adddouble(movie.popularity);
2557    resp->adddouble(movie.voteAverage);
2558    resp->addULONG(movie.poster.width);
2559    resp->addULONG(movie.poster.height);
2560    resp->addULONG(movie.fanart.width);
2561    resp->addULONG(movie.fanart.height);
2562    resp->addULONG(movie.collectionPoster.width);
2563    resp->addULONG(movie.collectionPoster.height);
2564    resp->addULONG(movie.collectionFanart.width);
2565    resp->addULONG(movie.collectionFanart.height);
2566    resp->addULONG(movie.actors.size());
2567    for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2568        ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2569        ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2570        resp->addULONG(movie.actors[acty].actorThumb.width);
2571        resp->addULONG(movie.actors[acty].actorThumb.height);
2572    }
2573   resp->finalise();
2574   
2575   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2576
2577   
2578   return 1;
2579
2580 }
2581
2582 int VompClientRRProc::processGetScraperSeriesInfo()
2583 {
2584    cSeries series;
2585    series.seriesId = ntohl(*(ULONG*)req->data);
2586    series.episodeId = ntohl(*(ULONG*)(req->data+4));
2587    if (!x.scrapQuery()) {
2588       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2589       return 0; //stupid, I have no scraper why are you still asking
2590    }
2591    x.scraper->Service("GetSeries",&series);
2592    
2593    ADDSTRING_TO_PAKET(series.name.c_str());
2594    ADDSTRING_TO_PAKET(series.overview.c_str());
2595    ADDSTRING_TO_PAKET(series.firstAired.c_str());
2596    ADDSTRING_TO_PAKET(series.network.c_str());
2597    ADDSTRING_TO_PAKET(series.genre.c_str());
2598    resp->adddouble(series.rating);
2599    ADDSTRING_TO_PAKET(series.status.c_str());
2600    
2601    resp->addLONG(series.episode.number);
2602    resp->addLONG(series.episode.season);
2603    ADDSTRING_TO_PAKET(series.episode.name.c_str());
2604    ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2605    ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2606    ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2607    resp->adddouble(series.episode.rating);
2608    resp->addULONG(series.episode.episodeImage.width);
2609    resp->addULONG(series.episode.episodeImage.height);
2610    
2611    ULONG num_actors = series.actors.size();
2612    resp->addULONG(num_actors);
2613    for (ULONG acty=0; acty < num_actors; acty++) {
2614        ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2615        ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2616        resp->addULONG(series.actors[acty].actorThumb.width);
2617        resp->addULONG(series.actors[acty].actorThumb.height);
2618    }
2619    ULONG num_posters = series.posters.size();
2620    resp->addULONG(num_posters);
2621    for (ULONG medias = 0; medias < num_posters; medias++ ) {
2622        cTvMedia media=series.posters[medias];
2623        resp->addULONG(media.width);
2624        resp->addULONG(media.height);
2625    }
2626
2627    ULONG num_banners = series.banners.size();
2628    resp->addULONG(num_banners);
2629    for (ULONG medias = 0; medias < num_banners; medias++ ) {
2630        cTvMedia media=series.banners[medias];
2631        resp->addULONG(media.width);
2632        resp->addULONG(media.height);
2633    }
2634    ULONG num_fanarts = series.fanarts.size();
2635    resp->addULONG(num_fanarts);
2636    for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2637        cTvMedia media=series.fanarts[medias];
2638        resp->addULONG(media.width);
2639        resp->addULONG(media.height);
2640    }
2641    resp->addULONG(series.seasonPoster.width);
2642    resp->addULONG(series.seasonPoster.height);
2643    
2644    resp->finalise();
2645
2646    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2647    
2648    return 1;
2649 }
2650
2651 int VompClientRRProc::processLoadTvMedia()
2652 {
2653    TVMediaRequest tvreq;
2654    tvreq.streamID = req->requestID;
2655    tvreq.type = ntohl(*(ULONG*)req->data);
2656    tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2657    tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2658    tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2659    tvreq.container = ntohl(*(ULONG*)(req->data+16));
2660    tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2661    log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2662    x.pict->addTVMediaRequest(tvreq);
2663
2664    
2665    resp->finalise();
2666
2667    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2668    
2669    return 1;
2670 }
2671
2672 int VompClientRRProc::processLoadTvMediaRecThumb()
2673 {
2674    TVMediaRequest tvreq;
2675    tvreq.streamID = req->requestID;
2676    tvreq.type = 3; // unknown but primary_name is set
2677    tvreq.primary_id = 0;
2678    tvreq.primary_name = std::string((const char*) req->data);
2679    tvreq.secondary_id = 0;
2680    tvreq.type_pict = 1;
2681    tvreq.container = 0;
2682    tvreq.container_member = 0;
2683    log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2684    x.pict->addTVMediaRequest(tvreq);
2685
2686    
2687    resp->finalise();
2688
2689    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2690    
2691    return 1;
2692 }
2693
2694 int VompClientRRProc::processLoadTvMediaEventThumb()
2695 {
2696    TVMediaRequest tvreq;
2697    tvreq.streamID = req->requestID;
2698    tvreq.type = 4; // unknown but primary_id is set
2699    UINT channelid = ntohl(*(ULONG*)req->data);
2700    tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2701    tvreq.secondary_id = 0;
2702
2703 #if VDRVERSNUM >= 20301
2704   LOCK_CHANNELS_READ;
2705   const cChannels* tChannels = Channels;
2706 #else
2707   cChannels* tChannels = &Channels;
2708 #endif
2709
2710    const cChannel* channel = tChannels->GetByNumber(channelid);
2711
2712    if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2713    tvreq.type_pict = 1;
2714    tvreq.container = 0;
2715    tvreq.container_member = 0;
2716    log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2717    x.pict->addTVMediaRequest(tvreq);
2718
2719    
2720    resp->finalise();
2721
2722    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2723    
2724    return 1;
2725 }
2726
2727 int VompClientRRProc::processLoadChannelLogo()
2728 {
2729    TVMediaRequest tvreq;
2730    tvreq.streamID = req->requestID;
2731    tvreq.type = 5; // channel logo
2732    UINT channelid = ntohl(*(ULONG*)req->data);
2733    tvreq.primary_id = channelid;
2734    tvreq.secondary_id = 0;
2735
2736 #if VDRVERSNUM >= 20301
2737   LOCK_CHANNELS_READ;
2738   const cChannels* tChannels = Channels;
2739 #else
2740   cChannels* tChannels = &Channels;
2741 #endif
2742
2743    const cChannel* channel = tChannels->GetByNumber(channelid);
2744
2745    if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2746    tvreq.type_pict = 1;
2747    tvreq.container = 0;
2748    tvreq.container_member = 0;
2749    if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2750    x.pict->addTVMediaRequest(tvreq);
2751
2752    
2753    resp->finalise();
2754
2755    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2756    
2757    return 1;
2758 }
2759
2760
2761
2762   
2763 #endif // !VOMPSTANDALONE
2764