2 Copyright 2004-2005 Chris Tallon
4 This file is part of VOMP.
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.
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.
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
21 #include "mvpclient.h"
23 MVPClient::MVPClient(int tsocket)
28 recordingManager = NULL;
29 log = Log::getInstance();
31 // Get IP address of client for config module
34 struct sockaddr_in peer;
35 socklen_t salen = sizeof(struct sockaddr);
36 if(getpeername(tsocket, (struct sockaddr*)&peer, &salen) == 0)
38 strcpy(ipa, inet_ntoa(peer.sin_addr));
43 log->log("Client", Log::DEBUG, "Cannot get peer name!");
46 const char* configDir = cPlugin::ConfigDirectory();
49 log->log("Client", Log::DEBUG, "No config dir!");
53 char configFileName[PATH_MAX];
54 snprintf(configFileName, PATH_MAX - strlen(configDir) - strlen(ipa) - 20, "%s/vomp-%s.conf", configDir, ipa);
55 config.init(configFileName);
57 log->log("Client", Log::DEBUG, "Config file name: %s", configFileName);
59 // processGetChannelSchedule(NULL, 0);
65 MVPClient::~MVPClient()
67 log->log("Client", Log::DEBUG, "MVP client destructor");
78 delete recordingManager;
80 recordingManager = NULL;
86 cChannel* MVPClient::channelFromNumber(unsigned long channelNumber)
88 cChannel* channel = NULL;
90 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
92 if (!channel->GroupSep())
94 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
96 if (channel->Number() == (int)channelNumber)
98 int vpid = channel->Vpid();
99 #if VDRVERSNUM < 10300
100 int apid1 = channel->Apid1();
102 int apid1 = channel->Apid(0);
104 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
112 log->log("Client", Log::DEBUG, "Channel not found");
119 void MVPClient::writeResumeData()
121 config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
124 void MVPClient::sendULONG(ULONG ul)
126 unsigned char sendBuffer[8];
127 *(unsigned long*)&sendBuffer[0] = htonl(4);
128 *(unsigned long*)&sendBuffer[4] = htonl(ul);
130 tcp.sendPacket(sendBuffer, 8);
131 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
134 void MVPClientStartThread(void* arg)
136 MVPClient* m = (MVPClient*)arg;
138 // Nothing external to this class has a reference to it
139 // This is the end of the thread.. so delete m
146 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
147 log->log("Client", Log::DEBUG, "MVPClient run success");
151 void MVPClient::run2()
156 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
157 pthread_detach(runThread); // Detach
159 tcp.disableReadTimeout();
161 tcp.setSoKeepTime(3);
162 tcp.setNonBlocking();
164 unsigned char* buffer;
167 unsigned long opcode;
171 log->log("Client", Log::DEBUG, "Waiting");
172 buffer = (unsigned char*)tcp.receivePacket();
173 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
176 log->log("Client", Log::DEBUG, "Detected connection closed");
180 packetLength = tcp.getDataLength() - 4;
181 opcode = ntohl(*(unsigned long*)buffer);
188 processLogin(data, packetLength);
191 processGetRecordingsList(data, packetLength);
194 processDeleteRecording(data, packetLength);
197 processGetSummary(data, packetLength);
200 processGetChannelsList(data, packetLength);
203 processStartStreamingChannel(data, packetLength);
206 processGetBlock(data, packetLength);
209 processStopStreaming(data, packetLength);
212 processStartStreamingRecording(data, packetLength);
215 processGetChannelSchedule(data, packetLength);
218 processConfigSave(data, packetLength);
221 processConfigLoad(data, packetLength);
224 processReScanRecording(data, packetLength);
232 void MVPClient::processLogin(unsigned char* buffer, int length)
234 time_t timeNow = time(NULL);
235 struct tm* timeStruct = localtime(&timeNow);
236 int timeOffset = timeStruct->tm_gmtoff;
238 unsigned char sendBuffer[12];
239 *(unsigned long*)&sendBuffer[0] = htonl(8);
240 *(unsigned long*)&sendBuffer[4] = htonl(timeNow);
241 *(signed int*)&sendBuffer[8] = htonl(timeOffset);
243 tcp.sendPacket(sendBuffer, 12);
244 log->log("Client", Log::DEBUG, "written login reply");
247 void MVPClient::processGetRecordingsList(unsigned char* data, int length)
249 unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
250 int count = 4; // leave space for the packet length
255 int Percent = VideoDiskSpace(&FreeMB);
256 int Total = (FreeMB / (100 - Percent)) * 100;
258 *(unsigned long*)&sendBuffer[count] = htonl(Total);
259 count += sizeof(unsigned long);
260 *(unsigned long*)&sendBuffer[count] = htonl(FreeMB);
261 count += sizeof(unsigned long);
262 *(unsigned long*)&sendBuffer[count] = htonl(Percent);
263 count += sizeof(unsigned long);
266 cRecordings Recordings;
269 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
271 if (count > 49000) break; // just how big is that hard disk?!
272 *(unsigned long*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
275 point = (char*)recording->Name();
276 strcpy((char*)&sendBuffer[count], point);
277 count += strlen(point) + 1;
279 point = (char*)recording->FileName();
280 strcpy((char*)&sendBuffer[count], point);
281 count += strlen(point) + 1;
284 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
286 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
288 tcp.sendPacket(sendBuffer, count);
290 log->log("Client", Log::DEBUG, "Written list");
293 void MVPClient::processDeleteRecording(unsigned char* data, int length)
295 // data is a pointer to the fileName string
297 cRecordings Recordings;
298 Recordings.Load(); // probably have to do this
300 cRecording* recording = Recordings.GetByName((char*)data);
302 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
306 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
316 void MVPClient::processGetSummary(unsigned char* data, int length)
318 // data is a pointer to the fileName string
320 cRecordings Recordings;
321 Recordings.Load(); // probably have to do this
323 cRecording *recording = Recordings.GetByName((char*)data);
325 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
329 unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
330 int count = 4; // leave space for the packet length
333 #if VDRVERSNUM < 10300
334 point = (char*)recording->Summary();
336 const cRecordingInfo *Info = recording->Info();
337 point = (char*)Info->ShortText();
338 log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
341 point = (char*)Info->Description();
342 log->log("Client", Log::DEBUG, "description pointer %p", point);
345 strcpy((char*)&sendBuffer[count], point);
346 count += strlen(point) + 1;
347 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
349 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
351 tcp.sendPacket(sendBuffer, count);
353 log->log("Client", Log::DEBUG, "Written summary");
363 void MVPClient::processGetChannelsList(unsigned char* data, int length)
365 unsigned char* sendBuffer = new unsigned char[50000]; // FIXME hope this is enough
366 int count = 4; // leave space for the packet length
370 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
373 // Re-enable later with some client side option
374 //#if VDRVERSNUM < 10300
375 // if (!channel->GroupSep() && !channel->Ca())
377 // if (!channel->GroupSep() && !channel->Ca(0))
381 if (!channel->GroupSep())
383 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
385 if (channel->Vpid()) type = 1;
386 #if VDRVERSNUM < 10300
389 else if (channel->Apid(0)) type = 2;
393 if (count > 49000) break;
394 *(unsigned long*)&sendBuffer[count] = htonl(channel->Number());
397 *(unsigned long*)&sendBuffer[count] = htonl(type);
400 point = (char*)channel->Name();
401 strcpy((char*)&sendBuffer[count], point);
402 count += strlen(point) + 1;
406 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
408 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
410 tcp.sendPacket(sendBuffer, count);
412 log->log("Client", Log::DEBUG, "Written channels list");
415 void MVPClient::processStartStreamingChannel(unsigned char* data, int length)
417 log->log("Client", Log::DEBUG, "length = %i", length);
418 unsigned long channelNumber = ntohl(*(unsigned long*)data);
420 cChannel* channel = channelFromNumber(channelNumber);
427 lp = MVPReceiver::create(channel);
446 void MVPClient::processStopStreaming(unsigned char* data, int length)
448 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
459 delete recordingManager;
461 recordingManager = NULL;
467 void MVPClient::processGetBlock(unsigned char* data, int length)
471 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
475 ULLONG position = ntohll(*(ULLONG*)data);
476 data += sizeof(ULLONG);
477 unsigned long amount = ntohl(*(unsigned long*)data);
479 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
481 unsigned char sendBuffer[amount + 4];
482 unsigned long amountReceived = 0; // compiler moan.
485 log->log("Client", Log::DEBUG, "getting from live");
486 amountReceived = lp->getBlock(&sendBuffer[4], amount);
490 // vdr has possibly disconnected the receiver
491 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
498 log->log("Client", Log::DEBUG, "getting from recording");
499 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
502 *(unsigned long*)&sendBuffer[0] = htonl(amountReceived);
503 tcp.sendPacket(sendBuffer, amountReceived + 4);
504 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
507 void MVPClient::processStartStreamingRecording(unsigned char* data, int length)
509 // data is a pointer to the fileName string
511 recordingManager = new cRecordings;
512 recordingManager->Load();
514 cRecording* recording = recordingManager->GetByName((char*)data);
516 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
520 rp = new RecPlayer(recording);
522 unsigned char sendBuffer[12];
523 *(unsigned long*)&sendBuffer[0] = htonl(8);
524 *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
526 tcp.sendPacket(sendBuffer, 12);
527 log->log("Client", Log::DEBUG, "written totalLength");
531 delete recordingManager;
532 recordingManager = NULL;
536 void MVPClient::processReScanRecording(unsigned char* data, int length)
542 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
547 retval = rp->getTotalLength();
550 unsigned char sendBuffer[12];
551 *(unsigned long*)&sendBuffer[0] = htonl(8);
552 *(ULLONG*)&sendBuffer[4] = htonll(retval);
554 tcp.sendPacket(sendBuffer, 12);
555 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
558 void MVPClient::processGetChannelSchedule(unsigned char* data, int length)
560 ULONG channelNumber = ntohl(*(ULLONG*)data);
561 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
563 cChannel* channel = channelFromNumber(channelNumber);
566 unsigned char sendBuffer[4];
567 *(unsigned long*)&sendBuffer[0] = htonl(0);
568 tcp.sendPacket(sendBuffer, 4);
569 log->log("Client", Log::DEBUG, "written null");
573 #if VDRVERSNUM < 10300
574 cMutexLock MutexLock;
575 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
577 cSchedulesLock MutexLock;
578 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
582 unsigned char sendBuffer[8];
583 *(unsigned long*)&sendBuffer[0] = htonl(4);
584 *(unsigned long*)&sendBuffer[4] = htonl(0);
585 tcp.sendPacket(sendBuffer, 8);
586 log->log("Client", Log::DEBUG, "written 0");
590 unsigned char sendBuffer[8];
591 *(unsigned long*)&sendBuffer[0] = htonl(4);
592 *(unsigned long*)&sendBuffer[4] = htonl(1);
593 tcp.sendPacket(sendBuffer, 8);
594 log->log("Client", Log::DEBUG, "written 1");
599 void MVPClient::testChannelSchedule(unsigned char* data, int length)
601 FILE* f = fopen("/tmp/s.txt", "w");
603 #if VDRVERSNUM < 10300
604 cMutexLock MutexLock;
605 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
607 cSchedulesLock MutexLock;
608 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
612 fprintf(f, "Schedules = NULL\n");
617 fprintf(f, "Schedules dump:\n");
621 const cSchedule *Schedule;
622 int scheduleNumber = 0;
625 cChannel *thisChannel;
627 #if VDRVERSNUM < 10300
628 const cEventInfo *event;
634 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
635 // Schedule = Schedules->GetSchedule();
636 Schedule = Schedules->First();
639 fprintf(f, "First Schedule = NULL\n");
646 fprintf(f, "Schedule #%i\n", scheduleNumber);
647 fprintf(f, "-------------\n\n");
649 #if VDRVERSNUM < 10300
650 tchid = Schedule->GetChannelID();
652 tchid = Schedule->ChannelID();
654 #if VDRVERSNUM < 10300
655 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
656 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
658 // put the count at the end.
660 thisChannel = Channels.GetByChannelID(tchid, true);
663 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
667 fprintf(f, "thisChannel = NULL for tchid\n");
670 #if VDRVERSNUM < 10300
671 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
673 event = Schedule->GetEventNumber(eventNumber);
674 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
675 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
676 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
677 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
678 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
679 fprintf(f, "Event %u dump:\n", eventNumber);
684 // This whole section needs rewriting to walk the list.
685 event = Schedule->Events()->First();
687 event = Schedule->Events()->Next(event);
692 fprintf(f, "\nDump from object:\n");
694 fprintf(f, "\nEND\n");
700 const cEventInfo *GetPresentEvent(void) const;
701 const cEventInfo *GetFollowingEvent(void) const;
702 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
703 const cEventInfo *GetEventAround(time_t tTime) const;
704 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
707 const unsigned char GetTableID(void) const;
708 const char *GetTimeString(void) const;
709 const char *GetEndTimeString(void) const;
710 const char *GetDate(void) const;
711 bool IsFollowing(void) const;
712 bool IsPresent(void) const;
713 const char *GetExtendedDescription(void) const;
714 const char *GetSubtitle(void) const;
715 const char *GetTitle(void) const;
716 unsigned short GetEventID(void) const;
717 long GetDuration(void) const;
718 time_t GetTime(void) const;
719 tChannelID GetChannelID(void) const;
720 int GetChannelNumber(void) const { return nChannelNumber; }
721 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
722 void Dump(FILE *f, const char *Prefix = "") const;
730 fprintf(f, "End of current Schedule\n\n\n");
732 Schedule = (const cSchedule *)Schedules->Next(Schedule);
739 void MVPClient::processConfigSave(unsigned char* buffer, int length)
741 char* section = (char*)buffer;
745 for (int k = 0; k < length; k++)
747 if (buffer[k] == '\0')
751 key = (char*)&buffer[k+1];
755 value = (char*)&buffer[k+1];
761 // if the last string (value) doesnt have null terminator, give up
762 if (buffer[length - 1] != '\0') return;
764 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
765 if (config.setValueString(section, key, value))
775 void MVPClient::processConfigLoad(unsigned char* buffer, int length)
777 char* section = (char*)buffer;
780 for (int k = 0; k < length; k++)
782 if (buffer[k] == '\0')
784 key = (char*)&buffer[k+1];
789 char* value = config.getValueString(section, key);
793 unsigned char sendBuffer[4 + strlen(value) + 1];
794 *(unsigned long*)&sendBuffer[0] = htonl(strlen(value) + 1);
795 strcpy((char*)&sendBuffer[4], value);
796 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
798 log->log("Client", Log::DEBUG, "Written config load packet");
803 unsigned char sendBuffer[8];
804 *(unsigned long*)&sendBuffer[0] = htonl(4);
805 *(unsigned long*)&sendBuffer[4] = htonl(0);
806 tcp.sendPacket(sendBuffer, 8);
808 log->log("Client", Log::DEBUG, "Written config load failed packet");
812 void MVPClient::cleanConfig()
814 log->log("Client", Log::DEBUG, "Clean config");
816 cRecordings Recordings;
821 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
822 char* position = resumes;
823 for(int k = 0; k < numReturns; k++)
825 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
827 cRecording* recording = Recordings.GetByName(position);
830 // doesn't exist anymore
831 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
832 config.deleteValue("ResumeData", position);
836 log->log("Client", Log::DEBUG, "This recording still exists");
839 position += strlen(position) + 1;