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