]> git.vomp.tv Git - vompserver.git/blob - vompclientrrproc.c
UTF-8 patch from Marten Richter, inc threadMethod err 1 fix
[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
276   delete resp;
277   resp = NULL;
278   
279   if (req->data) free(req->data);
280   delete req;
281   req = NULL;
282   
283   if (result) return true;
284   return false;
285 }
286
287 int VompClientRRProc::processLogin()
288 {
289   if (req->dataLength != 6) return 0;
290
291   // Open the config
292
293   char configFileName[PATH_MAX];
294   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]);
295   x.config.init(configFileName);
296
297   // Send the login reply
298
299   time_t timeNow = time(NULL);
300   struct tm* timeStruct = localtime(&timeNow);
301   int timeOffset = timeStruct->tm_gmtoff;
302
303   resp->addULONG(timeNow);
304   resp->addLONG(timeOffset);
305   resp->finalise();
306   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
307   log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
308     
309   x.loggedIn = true;
310   x.netLog(); // safe to run here since the client won't start net logging for a while yet
311   
312   return 1;
313 }
314
315 int VompClientRRProc::processSetCharset()
316 {
317    int charset = ntohl(*(ULONG*)req->data);
318    if (charset>0 && charset<3) {
319         log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
320         x.setCharset(charset);
321         resp->addULONG(1);
322     } else {
323        log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
324        resp->addULONG(0);
325     }
326   resp->finalise();
327   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
328
329 }
330
331     
332
333
334 int VompClientRRProc::processConfigSave()
335 {
336   char* section = (char*)req->data;
337   char* key = NULL;
338   char* value = NULL;
339
340   for (UINT k = 0; k < req->dataLength; k++)
341   {
342     if (req->data[k] == '\0')
343     {
344       if (!key)
345       {
346         key = (char*)&req->data[k+1];
347       }
348       else
349       {
350         value = (char*)&req->data[k+1];
351         break;
352       }
353     }
354   }
355
356   // if the last string (value) doesnt have null terminator, give up
357   if (req->data[req->dataLength - 1] != '\0') return 0;
358
359   log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
360   if (x.config.setValueString(section, key, value))
361   {
362     resp->addULONG(1);
363   }
364   else
365   {
366     resp->addULONG(0);
367   }
368
369   resp->finalise();
370   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
371   
372   return 1;
373 }
374
375 int VompClientRRProc::processConfigLoad()
376 {
377   char* section = (char*)req->data;
378   char* key = NULL;
379
380   for (UINT k = 0; k < req->dataLength; k++)
381   {
382     if (req->data[k] == '\0')
383     {
384       key = (char*)&req->data[k+1];
385       break;
386     }
387   }
388
389   char* value = x.config.getValueString(section, key);
390
391   if (value)
392   {
393     resp->addString(value);//client coding, do not touch
394     log->log("RRProc", Log::DEBUG, "Written config load packet");
395     delete[] value;
396   }
397   else
398   {
399     resp->addULONG(0);
400     log->log("RRProc", Log::DEBUG, "Written config load failed packet");
401   }
402
403   resp->finalise();
404   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
405   
406   return 1;
407 }
408
409
410 //helper for sending from a serialize buffer
411 //insert the used len into the first 4 Bytes of the buffer
412 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
413   resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
414   resp->finalise();
415   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
416 }
417
418 /**
419   * media List Request:
420   * Media List response:
421   * flags, mediaList
422 */
423 #define MLISTBUF 500000
424 int VompClientRRProc::processGetMediaList()
425 {
426   SerializeBuffer buffer(req->data,req->dataLength);
427   MediaURI uri(0,NULL,NULL);
428   VDR_GetMediaListRequest request(&uri);
429   if (request.deserialize(&buffer) != 0) {
430     log->log("Client", Log::ERR, "getMediaList unable to deserialize");
431     return 0;
432   }
433   const char *dirname=uri.getName();
434   log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
435
436   MediaList * ml=NULL;
437   if (dirname == NULL) {
438     ml=x.media->getRootList();
439   } else {
440     ml=x.media->getMediaList(&uri);
441   }
442   if (ml == NULL) {
443      log->log("Client", Log::ERR, "getMediaList returned NULL");
444      return 0;
445   }
446   SerializeBuffer rbuf(MLISTBUF,false,true);
447   ULONG flags=0; //TODO: real error handling by setting flags
448   VDR_GetMediaListResponse response(&flags,ml);
449   if (response.serialize(&rbuf) != 0) {
450      log->log("Client", Log::ERR, "getMediaList returned NULL");
451      delete ml;
452      return 0;
453   }
454   log->log("Client", Log::DEBUG, "getMediaList size  %u", ml->size());
455   delete ml;
456   
457
458   sendPacket(&rbuf);
459   log->log("Client", Log::DEBUG, "Written Media list");
460   return 1;
461 }
462 /**
463   * openMedia Request:
464   * openMedia response:
465 */
466 int VompClientRRProc::processOpenMedia()
467 {
468   SerializeBuffer buffer(req->data,req->dataLength);
469   MediaURI uri(0,NULL,NULL);
470   ULONG channel=0;
471   ULONG xs=0;
472   ULONG ys=0;
473   VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
474   if (request.deserialize(&buffer) != 0) {
475     log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
476     return 0;
477   }
478   const char *name=uri.getName();
479   log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
480   ULLONG size=0;
481   int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
482   ULONG flags=0;
483   if (rt != 0) {
484     size=0;
485     flags=1;
486     log->log("Client", Log::ERR, "openMediaRequest unable to open");
487   }
488   VDR_OpenMediumResponse response(&flags,&size);
489   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
490   if (response.serialize(&rbuf) != 0) {
491      log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
492      return 0;
493   }
494   log->log("Client", Log::DEBUG, "openMediaRequest size  %llu", size);
495   sendPacket(&rbuf);
496   return 1;
497 }
498 /**
499   * VDR_GETMEDIABLOCK
500   * resp
501   * packet - no serialized response!
502   */
503 int VompClientRRProc::processGetMediaBlock()
504 {
505   SerializeBuffer buffer(req->data,req->dataLength);
506   ULLONG position = 0;
507   ULONG amount = 0;
508   ULONG channel = 0;
509   VDR_GetMediaBlockRequest request(&channel,&position,&amount);
510   if (request.deserialize(&buffer) != 0) {
511     log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
512     return 0;
513   }
514   log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
515
516   UCHAR sendBuffer[amount ];
517   ULONG amountReceived = 0; 
518   UCHAR *rbuf=sendBuffer;
519   int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
520   if (!amountReceived || rt != 0)
521   {
522     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
523   }
524   else
525   {
526     if (rbuf != sendBuffer) {
527       //the provider did not use the optimized handling with using my buffer
528       resp->copyin(rbuf,amountReceived);
529       free(rbuf);
530     } else {
531       // the provider did not allocate a new buffer
532       resp->copyin(sendBuffer,amountReceived);
533     }
534   }
535   resp->finalise();
536   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
537   log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
538   return 1;
539 }
540 /**
541   * VDR_GETMEDIAINFO
542   */
543
544 int VompClientRRProc::processGetMediaInfo()
545 {
546   SerializeBuffer buffer(req->data,req->dataLength);
547   ULONG channel=0;
548   VDR_GetMediaInfoRequest request(&channel);
549   if (request.deserialize(&buffer) != 0) {
550     log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
551     return 0;
552   }
553   log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
554   ULONG flags=0;
555   MediaInfo mi;
556   int rt=x.media->getMediaInfo(channel,&mi);
557   if (rt != 0) {
558     flags=1;
559     log->log("Client", Log::ERR, "getMediaInfo unable to get");
560   }
561   VDR_GetMediaInfoResponse response(&flags,&mi);
562   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
563   if (response.serialize(&rbuf) != 0) {
564      log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
565      return 0;
566   }
567   sendPacket(&rbuf);
568   return 1;
569 }
570
571 /**
572   * VDR_CLOSECHANNEL
573   */
574
575
576 int VompClientRRProc::processCloseMediaChannel()
577 {
578   SerializeBuffer buffer(req->data,req->dataLength);
579   ULONG channel=0;
580   VDR_CloseMediaChannelRequest request(&channel);
581   if (request.deserialize(&buffer) != 0) {
582     log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
583     return 0;
584   }
585   ULONG flags=0;
586   log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
587   int rt=x.media->closeMediaChannel(channel);
588   if (rt != 0) {
589     flags=1;
590     log->log("Client", Log::ERR, "closeMediaChannel unable to get");
591   }
592   VDR_CloseMediaChannelResponse response(&flags);
593   SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
594   if (response.serialize(&rbuf) != 0) {
595      log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
596      return 0;
597   }
598   sendPacket(&rbuf);
599   return 1;
600 }
601
602
603
604 int VompClientRRProc::processGetLanguageList()
605 {
606   x.i18n.findLanguages();
607   const I18n::lang_code_list& languages = x.i18n.getLanguageList();
608   std::string result;
609   I18n::lang_code_list::const_iterator iter;
610   for (iter = languages.begin(); iter != languages.end(); ++iter)
611   {
612     resp->addString(iter->first.c_str()); // Source code is acsii
613     resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
614   }
615   resp->finalise();
616   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
617   return 1;
618 }
619
620 int VompClientRRProc::processGetLanguageContent()
621 {
622   if (req->dataLength <= 0) return 0;
623   std::string code, result;
624   code.assign((char*)req->data, req->dataLength - 1);
625   x.i18n.findLanguages();
626   I18n::trans_table texts = x.i18n.getLanguageContent(code);
627   I18n::trans_table::const_iterator iter;
628   for (iter = texts.begin(); iter != texts.end(); ++iter)
629   {
630     resp->addString(iter->first.c_str());// source code is acsii since it is english
631     resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
632   }
633   resp->finalise();
634   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
635   return 1;
636 }
637
638 #ifndef VOMPSTANDALONE
639
640 int VompClientRRProc::processGetRecordingsList()
641 {
642   int FreeMB;
643   int Percent = VideoDiskSpace(&FreeMB);
644   int Total = (FreeMB / (100 - Percent)) * 100;
645   
646   resp->addULONG(Total);
647   resp->addULONG(FreeMB);
648   resp->addULONG(Percent);
649
650   cRecordings Recordings;
651   Recordings.Load();
652
653   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
654   {
655 #if VDRVERSNUM < 10721
656     resp->addULONG(recording->start);
657 #else
658     resp->addULONG(recording->Start());
659 #endif
660     resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
661     resp->addString(recording->FileName());//file name are not  visible by user do not touch
662   }
663
664   resp->finalise();
665   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
666   
667   log->log("RRProc", Log::DEBUG, "Written recordings list");
668
669   return 1;
670 }
671
672 int VompClientRRProc::processDeleteRecording()
673 {
674   // data is a pointer to the fileName string
675
676   cRecordings Recordings;
677   Recordings.Load(); // probably have to do this
678
679   cRecording* recording = Recordings.GetByName((char*)req->data);
680
681   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
682
683   if (recording)
684   {
685     log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
686
687     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
688     if (!rc)
689     {
690       if (recording->Delete())
691       {
692         // Copy svdrp's way of doing this, see if it works
693 #if VDRVERSNUM > 10300
694         ::Recordings.DelByName(recording->FileName());
695 #endif
696         resp->addULONG(1);
697       }
698       else
699       {
700         resp->addULONG(2);
701       }
702     }
703     else
704     {
705       resp->addULONG(3);
706     }
707   }
708   else
709   {
710     resp->addULONG(4);
711   }
712
713   resp->finalise();
714   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
715   
716   return 1;
717 }
718
719 int VompClientRRProc::processMoveRecording()
720 {
721   log->log("RRProc", Log::DEBUG, "Process move recording");
722   char* fileName = (char*)req->data;
723   char* newPath = NULL;
724
725   for (UINT k = 0; k < req->dataLength; k++)
726   {
727     if (req->data[k] == '\0')
728     {
729       newPath = (char*)&req->data[k+1];
730       break;
731     }
732   }
733   if (!newPath) return 0;
734
735   cRecordings Recordings;
736   Recordings.Load(); // probably have to do this
737
738   cRecording* recording = Recordings.GetByName((char*)fileName);
739
740   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
741
742   if (recording)
743   {
744     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
745     if (!rc)
746     {
747       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
748       log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
749       log->log("RRProc", Log::DEBUG, "to: %s", newPath);
750
751       const char* t = recording->FileName();
752
753       char* dateDirName = NULL;   int k;
754       char* titleDirName = NULL;  int j;
755
756       // Find the datedirname
757       for(k = strlen(t) - 1; k >= 0; k--)
758       {
759         if (t[k] == '/')
760         {
761           log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
762           dateDirName = new char[strlen(&t[k+1]) + 1];
763           strcpy(dateDirName, &t[k+1]);
764           break;
765         }
766       }
767
768       // Find the titledirname
769
770       for(j = k-1; j >= 0; j--)
771       {
772         if (t[j] == '/')
773         {
774           log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
775           titleDirName = new char[(k - j - 1) + 1];
776           memcpy(titleDirName, &t[j+1], k - j - 1);
777           titleDirName[k - j - 1] = '\0';
778           break;
779         }
780       }
781
782       log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
783       log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
784       log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
785
786       char* newPathConv = new char[strlen(newPath)+1];
787       strcpy(newPathConv, newPath);
788       ExchangeChars(newPathConv, true);
789       log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
790
791       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
792       log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
793       sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
794       delete[] newPathConv;
795
796       log->log("RRProc", Log::DEBUG, "%s", newContainer);
797
798       struct stat dstat;
799       int statret = stat(newContainer, &dstat);
800       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
801       {
802         log->log("RRProc", Log::DEBUG, "new dir does not exist");
803         int mkdirret = mkdir(newContainer, 0755);
804         if (mkdirret != 0)
805         {
806           delete[] dateDirName;
807           delete[] titleDirName;
808           delete[] newContainer;
809
810           resp->addULONG(5);          
811           resp->finalise();
812           x.tcp.sendPacket(resp->getPtr(), resp->getLen());
813           return 1;
814         }
815       }
816       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
817       {
818         delete[] dateDirName;
819         delete[] titleDirName;
820         delete[] newContainer;
821
822         resp->addULONG(5);          
823         resp->finalise();
824         x.tcp.sendPacket(resp->getPtr(), resp->getLen());
825         return 1;
826       }
827
828       // Ok, the directory container has been made, or it pre-existed.
829
830       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
831       sprintf(newDir, "%s/%s", newContainer, dateDirName);
832
833       log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
834       int renameret = rename(t, newDir);
835       if (renameret == 0)
836       {
837         // Success. Test for remove old dir containter
838         char* oldTitleDir = new char[k+1];
839         memcpy(oldTitleDir, t, k);
840         oldTitleDir[k] = '\0';
841         log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
842         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
843         delete[] oldTitleDir;
844       }
845
846       if (renameret == 0)
847       {
848 #if VDRVERSNUM > 10311
849         // Tell VDR
850         ::Recordings.Update();
851 #endif
852         // Success. Send a different packet from just a ulong
853         resp->addULONG(1); // success
854         resp->addString(newDir); //system depent do not convert
855       }
856       else
857       {
858         resp->addULONG(5);          
859       }
860
861       resp->finalise();
862       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
863
864       delete[] dateDirName;
865       delete[] titleDirName;
866       delete[] newContainer;
867       delete[] newDir;
868     }
869     else
870     {
871       resp->addULONG(3);          
872       resp->finalise();
873       x.tcp.sendPacket(resp->getPtr(), resp->getLen());
874     }
875   }
876   else
877   {
878     resp->addULONG(4);          
879     resp->finalise();
880     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
881   }
882
883   return 1;
884 }
885
886 int VompClientRRProc::processGetChannelsList()
887 {
888   ULONG type;
889
890   char* chanConfig = x.config.getValueString("General", "Channels");
891   int allChans = 1;
892   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
893
894   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
895   {
896 #if VDRVERSNUM < 10300
897     if (!channel->GroupSep() && (!channel->Ca() || allChans))
898 #else
899     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
900 #endif
901     {
902       log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
903
904       if (channel->Vpid()) type = 1;
905 #if VDRVERSNUM < 10300
906       else type = 2;
907 #else
908       else if (channel->Apid(0)) type = 2;
909       else continue;
910 #endif
911
912       resp->addULONG(channel->Number());
913       resp->addULONG(type);      
914       resp->addString(x.charconvsys->Convert(channel->Name()));
915 #if VDRVERSNUM < 10703
916       resp->addULONG(2);
917 #else
918       resp->addULONG(channel->Vtype());
919 #endif      
920     }
921   }
922
923   resp->finalise();
924   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
925
926   log->log("RRProc", Log::DEBUG, "Written channels list");
927
928   return 1;
929 }
930
931 int VompClientRRProc::processGetChannelPids()
932 {
933   ULONG channelNumber = ntohl(*(ULONG*)req->data);
934
935   cChannel* channel = x.channelFromNumber(channelNumber);
936   if (!channel)
937   {
938     resp->addULONG(0);
939     resp->finalise();
940     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
941     return 1;
942   }
943
944   ULONG numApids = 0;
945   ULONG numDpids = 0;
946   ULONG numSpids = 0;
947
948
949 #if VDRVERSNUM < 10300
950
951   log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
952   log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
953
954   if (channel->Apid2())
955     numApids = 2;
956   else if (channel->Apid1())
957     numApids = 1;
958   else
959     numApids = 0;
960
961 #else
962
963   for (const int *Apid = channel->Apids(); *Apid; Apid++)
964   {
965     numApids++;
966   }
967   for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
968   {
969     numDpids++;
970   }
971   for (const int *Spid = channel->Spids(); *Spid; Spid++)
972   {
973     numSpids++;
974   }
975 #endif
976
977
978   // Format of response
979   // vpid
980   // number of apids
981   // {
982   //    apid
983   //    lang string
984   // }
985   // number of dpids
986   // {
987   //    dpid
988   //    lang string
989   // }
990   // number of spids
991   // {
992   //    spid
993   //    lang string
994   // }
995   // tpid
996
997   resp->addULONG(channel->Vpid());
998 #if VDRVERSNUM < 10703
999   resp->addULONG(2);
1000 #else
1001   resp->addULONG(channel->Vtype());
1002 #endif
1003   resp->addULONG(numApids);
1004
1005 #if VDRVERSNUM < 10300
1006   if (numApids >= 1)
1007   {
1008     resp->addULONG(channel->Apid1());
1009     resp->addString("");
1010   }
1011   if (numApids == 2)
1012   {
1013     resp->addULONG(channel->Apid2());
1014     resp->addString("");
1015   }
1016   resp->addULONG(0);
1017   resp->addULONG(0); 
1018 #else
1019   for (ULONG i = 0; i < numApids; i++)
1020   {
1021     resp->addULONG(channel->Apid(i));
1022     resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1023   }
1024   resp->addULONG(numDpids);
1025   for (ULONG i = 0; i < numDpids; i++)
1026   {
1027     resp->addULONG(channel->Dpid(i));
1028     resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1029   }
1030   resp->addULONG(numSpids);
1031   for (ULONG i = 0; i < numSpids; i++)
1032   {
1033     resp->addULONG(channel->Spid(i));
1034     resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1035   }
1036 #endif
1037   resp->addULONG(channel->Tpid());
1038
1039
1040   resp->finalise();
1041   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1042   
1043   log->log("RRProc", Log::DEBUG, "Written channels pids");
1044
1045   return 1;
1046 }
1047
1048 int VompClientRRProc::processStartStreamingChannel()
1049 {
1050   if (x.lp)
1051   {
1052     log->log("RRProc", Log::ERR, "Client called start streaming twice");
1053     return 0;
1054   }
1055   
1056   log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1057   ULONG channelNumber = ntohl(*(ULONG*)req->data);
1058
1059   cChannel* channel = x.channelFromNumber(channelNumber);
1060   if (!channel)
1061   {
1062     resp->addULONG(0);
1063     resp->finalise();
1064     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1065     return 1;
1066   }
1067
1068   // get the priority we should use
1069   int fail = 1;
1070   int priority = x.config.getValueLong("General", "Live priority", &fail);
1071   if (!fail)
1072   {
1073     log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1074   }
1075   else
1076   {
1077     log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1078     priority = 0;
1079   }
1080
1081   // a bit of sanity..
1082   if (priority < 0) priority = 0;
1083   if (priority > 99) priority = 99;
1084
1085   log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1086   x.lp = MVPReceiver::create(channel, priority);
1087
1088   if (!x.lp)
1089   {
1090     resp->addULONG(0);
1091     resp->finalise();
1092     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1093     return 1;
1094   }
1095
1096   if (!x.lp->init(&x.tcp, req->requestID))
1097   {
1098     delete x.lp;
1099     x.lp = NULL;
1100     resp->addULONG(0);
1101     resp->finalise();
1102     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1103     return 1;
1104   }
1105
1106   resp->addULONG(1);
1107   resp->finalise();
1108   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1109   return 1;
1110 }
1111
1112 int VompClientRRProc::processStopStreaming()
1113 {
1114   log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1115   if (x.lp)
1116   {
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