]> git.vomp.tv Git - vompserver.git/blob - vompclientrrproc.c
Add version control to protocoll and audio types needed for AAC and other
[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 (priority < 0) priority = 0;
1142   if (priority > 99) priority = 99;
1143
1144   log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1145   x.lp = MVPReceiver::create(channel, priority);
1146
1147   if (!x.lp)
1148   {
1149     resp->addULONG(0);
1150     resp->finalise();
1151     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1152     return 1;
1153   }
1154
1155   if (!x.lp->init(&x.tcp, req->requestID))
1156   {
1157     delete x.lp;
1158     x.lp = NULL;
1159     resp->addULONG(0);
1160     resp->finalise();
1161     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1162     return 1;
1163   }
1164
1165   resp->addULONG(1);
1166   resp->finalise();
1167   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1168   return 1;
1169 }
1170
1171 int VompClientRRProc::processStopStreaming()
1172 {
1173   log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1174   if (x.lp)
1175   {
1176     x.lp->detachMVPReceiver();
1177     delete x.lp;
1178     x.lp = NULL;
1179   }
1180   else if (x.recplayer)
1181   {
1182     x.writeResumeData();
1183
1184     delete x.recplayer;
1185     delete x.recordingManager;
1186     x.recplayer = NULL;
1187     x.recordingManager = NULL;
1188   }
1189
1190   resp->addULONG(1);
1191   resp->finalise();
1192   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1193   return 1;
1194 }
1195
1196 int VompClientRRProc::processGetBlock()
1197 {
1198   if (x.lp)
1199   {
1200     log->log("RRProc", Log::ERR, "Get block called during live streaming");
1201     return 0;
1202   }
1203
1204   if (!x.recplayer)
1205   {
1206     log->log("RRProc", Log::ERR, "Get block called when no recording open");
1207     return 0;
1208   }
1209
1210   UCHAR* data = req->data;
1211
1212   ULLONG position = x.ntohll(*(ULLONG*)data);
1213   data += sizeof(ULLONG);
1214   ULONG amount = ntohl(*(ULONG*)data);
1215
1216   log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1217
1218   UCHAR sendBuffer[amount];
1219   ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1220
1221   if (!amountReceived)
1222   {
1223     resp->addULONG(0);
1224     log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1225   }
1226   else
1227   {
1228     resp->copyin(sendBuffer, amountReceived);
1229     log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1230   }
1231
1232   resp->finalise();
1233   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1234   log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1235   return 1;
1236 }
1237
1238 int VompClientRRProc::processStartStreamingRecording()
1239 {
1240   // data is a pointer to the fileName string
1241
1242   x.recordingManager = new cRecordings;
1243   x.recordingManager->Load();
1244
1245   cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1246
1247   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1248
1249   if (recording)
1250   {
1251     x.recplayer = new RecPlayer(recording);
1252
1253     resp->addULLONG(x.recplayer->getLengthBytes());
1254     resp->addULONG(x.recplayer->getLengthFrames());
1255     
1256 #if VDRVERSNUM < 10703
1257     resp->addUCHAR(true);//added for TS    
1258 #else
1259     resp->addUCHAR(recording->IsPesRecording());//added for TS
1260 #endif
1261
1262     resp->finalise();
1263     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1264     
1265     log->log("RRProc", Log::DEBUG, "written totalLength");
1266   }
1267   else
1268   {
1269     delete x.recordingManager;
1270     x.recordingManager = NULL;
1271   }
1272   return 1;
1273 }
1274
1275 int VompClientRRProc::processPositionFromFrameNumber()
1276 {
1277   ULLONG retval = 0;
1278
1279   ULONG frameNumber = ntohl(*(ULONG*)req->data);
1280
1281   if (!x.recplayer)
1282   {
1283     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1284   }
1285   else
1286   {
1287     retval = x.recplayer->positionFromFrameNumber(frameNumber);
1288   }
1289
1290   resp->addULLONG(retval);
1291   resp->finalise();
1292   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1293
1294   log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1295   return 1;
1296 }
1297
1298 int VompClientRRProc::processFrameNumberFromPosition()
1299 {
1300   ULONG retval = 0;
1301
1302   ULLONG position = x.ntohll(*(ULLONG*)req->data);
1303
1304   if (!x.recplayer)
1305   {
1306     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1307   }
1308   else
1309   {
1310     retval = x.recplayer->frameNumberFromPosition(position);
1311   }
1312
1313   resp->addULONG(retval);
1314   resp->finalise();
1315   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1316
1317   log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1318   return 1;
1319 }
1320
1321 int VompClientRRProc::processGetIFrame()
1322 {
1323   bool success = false;
1324
1325   ULONG* data = (ULONG*)req->data;
1326
1327   ULONG frameNumber = ntohl(*data);
1328   data++;
1329   ULONG direction = ntohl(*data);
1330
1331   ULLONG rfilePosition = 0;
1332   ULONG rframeNumber = 0;
1333   ULONG rframeLength = 0;
1334
1335   if (!x.recplayer)
1336   {
1337     log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1338   }
1339   else
1340   {
1341     success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1342   }
1343
1344   // returns file position, frame number, length
1345
1346   if (success)
1347   {
1348     resp->addULLONG(rfilePosition);
1349     resp->addULONG(rframeNumber);
1350     resp->addULONG(rframeLength);
1351   }
1352   else
1353   {
1354     resp->addULONG(0);
1355   }
1356
1357   resp->finalise();
1358   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1359   
1360   log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1361   return 1;
1362 }
1363
1364 int VompClientRRProc::processGetChannelSchedule()
1365 {
1366   ULONG* data = (ULONG*)req->data;
1367
1368   ULONG channelNumber = ntohl(*data);
1369   data++;
1370   ULONG startTime = ntohl(*data);
1371   data++;
1372   ULONG duration = ntohl(*data);
1373
1374   log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1375
1376   cChannel* channel = x.channelFromNumber(channelNumber);
1377   if (!channel)
1378   {
1379     resp->addULONG(0);
1380     resp->finalise();
1381     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1382   
1383     log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1384     return 1;
1385   }
1386
1387   log->log("RRProc", Log::DEBUG, "Got channel");
1388
1389 #if VDRVERSNUM < 10300
1390   cMutexLock MutexLock;
1391   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1392 #else
1393   cSchedulesLock MutexLock;
1394   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1395 #endif
1396   if (!Schedules)
1397   {
1398     resp->addULONG(0);
1399     resp->finalise();
1400     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1401     
1402     log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1403     return 1;
1404   }
1405
1406   log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1407
1408   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1409   if (!Schedule)
1410   {
1411     resp->addULONG(0);
1412     resp->finalise();
1413     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1414     
1415     log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1416     return 1;
1417   }
1418
1419   log->log("RRProc", Log::DEBUG, "Got schedule object");
1420
1421   const char* empty = "";
1422   bool atLeastOneEvent = false;
1423
1424   ULONG thisEventID;
1425   ULONG thisEventTime;
1426   ULONG thisEventDuration;
1427   const char* thisEventTitle;
1428   const char* thisEventSubTitle;
1429   const char* thisEventDescription;
1430
1431 #if VDRVERSNUM < 10300
1432
1433   const cEventInfo *event;
1434   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1435   {
1436     event = Schedule->GetEventNumber(eventNumber);
1437
1438     thisEventID = event->GetEventID();
1439     thisEventTime = event->GetTime();
1440     thisEventDuration = event->GetDuration();
1441     thisEventTitle = event->GetTitle();
1442     thisEventSubTitle = event->GetSubtitle();
1443     thisEventDescription = event->GetExtendedDescription();
1444
1445 #else
1446
1447   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1448   {
1449     thisEventID = event->EventID();
1450     thisEventTime = event->StartTime();
1451     thisEventDuration = event->Duration();
1452     thisEventTitle = event->Title();
1453     thisEventSubTitle = NULL;
1454     thisEventDescription = event->Description();
1455
1456 #endif
1457
1458     //in the past filter
1459     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1460
1461     //start time filter
1462     if ((thisEventTime + thisEventDuration) <= startTime) continue;
1463
1464     //duration filter
1465     if (thisEventTime >= (startTime + duration)) continue;
1466
1467     if (!thisEventTitle) thisEventTitle = empty;
1468     if (!thisEventSubTitle) thisEventSubTitle = empty;
1469     if (!thisEventDescription) thisEventDescription = empty;
1470
1471     resp->addULONG(thisEventID);
1472     resp->addULONG(thisEventTime);
1473     resp->addULONG(thisEventDuration);
1474
1475     resp->addString(x.charconvsys->Convert(thisEventTitle));
1476     resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1477     resp->addString(x.charconvsys->Convert(thisEventDescription));
1478
1479     atLeastOneEvent = true;
1480   }
1481
1482   log->log("RRProc", Log::DEBUG, "Got all event data");
1483
1484   if (!atLeastOneEvent)
1485   {
1486     resp->addULONG(0);
1487     log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1488   }
1489   
1490   resp->finalise();
1491   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1492     
1493   log->log("RRProc", Log::DEBUG, "written schedules packet");
1494
1495   return 1;
1496 }
1497
1498 int VompClientRRProc::processGetTimers()
1499 {
1500   cTimer *timer;
1501   int numTimers = Timers.Count();
1502
1503   resp->addULONG(numTimers);
1504
1505   for (int i = 0; i < numTimers; i++)
1506   {
1507     timer = Timers.Get(i);
1508
1509 #if VDRVERSNUM < 10300
1510     resp->addULONG(timer->Active());
1511 #else
1512     resp->addULONG(timer->HasFlags(tfActive));
1513 #endif
1514     resp->addULONG(timer->Recording());
1515     resp->addULONG(timer->Pending());
1516     resp->addULONG(timer->Priority());
1517     resp->addULONG(timer->Lifetime());
1518     resp->addULONG(timer->Channel()->Number());
1519     resp->addULONG(timer->StartTime());
1520     resp->addULONG(timer->StopTime());
1521     resp->addULONG(timer->Day());
1522     resp->addULONG(timer->WeekDays());
1523     resp->addString(timer->File()); //Filename is system specific and not visible by user
1524   }
1525
1526   resp->finalise();
1527   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1528   
1529   log->log("RRProc", Log::DEBUG, "Written timers list");
1530
1531   return 1;
1532 }
1533
1534 int VompClientRRProc::processSetTimer()
1535 {
1536   char* timerString = new char[strlen((char*)req->data) + 1];
1537   strcpy(timerString, (char*)req->data);
1538
1539 #if VDRVERSNUM < 10300
1540
1541   // If this is VDR 1.2 the date part of the timer string must be reduced
1542   // to just DD rather than YYYY-MM-DD
1543
1544   int s = 0; // source
1545   int d = 0; // destination
1546   int c = 0; // count
1547   while(c != 2) // copy up to date section, including the second ':'
1548   {
1549     timerString[d] = req->data[s];
1550     if (req->data[s] == ':') c++;
1551     ++s;
1552     ++d;
1553   }
1554   // now it has copied up to the date section
1555   c = 0;
1556   while(c != 2) // waste YYYY-MM-
1557   {
1558     if (req->data[s] == '-') c++;
1559     ++s;
1560   }
1561   // now source is at the DD
1562   memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1563   d += req->dataLength - s;
1564   timerString[d] = '\0';
1565
1566   log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1567
1568 #endif
1569   log->log("RRProc", Log::DEBUG, "%s", timerString);
1570
1571   cTimer *timer = new cTimer;
1572   if (timer->Parse((char*)timerString))
1573   {
1574     cTimer *t = Timers.GetTimer(timer);
1575     if (!t)
1576     {
1577       Timers.Add(timer);
1578 #if VDRVERSNUM < 10300
1579       Timers.Save();
1580 #else
1581       Timers.SetModified();
1582 #endif
1583       resp->addULONG(0);
1584       resp->finalise();
1585       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1586       return 1; // FIXME - cTimer* timer is leaked here!
1587     }
1588     else
1589     {
1590       resp->addULONG(1);
1591       resp->finalise();
1592       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1593     }
1594   }
1595   else
1596   {
1597     resp->addULONG(2);
1598     resp->finalise();
1599     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1600   }
1601   delete timer;
1602   return 1;
1603 }
1604
1605 int VompClientRRProc::processDeleteTimer()
1606 {
1607   log->log("RRProc", Log::DEBUG, "Delete timer called");
1608   // get timer
1609   
1610   int position = 0;
1611   
1612   INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1613   INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1614   INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1615   INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;  
1616   INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1617     
1618   cTimer* ti = NULL;
1619   for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1620   {
1621     if  ( (ti->Channel()->Number() == delChannel)
1622      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1623      &&   (ti->StartTime() == delStart)
1624      &&   (ti->StopTime() == delStop) )
1625        break;
1626   }
1627   
1628   if (!ti)
1629   {
1630     resp->addULONG(4);
1631     resp->finalise();
1632     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1633     return 1;
1634   }
1635           
1636   if (!Timers.BeingEdited())
1637   {
1638     if (!ti->Recording())
1639     {
1640       Timers.Del(ti);
1641       Timers.SetModified();
1642       resp->addULONG(10);
1643       resp->finalise();
1644       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1645       return 1;
1646     }
1647     else
1648     {
1649       log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1650       resp->addULONG(3);
1651       resp->finalise();
1652       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1653       return 1;
1654     }  
1655   }
1656   else
1657   {
1658     log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1659     resp->addULONG(1);
1660     resp->finalise();
1661     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1662     return 1;
1663   }  
1664 }
1665
1666 int VompClientRRProc::processGetRecInfo()
1667 {
1668   // data is a pointer to the fileName string
1669
1670   cRecordings Recordings;
1671   Recordings.Load(); // probably have to do this
1672
1673   cRecording *recording = Recordings.GetByName((char*)req->data);
1674
1675   time_t timerStart = 0;
1676   time_t timerStop = 0;
1677   char* summary = NULL;
1678   char* shorttext = NULL;
1679   char* description = NULL;
1680   bool newsummary=false;
1681   ULONG resumePoint = 0;
1682
1683   if (!recording)
1684   {
1685     log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1686     resp->addULONG(0);
1687     resp->finalise();
1688     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1689     return 1;
1690   }
1691
1692   /* Return packet:
1693   4 bytes: start time for timer
1694   4 bytes: end time for timer
1695   4 bytes: resume point
1696   string: summary
1697   4 bytes: num components
1698   {
1699     1 byte: stream
1700     1 byte: type
1701     string: language
1702     string: description
1703   }
1704   8 bytes: frames per second
1705   */
1706
1707   // Get current timer
1708
1709   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1710   if (rc)
1711   {
1712     timerStart = rc->Timer()->StartTime();
1713     timerStop = rc->Timer()->StopTime();
1714     log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1715   }
1716
1717   resp->addULONG(timerStart);
1718   resp->addULONG(timerStop);
1719
1720   // Get resume point
1721
1722 /*  char* value = x.config.getValueString("ResumeData", (char*)req->data);
1723   if (value)
1724   {
1725     resumePoint = strtoul(value, NULL, 10);
1726     delete[] value;
1727   }*/
1728
1729   char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1730   int ResumeId;
1731   if (ResumeIdC) {
1732     ResumeId = atoi(ResumeIdC);
1733     delete[] ResumeIdC;
1734   }
1735   else
1736     ResumeId = 0;  //default if not defined in vomp-MAC.conf
1737
1738   while (ResumeIDLock)
1739     cCondWait::SleepMs(100);
1740   ResumeIDLock = true;
1741   int OldSetupResumeID = Setup.ResumeID;
1742   Setup.ResumeID = ResumeId;                            //UGLY: quickly change resumeid
1743 #if VDRVERSNUM < 10703
1744   cResumeFile ResumeFile(recording->FileName());        //get corresponding resume file
1745 #else
1746   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1747 #endif
1748   Setup.ResumeID = OldSetupResumeID;                    //and restore it back
1749   ResumeIDLock = false;
1750
1751   int resume = ResumeFile.Read();
1752   //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1753   if (resume >= 0) 
1754     resumePoint = ResumeFile.Read();
1755
1756   log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1757
1758   resp->addULONG(resumePoint);
1759
1760   // Get summary
1761
1762 #if VDRVERSNUM < 10300
1763   summary = (char*)recording->Summary();
1764 #else
1765   const cRecordingInfo *Info = recording->Info();
1766   shorttext = (char*)Info->ShortText();
1767   description = (char*) (char*)Info->Description();
1768   if (isempty(shorttext)) summary=description;
1769   else if (isempty(description)) summary=shorttext;
1770   else {
1771      int length=strlen(description)+strlen(shorttext)+4;
1772      summary=new char[length];
1773      snprintf(summary,length,"%s\n\n%s",shorttext,description);
1774      newsummary=true;
1775   }
1776   
1777   if (isempty(summary)) summary = (char*)Info->Description();
1778 #endif
1779   log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1780   if (summary)
1781   {
1782     resp->addString(x.charconvsys->Convert(summary));
1783     if (newsummary) delete [] summary;
1784   }
1785   else
1786   {
1787     resp->addString("");
1788   }
1789
1790   // Get channels
1791
1792 #if VDRVERSNUM < 10300
1793
1794   // Send 0 for numchannels - this signals the client this info is not available
1795   resp->addULONG(0);
1796
1797 #else
1798   const cComponents* components = Info->Components();
1799
1800   log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1801
1802   if (!components)
1803   {
1804     resp->addULONG(0);
1805   }
1806   else
1807   {
1808     resp->addULONG(components->NumComponents());
1809   
1810     tComponent* component;
1811     for (int i = 0; i < components->NumComponents(); i++)
1812     {
1813       component = components->Component(i);
1814
1815       log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1816       
1817       resp->addUCHAR(component->stream);
1818       resp->addUCHAR(component->type);
1819
1820       if (component->language)
1821       {
1822         resp->addString(x.charconvsys->Convert(component->language));
1823       }
1824       else
1825       {
1826         resp->addString("");
1827       }
1828       if (component->description)
1829       {
1830         resp->addString(x.charconvsys->Convert(component->description));
1831       }
1832       else
1833       {
1834         resp->addString("");
1835       }
1836     }
1837   }
1838
1839 #endif
1840   double framespersec;
1841 #if VDRVERSNUM < 10703
1842   framespersec = FRAMESPERSEC;
1843 #else
1844   framespersec = Info->FramesPerSecond();
1845 #endif
1846   resp->adddouble(framespersec);
1847
1848   // Done. send it
1849
1850   resp->finalise();
1851   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1852
1853   log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1854
1855   return 1;
1856 }
1857
1858
1859
1860
1861 // FIXME obselete
1862
1863 int VompClientRRProc::processReScanRecording()
1864 {
1865   if (!x.recplayer)
1866   {
1867     log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1868     return 0;
1869   }
1870
1871   x.recplayer->scan();
1872
1873   resp->addULLONG(x.recplayer->getLengthBytes());
1874   resp->addULONG(x.recplayer->getLengthFrames());
1875   resp->finalise();
1876   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1877   log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1878   return 1;
1879 }
1880
1881 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1882
1883 int VompClientRRProc::processGetMarks()
1884 {
1885   // data is a pointer to the fileName string
1886
1887   cMarks Marks;
1888   cRecordings Recordings;
1889   Recordings.Load(); // probably have to do this
1890
1891   cRecording *recording = Recordings.GetByName((char*)req->data);
1892
1893   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1894
1895   if (recording)
1896   {
1897 #if VDRVERSNUM < 10703
1898     Marks.Load(recording->FileName());
1899 #else
1900     Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1901 #endif
1902     if (Marks.Count())
1903     {
1904       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1905       {
1906 #if VDRVERSNUM < 10721
1907         ULLONG mposition = m->position;
1908 #else
1909         ULLONG mposition = m->Position();
1910 #endif
1911         log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1912
1913         resp->addULONG(mposition);
1914       }
1915     }
1916     else
1917     {
1918       log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1919       resp->addULONG(0);
1920     }
1921   }
1922
1923   resp->finalise();
1924   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1925   
1926   log->log("RRProc", Log::DEBUG, "Written Marks list");
1927
1928   return 1;
1929 }
1930
1931 #endif // !VOMPSTANDALONE
1932
1933