]> git.vomp.tv Git - vompserver.git/blob - vompclientrrproc.c
Add VDR_GETRECINFO2 call to return additional rec info
[vompserver.git] / vompclientrrproc.c
1 /*
2     Copyright 2008 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include <stdlib.h>
22
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
29 #include <vdr/menu.h>
30 #include <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
33 #include "services/scraper2vdr.h"
34 #endif
35
36 #include "vompclientrrproc.h"
37 #include "vompclient.h"
38 #include "log.h"
39 #include "media.h"
40 #include "mediaplayer.h"
41 #include "servermediafile.h"
42 #include "i18n.h"
43 #include "vdrcommand.h"
44 #include "picturereader.h"
45
46 bool ResumeIDLock;
47
48 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MIN = 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
1472   return 1;
1473 }
1474
1475 int VompClientRRProc::processPositionFromFrameNumber()
1476 {
1477   ULLONG retval = 0;
1478
1479   ULONG frameNumber = ntohl(*(ULONG*)req->data);
1480
1481   if (!x.recplayer)
1482   {
1483     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1484   }
1485   else
1486   {
1487     retval = x.recplayer->positionFromFrameNumber(frameNumber);
1488   }
1489
1490   resp->addULLONG(retval);
1491   resp->finalise();
1492   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1493
1494   log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1495   return 1;
1496 }
1497
1498 int VompClientRRProc::processFrameNumberFromPosition()
1499 {
1500   ULONG retval = 0;
1501
1502   ULLONG position = x.ntohll(*(ULLONG*)req->data);
1503
1504   if (!x.recplayer)
1505   {
1506     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1507   }
1508   else
1509   {
1510     retval = x.recplayer->frameNumberFromPosition(position);
1511   }
1512
1513   resp->addULONG(retval);
1514   resp->finalise();
1515   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1516
1517   log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1518   return 1;
1519 }
1520
1521 int VompClientRRProc::processGetIFrame()
1522 {
1523   bool success = false;
1524
1525   ULONG* data = (ULONG*)req->data;
1526
1527   ULONG frameNumber = ntohl(*data);
1528   data++;
1529   ULONG direction = ntohl(*data);
1530
1531   ULLONG rfilePosition = 0;
1532   ULONG rframeNumber = 0;
1533   ULONG rframeLength = 0;
1534
1535   if (!x.recplayer)
1536   {
1537     log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1538   }
1539   else
1540   {
1541     success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1542   }
1543
1544   // returns file position, frame number, length
1545
1546   if (success)
1547   {
1548     resp->addULLONG(rfilePosition);
1549     resp->addULONG(rframeNumber);
1550     resp->addULONG(rframeLength);
1551   }
1552   else
1553   {
1554     resp->addULONG(0);
1555   }
1556
1557   resp->finalise();
1558   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1559   
1560   log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1561   return 1;
1562 }
1563
1564 int VompClientRRProc::processGetChannelSchedule()
1565 {
1566   ULONG* data = (ULONG*)req->data;
1567
1568   ULONG channelNumber = ntohl(*data);
1569   data++;
1570   ULONG startTime = ntohl(*data);
1571   data++;
1572   ULONG duration = ntohl(*data);
1573
1574   log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1575
1576 #if VDRVERSNUM >= 20301
1577   LOCK_CHANNELS_READ;
1578   const cChannels* tChannels = Channels;
1579 #else
1580   cChannels* tChannels = &Channels;
1581 #endif
1582
1583   const cChannel* channel = tChannels->GetByNumber(channelNumber);
1584   if (!channel)
1585   {
1586     resp->addULONG(0);
1587     resp->finalise();
1588     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1589   
1590     log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1591     return 1;
1592   }
1593
1594   log->log("RRProc", Log::DEBUG, "Got channel");
1595
1596 #if VDRVERSNUM < 10300
1597   cMutexLock MutexLock;
1598   const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1599 #elif VDRVERSNUM < 20301
1600   cSchedulesLock MutexLock;
1601   const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1602 #else
1603   LOCK_SCHEDULES_READ;
1604   const cSchedules *tSchedules = Schedules;
1605 #endif
1606
1607   if (!tSchedules)
1608   {
1609     resp->addULONG(0);
1610     resp->finalise();
1611     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1612     
1613     log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1614     return 1;
1615   }
1616
1617   log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1618
1619   const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1620   if (!Schedule)
1621   {
1622     resp->addULONG(0);
1623     resp->finalise();
1624     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1625     
1626     log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1627     return 1;
1628   }
1629
1630   log->log("RRProc", Log::DEBUG, "Got schedule object");
1631
1632   const char* empty = "";
1633   bool atLeastOneEvent = false;
1634
1635   ULONG thisEventID;
1636   ULONG thisEventTime;
1637   ULONG thisEventDuration;
1638   const char* thisEventTitle;
1639   const char* thisEventSubTitle;
1640   const char* thisEventDescription;
1641
1642 #if VDRVERSNUM < 10300
1643
1644   const cEventInfo *event;
1645   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1646   {
1647     event = Schedule->GetEventNumber(eventNumber);
1648
1649     thisEventID = event->GetEventID();
1650     thisEventTime = event->GetTime();
1651     thisEventDuration = event->GetDuration();
1652     thisEventTitle = event->GetTitle();
1653     thisEventSubTitle = event->GetSubtitle();
1654     thisEventDescription = event->GetExtendedDescription();
1655
1656 #else
1657
1658   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1659   {
1660     thisEventID = event->EventID();
1661     thisEventTime = event->StartTime();
1662     thisEventDuration = event->Duration();
1663     thisEventTitle = event->Title();
1664     thisEventSubTitle = NULL;
1665     thisEventDescription = event->Description();
1666
1667 #endif
1668
1669     //in the past filter
1670     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1671
1672     //start time filter
1673     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1674
1675     //duration filter
1676     if (thisEventTime >= (startTime + duration)) continue;
1677
1678     if (!thisEventTitle) thisEventTitle = empty;
1679     if (!thisEventSubTitle) thisEventSubTitle = empty;
1680     if (!thisEventDescription) thisEventDescription = empty;
1681
1682     resp->addULONG(thisEventID);
1683     resp->addULONG(thisEventTime);
1684     resp->addULONG(thisEventDuration);
1685
1686     resp->addString(x.charconvsys->Convert(thisEventTitle));
1687     resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1688     resp->addString(x.charconvsys->Convert(thisEventDescription));
1689
1690     atLeastOneEvent = true;
1691   }
1692
1693   log->log("RRProc", Log::DEBUG, "Got all event data");
1694
1695   if (!atLeastOneEvent)
1696   {
1697     resp->addULONG(0);
1698     log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1699   }
1700   
1701   resp->finalise();
1702   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1703     
1704   log->log("RRProc", Log::DEBUG, "written schedules packet");
1705
1706   return 1;
1707 }
1708
1709 int VompClientRRProc::processGetTimers()
1710 {
1711 #if VDRVERSNUM >= 20301
1712   LOCK_TIMERS_READ;
1713   const cTimers* tTimers = Timers;
1714 #else
1715   const cTimers* tTimers = &Timers;
1716 #endif
1717
1718   const cTimer *timer;
1719   int numTimers = tTimers->Count();
1720
1721   resp->addULONG(numTimers);
1722
1723   for (int i = 0; i < numTimers; i++)
1724   {
1725     timer = tTimers->Get(i);
1726
1727 #if VDRVERSNUM < 10300
1728     resp->addULONG(timer->Active());
1729 #else
1730     resp->addULONG(timer->HasFlags(tfActive));
1731 #endif
1732     resp->addULONG(timer->Recording());
1733     resp->addULONG(timer->Pending());
1734     resp->addULONG(timer->Priority());
1735     resp->addULONG(timer->Lifetime());
1736     resp->addULONG(timer->Channel()->Number());
1737     resp->addULONG(timer->StartTime());
1738     resp->addULONG(timer->StopTime());
1739     resp->addULONG(timer->Day());
1740     resp->addULONG(timer->WeekDays());
1741     resp->addString(timer->File()); //Filename is system specific and not visible by user
1742   }
1743
1744   resp->finalise();
1745   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1746   
1747   log->log("RRProc", Log::DEBUG, "Written timers list");
1748
1749   return 1;
1750 }
1751
1752 int VompClientRRProc::processSetTimer()
1753 {
1754   char* timerString = new char[strlen((char*)req->data) + 1];
1755   strcpy(timerString, (char*)req->data);
1756
1757 #if VDRVERSNUM < 10300
1758
1759   // If this is VDR 1.2 the date part of the timer string must be reduced
1760   // to just DD rather than YYYY-MM-DD
1761
1762   int s = 0; // source
1763   int d = 0; // destination
1764   int c = 0; // count
1765   while(c != 2) // copy up to date section, including the second ':'
1766   {
1767     timerString[d] = req->data[s];
1768     if (req->data[s] == ':') c++;
1769     ++s;
1770     ++d;
1771   }
1772   // now it has copied up to the date section
1773   c = 0;
1774   while(c != 2) // waste YYYY-MM-
1775   {
1776     if (req->data[s] == '-') c++;
1777     ++s;
1778   }
1779   // now source is at the DD
1780   memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1781   d += req->dataLength - s;
1782   timerString[d] = '\0';
1783
1784   log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1785
1786 #endif
1787   log->log("RRProc", Log::DEBUG, "%s", timerString);
1788
1789   cTimer *timer = new cTimer;
1790   if (!timer->Parse((char*)timerString))
1791   {
1792     resp->addULONG(2);
1793     resp->finalise();
1794     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1795     delete timer;
1796     return 1;
1797   }
1798
1799 #if VDRVERSNUM >= 20301
1800   LOCK_TIMERS_WRITE;
1801   cTimers* tTimers = Timers;
1802 #else
1803   cTimers* tTimers = &Timers;
1804 #endif
1805
1806   cTimer *t = tTimers->GetTimer(timer);
1807   if (t)
1808   {
1809     resp->addULONG(1);
1810     resp->finalise();
1811     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1812     delete timer;
1813     return 1;
1814   }
1815
1816   timer->ClrFlags(tfRecording);
1817   tTimers->Add(timer);
1818 #if VDRVERSNUM < 10300
1819   tTimers->Save();
1820 #elif VDRVERSNUM < 20301
1821   tTimers->SetModified();
1822 #endif
1823
1824   resp->addULONG(0);
1825   resp->finalise();
1826   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1827   return 1;
1828 }
1829
1830 int VompClientRRProc::processDeleteTimer()
1831 {
1832   log->log("RRProc", Log::DEBUG, "Delete timer called");
1833   // get timer
1834   
1835   int position = 0;
1836   
1837   INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1838   INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1839   INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1840   INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1841   INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1842
1843 #if VDRVERSNUM >= 20301
1844   LOCK_TIMERS_WRITE;
1845   cTimers* tTimers = Timers;
1846 #else
1847   cTimers* tTimers = &Timers;
1848 #endif
1849
1850   cTimer* ti = NULL;
1851   for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1852   {
1853     if  ( (ti->Channel()->Number() == delChannel)
1854      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1855      &&   (ti->StartTime() == delStart)
1856      &&   (ti->StopTime() == delStop) )
1857        break;
1858   }
1859   
1860   if (!ti)
1861   {
1862     resp->addULONG(4);
1863     resp->finalise();
1864     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1865     return 1;
1866   }
1867
1868 #if VDRVERSNUM < 20301
1869 // I suppose with the new locking this just can't happen
1870   if (tTimers->BeingEdited())
1871   {
1872     log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1873     resp->addULONG(1);
1874     resp->finalise();
1875     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1876     return 1;
1877   }
1878 #endif
1879
1880   if (ti->Recording())
1881   {
1882     log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1883     resp->addULONG(3);
1884     resp->finalise();
1885     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1886     return 1;
1887   }
1888
1889   tTimers->Del(ti);
1890   tTimers->SetModified();
1891
1892   resp->addULONG(10);
1893   resp->finalise();
1894   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1895   return 1;
1896 }
1897
1898 int VompClientRRProc::processGetRecInfo()
1899 {
1900   // data is a pointer to the fileName string
1901 #if VDRVERSNUM >= 20301
1902   LOCK_RECORDINGS_READ;
1903   const cRecordings* tRecordings = Recordings;
1904 #else
1905   cThreadLock RecordingsLock(&Recordings);
1906   cRecordings* tRecordings = &Recordings;
1907 #endif
1908
1909   const cRecording *recording = tRecordings->GetByName((char*)req->data);
1910
1911   time_t timerStart = 0;
1912   time_t timerStop = 0;
1913   char* summary = NULL;
1914   char* shorttext = NULL;
1915   char* description = NULL;
1916   char* title = NULL;
1917   bool newsummary=false;
1918   ULONG resumePoint = 0;
1919
1920   if (!recording)
1921   {
1922     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1923     resp->addULONG(0);
1924     resp->finalise();
1925     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1926     return 1;
1927   }
1928
1929   /* Return packet:
1930   4 bytes: start time for timer
1931   4 bytes: end time for timer
1932   4 bytes: resume point
1933   string: summary
1934   4 bytes: num components
1935   {
1936     1 byte: stream
1937     1 byte: type
1938     string: language
1939     string: description
1940   }
1941   8 bytes: frames per second
1942   string: title/name
1943   */
1944
1945   // Get current timer
1946
1947   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1948   if (rc)
1949   {
1950     timerStart = rc->Timer()->StartTime();
1951     timerStop = rc->Timer()->StopTime();
1952     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1953   }
1954
1955   resp->addULONG(timerStart);
1956   resp->addULONG(timerStop);
1957
1958   // Get resume point
1959
1960 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
1961   if (value)
1962   {
1963     resumePoint = strtoul(value, NULL, 10);
1964     delete[] value;
1965   }*/
1966
1967   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1968   int ResumeId;
1969   if (ResumeIdC) {
1970     ResumeId = atoi(ResumeIdC);
1971     delete[] ResumeIdC;
1972   }
1973   else
1974     ResumeId = 0;  //default if not defined in vomp-MAC.conf
1975
1976   while (ResumeIDLock)
1977     cCondWait::SleepMs(100);
1978   ResumeIDLock = true;
1979   int OldSetupResumeID = Setup.ResumeID;
1980   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
1981 #if VDRVERSNUM < 10703
1982   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
1983 #else
1984   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1985 #endif
1986   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
1987   ResumeIDLock = false;
1988
1989   int resume = ResumeFile.Read();
1990   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1991   if (resume >= 0) 
1992     resumePoint = ResumeFile.Read();
1993
1994   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1995
1996   resp->addULONG(resumePoint);
1997
1998   // Get summary
1999
2000 #if VDRVERSNUM < 10300
2001   summary = (char*)recording->Summary();
2002 #else
2003   const cRecordingInfo *Info = recording->Info();
2004   shorttext = (char*)Info->ShortText();
2005   description = (char*) (char*)Info->Description();
2006   if (isempty(shorttext)) summary=description;
2007   else if (isempty(description)) summary=shorttext;
2008   else {
2009      int length=strlen(description)+strlen(shorttext)+4;
2010      summary=new char[length];
2011      snprintf(summary,length,"%s\n\n%s",shorttext,description);
2012      newsummary=true;
2013   }
2014   
2015   if (isempty(summary)) summary = (char*)Info->Description();
2016 #endif
2017   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2018   if (summary)
2019   {
2020     resp->addString(x.charconvsys->Convert(summary));
2021     if (newsummary) delete [] summary;
2022   }
2023   else
2024   {
2025     resp->addString("");
2026   }
2027
2028   // Get channels
2029
2030 #if VDRVERSNUM < 10300
2031
2032   // Send 0 for numchannels - this signals the client this info is not available
2033   resp->addULONG(0);
2034
2035 #else
2036   const cComponents* components = Info->Components();
2037
2038   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2039
2040   if (!components)
2041   {
2042     resp->addULONG(0);
2043   }
2044   else
2045   {
2046     resp->addULONG(components->NumComponents());
2047   
2048     tComponent* component;
2049     for (int i = 0; i < components->NumComponents(); i++)
2050     {
2051       component = components->Component(i);
2052
2053       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2054       
2055       resp->addUCHAR(component->stream);
2056       resp->addUCHAR(component->type);
2057
2058       if (component->language)
2059       {
2060         resp->addString(x.charconvsys->Convert(component->language));
2061       }
2062       else
2063       {
2064         resp->addString("");
2065       }
2066       if (component->description)
2067       {
2068         resp->addString(x.charconvsys->Convert(component->description));
2069       }
2070       else
2071       {
2072         resp->addString("");
2073       }
2074     }
2075   }
2076
2077 #endif
2078   double framespersec;
2079 #if VDRVERSNUM < 10703
2080   framespersec = FRAMESPERSEC;
2081 #else
2082   framespersec = Info->FramesPerSecond();
2083 #endif
2084   resp->adddouble(framespersec);
2085   title = (char*)Info->Title();
2086   if (title) 
2087   {
2088     resp->addString(x.charconvsys->Convert(title));
2089   }
2090   else
2091   {
2092       resp->addString(x.charconvsys->Convert(recording->Name()));
2093   }
2094   
2095   // Done. send it
2096
2097   resp->finalise();
2098   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2099
2100   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2101
2102   return 1;
2103 }
2104
2105 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2106
2107 int VompClientRRProc::processGetRecInfo2()
2108 {
2109   // data is a pointer to the fileName string
2110 #if VDRVERSNUM >= 20301
2111   LOCK_RECORDINGS_READ;
2112   const cRecordings* tRecordings = Recordings;
2113 #else
2114   cThreadLock RecordingsLock(&Recordings);
2115   cRecordings* tRecordings = &Recordings;
2116 #endif
2117
2118   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2119
2120   time_t timerStart = 0;
2121   time_t timerStop = 0;
2122   char* summary = NULL;
2123   char* shorttext = NULL;
2124   char* description = NULL;
2125   char* title = NULL;
2126   bool newsummary=false;
2127   ULONG resumePoint = 0;
2128
2129   if (!recording)
2130   {
2131     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2132     resp->addULONG(0);
2133     resp->finalise();
2134     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2135     return 1;
2136   }
2137
2138   /* Return packet:
2139   4 bytes: start time for timer
2140   4 bytes: end time for timer
2141   4 bytes: resume point
2142   string: summary
2143   4 bytes: num components
2144   {
2145     1 byte: stream
2146     1 byte: type
2147     string: language
2148     string: description
2149   }
2150   8 bytes: frames per second
2151   string: title/name
2152   // new stuff starts here
2153   string: channel name
2154   4 bytes: duration
2155   4 bytes : filesize
2156   4 bytes : priority
2157   4 bytes : lifetime
2158   */
2159
2160   // Get current timer
2161
2162   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2163   if (rc)
2164   {
2165     timerStart = rc->Timer()->StartTime();
2166     timerStop = rc->Timer()->StopTime();
2167     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2168   }
2169
2170   resp->addULONG(timerStart);
2171   resp->addULONG(timerStop);
2172
2173   // Get resume point
2174
2175 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
2176   if (value)
2177   {
2178     resumePoint = strtoul(value, NULL, 10);
2179     delete[] value;
2180   }*/
2181
2182   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2183   int ResumeId;
2184   if (ResumeIdC) {
2185     ResumeId = atoi(ResumeIdC);
2186     delete[] ResumeIdC;
2187   }
2188   else
2189     ResumeId = 0;  //default if not defined in vomp-MAC.conf
2190
2191   while (ResumeIDLock)
2192     cCondWait::SleepMs(100);
2193   ResumeIDLock = true;
2194   int OldSetupResumeID = Setup.ResumeID;
2195   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
2196 #if VDRVERSNUM < 10703
2197   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
2198 #else
2199   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2200 #endif
2201   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
2202   ResumeIDLock = false;
2203
2204   int resume = ResumeFile.Read();
2205   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2206   if (resume >= 0)
2207     resumePoint = ResumeFile.Read();
2208
2209   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2210
2211   resp->addULONG(resumePoint);
2212
2213   // Get summary
2214
2215 #if VDRVERSNUM < 10300
2216   summary = (char*)recording->Summary();
2217 #else
2218   const cRecordingInfo *Info = recording->Info();
2219   shorttext = (char*)Info->ShortText();
2220   description = (char*) (char*)Info->Description();
2221   if (isempty(shorttext)) summary=description;
2222   else if (isempty(description)) summary=shorttext;
2223   else {
2224      int length=strlen(description)+strlen(shorttext)+4;
2225      summary=new char[length];
2226      snprintf(summary,length,"%s\n\n%s",shorttext,description);
2227      newsummary=true;
2228   }
2229
2230   if (isempty(summary)) summary = (char*)Info->Description();
2231 #endif
2232   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2233   if (summary)
2234   {
2235     resp->addString(x.charconvsys->Convert(summary));
2236     if (newsummary) delete [] summary;
2237   }
2238   else
2239   {
2240     resp->addString("");
2241   }
2242
2243   // Get channels
2244
2245 #if VDRVERSNUM < 10300
2246
2247   // Send 0 for numchannels - this signals the client this info is not available
2248   resp->addULONG(0);
2249
2250 #else
2251   const cComponents* components = Info->Components();
2252
2253   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2254
2255   if (!components)
2256   {
2257     resp->addULONG(0);
2258   }
2259   else
2260   {
2261     resp->addULONG(components->NumComponents());
2262
2263     tComponent* component;
2264     for (int i = 0; i < components->NumComponents(); i++)
2265     {
2266       component = components->Component(i);
2267
2268       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2269
2270       resp->addUCHAR(component->stream);
2271       resp->addUCHAR(component->type);
2272
2273       if (component->language)
2274       {
2275         resp->addString(x.charconvsys->Convert(component->language));
2276       }
2277       else
2278       {
2279         resp->addString("");
2280       }
2281       if (component->description)
2282       {
2283         resp->addString(x.charconvsys->Convert(component->description));
2284       }
2285       else
2286       {
2287         resp->addString("");
2288       }
2289     }
2290   }
2291
2292 #endif
2293   double framespersec;
2294 #if VDRVERSNUM < 10703
2295   framespersec = FRAMESPERSEC;
2296 #else
2297   framespersec = Info->FramesPerSecond();
2298 #endif
2299   resp->adddouble(framespersec);
2300   title = (char*)Info->Title();
2301   if (title)
2302   {
2303     resp->addString(x.charconvsys->Convert(title));
2304   }
2305   else
2306   {
2307       resp->addString(x.charconvsys->Convert(recording->Name()));
2308   }
2309
2310   // New stuff
2311   if (Info->ChannelName())
2312   {
2313     resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2314   }
2315   else
2316   {
2317     resp->addString("");
2318   }
2319
2320   resp->addULONG(recording->LengthInSeconds());
2321   resp->addULONG(recording->FileSizeMB());
2322   resp->addULONG(recording->Priority());
2323   resp->addULONG(recording->Lifetime());
2324
2325   // Done. send it
2326
2327   resp->finalise();
2328   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2329
2330   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2331
2332   return 1;
2333 }
2334
2335 // FIXME obselete
2336
2337 int VompClientRRProc::processReScanRecording()
2338 {
2339   if (!x.recplayer)
2340   {
2341     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2342     return 0;
2343   }
2344
2345   x.recplayer->scan();
2346
2347   resp->addULLONG(x.recplayer->getLengthBytes());
2348   resp->addULONG(x.recplayer->getLengthFrames());
2349   resp->finalise();
2350   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2351   log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2352   return 1;
2353 }
2354
2355 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2356
2357 int VompClientRRProc::processGetMarks()
2358 {
2359   // data is a pointer to the fileName string
2360 #if VDRVERSNUM >= 20301
2361   LOCK_RECORDINGS_READ;
2362   const cRecordings* tRecordings = Recordings;
2363 #else
2364   cThreadLock RecordingsLock(&Recordings);
2365   cRecordings* tRecordings = &Recordings;
2366 #endif
2367
2368   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2369   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2370
2371   if (recording)
2372   {
2373     cMarks Marks;
2374 #if VDRVERSNUM < 10703
2375     Marks.Load(recording->FileName());
2376 #else
2377     Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2378 #endif
2379     if (Marks.Count())
2380     {
2381       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2382       {
2383 #if VDRVERSNUM < 10721
2384         ULLONG mposition = m->position;
2385 #else
2386         ULLONG mposition = m->Position();
2387 #endif
2388         log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2389
2390         resp->addULONG(mposition);
2391       }
2392     }
2393     else
2394     {
2395       log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2396       resp->addULONG(0);
2397     }
2398   }
2399
2400   resp->finalise();
2401   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2402   
2403   log->log("RRProc", Log::DEBUG, "Written Marks list");
2404
2405   return 1;
2406 }
2407
2408 int VompClientRRProc::processVDRShutdown()
2409 {
2410   log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2411   VompClient::decClients(); // Temporarily make this client disappear
2412   cRemote::Put(kPower);
2413   VompClient::incClients();
2414   resp->finalise();
2415   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2416   return 1;
2417 }
2418
2419 int VompClientRRProc::processGetRecScraperEventType()
2420 {
2421 #if VDRVERSNUM >= 20301
2422   LOCK_RECORDINGS_READ;
2423   const cRecordings* tRecordings = Recordings;
2424 #else
2425   cThreadLock RecordingsLock(&Recordings);
2426   cRecordings* tRecordings = &Recordings;
2427 #endif
2428
2429   const cRecording *recording = tRecordings->GetByName((char*)req->data);
2430   ScraperGetEventType call;
2431   call.type = tNone;
2432
2433   if (recording && x.scrapQuery()) 
2434   {
2435      call.recording = recording;
2436      x.scraper->Service("GetEventType", &call);
2437   }
2438   resp->addUCHAR(call.type);
2439   if (call.type == tMovie)
2440   {
2441      resp->addLONG(call.movieId);
2442   }
2443   else if (call.type == tSeries)
2444   {
2445      resp->addLONG(call.seriesId);
2446      resp->addLONG(call.episodeId);
2447   }
2448   resp->finalise();
2449   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2450
2451   return 1;
2452 }
2453
2454 int VompClientRRProc::processGetEventScraperEventType()
2455 {
2456   ScraperGetEventType call;
2457   call.type = tNone;
2458   ULONG channelid = ntohl(*(ULONG*)req->data);
2459   ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2460   const cEvent *event = NULL; 
2461
2462 #if VDRVERSNUM >= 20301
2463   LOCK_CHANNELS_READ;
2464   const cChannels* tChannels = Channels;
2465 #else
2466   cChannels* tChannels = &Channels;
2467 #endif
2468
2469   const cChannel* channel = tChannels->GetByNumber(channelid);
2470
2471 #if VDRVERSNUM < 10300
2472   cMutexLock MutexLock;
2473   const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2474 #elif VDRVERSNUM < 20301
2475   cSchedulesLock MutexLock;
2476   const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2477 #else
2478   LOCK_SCHEDULES_READ;
2479   const cSchedules *tSchedules = Schedules;
2480 #endif
2481
2482   if (tSchedules && channel)
2483   {
2484      const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2485      if (Schedule) {
2486         event = Schedule->GetEvent(eventid);
2487     }
2488   }
2489     
2490   if (event && x.scrapQuery()) 
2491   {
2492      call.event = event;
2493      x.scraper->Service("GetEventType",&call);
2494   }
2495   resp->addUCHAR(call.type);
2496   if (call.type == tMovie)
2497   {
2498      resp->addLONG(call.movieId);
2499   } else if (call.type == tSeries){
2500      resp->addLONG(call.seriesId);
2501      resp->addLONG(call.episodeId);
2502   }
2503   if (x.pict->epgImageExists(eventid)) {
2504      resp->addLONG(1);
2505   } else {
2506      resp->addLONG(0);
2507   }
2508     
2509   resp->finalise();
2510   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2511
2512   return 1;
2513 }
2514
2515 #define ADDSTRING_TO_PAKET(y) if ((y)!=0)  resp->addString(x.charconvutf8->Convert(y)); else resp->addString(""); 
2516
2517 int VompClientRRProc::processGetScraperMovieInfo()
2518 {
2519    
2520    cMovie movie;
2521    movie.movieId = ntohl(*(ULONG*)req->data);
2522    if (!x.scrapQuery()) {
2523       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2524       return 0; //stupid, I have no scraper why are you still asking
2525    }
2526    x.scraper->Service("GetMovie",&movie);
2527    
2528
2529    ADDSTRING_TO_PAKET(movie.title.c_str());
2530    ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2531    ADDSTRING_TO_PAKET(movie.tagline.c_str());
2532    ADDSTRING_TO_PAKET(movie.overview.c_str());
2533    resp->addUCHAR(movie.adult);
2534    ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2535
2536    resp->addLONG(movie.budget);
2537    resp->addLONG(movie.revenue);
2538    ADDSTRING_TO_PAKET(movie.genres.c_str());
2539    ADDSTRING_TO_PAKET(movie.homepage.c_str());
2540    ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2541    resp->addLONG(movie.runtime);
2542    resp->adddouble(movie.popularity);
2543    resp->adddouble(movie.voteAverage);
2544    resp->addULONG(movie.poster.width);
2545    resp->addULONG(movie.poster.height);
2546    resp->addULONG(movie.fanart.width);
2547    resp->addULONG(movie.fanart.height);
2548    resp->addULONG(movie.collectionPoster.width);
2549    resp->addULONG(movie.collectionPoster.height);
2550    resp->addULONG(movie.collectionFanart.width);
2551    resp->addULONG(movie.collectionFanart.height);
2552    resp->addULONG(movie.actors.size());
2553    for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2554        ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2555        ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2556        resp->addULONG(movie.actors[acty].actorThumb.width);
2557        resp->addULONG(movie.actors[acty].actorThumb.height);
2558    }
2559   resp->finalise();
2560   
2561   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2562
2563   
2564   return 1;
2565
2566 }
2567
2568 int VompClientRRProc::processGetScraperSeriesInfo()
2569 {
2570    cSeries series;
2571    series.seriesId = ntohl(*(ULONG*)req->data);
2572    series.episodeId = ntohl(*(ULONG*)(req->data+4));
2573    if (!x.scrapQuery()) {
2574       log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2575       return 0; //stupid, I have no scraper why are you still asking
2576    }
2577    x.scraper->Service("GetSeries",&series);
2578    
2579    ADDSTRING_TO_PAKET(series.name.c_str());
2580    ADDSTRING_TO_PAKET(series.overview.c_str());
2581    ADDSTRING_TO_PAKET(series.firstAired.c_str());
2582    ADDSTRING_TO_PAKET(series.network.c_str());
2583    ADDSTRING_TO_PAKET(series.genre.c_str());
2584    resp->adddouble(series.rating);
2585    ADDSTRING_TO_PAKET(series.status.c_str());
2586    
2587    resp->addLONG(series.episode.number);
2588    resp->addLONG(series.episode.season);
2589    ADDSTRING_TO_PAKET(series.episode.name.c_str());
2590    ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2591    ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2592    ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2593    resp->adddouble(series.episode.rating);
2594    resp->addULONG(series.episode.episodeImage.width);
2595    resp->addULONG(series.episode.episodeImage.height);
2596    
2597    ULONG num_actors = series.actors.size();
2598    resp->addULONG(num_actors);
2599    for (ULONG acty=0; acty < num_actors; acty++) {
2600        ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2601        ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2602        resp->addULONG(series.actors[acty].actorThumb.width);
2603        resp->addULONG(series.actors[acty].actorThumb.height);
2604    }
2605    ULONG num_posters = series.posters.size();
2606    resp->addULONG(num_posters);
2607    for (ULONG medias = 0; medias < num_posters; medias++ ) {
2608        cTvMedia media=series.posters[medias];
2609        resp->addULONG(media.width);
2610        resp->addULONG(media.height);
2611    }
2612
2613    ULONG num_banners = series.banners.size();
2614    resp->addULONG(num_banners);
2615    for (ULONG medias = 0; medias < num_banners; medias++ ) {
2616        cTvMedia media=series.banners[medias];
2617        resp->addULONG(media.width);
2618        resp->addULONG(media.height);
2619    }
2620    ULONG num_fanarts = series.fanarts.size();
2621    resp->addULONG(num_fanarts);
2622    for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2623        cTvMedia media=series.fanarts[medias];
2624        resp->addULONG(media.width);
2625        resp->addULONG(media.height);
2626    }
2627    resp->addULONG(series.seasonPoster.width);
2628    resp->addULONG(series.seasonPoster.height);
2629    
2630    resp->finalise();
2631
2632    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2633    
2634    return 1;
2635 }
2636
2637 int VompClientRRProc::processLoadTvMedia()
2638 {
2639    TVMediaRequest tvreq;
2640    tvreq.streamID = req->requestID;
2641    tvreq.type = ntohl(*(ULONG*)req->data);
2642    tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2643    tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2644    tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2645    tvreq.container = ntohl(*(ULONG*)(req->data+16));
2646    tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2647    log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2648    x.pict->addTVMediaRequest(tvreq);
2649
2650    
2651    resp->finalise();
2652
2653    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2654    
2655    return 1;
2656 }
2657
2658 int VompClientRRProc::processLoadTvMediaRecThumb()
2659 {
2660    TVMediaRequest tvreq;
2661    tvreq.streamID = req->requestID;
2662    tvreq.type = 3; // unknown but primary_name is set
2663    tvreq.primary_id = 0;
2664    tvreq.primary_name = std::string((const char*) req->data);
2665    tvreq.secondary_id = 0;
2666    tvreq.type_pict = 1;
2667    tvreq.container = 0;
2668    tvreq.container_member = 0;
2669    log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2670    x.pict->addTVMediaRequest(tvreq);
2671
2672    
2673    resp->finalise();
2674
2675    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2676    
2677    return 1;
2678 }
2679
2680 int VompClientRRProc::processLoadTvMediaEventThumb()
2681 {
2682    TVMediaRequest tvreq;
2683    tvreq.streamID = req->requestID;
2684    tvreq.type = 4; // unknown but primary_id is set
2685    UINT channelid = ntohl(*(ULONG*)req->data);
2686    tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2687    tvreq.secondary_id = 0;
2688
2689 #if VDRVERSNUM >= 20301
2690   LOCK_CHANNELS_READ;
2691   const cChannels* tChannels = Channels;
2692 #else
2693   cChannels* tChannels = &Channels;
2694 #endif
2695
2696    const cChannel* channel = tChannels->GetByNumber(channelid);
2697
2698    if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2699    tvreq.type_pict = 1;
2700    tvreq.container = 0;
2701    tvreq.container_member = 0;
2702    log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2703    x.pict->addTVMediaRequest(tvreq);
2704
2705    
2706    resp->finalise();
2707
2708    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2709    
2710    return 1;
2711 }
2712
2713 int VompClientRRProc::processLoadChannelLogo()
2714 {
2715    TVMediaRequest tvreq;
2716    tvreq.streamID = req->requestID;
2717    tvreq.type = 5; // channel logo
2718    UINT channelid = ntohl(*(ULONG*)req->data);
2719    tvreq.primary_id = channelid;
2720    tvreq.secondary_id = 0;
2721
2722 #if VDRVERSNUM >= 20301
2723   LOCK_CHANNELS_READ;
2724   const cChannels* tChannels = Channels;
2725 #else
2726   cChannels* tChannels = &Channels;
2727 #endif
2728
2729    const cChannel* channel = tChannels->GetByNumber(channelid);
2730
2731    if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2732    tvreq.type_pict = 1;
2733    tvreq.container = 0;
2734    tvreq.container_member = 0;
2735    if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2736    x.pict->addTVMediaRequest(tvreq);
2737
2738    
2739    resp->finalise();
2740
2741    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2742    
2743    return 1;
2744 }
2745
2746
2747
2748   
2749 #endif // !VOMPSTANDALONE
2750