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