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