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