]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
Patch from Dave Pickles for VDR 1.3 tv/radio channel detection
[vompserver.git] / mvpclient.c
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 "mvpclient.h"
22
23 MVPClient::MVPClient(int tsocket)
24  : tcp(tsocket)
25 {
26   cm = NULL;
27   rp = NULL;
28   recordingManager = NULL;
29
30   // Get IP address of client for config module
31
32   char ipa[20];
33   struct sockaddr_in peer;
34   socklen_t salen = sizeof(struct sockaddr);
35   if(getpeername(tsocket, (struct sockaddr*)&peer, &salen) == 0)
36   {
37     strcpy(ipa, inet_ntoa(peer.sin_addr));
38   }
39   else
40   {
41     ipa[0] = '\0';
42     printf("Cannot get peer name!\n");
43   }
44
45   const char* configDir = cPlugin::ConfigDirectory();
46   if (!configDir)
47   {
48     printf("No config dir!\n");
49     return;
50   }
51
52   char configFileName[PATH_MAX];
53   snprintf(configFileName, PATH_MAX - strlen(configDir) - strlen(ipa) - 20, "%s/vomp-%s.conf", configDir, ipa);
54   config.init(configFileName);
55
56   printf("Config file name: %s\n", configFileName);
57
58 //  processGetChannelSchedule(NULL, 0);
59
60 //  printf("here\n");
61 //test();
62
63 }
64
65 MVPClient::~MVPClient()
66 {
67   printf("MVP client destructor\n");
68   if (cm)
69   {
70     cm->Stop();
71     delete cm;
72     cm = NULL;
73   }
74   else if (rp)
75   {
76     writeResumeData();
77
78     delete rp;
79     delete recordingManager;
80     rp = NULL;
81     recordingManager = NULL;
82   }
83
84   cleanConfig();
85 }
86
87 cChannel* MVPClient::channelFromNumber(unsigned long channelNumber)
88 {
89   cChannel* channel = NULL;
90
91   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
92   {
93     if (!channel->GroupSep())
94     {
95       printf("Looking for channel %lu::: number: %i name: '%s'\n", channelNumber, channel->Number(), channel->Name());
96
97       if (channel->Number() == (int)channelNumber)
98       {
99         int vpid = channel->Vpid();
100 #if VDRVERSNUM < 10300
101         int apid1 = channel->Apid1();
102 #else
103         int apid1 = channel->Apid(0);
104 #endif
105         printf("Found channel number %lu, vpid = %i, apid1 = %i\n", channelNumber, vpid, apid1);
106         return channel;
107       }
108     }
109   }
110
111   if (!channel)
112   {
113     printf("Channel not found\n");
114   }
115
116   return channel;
117 }
118
119
120 void MVPClient::writeResumeData()
121 {
122   config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
123 }
124
125 void MVPClient::sendULONG(ULONG ul)
126 {
127   unsigned char sendBuffer[8];
128   *(unsigned long*)&sendBuffer[0] = htonl(4);
129   *(unsigned long*)&sendBuffer[4] = htonl(ul);
130
131   tcp.sendPacket(sendBuffer, 8);
132   printf("written ULONG %lu\n", ul);
133 }
134
135 void MVPClientStartThread(void* arg)
136 {
137   MVPClient* m = (MVPClient*)arg;
138   m->run2();
139   // Nothing external to this class has a reference to it
140   // This is the end of the thread.. so delete m
141   delete m;
142   pthread_exit(NULL);
143 }
144
145 int MVPClient::run()
146 {
147   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
148   printf("MVPClient run success\n");
149   return 1;
150 }
151
152 void MVPClient::run2()
153 {
154   // Thread stuff
155   sigset_t sigset;
156   sigfillset(&sigset);
157   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
158   pthread_detach(runThread);  // Detach
159
160   tcp.disableReadTimeout();
161
162   tcp.setSoKeepTime(3);
163   tcp.setNonBlocking();
164
165   unsigned char* buffer;
166   unsigned char* data;
167   int packetLength;
168   unsigned long opcode;
169
170   while(1)
171   {
172     printf("starting wait\n");
173     buffer = (unsigned char*)tcp.receivePacket();
174     printf("back from wait\n");
175     if (buffer == NULL)
176     {
177       printf("Detected connection closed\n");
178       break;
179     }
180
181     packetLength = tcp.getDataLength() - 4;
182     opcode = ntohl(*(unsigned long*)buffer);
183     data = buffer + 4;
184
185
186     switch(opcode)
187     {
188       case 1:
189         processLogin(data, packetLength);
190         break;
191       case 2:
192         processGetRecordingsList(data, packetLength);
193         break;
194       case 3:
195         processDeleteRecording(data, packetLength);
196         break;
197       case 4:
198         processGetSummary(data, packetLength);
199         break;
200       case 5:
201         processGetChannelsList(data, packetLength);
202         break;
203       case 6:
204         processStartStreamingChannel(data, packetLength);
205         break;
206       case 7:
207         processGetBlock(data, packetLength);
208         break;
209       case 8:
210         processStopStreaming(data, packetLength);
211         break;
212       case 9:
213         processStartStreamingRecording(data, packetLength);
214         break;
215       case 10:
216         processGetChannelSchedule(data, packetLength);
217         break;
218       case 11:
219         processConfigSave(data, packetLength);
220         break;
221       case 12:
222         processConfigLoad(data, packetLength);
223         break;
224     }
225
226     free(buffer);
227   }
228 }
229
230 void MVPClient::processLogin(unsigned char* buffer, int length)
231 {
232   time_t timeNow = time(NULL);
233   struct tm* timeStruct = localtime(&timeNow);
234   int timeOffset = timeStruct->tm_gmtoff;
235
236   unsigned char sendBuffer[12];
237   *(unsigned long*)&sendBuffer[0] = htonl(8);
238   *(unsigned long*)&sendBuffer[4] = htonl(timeNow);
239   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
240
241   tcp.sendPacket(sendBuffer, 12);
242   printf("written login reply\n");
243 }
244
245 void MVPClient::processGetRecordingsList(unsigned char* data, int length)
246 {
247   unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
248   int count = 4; // leave space for the packet length
249   char* point;
250
251
252   int FreeMB;
253   int Percent = VideoDiskSpace(&FreeMB);
254   int Total = (FreeMB / (100 - Percent)) * 100;
255
256   *(unsigned long*)&sendBuffer[count] = htonl(Total);
257   count += sizeof(unsigned long);
258   *(unsigned long*)&sendBuffer[count] = htonl(FreeMB);
259   count += sizeof(unsigned long);
260   *(unsigned long*)&sendBuffer[count] = htonl(Percent);
261   count += sizeof(unsigned long);
262
263
264   cRecordings Recordings;
265   Recordings.Load();
266
267   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
268   {
269     if (count > 49000) break; // just how big is that hard disk?!
270     *(unsigned long*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
271     count += 4;
272
273     point = (char*)recording->Name();
274     strcpy((char*)&sendBuffer[count], point);
275     count += strlen(point) + 1;
276
277     point = (char*)recording->FileName();
278     strcpy((char*)&sendBuffer[count], point);
279     count += strlen(point) + 1;
280   }
281
282   *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
283
284   printf("recorded size as %u\n", ntohl(*(unsigned long*)&sendBuffer[0]));
285
286   tcp.sendPacket(sendBuffer, count);
287   delete[] sendBuffer;
288   printf("Written list\n");
289 }
290
291 void MVPClient::processDeleteRecording(unsigned char* data, int length)
292 {
293   // data is a pointer to the fileName string
294
295   cRecordings Recordings;
296   Recordings.Load(); // probably have to do this
297
298   cRecording* recording = Recordings.GetByName((char*)data);
299
300   printf("recording pointer %p\n", recording);
301
302   if (recording)
303   {
304     printf("deleting recording: %s\n", recording->Name());
305     recording->Delete();
306     sendULONG(1);
307   }
308   else
309   {
310     sendULONG(0);
311   }
312 }
313
314 void MVPClient::processGetSummary(unsigned char* data, int length)
315 {
316   // data is a pointer to the fileName string
317
318   cRecordings Recordings;
319   Recordings.Load(); // probably have to do this
320
321   cRecording *recording = Recordings.GetByName((char*)data);
322
323   printf("recording pointer %p\n", recording);
324
325   if (recording)
326   {
327     unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
328     int count = 4; // leave space for the packet length
329
330     char* point;
331 #if VDRVERSNUM < 10300
332     point = (char*)recording->Summary();
333 #else
334     point = (char*)recording->Info()->ShortText();
335 #endif
336     strcpy((char*)&sendBuffer[count], point);
337     count += strlen(point) + 1;
338     *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
339
340     printf("recorded size as %u\n", ntohl(*(unsigned long*)&sendBuffer[0]));
341
342     tcp.sendPacket(sendBuffer, count);
343     delete[] sendBuffer;
344     printf("Written summary\n");
345
346
347   }
348   else
349   {
350     sendULONG(0);
351   }
352 }
353
354 void MVPClient::processGetChannelsList(unsigned char* data, int length)
355 {
356   unsigned char* sendBuffer = new unsigned char[50000]; // FIXME hope this is enough
357   int count = 4; // leave space for the packet length
358   char* point;
359   unsigned long type;
360
361   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
362   {
363 #if VDRVERSNUM < 10300
364     if (!channel->GroupSep() && !channel->Ca())
365 #else
366     if (!channel->GroupSep() && !channel->Ca(0))
367 #endif
368     {
369       printf("name: '%s'\n", channel->Name());
370
371       if (channel->Vpid()) type = 1;
372 #if VDRVERSNUM < 10300
373       else type = 2;
374 #else
375       else if (channel->Apid(0)) type = 2;
376       else continue;
377 #endif
378
379       if (count > 49000) break;
380       *(unsigned long*)&sendBuffer[count] = htonl(channel->Number());
381       count += 4;
382
383       *(unsigned long*)&sendBuffer[count] = htonl(type);
384       count += 4;
385
386       point = (char*)channel->Name();
387       strcpy((char*)&sendBuffer[count], point);
388       count += strlen(point) + 1;
389     }
390   }
391
392   *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
393
394   printf("recorded size as %u\n", ntohl(*(unsigned long*)&sendBuffer[0]));
395
396   tcp.sendPacket(sendBuffer, count);
397   delete[] sendBuffer;
398   printf("Written channels list\n");
399 }
400
401 void MVPClient::processStartStreamingChannel(unsigned char* data, int length)
402 {
403   printf("length = %i\n", length);
404   unsigned long channelNumber = ntohl(*(unsigned long*)data);
405
406   cChannel* channel = channelFromNumber(channelNumber);
407   if (!channel)
408   {
409     sendULONG(0);
410     return;
411   }
412
413 //  MVPReceiver* m = new MVPReceiver(channel->Vpid(), channel->Apid1());
414   cm = new cMediamvpTransceiver(channel, 0, 0, cDevice::ActualDevice());
415   cDevice::ActualDevice()->AttachReceiver(cm);
416   //cDevice::ActualDevice()->SwitchChannel(channel, false);
417
418   sendULONG(1);
419 }
420
421 void MVPClient::processStopStreaming(unsigned char* data, int length)
422 {
423   printf("STOP STREAMING RECEIVED\n");
424   if (cm)
425   {
426     delete cm;
427     cm = NULL;
428   }
429   else if (rp)
430   {
431     writeResumeData();
432
433     delete rp;
434     delete recordingManager;
435     rp = NULL;
436     recordingManager = NULL;
437   }
438
439   sendULONG(1);
440 }
441
442 void MVPClient::processGetBlock(unsigned char* data, int length)
443 {
444   if (!cm && !rp)
445   {
446     printf("Get block called when no streaming happening!\n");
447     return;
448   }
449
450   ULLONG position = ntohll(*(ULLONG*)data);
451   printf("getblock called for position = %llu\n", position);
452
453   data += sizeof(ULLONG);
454
455   unsigned long amount = ntohl(*(unsigned long*)data);
456   printf("getblock called for length = %lu\n", amount);
457
458   unsigned char sendBuffer[amount + 4];
459   unsigned long amountReceived = 0; // compiler moan.
460   if (cm)
461   {
462     printf("getting from live\n");
463     amountReceived = cm->getBlock(&sendBuffer[4], amount);
464   }
465   else if (rp)
466   {
467     printf("getting from recording\n");
468     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
469   }
470
471   *(unsigned long*)&sendBuffer[0] = htonl(amountReceived);
472   printf("sendpacket go\n");
473   tcp.sendPacket(sendBuffer, amountReceived + 4);
474   printf("written ok %lu\n", amountReceived);
475 }
476
477 void MVPClient::processStartStreamingRecording(unsigned char* data, int length)
478 {
479   // data is a pointer to the fileName string
480
481   recordingManager = new cRecordings;
482   recordingManager->Load();
483
484   cRecording* recording = recordingManager->GetByName((char*)data);
485
486   printf("recording pointer %p\n", recording);
487
488   if (recording)
489   {
490     rp = new RecPlayer(recording);
491
492     unsigned char sendBuffer[12];
493     *(unsigned long*)&sendBuffer[0] = htonl(8);
494     *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
495
496     tcp.sendPacket(sendBuffer, 12);
497     printf("written totalLength\n");
498   }
499   else
500   {
501     delete recordingManager;
502     recordingManager = NULL;
503   }
504 }
505
506 void MVPClient::processGetChannelSchedule(unsigned char* data, int length)
507 {
508   ULONG channelNumber = ntohl(*(ULLONG*)data);
509   printf("get schedule called for channel %lu\n", channelNumber);
510
511   cChannel* channel = channelFromNumber(channelNumber);
512   if (!channel)
513   {
514     unsigned char sendBuffer[4];
515     *(unsigned long*)&sendBuffer[0] = htonl(0);
516     tcp.sendPacket(sendBuffer, 4);
517     printf("written null\n");
518     return;
519   }
520
521 #if VDRVERSNUM < 10300
522   cMutexLock MutexLock;
523   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
524 #else
525   cSchedulesLock MutexLock;
526   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
527 #endif
528   if (!Schedules)
529   {
530     unsigned char sendBuffer[8];
531     *(unsigned long*)&sendBuffer[0] = htonl(4);
532     *(unsigned long*)&sendBuffer[4] = htonl(0);
533     tcp.sendPacket(sendBuffer, 8);
534     printf("written 0\n");
535     return;
536   }
537
538   unsigned char sendBuffer[8];
539   *(unsigned long*)&sendBuffer[0] = htonl(4);
540   *(unsigned long*)&sendBuffer[4] = htonl(1);
541   tcp.sendPacket(sendBuffer, 8);
542   printf("written 1\n");
543
544
545 }
546
547 void MVPClient::testChannelSchedule(unsigned char* data, int length)
548 {
549   FILE* f = fopen("/tmp/s.txt", "w");
550
551 #if VDRVERSNUM < 10300
552   cMutexLock MutexLock;
553   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
554 #else
555   cSchedulesLock MutexLock;
556   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
557 #endif
558   if (!Schedules)
559   {
560     fprintf(f, "Schedules = NULL\n");
561     fclose(f);
562     return;
563   }
564
565   fprintf(f, "Schedules dump:\n");
566   Schedules->Dump(f);
567
568
569   const cSchedule *Schedule;
570   int scheduleNumber = 0;
571
572   tChannelID tchid;
573   cChannel *thisChannel;
574
575 #if VDRVERSNUM < 10300
576   const cEventInfo *event;
577   int eventNumber = 0;
578 #else
579   const cEvent *event;
580 #endif
581
582 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
583 //    Schedule = Schedules->GetSchedule();
584   Schedule = Schedules->First();
585   if (!Schedule)
586   {
587     fprintf(f, "First Schedule = NULL\n");
588     fclose(f);
589     return;
590   }
591
592   while (Schedule)
593   {
594     fprintf(f, "Schedule #%i\n", scheduleNumber);
595     fprintf(f, "-------------\n\n");
596
597 #if VDRVERSNUM < 10300
598     tchid = Schedule->GetChannelID();
599 #else
600     tchid = Schedule->ChannelID();
601 #endif
602 #if VDRVERSNUM < 10300
603     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
604     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
605 #else
606 //  put the count at the end.
607 #endif
608     thisChannel = Channels.GetByChannelID(tchid, true);
609     if (thisChannel)
610     {
611       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
612     }
613     else
614     {
615       fprintf(f, "thisChannel = NULL for tchid\n");
616     }
617
618 #if VDRVERSNUM < 10300
619     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
620     {
621       event = Schedule->GetEventNumber(eventNumber);
622       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
623       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
624       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
625       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
626       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
627       fprintf(f, "Event %u dump:\n", eventNumber);
628       event->Dump(f);
629       fprintf(f, "\n\n");
630     }
631 #else
632 //  This whole section needs rewriting to walk the list.
633     event = Schedule->Events()->First();
634     while (event) {
635       event = Schedule->Events()->Next(event);
636     }
637 #endif
638
639
640     fprintf(f, "\nDump from object:\n");
641     Schedule->Dump(f);
642     fprintf(f, "\nEND\n");
643
644
645
646
647 /*
648   const cEventInfo *GetPresentEvent(void) const;
649   const cEventInfo *GetFollowingEvent(void) const;
650   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
651   const cEventInfo *GetEventAround(time_t tTime) const;
652   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
653
654
655   const unsigned char GetTableID(void) const;
656   const char *GetTimeString(void) const;
657   const char *GetEndTimeString(void) const;
658   const char *GetDate(void) const;
659   bool IsFollowing(void) const;
660   bool IsPresent(void) const;
661   const char *GetExtendedDescription(void) const;
662   const char *GetSubtitle(void) const;
663   const char *GetTitle(void) const;
664   unsigned short GetEventID(void) const;
665   long GetDuration(void) const;
666   time_t GetTime(void) const;
667   tChannelID GetChannelID(void) const;
668   int GetChannelNumber(void) const { return nChannelNumber; }
669   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
670   void Dump(FILE *f, const char *Prefix = "") const;
671
672 */
673
674
675
676
677
678     fprintf(f, "End of current Schedule\n\n\n");
679
680     Schedule = (const cSchedule *)Schedules->Next(Schedule);
681     scheduleNumber++;
682   }
683
684   fclose(f);
685 }
686
687 void MVPClient::processConfigSave(unsigned char* buffer, int length)
688 {
689   char* section = (char*)buffer;
690   char* key = NULL;
691   char* value = NULL;
692
693   for (int k = 0; k < length; k++)
694   {
695     if (buffer[k] == '\0')
696     {
697       if (!key)
698       {
699         key = (char*)&buffer[k+1];
700       }
701       else
702       {
703         value = (char*)&buffer[k+1];
704         break;
705       }
706     }
707   }
708
709   // if the last string (value) doesnt have null terminator, give up
710   if (buffer[length - 1] != '\0') return;
711
712   printf("Config save:\n%s\n%s\n%s\n", section, key, value);
713   if (config.setValueString(section, key, value))
714   {
715     sendULONG(1);
716   }
717   else
718   {
719     sendULONG(0);
720   }
721 }
722
723 void MVPClient::processConfigLoad(unsigned char* buffer, int length)
724 {
725   char* section = (char*)buffer;
726   char* key = NULL;
727
728   for (int k = 0; k < length; k++)
729   {
730     if (buffer[k] == '\0')
731     {
732       key = (char*)&buffer[k+1];
733       break;
734     }
735   }
736
737   char* value = config.getValueString(section, key);
738
739   if (value)
740   {
741     unsigned char sendBuffer[4 + strlen(value) + 1];
742     *(unsigned long*)&sendBuffer[0] = htonl(strlen(value) + 1);
743     strcpy((char*)&sendBuffer[4], value);
744     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
745
746     printf("Written config load packet\n");
747     delete[] value;
748   }
749   else
750   {
751     unsigned char sendBuffer[8];
752     *(unsigned long*)&sendBuffer[0] = htonl(4);
753     *(unsigned long*)&sendBuffer[4] = htonl(0);
754     tcp.sendPacket(sendBuffer, 8);
755
756     printf("Written config load failed packet\n");
757   }
758 }
759
760 void MVPClient::cleanConfig()
761 {
762   printf("Clean config\n");
763
764   cRecordings Recordings;
765   Recordings.Load();
766
767   int numReturns;
768   int length;
769   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
770   char* position = resumes;
771   for(int k = 0; k < numReturns; k++)
772   {
773     printf("EXAMINING: %i %i %p %s\n", k, numReturns, position, position);
774
775     cRecording* recording = Recordings.GetByName(position);
776     if (!recording)
777     {
778       // doesn't exist anymore
779       printf("Found a recording that doesn't exist anymore\n");
780       config.deleteValue("ResumeData", position);
781     }
782     else
783     {
784       printf("This recording still exists\n");
785     }
786
787     position += strlen(position) + 1;
788   }
789
790   delete[] resumes;
791 }
792
793