]> git.vomp.tv Git - vompclient.git/blob - vdr.cc
A bit more on client side timeouts
[vompclient.git] / vdr.cc
1 /*
2     Copyright 2004-2005 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 "vdr.h"
22
23 #include "recman.h"
24 #include "tcp.h"
25 #include "log.h"
26 #include "recinfo.h"
27 #include "dsock.h"
28 #include "channel.h"
29 #include "event.h"
30 #include "wol.h"
31 #include "vdrrequestpacket.h"
32 #include "vdrresponsepacket.h"
33 #include "command.h"
34
35 VDR* VDR::instance = NULL;
36
37 VDR::VDR()
38 {
39   if (instance) return;
40   instance = this;
41   initted = 0;
42   findingServer = 0;
43   tcp = NULL;
44   connected = false;
45   maxChannelNumber = 0;
46   channelNumberWidth = 1;
47   TEMP_SINGLE_VDR_PR = NULL;
48 }
49
50 VDR::~VDR()
51 {
52   instance = NULL;
53   if (initted) shutdown();
54 }
55
56 VDR* VDR::getInstance()
57 {
58   return instance;
59 }
60
61 int VDR::init(int tport)
62 {
63   if (initted) return 0;
64   initted = 1;
65   port = tport;
66   logger = Log::getInstance();
67   return 1;
68 }
69
70 int VDR::shutdown()
71 {
72   if (!initted) return 0;
73   initted = 0;
74   disconnect();
75   return 1;
76 }
77
78 void VDR::findServers(vector<VDRServer>& servers)
79 {
80   Wol* wol = Wol::getInstance();
81   findingServer = 1;
82   char* message = "VOMP";
83
84   DatagramSocket ds(port);
85   int haveAtLeastOne = 0;
86   int retval;
87   int waitType = 1;
88   bool firstloop = true;
89   while(findingServer)
90   {
91     if (waitType == 1)
92     {
93       ds.shutdown();
94       ds.init();
95       logger->log("VDR", Log::NOTICE, "Broadcasting for server");
96       ds.send("255.255.255.255", 3024, message, strlen(message));
97       if(!firstloop) wol->doWakeUp();
98     }
99     retval = ds.waitforMessage(waitType);
100
101     if (retval == 2) // we got a reply
102     {
103       if (!strcmp(ds.getData(), "VOMP")) // echo.....
104       {
105         waitType = 2;
106       }
107       else
108       {
109         VDRServer newServer;
110         newServer.ip = new char[16];
111         strcpy(newServer.ip, ds.getFromIPA());
112
113         if (ds.getDataLength() == 0)
114         {
115           newServer.name = new char[1];
116           newServer.name[0] = '\0';
117         }
118         else
119         {
120           newServer.name = new char[strlen(ds.getData())+1];
121           strcpy(newServer.name, ds.getData());
122         }
123
124         servers.push_back(newServer);
125         waitType = 2;
126         haveAtLeastOne = 1;
127       }
128     }
129     else
130     {
131       if (haveAtLeastOne) break;
132       waitType = 1;
133       firstloop = false;
134     }
135   }
136   sort(servers.begin(), servers.end(), ServerSorter());
137 }
138
139 void VDR::cancelFindingServer()
140 {
141   findingServer = 0;
142 }
143
144 void VDR::setServerIP(char* newIP)
145 {
146   strcpy(serverIP, newIP);
147 }
148
149 int VDR::connect()
150 {
151   maxChannelNumber = 0;
152   channelNumberWidth = 1;
153
154   if (tcp) delete tcp;
155   tcp = new TCP();
156   if (tcp->connectTo(serverIP, 3024))
157   {
158     connected = true;
159     threadStart();
160     return 1;
161   }
162   else
163   {
164     return 0;
165   }
166 }
167
168 void VDR::disconnect()
169 {
170   threadCancel();
171   if (tcp) delete tcp;
172   tcp = NULL;
173   connected = false;
174   logger->log("VDR", Log::DEBUG, "Disconnect");
175 }
176
177 void VDR::setReceiveWindow(size_t size)
178 {
179   if (connected) tcp->setReceiveWindow(size);
180 }
181
182 ///////////////////////////////////////////////////////
183
184 void VDR::threadMethod()
185 {
186   logger->log("VDR", Log::DEBUG, "VDR RUN");  
187
188   threadSetKillable(); // FIXME - change this to deal with the EDRs
189   
190   ULONG channelID;
191   
192   ULONG requestID;
193   ULONG userDataLength;
194   UCHAR* userData;
195
196   ULONG streamID;
197   ULONG flag;
198
199   VDR_ResponsePacket* vresp;
200   
201   int timeoutCount = 0;
202   ULONG lastKAsent = 0;
203   ULONG timeNow = 0;
204   
205   while(1) 
206   {  
207     if (!tcp->readData((UCHAR*)&channelID, sizeof(ULONG))) // 2s timeout atm
208     {
209       ++timeoutCount;
210     
211       // Error or timeout.
212
213       /* This is the simple version of timeout system until I work out how to make it better
214          e.g. different timeout lengths for different requests, a decent keepalive system
215          running in parallel etc etc.
216         
217          Ignore the stream channel
218          Start with lastPacketReceived = 0
219          When a packet comes in, set lastPacketReceived = time(NULL)
220          loop
221          {
222            if last packet is over 20s ago
223            {
224              if lastKAsent is less than 20s ago, continue
225              
226              if lastKAsent = 0, send a KA, continue
227              
228              if lastKAsent is over 20s ago, connection is dead, kill it
229            }
230          }         
231       */
232
233       logger->log("VDR", Log::DEBUG, "Net read timeout %i", timeoutCount);
234
235       if (!tcp->isConnected()) { connectionDied(); return; } // return to stop this thread
236       
237       timeoutCount = 0; // disable it for now
238       
239       if (timeoutCount >= 10) //20s
240       {
241         timeNow = (ULONG)time(NULL);
242       
243         if (lastKAsent > (timeNow - 20)) continue;
244         
245         if (lastKAsent == 0)
246         {
247           if (!sendKA(timeNow)) { connectionDied(); return; }
248           lastKAsent = timeNow;
249           continue;
250         }
251         
252         if (lastKAsent <= (timeNow - 20)) { connectionDied(); return; }
253       }
254      
255       continue;      
256     }
257     
258     // Data was read
259     
260     timeoutCount = 0;
261     
262     channelID = ntohl(channelID);
263    
264     if (channelID == CHANNEL_REQUEST_RESPONSE)
265     {
266       if (!tcp->readData((UCHAR*)&requestID, sizeof(ULONG))) break;
267       requestID = ntohl(requestID);
268       if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break;
269       userDataLength = ntohl(userDataLength);
270       if (userDataLength > 5000000) break; // how big can these packets get?
271       userData = NULL;
272       if (userDataLength > 0)
273       {
274         userData = (UCHAR*)malloc(userDataLength);
275         if (!userData) break;
276         if (!tcp->readData(userData, userDataLength)) break;
277       }
278
279       vresp = new VDR_ResponsePacket();  
280       vresp->setResponse(requestID, userData, userDataLength);
281       logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
282
283       if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
284       {
285         // If edFindAndCall returns true, edr was called and vresp was handed off.
286         // else, delete vresp here.
287         delete vresp;
288       }
289     }
290     else if (channelID == CHANNEL_STREAM)
291     {
292       if (!tcp->readData((UCHAR*)&streamID, sizeof(ULONG))) break;
293       streamID = ntohl(streamID);
294
295       if (!tcp->readData((UCHAR*)&flag, sizeof(ULONG))) break;
296       flag = ntohl(flag);
297
298       if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; 
299       userDataLength = ntohl(userDataLength);
300       userData = NULL;
301       if (userDataLength > 0)
302       {
303         userData = (UCHAR*)malloc(userDataLength);
304         if (!userData) break;
305         if (!tcp->readData(userData, userDataLength)) break;
306       }
307
308       vresp = new VDR_ResponsePacket();    
309       vresp->setStream(streamID, flag, userData, userDataLength);
310 //      logger->log("VDR", Log::DEBUG, "Rxd a stream packet, streamID=%lu, flag=%lu, len=%lu", streamID, flag, userDataLength);
311
312       if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
313       {
314         // If edFindAndCall returns true, edr was called and vresp was handed off.
315         // else, delete vresp here.
316         delete vresp;
317       }
318     }
319     else if (channelID == CHANNEL_KEEPALIVE)
320     {
321       ULONG KAreply = 0;
322       if (!tcp->readData((UCHAR*)&KAreply, sizeof(ULONG))) break;
323       KAreply = (ULONG)ntohl(KAreply);
324       if (KAreply == lastKAsent) // successful KA response
325       {
326         lastKAsent = 0;
327         timeoutCount = 0;
328       }
329     }
330     else
331     {
332       logger->log("VDR", Log::ERR, "Rxd a response packet on channel %lu !!", channelID);
333       break;
334     }
335
336     // Who deletes vresp?
337     // If RR, the individual protocol functions must delete vresp.
338     // If stream, the data and length is taken out in ed_cb_call and vresp is deleted there.
339   }
340 }
341
342 void VDR::connectionDied()
343 {
344   // Called from within threadMethod to do cleanup if it decides the connection has died
345
346   connected = false; // though actually it could still be connected until someone calls vdr->disconnect
347
348   // Need to wake up any waiting channel 1 request-response threads
349   // Normally this is done by a packet coming in with channelid and requestid      
350   // Instead, go through the list and for each channel 1 edr, make an empty vresp
351   // An empty vresp will have userData == NULL, which means vresp->noResponse() == true
352
353   // If it's a stream receiver, generate a stream packet with flag == connection_lost
354
355   edLock();
356   VDR_PacketReceiver* vdrpr;
357   VDR_ResponsePacket* vresp;
358   while(receivers.size())
359   {
360     vdrpr = (VDR_PacketReceiver*) *(receivers.begin());
361     if (vdrpr->receiverChannel == CHANNEL_REQUEST_RESPONSE)
362     {
363       vresp = new VDR_ResponsePacket();
364       vresp->setResponse(vdrpr->requestSerialNumber, NULL, 0);
365       logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for request serial %lu", vdrpr->requestSerialNumber);
366       edUnlock();
367       if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
368       {
369         // If edFindAndCall returns true, edr was called and vresp was handed off.
370         // else, delete vresp here.
371         logger->log("VDR", Log::ERR, "Timeouts: no waiting thread found for request serial %lu !!!", vdrpr->requestSerialNumber);
372         delete vresp;
373       }
374       edLock();
375     }
376     else if (vdrpr->receiverChannel == CHANNEL_STREAM)
377     {
378       vresp = new VDR_ResponsePacket();
379       vresp->setStream(vdrpr->streamID, 2 /* connection-lost flag */ , NULL, 0);
380       logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for streamid %lu", vdrpr->streamID);
381       edUnlock();
382       if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
383       {
384         // If edFindAndCall returns true, edr was called and vresp was handed off.
385         // else, delete vresp here.
386         logger->log("VDR", Log::ERR, "Timeouts: no waiting stream receiver found for streamid %lu !!!", vdrpr->streamID);
387         delete vresp;
388       }
389       edLock();  
390     }
391   }
392   edUnlock();
393   // Ok, all event receviers should be dealt with. just in case there weren't any, inform command
394   
395   Command::getInstance()->connectionLost();
396 }
397
398 bool VDR::ed_cb_find(EDReceiver* edr, void* userTag)
399 {
400   // edr is a VDR_PacketReceiver object made in VDR::RequestResponse
401   // userTag is a VDR_ResponsePacket made in threadMethod
402
403   VDR_PacketReceiver* vdrpr = (VDR_PacketReceiver*)edr;
404   VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag;
405   
406   // Is vresp for vdrpr ?
407   
408   ULONG packetChannel = vresp->getChannelID();
409   if (vdrpr->receiverChannel != packetChannel) return false;
410
411   if (packetChannel == CHANNEL_REQUEST_RESPONSE)
412   {
413     if (vdrpr->requestSerialNumber == vresp->getRequestID()) return true;
414   }
415   else if (packetChannel == CHANNEL_STREAM)
416   {
417     if (vdrpr->streamID == vresp->getStreamID()) return true;
418   }
419  
420   return false;
421 }
422
423 VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
424 {
425   logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode());
426
427   if (!connected)
428   {
429     VDR_ResponsePacket* vresp = new VDR_ResponsePacket();
430     return vresp; // "no-response" return
431   }
432
433   // ED make new VDR and register
434   // make a VDR_PacketReceiver
435   // - init with serial number of request packet
436
437   VDR_PacketReceiver vdrpr;
438 //  vdrpr.requestTime = time(NULL);
439   vdrpr.receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE;
440   vdrpr.requestSerialNumber = vrp->getSerial();
441   edRegister(&vdrpr);
442   
443   edLock();  
444   if ((ULONG)tcp->sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen())
445   {
446     edUnlock();
447     edUnregister(&vdrpr);
448     VDR_ResponsePacket* vresp = new VDR_ResponsePacket();
449     return vresp; // "no-response" return
450   }
451
452   // Sleep and block this thread. The sleep unlocks the mutex
453   logger->log("VDR", Log::DEBUG, "RR sleep");
454   edSleepThisReceiver(&vdrpr);
455   logger->log("VDR", Log::DEBUG, "RR unsleep");
456     
457   // Woken because a response packet has arrived, mutex will be locked
458   
459   edUnlock();
460   return vdrpr.save_vresp;
461 }
462
463 bool VDR::sendKA(ULONG timeStamp)
464 {
465   char buffer[8];
466   *(ULONG*)&buffer[0] = htonl(CHANNEL_KEEPALIVE);
467   *(ULONG*)&buffer[4] = htonl(timeStamp);
468   if ((ULONG)tcp->sendData(buffer, 8) != 8) return false;
469   return true;
470 }
471
472 /////////////////////////////////////////////////////////////////////////////
473
474 // Here VDR takes a break for the VDR_PacketReceiver helper class
475
476 bool VDR_PacketReceiver::call(void* userTag)
477 {
478   if (receiverChannel == VDR::CHANNEL_REQUEST_RESPONSE)
479   {
480     // It's a RR. Save vresp and, signal the waiting thread and return.
481     // VDR::RequestResponse will be blocking waiting for this to happen.
482     // That function has a pointer to this object and can read save_vresp.
483     save_vresp = (VDR_ResponsePacket*)userTag;
484     return true; // Signals ED to remove edr from receivers and wake up edr thread
485   }
486   
487   if (receiverChannel == VDR::CHANNEL_STREAM)
488   {
489     // It's a stream packet.
490     VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag;
491     streamReceiver->streamReceive(vresp->getFlag(), vresp->getUserData(), vresp->getUserDataLength());
492     delete vresp;
493     return false;
494   }
495
496   abort(); // unknown receiverChannel, should not happen
497 }
498
499 /////////////////////////////////////////////////////////////////////////////
500
501 int VDR::doLogin()
502 {
503   VDR_RequestPacket vrp;
504   if (!vrp.init(VDR_LOGIN, true, 6)) return 0;
505
506   char* mactemp[6];
507   tcp->getMAC((char*)mactemp);
508   if (!vrp.copyin((UCHAR*)mactemp, 6)) return 0;
509
510   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
511   if (vresp->noResponse()) { delete vresp; return 0; }
512
513   ULONG vdrTime = vresp->extractULONG();
514   logger->log("VDR", Log::DEBUG, "vdrtime = %lu", vdrTime);
515   long vdrTimeOffset = vresp->extractLONG();
516   logger->log("VDR", Log::DEBUG, "offset = %i", vdrTimeOffset);
517
518   delete vresp;
519
520   // Set the time and zone on the MVP
521
522 #ifndef WIN32
523   struct timespec currentTime;
524   currentTime.tv_sec = vdrTime;
525   currentTime.tv_nsec = 0;
526   int b = clock_settime(CLOCK_REALTIME, &currentTime);
527
528   logger->log("VDR", Log::DEBUG, "set clock = %u", b);
529
530   // now make a TZ variable and set it
531   char sign;
532   int hours;
533   int minutes;
534   if (vdrTimeOffset > 0) sign = '-';
535   else sign = '+';
536
537   vdrTimeOffset = abs(vdrTimeOffset);
538
539   hours = (int)vdrTimeOffset / 3600;
540   minutes = vdrTimeOffset % 3600;
541
542   logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);
543
544   minutes = (int)minutes / 60;
545
546   logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);
547
548   char newTZ[30];
549   sprintf(newTZ, "MVP%c%i:%i", sign, hours, minutes);
550   setenv("TZ", newTZ, 1);
551
552   logger->log("VDR", Log::DEBUG, "Timezone data: %s", newTZ);
553 #endif
554
555   return 1;
556 }
557
558 bool VDR::getRecordingsList(RecMan* recman)
559 {
560   VDR_RequestPacket vrp;
561   if (!vrp.init(VDR_GETRECORDINGLIST, true, 0)) return false;
562
563   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
564   if (vresp->noResponse()) { delete vresp; return false; }
565   
566   ULONG totalSpace = vresp->extractULONG();
567   ULONG freeSpace = vresp->extractULONG();
568   ULONG percent = vresp->extractULONG();
569   recman->setStats(totalSpace, freeSpace, percent);
570
571   ULONG start;
572   char* name;
573   char* fileName;
574
575   while (!vresp->end())
576   {
577     start = vresp->extractULONG();
578     name = vresp->extractString();
579     fileName = vresp->extractString();
580
581     recman->addEntry(start, name, fileName);
582
583     delete[] name;
584     delete[] fileName;
585   }
586
587   delete vresp;
588
589   return true;
590 }
591
592 int VDR::deleteRecording(char* fileName)
593 {
594   VDR_RequestPacket vrp;
595   if (!vrp.init(VDR_DELETERECORDING, true, strlen(fileName) + 1)) return 0;
596   if (!vrp.addString(fileName)) return 0;
597   
598   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
599   if (vresp->noResponse()) { delete vresp; return 0; }
600   
601   int toReturn = (int)vresp->extractULONG();
602   delete vresp;
603
604   return toReturn;
605 }
606
607 char* VDR::moveRecording(char* fileName, char* newPath)
608 {
609   VDR_RequestPacket vrp;
610   if (!vrp.init(VDR_MOVERECORDING, true, strlen(fileName) + 1 + strlen(newPath) + 1)) return NULL;
611   if (!vrp.addString(fileName)) return NULL;
612   if (!vrp.addString(newPath)) return NULL;
613   
614   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
615   if (vresp->noResponse()) { delete vresp; return NULL; }
616   
617   char* toReturn = NULL;
618   int success = (int)vresp->extractULONG();
619   if (success == 1)
620   {
621     toReturn = vresp->extractString();
622   }
623
624   delete vresp;
625
626   return toReturn;
627 }
628
629 ChannelList* VDR::getChannelsList(ULONG type)
630 {
631   VDR_RequestPacket vrp;
632   if (!vrp.init(VDR_GETCHANNELLIST, true, 0)) return NULL;
633
634   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
635   if (vresp->noResponse()) { delete vresp; return NULL; }
636   
637   ChannelList* chanList = new ChannelList();
638
639   while (!vresp->end())
640   {
641     Channel* chan = new Channel();
642     chan->number = vresp->extractULONG();
643     chan->type = vresp->extractULONG();
644     chan->name = vresp->extractString();
645
646     if (chan->type == type)
647     {
648       chanList->push_back(chan);
649       logger->log("VDR", Log::DEBUG, "Have added a channel to list. %lu %lu %s", chan->number, chan->type, chan->name);
650       if (chan->number > maxChannelNumber) maxChannelNumber = chan->number;
651     }
652     else
653     {
654       delete chan;
655     }
656   }
657
658   delete vresp;
659
660   if (maxChannelNumber > 99999)
661     channelNumberWidth = 6;
662   else if (maxChannelNumber > 9999)
663     channelNumberWidth = 5;
664   else if (maxChannelNumber > 999)
665     channelNumberWidth = 4;
666   else if (maxChannelNumber > 99)
667     channelNumberWidth = 3;
668   else if (maxChannelNumber > 9)
669     channelNumberWidth = 2;
670   else
671     channelNumberWidth = 1;
672
673   return chanList;
674 }
675
676
677 int VDR::streamChannel(ULONG number)
678 {
679   // FIXME radio
680   return 0;
681 }
682
683 int VDR::streamChannel(ULONG number, StreamReceiver* tstreamReceiver)
684 {
685   VDR_RequestPacket vrp;
686   if (!vrp.init(VDR_STREAMCHANNEL, true, sizeof(ULONG))) return 0;
687   if (!vrp.addULONG(number)) return 0;
688   
689   
690   VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver();
691   vdrpr->receiverChannel = VDR::CHANNEL_STREAM;
692   vdrpr->streamID = vrp.getSerial();
693   vdrpr->streamReceiver = tstreamReceiver;
694   edRegister(vdrpr);
695   TEMP_SINGLE_VDR_PR = vdrpr;
696   
697   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
698   if (vresp->noResponse())
699   {
700     delete vresp;
701     edUnregister(vdrpr);
702     delete vdrpr;
703     return 0;
704   }
705   
706   int toReturn = (int)vresp->extractULONG();
707   logger->log("VDR", Log::DEBUG, "VDR said %lu to start streaming request", toReturn);
708   delete vresp;
709
710   return toReturn;
711 }
712
713 int VDR::stopStreaming()
714 {
715   VDR_RequestPacket vrp;
716   if (!vrp.init(VDR_STOPSTREAMING, true, 0)) return 0;
717
718   if (TEMP_SINGLE_VDR_PR) // this block only needs to be done if it was a live stream
719                           // TEMP_SINGLE_VDR_PR will not be set unless we are streaming a channel
720   {
721     edUnregister(TEMP_SINGLE_VDR_PR);
722     delete TEMP_SINGLE_VDR_PR;
723     TEMP_SINGLE_VDR_PR = NULL;
724   }
725
726   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
727   if (vresp->noResponse()) { delete vresp; return 0; }
728   
729   int toReturn = (int)vresp->extractULONG();
730   delete vresp;
731
732   return toReturn;
733 }
734
735 UCHAR* VDR::getImageBlock(ULONG position, UINT maxAmount, UINT* amountReceived)
736 {
737   return getBlock(position, maxAmount, amountReceived, VDR_GETIMAGEBLOCK);
738 }
739
740 UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived)
741 {
742   return getBlock(position, maxAmount, amountReceived, VDR_GETBLOCK);
743 }
744
745 UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived, ULONG cmd)
746 {
747   VDR_RequestPacket vrp;
748   if (!vrp.init(cmd, true, sizeof(ULLONG) + sizeof(ULONG))) return NULL;
749   if (!vrp.addULLONG(position)) return NULL;
750   if (!vrp.addULONG(maxAmount)) return NULL;
751
752   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
753   if (vresp->noResponse()) { delete vresp; return NULL; }
754
755   if (vresp->serverError())
756   {
757     logger->log("VDR", Log::DEBUG, "Detected getblock 0");
758     delete vresp;
759     return NULL;
760   }
761
762   // Special handling for getblock
763   UCHAR* toReturn = vresp->getUserData();
764   *amountReceived = vresp->getUserDataLength();
765   
766   delete vresp;
767   
768   return toReturn;
769 }
770
771 ULLONG VDR::streamRecording(char* fileName, ULONG* totalFrames)
772 {
773   VDR_RequestPacket vrp;
774   if (!vrp.init(VDR_STREAMRECORDING, true, strlen(fileName) + 1)) return 0;
775   if (!vrp.addString(fileName)) return 0;
776
777   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
778   if (vresp->noResponse()) { delete vresp; return 0; }
779   
780   ULLONG lengthBytes = vresp->extractULLONG();
781   ULONG lengthFrames = vresp->extractULONG();
782   delete vresp;
783
784   logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu", lengthBytes, lengthFrames);
785
786   *totalFrames = lengthFrames;
787   return lengthBytes;
788 }
789
790 ULLONG VDR::positionFromFrameNumber(ULONG frameNumber)
791 {
792   VDR_RequestPacket vrp;
793   if (!vrp.init(VDR_POSFROMFRAME, true, sizeof(ULONG))) return 0;
794   if (!vrp.addULONG(frameNumber)) return 0;
795
796   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
797   if (vresp->noResponse()) { delete vresp; return 0; }
798   
799   ULLONG position = vresp->extractULLONG();
800   delete vresp;
801   
802   logger->log("VDR", Log::DEBUG, "VDR said new position is: %llu", position);
803
804   return position;
805 }
806
807 ULONG VDR::frameNumberFromPosition(ULLONG position)
808 {
809   VDR_RequestPacket vrp;
810   if (!vrp.init(VDR_FRAMEFROMPOS, true, sizeof(ULLONG))) return 0;
811   if (!vrp.addULLONG(position)) return 0;
812
813   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
814   if (vresp->noResponse()) { delete vresp; return 0; }
815   
816   ULONG framenumber = vresp->extractULONG();
817   delete vresp;
818   
819   logger->log("VDR", Log::DEBUG, "VDR said new framenumber is: %u", framenumber);
820
821   return framenumber;
822 }
823
824 bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength)
825 {
826   VDR_RequestPacket vrp;
827   if (!vrp.init(VDR_GETNEXTIFRAME, true, sizeof(ULONG)*2)) return false;
828   if (!vrp.addULONG(frameNumber)) return false;
829   if (!vrp.addULONG(direction)) return false;
830
831   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
832   if (vresp->noResponse()) { delete vresp; return false; }
833   
834   if (vresp->serverError())
835   {
836     logger->log("VDR", Log::DEBUG, "Detected getNextIFrame error");
837     delete vresp;
838     return false;
839   }
840
841   *rfilePosition = vresp->extractULLONG();
842   *rframeNumber = vresp->extractULONG();
843   *rframeLength = vresp->extractULONG();
844
845   delete vresp;
846
847 //  logger->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength);
848
849   return true;
850 }
851
852 EventList* VDR::getChannelSchedule(ULONG number)
853 {
854   time_t now;
855   time(&now);
856   return getChannelSchedule(number, now, 24 * 60 * 60);
857 }
858
859 EventList* VDR::getChannelSchedule(ULONG number, time_t start, ULONG duration)
860 {
861 // retrieve event list (vector of events) from vdr within filter window. duration is in seconds
862
863   VDR_RequestPacket vrp;
864   if (!vrp.init(VDR_GETCHANNELSCHEDULE, true, sizeof(ULONG)*3)) return NULL;
865   if (!vrp.addULONG(number)) return NULL;
866   if (!vrp.addULONG(start)) return NULL;
867   if (!vrp.addULONG(duration)) return NULL;
868
869   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
870   if (vresp->noResponse()) { delete vresp; return NULL; }
871   
872   // received a ulong(0) - schedules error in the plugin
873   if (vresp->serverError())
874   {
875     delete vresp;
876     return NULL;
877   }
878
879   EventList* eventList = new EventList();
880
881   while (!vresp->end())
882   {
883     Event* event = new Event();
884     event->id = vresp->extractULONG();
885     event->time = vresp->extractULONG();
886     event->duration = vresp->extractULONG();
887     event->title = vresp->extractString();
888     event->subtitle = vresp->extractString();
889     event->description = vresp->extractString();
890     eventList->push_back(event);
891   }
892
893   delete vresp;
894
895   logger->log("VDR", Log::DEBUG, "Success got to end of getChannelSchedule");
896   return eventList;
897 }
898
899 int VDR::configSave(const char* section, const char* key, const char* value)
900 {
901   VDR_RequestPacket vrp;
902   if (!vrp.init(VDR_CONFIGSAVE, false, 0)) return 0;
903   if (!vrp.addString(section)) return 0;
904   if (!vrp.addString(key)) return 0;
905   if (!vrp.addString(value)) return 0;
906
907   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
908   if (vresp->noResponse()) { delete vresp; return 0; }
909   
910   int toReturn = (int)vresp->extractULONG();
911   delete vresp;
912
913   return toReturn;
914 }
915
916 char* VDR::configLoad(const char* section, const char* key)
917 {
918   VDR_RequestPacket vrp;
919   if (!vrp.init(VDR_CONFIGLOAD, false, 0)) return NULL;
920   if (!vrp.addString(section)) return NULL;
921   if (!vrp.addString(key)) return NULL;
922
923   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
924   if (vresp->noResponse()) { delete vresp; return NULL; }
925   
926   char* toReturn = vresp->extractString();
927   delete vresp;
928
929   return toReturn;
930 }
931
932 RecTimerList* VDR::getRecTimersList()
933 {
934   VDR_RequestPacket vrp;
935   if (!vrp.init(VDR_GETTIMERS, true, 0)) return NULL;
936
937   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
938   if (vresp->noResponse()) { delete vresp; return NULL; }
939
940   RecTimerList* recTimerList = new RecTimerList();
941
942   ULONG numTimers = vresp->extractULONG();
943   if (numTimers > 0)
944   {
945     RecTimer* newRecTimer;
946     char* tempString;
947
948     while (!vresp->end())
949     {
950       newRecTimer = new RecTimer();
951       newRecTimer->active = vresp->extractULONG();
952       newRecTimer->recording = vresp->extractULONG();
953       newRecTimer->pending = vresp->extractULONG();
954       newRecTimer->priority = vresp->extractULONG();
955       newRecTimer->lifeTime = vresp->extractULONG();
956       newRecTimer->channelNumber = vresp->extractULONG();
957       newRecTimer->startTime = vresp->extractULONG();
958       newRecTimer->stopTime = vresp->extractULONG();
959       newRecTimer->day = vresp->extractULONG();
960       newRecTimer->weekDays = vresp->extractULONG();
961
962       tempString = vresp->extractString();
963       newRecTimer->setFile(tempString);
964       delete[] tempString;
965
966       recTimerList->push_back(newRecTimer);
967       logger->log("VDR", Log::DEBUG, "TL: %lu %lu %lu %lu %lu %lu %lu %lu %s",
968         newRecTimer->active, newRecTimer->recording, newRecTimer->pending, newRecTimer->priority, newRecTimer->lifeTime,
969         newRecTimer->channelNumber, newRecTimer->startTime, newRecTimer->stopTime, newRecTimer->getFile());
970     }
971   }
972
973   delete vresp;
974
975   sort(recTimerList->begin(), recTimerList->end(), RecTimerSorter());
976
977   return recTimerList;
978 }
979
980 ULONG VDR::setEventTimer(char* timerString)
981 {
982   VDR_RequestPacket vrp;
983   if (!vrp.init(VDR_SETTIMER, true, strlen(timerString) + 1)) return 0;
984   if (!vrp.addString(timerString)) return 0;
985
986   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
987   if (vresp->noResponse()) { delete vresp; return 0; }
988   
989   ULONG toReturn = vresp->extractULONG();
990   delete vresp;
991
992   return toReturn;
993 }
994
995 RecInfo* VDR::getRecInfo(char* fileName)
996 {
997   VDR_RequestPacket vrp;
998   if (!vrp.init(VDR_GETRECINFO, true, strlen(fileName) + 1)) return NULL;
999   if (!vrp.addString(fileName)) return NULL;
1000   
1001   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1002   if (vresp->noResponse()) { delete vresp; return NULL; }
1003   
1004   if (vresp->serverError())
1005   {
1006     logger->log("VDR", Log::DEBUG, "Could not get rec info");
1007     delete vresp;
1008     return NULL;
1009   }
1010
1011   RecInfo* recInfo = new RecInfo();
1012
1013   recInfo->timerStart = vresp->extractULONG();
1014   recInfo->timerEnd = vresp->extractULONG();
1015   recInfo->resumePoint = vresp->extractULONG();
1016   recInfo->summary = vresp->extractString();
1017
1018   ULONG numComponents = vresp->extractULONG();
1019   if (numComponents)
1020   {
1021     recInfo->setNumComponents(numComponents);
1022     for (ULONG i = 0; i < numComponents; i++)
1023     {
1024       recInfo->streams[i] = vresp->extractUCHAR();
1025       recInfo->types[i] = vresp->extractUCHAR();
1026       recInfo->languages[i] = vresp->extractString();
1027       recInfo->descriptions[i] = vresp->extractString();
1028     }
1029   }
1030
1031   recInfo->print();
1032
1033   delete vresp;
1034   return recInfo;
1035 }
1036
1037 // FIXME obselete
1038 ULLONG VDR::rescanRecording(ULONG* totalFrames)
1039 {
1040   VDR_RequestPacket vrp;
1041   if (!vrp.init(VDR_RESCANRECORDING, true, 0)) return 0;
1042
1043   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1044   if (vresp->noResponse()) { delete vresp; return 0; }
1045   
1046   ULLONG lengthBytes = vresp->extractULLONG();
1047   ULONG lengthFrames = vresp->extractULONG();
1048   delete vresp;
1049   
1050   logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu", lengthBytes, lengthFrames);
1051
1052   *totalFrames = lengthFrames;
1053   return lengthBytes;
1054 }
1055
1056 MarkList* VDR::getMarks(char* fileName)
1057 {
1058   VDR_RequestPacket vrp;
1059   if (!vrp.init(VDR_GETMARKS, true, strlen(fileName) + 1)) return NULL;
1060   if (!vrp.addString(fileName)) return NULL;
1061
1062   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1063   if (vresp->noResponse()) { delete vresp; return NULL; }
1064   
1065   if (vresp->serverError())
1066   {
1067     delete vresp;
1068     return NULL;
1069   }
1070
1071   MarkList* markList = new MarkList();
1072
1073   while (!vresp->end())
1074   {
1075     Mark* mark = new Mark();
1076     mark->pos = vresp->extractULONG();
1077
1078     markList->push_back(mark);
1079     logger->log("VDR", Log::DEBUG, "Have added a mark to list. %lu", mark->pos);
1080   }
1081
1082   delete vresp;
1083   
1084   return markList;
1085 }
1086
1087 void VDR::getChannelPids(Channel* channel)
1088 {
1089   VDR_RequestPacket vrp;
1090   if (!vrp.init(VDR_GETCHANNELPIDS, true, sizeof(ULONG))) return ;
1091   if (!vrp.addULONG(channel->number)) return ;
1092
1093   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1094   if (vresp->noResponse()) { delete vresp; return ; }
1095   
1096   // Format of response
1097   // vpid
1098   // number of apids
1099   // {
1100   //    apid
1101   //    lang string
1102   // }
1103
1104   channel->vpid = vresp->extractULONG();
1105   channel->numAPids = vresp->extractULONG();
1106
1107   for (ULONG i = 0; i < channel->numAPids; i++)
1108   {
1109     apid newapid;
1110     newapid.pid = vresp->extractULONG();
1111     newapid.name = vresp->extractString();
1112     channel->apids.push_back(newapid);
1113   }
1114
1115   channel->numDPids = vresp->extractULONG();
1116
1117   for (ULONG i = 0; i < channel->numDPids; i++)
1118   {
1119     apid newdpid;
1120     newdpid.pid = vresp->extractULONG();
1121     newdpid.name = vresp->extractString();
1122     channel->dpids.push_back(newdpid);
1123   }
1124
1125   channel->numSPids = vresp->extractULONG();
1126
1127   for (ULONG i = 0; i < channel->numSPids; i++)
1128   {
1129     apid newspid;
1130     newspid.pid = vresp->extractULONG();
1131     newspid.name = vresp->extractString();
1132     channel->spids.push_back(newspid);
1133   }
1134   channel->tpid = vresp->extractULONG();
1135
1136   delete vresp;
1137   
1138   return ;
1139 }
1140
1141 /**
1142   * media List Request:
1143   * 4 flags (currently unused)
1144   * n dirname
1145   * n+1 0
1146   * Media List response:
1147   * 4 length
1148   * 4 VDR_
1149   * 4 numentries
1150   * per entry:
1151   * 4 media type
1152   * 4 time stamp
1153   * 4 flags
1154   * 4 strlen (incl. 0 Byte)
1155   * string
1156   * 0
1157 */
1158 MediaList* VDR::getMediaList(const char* parent,int mediaType)
1159 {
1160   logger->log("VDR", Log::DEBUG, "getMediaList %s,type=%d", (parent?parent:"NULL"), mediaType);
1161
1162   VDR_RequestPacket vrp;
1163   if (!vrp.init(VDR_GETMEDIALIST, false, 0)) return NULL;
1164   if (!vrp.addULONG(0)) return NULL; // unused flags
1165
1166   //name
1167   if (parent) {
1168     if (!vrp.addString(parent)) return NULL;
1169   }
1170     
1171   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1172   if (vresp->noResponse()) { delete vresp; return NULL; }
1173   
1174   if (vresp->serverError())
1175   {
1176     delete vresp;
1177     return NULL;
1178   }
1179   
1180   if (vresp->getUserDataLength() < 12)
1181   {
1182     logger->log("VDR", Log::ERR, "receiveMediaList packet too short, expected 12, got %d", vresp->getUserDataLength());
1183     delete vresp;
1184     return NULL;
1185   }
1186
1187   MediaList* mediaList = new MediaList();
1188   ULONG code=0;
1189   code = vresp->extractULONG();
1190   ULONG numEntries = vresp->extractULONG();
1191   Log::getInstance()->log("VDR", Log::DEBUG, "receiveMediaList with %d entries",numEntries);
1192   while (!vresp->end() && numEntries >0)
1193   {
1194     Media* m = new Media();
1195     ULONG mtype = vresp->extractULONG();
1196     ULONG mtime=vresp->extractULONG();
1197     ULONG flags=0;
1198     flags=vresp->extractULONG();
1199     ULONG stsize=vresp->extractULONG();
1200     char * name=vresp->extractString();
1201     if (! name || stsize != (strlen(name)+1)) {
1202       Log::getInstance()->log("VDR", Log::ERR, "receiveMediaList invalid packet entry, read size %d, strlen %d", stsize, strlen(name)+1);
1203       delete m;
1204       delete mediaList;
1205       delete vresp;
1206       return NULL;
1207       }
1208     //ignore . and .. entries
1209     if (strcmp(name,".") == 0 || strcmp(name,"..")==0) {
1210   delete m;
1211   continue;
1212     }
1213     m->setFileName(name);
1214     m->setTime(mtime);
1215     m->setMediaType(mtype);
1216     mediaList->push_back(m);
1217     Log::getInstance()->log("VDR", Log::DEBUG, "Have added a media to list. %s, type=%d, time=%d", name,mtype,mtime);
1218     numEntries--;
1219   }
1220
1221   delete vresp;
1222   return mediaList;
1223 }
1224
1225 /**
1226   * get image Request:
1227   * 4 flags (currently unused)
1228   * 4 x size
1229   * 4 y size
1230   * n filename
1231   * n+1 0
1232   * get image response:
1233   * 4 length
1234   * 4 VDR_GETIMAGE
1235   * 4 len of image
1236 */
1237 ULONG VDR::loadImage(const char* fileName, ULONG x, ULONG y)
1238 {
1239   VDR_RequestPacket vrp;
1240   if (!vrp.init(VDR_GETIMAGE, false, 0)) return 0;
1241   if (!vrp.addULONG(0)) return 0; // unused flags
1242   if (!vrp.addULONG(x)) return 0;
1243   if (!vrp.addULONG(y)) return 0;
1244   if (!vrp.addString(fileName)) return 0;
1245
1246   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1247   if (vresp->noResponse()) { delete vresp; return 0; }
1248
1249   ULONG cmd = vresp->extractULONG();
1250   ULONG lengthBytes = vresp->extractULONG();
1251   delete vresp;
1252
1253   Log::getInstance()->log("VDR", Log::DEBUG, "getImage %s: cmd=%lu len=%lu", fileName, cmd, lengthBytes);
1254   return lengthBytes;
1255 }
1256
1257 int VDR::deleteTimer(RecTimer* delTimer)
1258 {
1259   Log::getInstance()->log("VDR", Log::DEBUG, "Delete timer called");
1260   
1261   VDR_RequestPacket vrp;
1262   if (!vrp.init(VDR_DELETETIMER, false, 0)) return 0;
1263   if (!vrp.addULONG(delTimer->channelNumber)) return 0;
1264   if (!vrp.addULONG(delTimer->weekDays)) return 0;    
1265   if (!vrp.addULONG(delTimer->day)) return 0;
1266   if (!vrp.addULONG(delTimer->startTime)) return 0;  
1267   if (!vrp.addULONG(delTimer->stopTime)) return 0; 
1268    
1269   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1270   if (vresp->noResponse()) { delete vresp; return 0; }
1271   
1272   int toReturn = (int)vresp->extractULONG();
1273   delete vresp;
1274
1275   return toReturn;
1276 }
1277
1278 I18n::lang_code_list VDR::getLanguageList()
1279 {
1280   I18n::lang_code_list CodeList;
1281   CodeList["en"] = "English"; // Default entry
1282   VDR_RequestPacket vrp;
1283   if (!vrp.init(VDR_GETLANGUAGELIST, false, 0)) return CodeList;
1284   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1285   if (vresp->noResponse() || vresp->end())
1286   {
1287     delete vresp;
1288     return CodeList;
1289   }
1290   CodeList.clear();
1291   while (!vresp->end())
1292   {
1293     char* c_code = vresp->extractString();
1294     char* c_name = vresp->extractString();
1295     string code = c_code;
1296     string name = c_name;
1297     CodeList[code] = name;
1298     delete[] c_code;
1299     delete[] c_name;
1300   }
1301   delete vresp;
1302   return CodeList;
1303 }
1304
1305 int VDR::getLanguageContent(const std::string code, I18n::trans_table& texts)
1306 {
1307   VDR_RequestPacket vrp;
1308   if (!vrp.init(VDR_GETLANGUAGECONTENT, false, 0)) return 0;
1309   if (!vrp.addString(code.c_str())) return 0;
1310   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
1311   if (vresp->noResponse()) { delete vresp; return 0; }
1312   texts.clear();
1313   while (!vresp->end())
1314   {
1315     char* c_key = vresp->extractString();
1316     char* c_text = vresp->extractString();
1317     string key = c_key;
1318     string text = c_text;
1319     texts[key] = text;
1320     delete[] c_key;
1321     delete[] c_text;
1322   }
1323   delete vresp;
1324   return 1;
1325 }