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