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