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