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");
79 delete recordingManager;
81 recordingManager = NULL;
87 cChannel* MVPClient::channelFromNumber(unsigned long channelNumber)
89 cChannel* channel = NULL;
91 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
93 if (!channel->GroupSep())
95 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
97 if (channel->Number() == (int)channelNumber)
99 int vpid = channel->Vpid();
100 #if VDRVERSNUM < 10300
101 int apid1 = channel->Apid1();
103 int apid1 = channel->Apid(0);
105 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
113 log->log("Client", Log::DEBUG, "Channel not found");
120 void MVPClient::writeResumeData()
122 config.setValueLongLong("ResumeData", (char*)rp->getCurrentRecording()->FileName(), rp->getLastPosition());
125 void MVPClient::sendULONG(ULONG ul)
127 unsigned char sendBuffer[8];
128 *(unsigned long*)&sendBuffer[0] = htonl(4);
129 *(unsigned long*)&sendBuffer[4] = htonl(ul);
131 tcp.sendPacket(sendBuffer, 8);
132 log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
135 void MVPClientStartThread(void* arg)
137 MVPClient* m = (MVPClient*)arg;
139 // Nothing external to this class has a reference to it
140 // This is the end of the thread.. so delete m
147 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
148 log->log("Client", Log::DEBUG, "MVPClient run success");
152 void MVPClient::run2()
157 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
158 pthread_detach(runThread); // Detach
160 tcp.disableReadTimeout();
162 tcp.setSoKeepTime(3);
163 tcp.setNonBlocking();
165 unsigned char* buffer;
168 unsigned long opcode;
172 log->log("Client", Log::DEBUG, "Waiting");
173 buffer = (unsigned char*)tcp.receivePacket();
174 log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
177 log->log("Client", Log::DEBUG, "Detected connection closed");
181 packetLength = tcp.getDataLength() - 4;
182 opcode = ntohl(*(unsigned long*)buffer);
189 processLogin(data, packetLength);
192 processGetRecordingsList(data, packetLength);
195 processDeleteRecording(data, packetLength);
198 processGetSummary(data, packetLength);
201 processGetChannelsList(data, packetLength);
204 processStartStreamingChannel(data, packetLength);
207 processGetBlock(data, packetLength);
210 processStopStreaming(data, packetLength);
213 processStartStreamingRecording(data, packetLength);
216 processGetChannelSchedule(data, packetLength);
219 processConfigSave(data, packetLength);
222 processConfigLoad(data, packetLength);
230 void MVPClient::processLogin(unsigned char* buffer, int length)
232 time_t timeNow = time(NULL);
233 struct tm* timeStruct = localtime(&timeNow);
234 int timeOffset = timeStruct->tm_gmtoff;
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);
241 tcp.sendPacket(sendBuffer, 12);
242 log->log("Client", Log::DEBUG, "written login reply");
245 void MVPClient::processGetRecordingsList(unsigned char* data, int length)
247 unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
248 int count = 4; // leave space for the packet length
253 int Percent = VideoDiskSpace(&FreeMB);
254 int Total = (FreeMB / (100 - Percent)) * 100;
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);
264 cRecordings Recordings;
267 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
269 if (count > 49000) break; // just how big is that hard disk?!
270 *(unsigned long*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
273 point = (char*)recording->Name();
274 strcpy((char*)&sendBuffer[count], point);
275 count += strlen(point) + 1;
277 point = (char*)recording->FileName();
278 strcpy((char*)&sendBuffer[count], point);
279 count += strlen(point) + 1;
282 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
284 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
286 tcp.sendPacket(sendBuffer, count);
288 log->log("Client", Log::DEBUG, "Written list");
291 void MVPClient::processDeleteRecording(unsigned char* data, int length)
293 // data is a pointer to the fileName string
295 cRecordings Recordings;
296 Recordings.Load(); // probably have to do this
298 cRecording* recording = Recordings.GetByName((char*)data);
300 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
304 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
314 void MVPClient::processGetSummary(unsigned char* data, int length)
316 // data is a pointer to the fileName string
318 cRecordings Recordings;
319 Recordings.Load(); // probably have to do this
321 cRecording *recording = Recordings.GetByName((char*)data);
323 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
327 unsigned char* sendBuffer = new unsigned char[50000]; // hope this is enough
328 int count = 4; // leave space for the packet length
331 #if VDRVERSNUM < 10300
332 point = (char*)recording->Summary();
334 const cRecordingInfo *Info = recording->Info();
335 point = (char*)Info->ShortText();
336 log->log("Client", Log::DEBUG, "info pointer %p summary pointer %p", Info, point);
339 point = (char*)Info->Description();
340 log->log("Client", Log::DEBUG, "description pointer %p", point);
343 strcpy((char*)&sendBuffer[count], point);
344 count += strlen(point) + 1;
345 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
347 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
349 tcp.sendPacket(sendBuffer, count);
351 log->log("Client", Log::DEBUG, "Written summary");
361 void MVPClient::processGetChannelsList(unsigned char* data, int length)
363 unsigned char* sendBuffer = new unsigned char[50000]; // FIXME hope this is enough
364 int count = 4; // leave space for the packet length
368 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
370 #if VDRVERSNUM < 10300
371 if (!channel->GroupSep() && !channel->Ca())
373 if (!channel->GroupSep() && !channel->Ca(0))
376 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
378 if (channel->Vpid()) type = 1;
379 #if VDRVERSNUM < 10300
382 else if (channel->Apid(0)) type = 2;
386 if (count > 49000) break;
387 *(unsigned long*)&sendBuffer[count] = htonl(channel->Number());
390 *(unsigned long*)&sendBuffer[count] = htonl(type);
393 point = (char*)channel->Name();
394 strcpy((char*)&sendBuffer[count], point);
395 count += strlen(point) + 1;
399 *(unsigned long*)&sendBuffer[0] = htonl(count - 4); // -4 : take off the size field
401 log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(unsigned long*)&sendBuffer[0]));
403 tcp.sendPacket(sendBuffer, count);
405 log->log("Client", Log::DEBUG, "Written channels list");
408 void MVPClient::processStartStreamingChannel(unsigned char* data, int length)
410 log->log("Client", Log::DEBUG, "length = %i", length);
411 unsigned long channelNumber = ntohl(*(unsigned long*)data);
413 cChannel* channel = channelFromNumber(channelNumber);
421 // static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
422 ///< Returns a device that is able to receive the given Channel at the
424 ///< See ProvidesChannel() for more information on how
425 ///< priorities are handled, and the meaning of NeedsDetachReceivers.
427 bool NeedsDetachReceivers;
428 cDevice* device = cDevice::GetDevice(channel, 0, &NeedsDetachReceivers);
431 log->log("Client", Log::DEBUG, "No device found to receive this channel at this priority");
434 else if (NeedsDetachReceivers)
436 // can't really happen since we stream with priority zero. if a rec has pri zero maybe
437 log->log("Client", Log::DEBUG, "Needs detach receivers");
442 cm = new cMediamvpTransceiver(channel, 0, 0, device);
443 device->AttachReceiver(cm);
448 ////// MVPReceiver* m = new MVPReceiver(channel->Vpid(), channel->Apid1());
449 // cm = new cMediamvpTransceiver(channel, 0, 0, cDevice::ActualDevice());
450 // cDevice::ActualDevice()->AttachReceiver(cm);
451 //// //cDevice::ActualDevice()->SwitchChannel(channel, false);
456 void MVPClient::processStopStreaming(unsigned char* data, int length)
458 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
469 delete recordingManager;
471 recordingManager = NULL;
477 void MVPClient::processGetBlock(unsigned char* data, int length)
481 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
485 ULLONG position = ntohll(*(ULLONG*)data);
486 data += sizeof(ULLONG);
487 unsigned long amount = ntohl(*(unsigned long*)data);
489 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
491 unsigned char sendBuffer[amount + 4];
492 unsigned long amountReceived = 0; // compiler moan.
495 log->log("Client", Log::DEBUG, "getting from live");
496 amountReceived = cm->getBlock(&sendBuffer[4], amount);
500 log->log("Client", Log::DEBUG, "getting from recording");
501 amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
504 *(unsigned long*)&sendBuffer[0] = htonl(amountReceived);
505 tcp.sendPacket(sendBuffer, amountReceived + 4);
506 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
509 void MVPClient::processStartStreamingRecording(unsigned char* data, int length)
511 // data is a pointer to the fileName string
513 recordingManager = new cRecordings;
514 recordingManager->Load();
516 cRecording* recording = recordingManager->GetByName((char*)data);
518 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
522 rp = new RecPlayer(recording);
524 unsigned char sendBuffer[12];
525 *(unsigned long*)&sendBuffer[0] = htonl(8);
526 *(ULLONG*)&sendBuffer[4] = htonll(rp->getTotalLength());
528 tcp.sendPacket(sendBuffer, 12);
529 log->log("Client", Log::DEBUG, "written totalLength");
533 delete recordingManager;
534 recordingManager = NULL;
538 void MVPClient::processGetChannelSchedule(unsigned char* data, int length)
540 ULONG channelNumber = ntohl(*(ULLONG*)data);
541 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
543 cChannel* channel = channelFromNumber(channelNumber);
546 unsigned char sendBuffer[4];
547 *(unsigned long*)&sendBuffer[0] = htonl(0);
548 tcp.sendPacket(sendBuffer, 4);
549 log->log("Client", Log::DEBUG, "written null");
553 #if VDRVERSNUM < 10300
554 cMutexLock MutexLock;
555 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
557 cSchedulesLock MutexLock;
558 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
562 unsigned char sendBuffer[8];
563 *(unsigned long*)&sendBuffer[0] = htonl(4);
564 *(unsigned long*)&sendBuffer[4] = htonl(0);
565 tcp.sendPacket(sendBuffer, 8);
566 log->log("Client", Log::DEBUG, "written 0");
570 unsigned char sendBuffer[8];
571 *(unsigned long*)&sendBuffer[0] = htonl(4);
572 *(unsigned long*)&sendBuffer[4] = htonl(1);
573 tcp.sendPacket(sendBuffer, 8);
574 log->log("Client", Log::DEBUG, "written 1");
579 void MVPClient::testChannelSchedule(unsigned char* data, int length)
581 FILE* f = fopen("/tmp/s.txt", "w");
583 #if VDRVERSNUM < 10300
584 cMutexLock MutexLock;
585 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
587 cSchedulesLock MutexLock;
588 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
592 fprintf(f, "Schedules = NULL\n");
597 fprintf(f, "Schedules dump:\n");
601 const cSchedule *Schedule;
602 int scheduleNumber = 0;
605 cChannel *thisChannel;
607 #if VDRVERSNUM < 10300
608 const cEventInfo *event;
614 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
615 // Schedule = Schedules->GetSchedule();
616 Schedule = Schedules->First();
619 fprintf(f, "First Schedule = NULL\n");
626 fprintf(f, "Schedule #%i\n", scheduleNumber);
627 fprintf(f, "-------------\n\n");
629 #if VDRVERSNUM < 10300
630 tchid = Schedule->GetChannelID();
632 tchid = Schedule->ChannelID();
634 #if VDRVERSNUM < 10300
635 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
636 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
638 // put the count at the end.
640 thisChannel = Channels.GetByChannelID(tchid, true);
643 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
647 fprintf(f, "thisChannel = NULL for tchid\n");
650 #if VDRVERSNUM < 10300
651 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
653 event = Schedule->GetEventNumber(eventNumber);
654 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
655 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
656 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
657 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
658 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
659 fprintf(f, "Event %u dump:\n", eventNumber);
664 // This whole section needs rewriting to walk the list.
665 event = Schedule->Events()->First();
667 event = Schedule->Events()->Next(event);
672 fprintf(f, "\nDump from object:\n");
674 fprintf(f, "\nEND\n");
680 const cEventInfo *GetPresentEvent(void) const;
681 const cEventInfo *GetFollowingEvent(void) const;
682 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
683 const cEventInfo *GetEventAround(time_t tTime) const;
684 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
687 const unsigned char GetTableID(void) const;
688 const char *GetTimeString(void) const;
689 const char *GetEndTimeString(void) const;
690 const char *GetDate(void) const;
691 bool IsFollowing(void) const;
692 bool IsPresent(void) const;
693 const char *GetExtendedDescription(void) const;
694 const char *GetSubtitle(void) const;
695 const char *GetTitle(void) const;
696 unsigned short GetEventID(void) const;
697 long GetDuration(void) const;
698 time_t GetTime(void) const;
699 tChannelID GetChannelID(void) const;
700 int GetChannelNumber(void) const { return nChannelNumber; }
701 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
702 void Dump(FILE *f, const char *Prefix = "") const;
710 fprintf(f, "End of current Schedule\n\n\n");
712 Schedule = (const cSchedule *)Schedules->Next(Schedule);
719 void MVPClient::processConfigSave(unsigned char* buffer, int length)
721 char* section = (char*)buffer;
725 for (int k = 0; k < length; k++)
727 if (buffer[k] == '\0')
731 key = (char*)&buffer[k+1];
735 value = (char*)&buffer[k+1];
741 // if the last string (value) doesnt have null terminator, give up
742 if (buffer[length - 1] != '\0') return;
744 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
745 if (config.setValueString(section, key, value))
755 void MVPClient::processConfigLoad(unsigned char* buffer, int length)
757 char* section = (char*)buffer;
760 for (int k = 0; k < length; k++)
762 if (buffer[k] == '\0')
764 key = (char*)&buffer[k+1];
769 char* value = config.getValueString(section, key);
773 unsigned char sendBuffer[4 + strlen(value) + 1];
774 *(unsigned long*)&sendBuffer[0] = htonl(strlen(value) + 1);
775 strcpy((char*)&sendBuffer[4], value);
776 tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
778 log->log("Client", Log::DEBUG, "Written config load packet");
783 unsigned char sendBuffer[8];
784 *(unsigned long*)&sendBuffer[0] = htonl(4);
785 *(unsigned long*)&sendBuffer[4] = htonl(0);
786 tcp.sendPacket(sendBuffer, 8);
788 log->log("Client", Log::DEBUG, "Written config load failed packet");
792 void MVPClient::cleanConfig()
794 log->log("Client", Log::DEBUG, "Clean config");
796 cRecordings Recordings;
801 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
802 char* position = resumes;
803 for(int k = 0; k < numReturns; k++)
805 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
807 cRecording* recording = Recordings.GetByName(position);
810 // doesn't exist anymore
811 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
812 config.deleteValue("ResumeData", position);
816 log->log("Client", Log::DEBUG, "This recording still exists");
819 position += strlen(position) + 1;