]> git.vomp.tv Git - vompclient-marten.git/blob - vdr.cc
EPG tweaks, EPG made NTSC compatible
[vompclient-marten.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "vdr.h"
22
23 VDR* VDR::instance = NULL;
24
25 VDR::VDR()
26 {
27   if (instance) return;
28   instance = this;
29   initted = 0;
30   findingServer = 0;
31   tcp = NULL;
32   pthread_mutex_init(&mutex, NULL);
33   packetLength = 0;
34   packetPos = 0;
35   packet = NULL;
36   connected = false;
37 }
38
39 VDR::~VDR()
40 {
41   instance = NULL;
42   if (initted) shutdown();
43 }
44
45 VDR* VDR::getInstance()
46 {
47   return instance;
48 }
49
50 int VDR::init(int tport)
51 {
52   if (initted) return 0;
53   initted = 1;
54   port = tport;
55   logger = Log::getInstance();
56   return 1;
57 }
58
59 int VDR::shutdown()
60 {
61   if (!initted) return 0;
62   initted = 0;
63   disconnect();
64   return 1;
65 }
66
67 void VDR::findServers(vector<char*>& serverIPs)
68 {
69   findingServer = 1;
70   char* message = "VOMP CLIENT";
71   DatagramSocket ds(port);
72
73   int haveAtLeastOne = 0;
74   char* newIP;
75   int retval;
76   int waitType = 1;
77   while(findingServer)
78   {
79     if (waitType == 1)
80     {
81       logger->log("VDR", Log::NOTICE, "Broadcasting for server");
82       ds.send("255.255.255.255", 3024, message, strlen(message));
83     }
84     retval = ds.waitforMessage(waitType);
85
86     if (retval == 2) // we got a reply
87     {
88       if (strcmp(ds.getData(), "VOMP SERVER")) // echo.....
89       {
90         waitType = 2;
91       }
92       else
93       {
94         newIP = new char[16];
95         strcpy(newIP, ds.getFromIPA());
96         serverIPs.push_back(newIP);
97         waitType = 2;
98         haveAtLeastOne = 1;
99       }
100     }
101     else
102     {
103       if (haveAtLeastOne) break;
104       waitType = 1;
105     }
106   }
107 }
108
109 void VDR::cancelFindingServer()
110 {
111   findingServer = 0;
112 }
113
114 void VDR::setServerIP(char* newIP)
115 {
116   strcpy(serverIP, newIP);
117 }
118
119 int VDR::connect()
120 {
121   if (tcp) delete tcp;
122   tcp = new TCP();
123   if (tcp->connectTo(serverIP, 3024))
124   {
125     connected = true;
126     return 1;
127   }
128   else
129   {
130     return 0;
131   }
132 }
133
134 void VDR::disconnect()
135 {
136   if (tcp) delete tcp;
137   tcp = NULL;
138   connected = false;
139   Log::getInstance()->log("VDR", Log::DEBUG, "Disconnect");
140 }
141
142 ///////////////////////////////////////////////////////
143
144 int VDR::getPacket()
145 {
146   packet = (UCHAR*)tcp->receivePacket();
147   if (!packet) return 0;
148   packetLength = tcp->getDataLength();
149   return 1;
150 }
151
152 void VDR::freePacket()
153 {
154   // Must be called if getPacket return 1, except in getBlock
155   packetLength = 0;
156   packetPos = 0;
157   free(packet);
158   packet = NULL;
159 }
160
161 int VDR::serverError()
162 {
163   if ((packetPos == 0) && (packetLength == 4) && !ntohl(*(ULONG*)packet)) return 1;
164   else return 0;
165 }
166
167 char* VDR::extractString()
168 {
169   if (serverError()) return NULL;
170
171   int length = strlen((char*)&packet[packetPos]);
172   if ((packetPos + length) > packetLength) return NULL;
173   char* str = new char[length + 1];
174   strcpy(str, (char*)&packet[packetPos]);
175   packetPos += length + 1;
176   return str;
177 }
178
179 ULONG VDR::extractULONG()
180 {
181   if ((packetPos + sizeof(ULONG)) > packetLength) return 0;
182   ULONG ul = ntohl(*(ULONG*)&packet[packetPos]);
183   packetPos += sizeof(ULONG);
184   return ul;
185 }
186
187 ULLONG VDR::extractULLONG()
188 {
189   if ((packetPos + sizeof(ULLONG)) > packetLength) return 0;
190   ULLONG ull = ntohll(*(ULLONG*)&packet[packetPos]);
191   packetPos += sizeof(ULLONG);
192   return ull;
193 }
194
195 long VDR::extractLONG()
196 {
197   if ((packetPos + sizeof(long)) > packetLength) return 0;
198   long l = ntohl(*(long*)&packet[packetPos]);
199   packetPos += sizeof(long);
200   return l;
201 }
202
203 /////////////////////////////////////////////////////////////////////////////
204
205 int VDR::doLogin()
206 {
207   if (!connected) return 0;
208
209   UCHAR buffer[14];
210
211   *(unsigned long*)&buffer[0] = htonl(10);
212   *(unsigned long*)&buffer[4] = htonl(VDR_LOGIN);
213
214   tcp->getMAC((char*)&buffer[8]);
215
216
217   pthread_mutex_lock(&mutex);
218   int a = tcp->sendPacket(buffer, 14);
219   if (a != 14)
220   {
221     pthread_mutex_unlock(&mutex);
222     return 0;
223   }
224
225   // reply
226
227   if (!getPacket())
228   {
229     pthread_mutex_unlock(&mutex);
230     return 0;
231   }
232
233   ULONG vdrTime = extractULONG();
234   logger->log("VDR", Log::DEBUG, "vdrtime = %lu", vdrTime);
235   long vdrTimeOffset = extractLONG();
236   logger->log("VDR", Log::DEBUG, "offset = %i", vdrTimeOffset);
237
238   freePacket();
239   pthread_mutex_unlock(&mutex);
240
241
242   struct timespec currentTime;
243   currentTime.tv_sec = vdrTime;
244   currentTime.tv_nsec = 0;
245   int b = clock_settime(CLOCK_REALTIME, &currentTime);
246
247   logger->log("VDR", Log::DEBUG, "set clock = %u", b);
248
249   // now make a TZ variable and set it
250   char sign;
251   int hours;
252   int minutes;
253   if (vdrTimeOffset > 0) sign = '-';
254   else sign = '+';
255
256   vdrTimeOffset = abs(vdrTimeOffset);
257
258   hours = (int)vdrTimeOffset / 3600;
259   minutes = vdrTimeOffset % 3600;
260
261   logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);
262
263   minutes = (int)minutes / 60;
264
265   logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);
266
267   char newTZ[30];
268   sprintf(newTZ, "MVP%c%i:%i", sign, hours, minutes);
269   setenv("TZ", newTZ, 1);
270
271   logger->log("VDR", Log::DEBUG, "Timezone data: %s", newTZ);
272
273   return 1;
274 }
275
276 Directory* VDR::getRecordingsList()
277 {
278   if (!connected) return 0;
279
280   UCHAR buffer[8];
281
282   *(unsigned long*)&buffer[0] = htonl(4);
283   *(unsigned long*)&buffer[4] = htonl(VDR_GETRECORDINGLIST);
284
285   pthread_mutex_lock(&mutex);
286   int a = tcp->sendPacket(buffer, 8);
287   if (a != 8)
288   {
289     pthread_mutex_unlock(&mutex);
290     return NULL;
291   }
292
293   // reply
294
295   if (!getPacket())
296   {
297     pthread_mutex_unlock(&mutex);
298     return NULL;
299   }
300
301   Directory* recDir = new Directory();
302   recDir->setName("/");
303   recDir->isRoot = 1;
304
305   Directory::totalSpace = extractULONG();
306   Directory::freeSpace = extractULONG();
307   Directory::usedPercent = extractULONG();
308
309   char* string;
310
311   while (packetPos < packetLength)
312   {
313     Recording* rec = new Recording();
314
315     rec->start = extractULONG();
316
317     string = extractString();
318     rec->setName(string);
319     delete[] string;
320
321     rec->fileName = extractString();
322
323     if(rec->isInDir())
324     {
325       char* dirName = rec->getDirName();
326
327       Directory* d = recDir->getDirByName(dirName);
328       if (!d)
329       {
330         d = new Directory();
331         d->setName(dirName);
332         Log::getInstance()->log("VDR", Log::DEBUG, "Added a new directory = %s", d->name);
333         recDir->dirList.push_back(d);
334       }
335
336       d->recList.push_back(rec);
337     }
338     else
339     {
340       recDir->recList.push_back(rec);
341     }
342
343     Log::getInstance()->log("VDR", Log::DEBUG, "%s", rec->fileName);
344   }
345
346   freePacket();
347   pthread_mutex_unlock(&mutex);
348
349   return recDir;
350 }
351
352 int VDR::deleteRecording(char* fileName)
353 {
354   if (!connected) return 0;
355
356   unsigned long totalLength = 8 + strlen(fileName) + 1;
357   UCHAR buffer[totalLength];
358
359   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
360   *(unsigned long*)&buffer[4] = htonl(VDR_DELETERECORDING);
361   strcpy((char*)&buffer[8], fileName);
362
363   pthread_mutex_lock(&mutex);
364   unsigned int a = tcp->sendPacket(buffer, totalLength);
365   if (a != totalLength)
366   {
367     pthread_mutex_unlock(&mutex);
368     return 0;
369   }
370
371   if (!getPacket())
372   {
373     pthread_mutex_unlock(&mutex);
374     return 0;
375   }
376
377   int toReturn = (int)extractULONG();
378   freePacket();
379   pthread_mutex_unlock(&mutex);
380
381   return toReturn;
382 }
383
384 char* VDR::getRecordingSummary(char* fileName)
385 {
386   if (!connected) return 0;
387
388   unsigned long totalLength = 8 + strlen(fileName) + 1;
389   UCHAR buffer[totalLength];
390
391   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
392   *(unsigned long*)&buffer[4] = htonl(VDR_GETSUMMARY);
393   strcpy((char*)&buffer[8], fileName);
394
395   pthread_mutex_lock(&mutex);
396   unsigned int a = tcp->sendPacket(buffer, totalLength);
397   if (a != totalLength)
398   {
399     pthread_mutex_unlock(&mutex);
400     return NULL;
401   }
402
403   if (!getPacket())
404   {
405     pthread_mutex_unlock(&mutex);
406     return NULL;
407   }
408   char* toReturn = extractString();
409   freePacket();
410   pthread_mutex_unlock(&mutex);
411
412   return toReturn;
413 }
414
415 ChannelList* VDR::getChannelsList(ULONG type)
416 {
417   if (!connected) return 0;
418
419   UCHAR buffer[8];
420
421   *(unsigned long*)&buffer[0] = htonl(4);
422   *(unsigned long*)&buffer[4] = htonl(VDR_GETCHANNELLIST);
423
424   pthread_mutex_lock(&mutex);
425   int a = tcp->sendPacket(buffer, 8);
426   if (a != 8)
427   {
428     pthread_mutex_unlock(&mutex);
429     return NULL;
430   }
431
432   // reply
433
434   if (!getPacket())
435   {
436     pthread_mutex_unlock(&mutex);
437     return NULL;
438   }
439
440   ChannelList* chanList = new ChannelList();
441
442   while (packetPos < packetLength)
443   {
444     Channel* chan = new Channel();
445     chan->number = extractULONG();
446     chan->type = extractULONG();
447     chan->name = extractString();
448
449     if (chan->type == type)
450     {
451       chanList->push_back(chan);
452       Log::getInstance()->log("VDR", Log::DEBUG, "Have added a channel to list. %lu %lu %s", chan->number, chan->type, chan->name);
453     }
454     else
455     {
456       delete chan;
457     }
458   }
459
460   freePacket();
461   pthread_mutex_unlock(&mutex);
462
463   return chanList;
464 }
465
466 int VDR::streamChannel(ULONG number)
467 {
468   if (!connected) return 0;
469
470   UCHAR buffer[12];
471
472   *(unsigned long*)&buffer[0] = htonl(8);
473   *(unsigned long*)&buffer[4] = htonl(VDR_STREAMCHANNEL);
474   *(unsigned long*)&buffer[8] = htonl(number);
475
476   pthread_mutex_lock(&mutex);
477   int a = tcp->sendPacket(buffer, 12);
478
479   if (a != 12)
480   {
481     pthread_mutex_unlock(&mutex);
482     return 0;
483   }
484
485   if (!getPacket())
486   {
487     pthread_mutex_unlock(&mutex);
488     return 0;
489   }
490
491   int toReturn = (int)extractULONG();
492   freePacket();
493   pthread_mutex_unlock(&mutex);
494
495   return toReturn;
496 }
497
498 int VDR::stopStreaming()
499 {
500   if (!connected) return 0;
501
502   UCHAR buffer[8];
503
504   *(unsigned long*)&buffer[0] = htonl(4);
505   *(unsigned long*)&buffer[4] = htonl(VDR_STOPSTREAMING);
506
507   pthread_mutex_lock(&mutex);
508   int a = tcp->sendPacket(buffer, 8);
509
510   if (a != 8)
511   {
512     pthread_mutex_unlock(&mutex);
513     return 0;
514   }
515
516 printf("sent request\n");
517
518   if (!getPacket())
519   {
520     pthread_mutex_unlock(&mutex);
521     return 0;
522   }
523 printf("got reply\n");
524
525
526   int toReturn = (int)extractULONG();
527   freePacket();
528   pthread_mutex_unlock(&mutex);
529
530   return toReturn;
531 }
532
533 UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived)
534 {
535   if (!connected) return 0;
536
537   UCHAR buffer[20];
538
539   *(unsigned long*)&buffer[0] = htonl(16);
540   *(unsigned long*)&buffer[4] = htonl(VDR_GETBLOCK);
541   *(ULLONG*)&buffer[8]        = htonll(position);
542   *(unsigned long*)&buffer[16] = htonl(maxAmount);
543
544   pthread_mutex_lock(&mutex);
545   int a = tcp->sendPacket(buffer, 20);
546   if (a != 20)
547   {
548     pthread_mutex_unlock(&mutex);
549     return NULL;
550   }
551
552   if (!getPacket())
553   {
554     pthread_mutex_unlock(&mutex);
555     return NULL;
556   }
557
558   UCHAR* toReturn = packet;
559   *amountReceived = packetLength;
560   // Manually clean up instead of running freePacket to keep the block
561   packet = NULL;
562   packetLength = 0;
563   packetPos = 0;
564   pthread_mutex_unlock(&mutex);
565
566   return toReturn;
567 }
568
569 ULLONG VDR::streamRecording(Recording* rec)
570 {
571   if (!connected) return 0;
572
573   unsigned long totalLength = 8 + strlen(rec->fileName) + 1;
574   UCHAR buffer[totalLength];
575
576   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
577   *(unsigned long*)&buffer[4] = htonl(VDR_STREAMRECORDING);
578   strcpy((char*)&buffer[8], rec->fileName);
579
580   pthread_mutex_lock(&mutex);
581   unsigned int a = tcp->sendPacket(buffer, totalLength);
582   if (a != totalLength)
583   {
584     pthread_mutex_unlock(&mutex);
585     return 0;
586   }
587
588   if (!getPacket())
589   {
590     pthread_mutex_unlock(&mutex);
591     return 0;
592   }
593
594   ULLONG recordingLength = extractULLONG();
595   freePacket();
596   pthread_mutex_unlock(&mutex);
597
598   Log::getInstance()->log("VDR", Log::DEBUG, "VDR said length is: %llu", recordingLength);
599
600   return recordingLength;
601 }
602
603 ULLONG VDR::rescanRecording()
604 {
605   if (!connected) return 0;
606
607   unsigned long totalLength = 8;
608   UCHAR buffer[totalLength];
609
610   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
611   *(unsigned long*)&buffer[4] = htonl(VDR_RESCANRECORDING);
612
613   pthread_mutex_lock(&mutex);
614   unsigned int a = tcp->sendPacket(buffer, totalLength);
615   if (a != totalLength)
616   {
617     pthread_mutex_unlock(&mutex);
618     return 0;
619   }
620
621   if (!getPacket())
622   {
623     pthread_mutex_unlock(&mutex);
624     return 0;
625   }
626
627   ULLONG recordingLength = extractULLONG();
628   freePacket();
629   pthread_mutex_unlock(&mutex);
630
631   Log::getInstance()->log("VDR", Log::DEBUG, "VDR said length is: %llu", recordingLength);
632
633   return recordingLength;
634 }
635
636 EventList* VDR::getChannelSchedule(ULONG number)
637 {
638   time_t now;
639   time(&now);
640   return getChannelSchedule(number, now, 24 * 60 * 60);
641 }
642
643 EventList* VDR::getChannelSchedule(ULONG number, time_t start, ULONG duration)
644 {
645 // retrieve event list (vector of events) from vdr within filter window. duration is in seconds
646   if (!connected) return 0;
647
648   UCHAR buffer[20];
649
650   *(unsigned long*)&buffer[0] = htonl(16);
651   *(unsigned long*)&buffer[4] = htonl(VDR_GETCHANNELSCHEDULE);
652   *(unsigned long*)&buffer[8] = htonl(number);
653   *(unsigned long*)&buffer[12] = htonl(start);
654   *(unsigned long*)&buffer[16] = htonl(duration);
655
656   pthread_mutex_lock(&mutex);
657   int a = tcp->sendPacket(buffer, 20);
658
659   if (a != 20)
660   {
661     pthread_mutex_unlock(&mutex);
662     return NULL;
663   }
664
665   if (!getPacket())
666   {
667     pthread_mutex_unlock(&mutex);
668     return NULL;
669   }
670
671   if (serverError())
672   {
673     freePacket();
674     pthread_mutex_unlock(&mutex);
675     return NULL;
676   }
677
678
679   EventList* eventList = new EventList();
680
681   while (packetPos < packetLength)
682   {
683     Event* event = new Event();
684     event->id = extractULONG();
685     event->time = extractULONG();
686     event->duration = extractULONG();
687     event->title = extractString();
688     event->subtitle = extractString();
689     event->description = extractString();
690     eventList->push_back(event);
691 //    eventList->next();
692   }
693
694   freePacket();
695   pthread_mutex_unlock(&mutex);
696
697   Log::getInstance()->log("VDR", Log::DEBUG, "Success got to end of getChannelSchedule");
698
699
700   // debug
701 /*
702   Log* l = Log::getInstance();
703
704
705   l->log("VDR", Log::DEBUG, "datalength = %i count = %i", dataLength, count);
706
707   Event* currentEvent;
708   for(eventList->reset(); !eventList->eol(); eventList->next())
709   {
710     currentEvent = (Event*)eventList->getCurrent();
711     l->log("VDR", Log::DEBUG, "%lu %lu %lu %s %s %s", currentEvent->id, currentEvent->time, currentEvent->duration, currentEvent->title, currentEvent->subtitle, currentEvent->description);
712   }
713 */
714
715   return eventList;
716 }
717
718 ULLONG VDR::getResumePoint(char* fileName)
719 {
720   if (!connected) return 0;
721
722   char* resumeString = configLoad("ResumeData", fileName);
723   if (!resumeString) return 0;
724
725   ULLONG toReturn = strtoull(resumeString, NULL, 10);
726   delete[] resumeString;
727   return toReturn;
728 }
729
730 int VDR::configSave(char* section, char* key, const char* value)
731 {
732   if (!connected) return 0;
733
734   ULONG totalLength = 8 + strlen(section) + strlen(key) + strlen(value) + 3; // 8 for headers, 3 for nulls
735   UCHAR buffer[totalLength];
736
737   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
738   *(unsigned long*)&buffer[4] = htonl(VDR_CONFIGSAVE);
739
740   int position = 8;
741   strcpy((char*)&buffer[position], section);
742   position += strlen(section) + 1;
743   strcpy((char*)&buffer[position], key);
744   position += strlen(key) + 1;
745   strcpy((char*)&buffer[position], value);
746
747   pthread_mutex_lock(&mutex);
748   unsigned int a = tcp->sendPacket(buffer, totalLength);
749   if (a != totalLength)
750   {
751     pthread_mutex_unlock(&mutex);
752     return 0;
753   }
754
755   if (!getPacket())
756   {
757     pthread_mutex_unlock(&mutex);
758     return 0;
759   }
760
761   int toReturn = (int)extractULONG();
762   freePacket();
763   pthread_mutex_unlock(&mutex);
764
765   return toReturn;
766 }
767
768 char* VDR::configLoad(char* section, char* key)
769 {
770   if (!connected) return 0;
771
772   ULONG totalLength = 8 + strlen(section) + strlen(key) + 2; // 8 for headers, 2 for nulls
773   UCHAR buffer[totalLength];
774
775   *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
776   *(unsigned long*)&buffer[4] = htonl(VDR_CONFIGLOAD);
777
778   int position = 8;
779   strcpy((char*)&buffer[position], section);
780   position += strlen(section) + 1;
781   strcpy((char*)&buffer[position], key);
782
783   pthread_mutex_lock(&mutex);
784   unsigned int a = tcp->sendPacket(buffer, totalLength);
785   if (a != totalLength)
786   {
787     pthread_mutex_unlock(&mutex);
788     return NULL;
789   }
790
791   if (!getPacket())
792   {
793     pthread_mutex_unlock(&mutex);
794     return NULL;
795   }
796   char* toReturn = extractString();
797   freePacket();
798   pthread_mutex_unlock(&mutex);
799
800   return toReturn;
801 }