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