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