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