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