From 450c8fd4a7ec7eb878abfce0e4a499e03762b4f8 Mon Sep 17 00:00:00 2001 From: lordjaxom Date: Mon, 9 May 2005 20:22:29 +0000 Subject: [PATCH] - added TS compatibility mode --- common.h | 5 +- server/component.c | 50 +-- server/component.h | 27 +- server/componentHTTP.c | 11 +- server/componentHTTP.h | 16 +- server/componentVTP.c | 11 +- server/componentVTP.h | 17 +- server/connection.c | 155 ++++--- server/connection.h | 75 ++-- server/connectionHTTP.c | 45 +- server/connectionVTP.c | 949 +++++++++++++++++++++++++++++----------- server/connectionVTP.h | 42 +- server/server.c | 133 +++--- server/server.h | 27 +- server/setup.c | 6 +- server/streamer.c | 9 +- streamdev-server.c | 56 ++- streamdev-server.h | 22 +- 18 files changed, 1055 insertions(+), 601 deletions(-) diff --git a/common.h b/common.h index 461f636..065521e 100644 --- a/common.h +++ b/common.h @@ -1,5 +1,5 @@ /* - * $Id: common.h,v 1.5 2005/02/11 16:44:14 lordjaxom Exp $ + * $Id: common.h,v 1.6 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_COMMON_H @@ -50,9 +50,6 @@ const cChannel *ChannelFromString(const char *String, int *Apid = NULL); #define BUFOVERTIME 5000 #define BUFOVERCOUNT 100 -#define STREAMDEVHOSTS (const char*)AddDirectory(cPlugin::ConfigDirectory(), \ - "streamdevhosts.conf") - #define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno)) #define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno)) #define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno)) diff --git a/server/component.c b/server/component.c index 61e7c6b..1a584b5 100644 --- a/server/component.c +++ b/server/component.c @@ -1,50 +1,48 @@ /* - * $Id: component.c,v 1.2 2005/02/08 17:22:35 lordjaxom Exp $ + * $Id: component.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/component.h" #include "server/connection.h" -#include -#include -#include - cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, - uint ListenPort) { - m_Protocol = Protocol; - m_ListenIp = ListenIp; - m_ListenPort = ListenPort; + uint ListenPort): + m_Protocol(Protocol), + m_ListenIp(ListenIp), + m_ListenPort(ListenPort) +{ } -cServerComponent::~cServerComponent() { +cServerComponent::~cServerComponent() +{ } -bool cServerComponent::Init(void) { +bool cServerComponent::Initialize(void) +{ if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) { - esyslog("Streamdev: Couldn't listen (%s) %s:%d: %s", m_Protocol, m_ListenIp, - m_ListenPort, strerror(errno)); + esyslog("Streamdev: Couldn't listen (%s) %s:%d: %m", + m_Protocol, m_ListenIp, m_ListenPort); return false; } isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort); return true; } -void cServerComponent::Exit(void) { +void cServerComponent::Destruct(void) +{ m_Listen.Close(); } -cServerConnection *cServerComponent::CanAct(const cTBSelect &Select) { - if (Select.CanRead(m_Listen)) { - cServerConnection *client = NewConnection(); - if (client->Accept(m_Listen)) { - isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol, - client->RemoteIp().c_str(), client->RemotePort()); - return client; - } else { - esyslog("Streamdev: Couldn't accept (%s): %s", m_Protocol, - strerror(errno)); - delete client; - } +cServerConnection *cServerComponent::Accept(void) +{ + cServerConnection *client = NewClient(); + if (client->Accept(m_Listen)) { + isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol, + client->RemoteIp().c_str(), client->RemotePort()); + return client; + } else { + esyslog("Streamdev: Couldn't accept (%s): %m", m_Protocol); + delete client; } return NULL; } diff --git a/server/component.h b/server/component.h index 2f8e605..8703348 100644 --- a/server/component.h +++ b/server/component.h @@ -1,5 +1,5 @@ /* - * $Id: component.h,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $ + * $Id: component.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H @@ -22,29 +22,30 @@ private: const char *m_ListenIp; uint m_ListenPort; +protected: + /* Returns a new connection object for Accept() */ + virtual cServerConnection *NewClient(void) = 0; + public: cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort); virtual ~cServerComponent(); /* Starts listening on the specified Port, override if you want to do things different */ - virtual bool Init(void); + virtual bool Initialize(void); /* Stops listening, override if you want to do things different */ - virtual void Exit(void); + virtual void Destruct(void); + + /* Get the listening socket's file number */ + virtual int Socket(void) const { return (int)m_Listen; } /* Adds the listening socket to the Select object */ - virtual void AddSelect(cTBSelect &Select) const { Select.Add(m_Listen); } - - /* Accepts the connection on a NewConnection() object and calls the + virtual void Add(cTBSelect &Select) const { Select.Add(m_Listen); } + + /* Accepts the connection on a NewClient() object and calls the Welcome() on it, override if you want to do things different */ - virtual cServerConnection *CanAct(const cTBSelect &Select); - - /* Returns a new connection object for CanAct */ - virtual cServerConnection *NewConnection(void) const = 0; -}; - -class cServerComponents: public cList { + virtual cServerConnection *Accept(void); }; #endif // VDR_STREAMDEV_SERVERS_COMPONENT_H diff --git a/server/componentHTTP.c b/server/componentHTTP.c index 035cb01..70e95e9 100644 --- a/server/componentHTTP.c +++ b/server/componentHTTP.c @@ -1,5 +1,5 @@ /* - * $Id: componentHTTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $ + * $Id: componentHTTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/componentHTTP.h" @@ -7,9 +7,12 @@ #include "server/setup.h" cComponentHTTP::cComponentHTTP(void): - cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP, - StreamdevServerSetup.HTTPServerPort) { + cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP, + StreamdevServerSetup.HTTPServerPort) +{ } -cComponentHTTP::~cComponentHTTP() { +cServerConnection *cComponentHTTP::NewClient(void) +{ + return new cConnectionHTTP; } diff --git a/server/componentHTTP.h b/server/componentHTTP.h index 46174d9..29dc087 100644 --- a/server/componentHTTP.h +++ b/server/componentHTTP.h @@ -1,26 +1,18 @@ /* - * $Id: componentHTTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $ + * $Id: componentHTTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_HTTPSERVER_H #define VDR_STREAMDEV_HTTPSERVER_H #include "server/component.h" -#include "server/connectionHTTP.h" - -#include -#include class cComponentHTTP: public cServerComponent { +protected: + virtual cServerConnection *NewClient(void); + public: cComponentHTTP(void); - ~cComponentHTTP(void); - - virtual cServerConnection *NewConnection(void) const; }; -inline cServerConnection *cComponentHTTP::NewConnection(void) const { - return new cConnectionHTTP; -} - #endif // VDR_STREAMDEV_HTTPSERVER_H diff --git a/server/componentVTP.c b/server/componentVTP.c index 5fb9dcd..ed2df1c 100644 --- a/server/componentVTP.c +++ b/server/componentVTP.c @@ -1,5 +1,5 @@ /* - * $Id: componentVTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $ + * $Id: componentVTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/componentVTP.h" @@ -7,9 +7,12 @@ #include "server/setup.h" cComponentVTP::cComponentVTP(void): - cServerComponent("VTP", StreamdevServerSetup.VTPBindIP, - StreamdevServerSetup.VTPServerPort) { + cServerComponent("VTP", StreamdevServerSetup.VTPBindIP, + StreamdevServerSetup.VTPServerPort) +{ } -cComponentVTP::~cComponentVTP() { +cServerConnection *cComponentVTP::NewClient(void) +{ + return new cConnectionVTP; } diff --git a/server/componentVTP.h b/server/componentVTP.h index fe411ff..4f61eb5 100644 --- a/server/componentVTP.h +++ b/server/componentVTP.h @@ -1,29 +1,18 @@ /* - * $Id: componentVTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $ + * $Id: componentVTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H #define VDR_STREAMDEV_SERVERS_SERVERVTP_H #include "server/component.h" -#include "server/connectionVTP.h" - -#include -#include class cComponentVTP: public cServerComponent { -private: - cTBSocket m_Listen; +protected: + virtual cServerConnection *NewClient(void); public: cComponentVTP(void); - virtual ~cComponentVTP(); - - virtual cServerConnection *NewConnection(void) const; }; -inline cServerConnection *cComponentVTP::NewConnection(void) const { - return new cConnectionVTP; -} - #endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H diff --git a/server/connection.c b/server/connection.c index 762d184..38ba214 100644 --- a/server/connection.c +++ b/server/connection.c @@ -1,5 +1,5 @@ /* - * $Id: connection.c,v 1.3 2005/03/24 21:31:38 lordjaxom Exp $ + * $Id: connection.c,v 1.4 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/connection.h" @@ -11,53 +11,79 @@ #include #include -cServerConnection::cServerConnection(const char *Protocol) { - m_RdBytes = 0; - m_WrBytes = 0; - m_WrOffs = 0; - m_DeferClose = false; - m_Protocol = Protocol; +cServerConnection::cServerConnection(const char *Protocol): + m_Protocol(Protocol), + m_DeferClose(false), + m_Pending(false), + m_ReadBytes(0), + m_WriteBytes(0), + m_WriteIndex(0) +{ } -cServerConnection::~cServerConnection() { +cServerConnection::~cServerConnection() +{ } -bool cServerConnection::CanAct(const cTBSelect &Select) { - if (Select.CanRead(*this)) { - int b; - if ((b = Read(m_RdBuf + m_RdBytes, sizeof(m_RdBuf) - m_RdBytes - 1)) < 0) { - esyslog("Streamdev: Read from client (%s) %s:%d failed: %s", m_Protocol, - RemoteIp().c_str(), RemotePort(), strerror(errno)); - return false; - } - - if (b == 0) { - isyslog("Streamdev: Client (%s) %s:%d closed connection", m_Protocol, - RemoteIp().c_str(), RemotePort()); - return false; - } - - m_RdBytes += b; - m_RdBuf[m_RdBytes] = '\0'; - return ParseBuffer(); +bool cServerConnection::Read(void) +{ + int b; + if ((b = cTBSocket::Read(m_ReadBuffer + m_ReadBytes, + sizeof(m_ReadBuffer) - m_ReadBytes - 1)) < 0) { + esyslog("ERROR: read from client (%s) %s:%d failed: %m", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; } - if (Select.CanWrite(*this)) { - int b; - if ((b = Write(m_WrBuf + m_WrOffs, m_WrBytes - m_WrOffs)) < 0) { - esyslog("Streamdev: Write to client (%s) %s:%d failed: %s", m_Protocol, - RemoteIp().c_str(), RemotePort(), strerror(errno)); - return false; - } - - m_WrOffs += b; - if (m_WrOffs == m_WrBytes) { - m_WrBytes = 0; - m_WrOffs = 0; - } + if (b == 0) { + isyslog("client (%s) %s:%d has closed connection", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; } - if (m_WrBytes == 0) { + m_ReadBytes += b; + m_ReadBuffer[m_ReadBytes] = '\0'; + + char *end; + bool result = true; + while ((end = strchr(m_ReadBuffer, '\012')) != NULL) { + *end = '\0'; + if (end > m_ReadBuffer && *(end - 1) == '\015') + *(end - 1) = '\0'; + + if (!Command(m_ReadBuffer)) + return false; + + m_ReadBytes -= ++end - m_ReadBuffer; + if (m_ReadBytes > 0) + memmove(m_ReadBuffer, end, m_ReadBytes); + } + + if (m_ReadBytes == sizeof(m_ReadBuffer) - 1) { + esyslog("ERROR: streamdev: input buffer overflow (%s) for %s:%d", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + return result; +} + +bool cServerConnection::Write(void) +{ + int b; + if ((b = cTBSocket::Write(m_WriteBuffer + m_WriteIndex, + m_WriteBytes - m_WriteIndex)) < 0) { + esyslog("ERROR: streamdev: write to client (%s) %s:%d failed: %m", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + m_WriteIndex += b; + if (m_WriteIndex == m_WriteBytes) { + m_WriteIndex = 0; + m_WriteBytes = 0; + if (m_Pending) + Command(NULL); if (m_DeferClose) return false; Flushed(); @@ -65,42 +91,33 @@ bool cServerConnection::CanAct(const cTBSelect &Select) { return true; } -bool cServerConnection::ParseBuffer(void) { - char *ep; - bool res; +bool cServerConnection::Respond(const char *Message, bool Last, ...) +{ + char *buffer; + int length; + va_list ap; + va_start(ap, Last); + length = vasprintf(&buffer, Message, ap); + va_end(ap); - while ((ep = strchr(m_RdBuf, '\012')) != NULL) { - *ep = '\0'; - if (ep > m_RdBuf && *(ep-1) == '\015') - *(ep-1) = '\0'; - - Dprintf("IN: |%s|\n", m_RdBuf); - res = Command(m_RdBuf); - m_RdBytes -= ++ep - m_RdBuf; - if (m_RdBytes > 0) - memmove(m_RdBuf, ep, m_RdBytes); - if (res == false) - return false; - } - return true; -} - -bool cServerConnection::Respond(const std::string &Message) { - if (m_WrBytes + Message.size() + 2 > sizeof(m_WrBuf)) { - esyslog("Streamdev: Output buffer overflow (%s) for %s:%d", m_Protocol, - RemoteIp().c_str(), RemotePort()); + if (m_WriteBytes + length + 2 > sizeof(m_WriteBuffer)) { + esyslog("ERROR: streamdev: output buffer overflow (%s) for %s:%d", + m_Protocol, RemoteIp().c_str(), RemotePort()); return false; } - Dprintf("OUT: |%s|\n", Message.c_str()); - memcpy(m_WrBuf + m_WrBytes, Message.c_str(), Message.size()); + Dprintf("OUT: |%s|\n", buffer); + memcpy(m_WriteBuffer + m_WriteBytes, buffer, length); + free(buffer); - m_WrBytes += Message.size(); - m_WrBuf[m_WrBytes++] = '\015'; - m_WrBuf[m_WrBytes++] = '\012'; + m_WriteBytes += length; + m_WriteBuffer[m_WriteBytes++] = '\015'; + m_WriteBuffer[m_WriteBytes++] = '\012'; + m_Pending = !Last; return true; } -cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) { +cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) +{ cDevice *device = NULL; /*Dprintf("+ Statistics:\n"); diff --git a/server/connection.h b/server/connection.h index a4a4379..f68f84a 100644 --- a/server/connection.h +++ b/server/connection.h @@ -1,13 +1,11 @@ /* - * $Id: connection.h,v 1.2 2005/02/08 17:22:35 lordjaxom Exp $ + * $Id: connection.h,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H #define VDR_STREAMDEV_SERVER_CONNECTION_H #include "tools/socket.h" -#include "tools/select.h" - #include "common.h" class cChannel; @@ -16,18 +14,32 @@ class cDevice; /* Basic capabilities of a straight text-based protocol, most functions virtual to support more complicated protocols */ -class cServerConnection: public cListObject, public cTBSocket { +class cServerConnection: public cListObject, public cTBSocket +{ private: - char m_RdBuf[8192]; - uint m_RdBytes; - - char m_WrBuf[8192]; - uint m_WrBytes; - uint m_WrOffs; - const char *m_Protocol; + bool m_DeferClose; + bool m_Pending; - bool m_DeferClose; + char m_ReadBuffer[MAXPARSEBUFFER]; + uint m_ReadBytes; + + char m_WriteBuffer[MAXPARSEBUFFER]; + uint m_WriteBytes; + uint m_WriteIndex; + +protected: + /* Will be called when a command terminated by a newline has been + received */ + virtual bool Command(char *Cmd) = 0; + + /* Will put Message into the response queue, which will be sent in the next + server cycle. Note that Message will be line-terminated by Respond. + Only one line at a time may be sent. If there are lines to follow, set + Last to false. Command(NULL) will be called in the next cycle, so you can + post the next line. */ + virtual bool Respond(const char *Message, bool Last = true, ...) + __attribute__ ((format (printf, 2, 4))); public: /* If you derive, specify a short string such as HTTP for Protocol, which @@ -41,24 +53,21 @@ public: /* Gets called if the client has been rejected by the core */ virtual void Reject(void) { DeferClose(); } - /* Adds itself to the Select object, if data can be received or if data is - to be sent. Override if necessary */ - virtual void AddSelect(cTBSelect &Select) const; + /* Get the client socket's file number */ + virtual int Socket(void) const { return (int)*this; } - /* Receives incoming data and calls ParseBuffer on it. Also writes queued - output data if possible. Override if necessary */ - virtual bool CanAct(const cTBSelect &Select); + /* Determine if there is data to send or any command pending */ + virtual bool HasData(void) const; - /* Called by CanAct(), parses the input buffer for full lines (terminated - either by '\012' or '\015\012') and calls Command on them, if any */ - virtual bool ParseBuffer(void); + /* Gets called by server when the socket can accept more data. Writes + the buffer filled up by Respond(). Calls Command(NULL) if there is a + command pending. Returns false in case of an error */ + virtual bool Write(void); - /* Will be called when a command terminated by a newline has been received */ - virtual bool Command(char *Cmd) = 0; - - /* Will put Message into the response queue, which will be sent in the next - server cycle. Note that Message will be line-terminated by Respond */ - bool Respond(const std::string &Message); + /* Gets called by server when there is incoming data to read. Calls + Command() for each line. Returns false in case of an error, or if + the connection shall be closed and removed by the server */ + virtual bool Read(void); /* Will make the socket close after sending all queued output data */ void DeferClose(void) { m_DeferClose = true; } @@ -73,15 +82,9 @@ public: virtual void Attach(void) = 0; }; -class cServerConnections: public cList { -}; - -inline void cServerConnection::AddSelect(cTBSelect &Select) const { - if (m_WrBytes > 0) - Select.Add(*this, true); - - if (m_WrBytes == 0 && m_RdBytes < sizeof(m_RdBuf) - 1) - Select.Add(*this, false); +inline bool cServerConnection::HasData(void) const +{ + return m_WriteBytes > 0 || m_Pending || m_DeferClose; } #endif // VDR_STREAMDEV_SERVER_CONNECTION_H diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c index 2c912bc..ffd987a 100644 --- a/server/connectionHTTP.c +++ b/server/connectionHTTP.c @@ -1,5 +1,5 @@ /* - * $Id: connectionHTTP.c,v 1.8 2005/04/24 16:26:14 lordjaxom Exp $ + * $Id: connectionHTTP.c,v 1.9 2005/05/09 20:22:29 lordjaxom Exp $ */ #include @@ -45,7 +45,8 @@ bool cConnectionHTTP::Command(char *Cmd) return false; // ??? shouldn't happen } -bool cConnectionHTTP::ProcessRequest(void) { +bool cConnectionHTTP::ProcessRequest(void) +{ Dprintf("process\n"); if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) { switch (m_Job) { @@ -71,7 +72,7 @@ bool cConnectionHTTP::ProcessRequest(void) { if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) { return Respond("HTTP/1.0 200 OK") && Respond("Content-Type: audio/mpeg") - && Respond((std::string)"icy-name: " + m_Channel->Name()) + && Respond("icy-name: %s", true, m_Channel->Name()) && Respond(""); } else { return Respond("HTTP/1.0 200 OK") @@ -90,7 +91,8 @@ bool cConnectionHTTP::ProcessRequest(void) { return Respond("HTTP/1.0 400 Bad Request"); } -void cConnectionHTTP::Flushed(void) { +void cConnectionHTTP::Flushed(void) +{ std::string line; if (m_Status != hsBody) @@ -132,7 +134,7 @@ void cConnectionHTTP::Flushed(void) { } line += ""; } - if (!Respond(line)) + if (!Respond(line.c_str())) DeferClose(); m_ListChannel = Channels.Next(m_ListChannel); break; @@ -145,7 +147,8 @@ void cConnectionHTTP::Flushed(void) { } } -bool cConnectionHTTP::CmdGET(const std::string &Opts) { +bool cConnectionHTTP::CmdGET(const std::string &Opts) +{ const char *sp = Opts.c_str(), *ptr = sp, *ep; const cChannel *chan; int apid = 0, pos; @@ -193,33 +196,3 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts) { return true; } -#if 0 -bool cHTTPConnection::Listing(void) { - cChannel *chan; - cTBString line; - - Respond(200, "OK"); - Respond("Content-Type: text/html"); - Respond(""); - Respond("VDR Channel Listing"); - Respond("
    "); - - for (chan = Channels.First(); chan != NULL; chan = Channels.Next(chan)) { - if (chan->GroupSep() && !*chan->Name()) - continue; - - if (chan->GroupSep()) - line.Format("
  • --- %s ---
  • ", chan->Name()); - else - line.Format("
  • %s
  • ", - (const char*)m_Socket.LocalIp(), StreamdevServerSetup.HTTPServerPort, - chan->GetChannelID().ToString(), chan->Name()); - Respond(line); - } - - Respond("
"); - - m_DeferClose = true; - return true; -} -#endif diff --git a/server/connectionVTP.c b/server/connectionVTP.c index a4ea245..b66d224 100644 --- a/server/connectionVTP.c +++ b/server/connectionVTP.c @@ -1,5 +1,5 @@ /* - * $Id: connectionVTP.c,v 1.6 2005/04/24 16:26:14 lordjaxom Exp $ + * $Id: connectionVTP.c,v 1.7 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/connectionVTP.h" @@ -17,82 +17,562 @@ /* VTP Response codes: 220: Service ready 221: Service closing connection + 451: Requested action aborted: try again 500: Syntax error or Command unrecognized 501: Wrong parameters or missing parameters - 550: Action not done + 550: Requested action not taken 551: Data connection not accepted 560: Channel not available currently - 561: Capability not known + 561: Capability not known 562: Pid not available currently 563: Recording not available (currently?) */ -cConnectionVTP::cConnectionVTP(void): cServerConnection("VTP") { - m_LiveStreamer = NULL; - memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count); +// --- cLSTEHandler ----------------------------------------------------------- + +class cLSTEHandler +{ +private: + enum eStates { Channel, Event, Title, Subtitle, Description, Vps, + EndEvent, EndChannel, EndEPG }; + cConnectionVTP *m_Client; + cSchedulesLock *m_SchedulesLock; + const cSchedules *m_Schedules; + const cSchedule *m_Schedule; + const cEvent *m_Event; + int m_Errno; + char *m_Error; + eStates m_State; + bool m_Traverse; +public: + cLSTEHandler(cConnectionVTP *Client, const char *Option); + ~cLSTEHandler(); + bool Next(bool &Last); +}; + +cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_SchedulesLock(new cSchedulesLock(false, 500)), + m_Schedules(cSchedules::Schedules(*m_SchedulesLock)), + m_Schedule(NULL), + m_Event(NULL), + m_Errno(0), + m_Error(NULL), + m_State(Channel), + m_Traverse(false) +{ + eDumpMode dumpmode = dmAll; + time_t attime = 0; + + if (m_Schedules != NULL && *Option) { + char buf[strlen(Option) + 1]; + strcpy(buf, Option); + const char *delim = " \t"; + char *strtok_next; + char *p = strtok_r(buf, delim, &strtok_next); + while (p && dumpmode == dmAll) { + if (strcasecmp(p, "NOW") == 0) + dumpmode = dmPresent; + else if (strcasecmp(p, "NEXT") == 0) + dumpmode = dmFollowing; + else if (strcasecmp(p, "AT") == 0) { + dumpmode = dmAtTime; + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (isnumber(p)) + attime = strtol(p, NULL, 10); + else { + m_Errno = 501; + m_Error = strdup("Invalid time"); + break; + } + } else { + m_Errno = 501; + m_Error = strdup("Missing time"); + break; + } + } else if (!m_Schedule) { + cChannel* Channel = NULL; + if (isnumber(p)) + Channel = Channels.GetByNumber(strtol(Option, NULL, 10)); + else + Channel = Channels.GetByChannelID(tChannelID::FromString( + Option)); + if (Channel) { + m_Schedule = m_Schedules->GetSchedule(Channel->GetChannelID()); + if (!m_Schedule) { + m_Errno = 550; + m_Error = strdup("No schedule found"); + break; + } + } else { + m_Errno = 550; + asprintf(&m_Error, "Channel \"%s\" not defined", p); + break; + } + } else { + m_Errno = 501; + asprintf(&m_Error, "Unknown option: \"%s\"", p); + break; + } + p = strtok_r(NULL, delim, &strtok_next); + } + } else if (m_Schedules == NULL) { + m_Errno = 451; + m_Error = strdup("EPG data is being modified, try again"); + } + + if (m_Error == NULL) { + if (m_Schedule != NULL) + m_Schedules = NULL; + else if (m_Schedules != NULL) + m_Schedule = m_Schedules->First(); + + if (m_Schedule != NULL && m_Schedule->Events() != NULL) { + switch (dumpmode) { + case dmAll: m_Event = m_Schedule->Events()->First(); + m_Traverse = true; + break; + case dmPresent: m_Event = m_Schedule->GetPresentEvent(); + break; + case dmFollowing: m_Event = m_Schedule->GetFollowingEvent(); + break; + case dmAtTime: m_Event = m_Schedule->GetEventAround(attime); + break; + + } + } + } } -cConnectionVTP::~cConnectionVTP() { - if (m_LiveStreamer != NULL) delete m_LiveStreamer; - - for (int idx = 0; idx < si_Count; ++idx) - if (m_DataSockets[idx] != NULL) delete m_DataSockets[idx]; +cLSTEHandler::~cLSTEHandler() +{ + delete m_SchedulesLock; + if (m_Error != NULL) + free(m_Error); } -void cConnectionVTP::Welcome(void) { +bool cLSTEHandler::Next(bool &Last) +{ + char *buffer; + + if (m_Error != NULL) { + Last = true; + cString str(m_Error, true); + m_Error = NULL; + return m_Client->Respond(m_Errno, *str); + } + + Last = false; + switch (m_State) { + case Channel: + if (m_Schedule != NULL) { + cChannel *channel = Channels.GetByChannelID(m_Schedule->ChannelID(), + true); + m_State = Event; + return m_Client->Respond(-215, "C %s %s", + *channel->GetChannelID().ToString(), + channel->Name()); + } else { + m_State = EndEPG; + return Next(Last); + } + break; + + case Event: + if (m_Event != NULL) { + m_State = Title; + return m_Client->Respond(-215, "E %u %ld %d %X", m_Event->EventID(), + m_Event->StartTime(), m_Event->Duration(), + m_Event->TableID()); + } else { + m_State = EndChannel; + return Next(Last); + } + break; + + case Title: + m_State = Subtitle; + if (!isempty(m_Event->Title())) + return m_Client->Respond(-215, "T %s", m_Event->Title()); + else + return Next(Last); + break; + + case Subtitle: + m_State = Description; + if (!isempty(m_Event->ShortText())) + return m_Client->Respond(-215, "S %s", m_Event->ShortText()); + else + return Next(Last); + break; + + case Description: + m_State = Vps; + if (!isempty(m_Event->Description())) { + char *copy = strdup(m_Event->Description()); + cString cpy(copy, true); + strreplace(copy, '\n', '|'); + return m_Client->Respond(-215, "D %s", copy); + } else + return Next(Last); + break; + + case Vps: + m_State = EndEvent; + if (m_Event->Vps()) + return m_Client->Respond(-215, "V %ld", m_Event->Vps()); + else + return Next(Last); + break; + + case EndEvent: + if (m_Traverse) + m_Event = m_Schedule->Events()->Next(m_Event); + else + m_Event = NULL; + + if (m_Event != NULL) + m_State = Event; + else + m_State = EndChannel; + + return m_Client->Respond(-215, "e"); + + case EndChannel: + if (m_Schedules != NULL) { + m_Schedule = m_Schedules->Next(m_Schedule); + if (m_Schedule != NULL) { + if (m_Schedule->Events() != NULL) + m_Event = m_Schedule->Events()->First(); + m_State = Channel; + } + } + + if (m_Schedules == NULL || m_Schedule == NULL) + m_State = EndEPG; + + return m_Client->Respond(-215, "c"); + + case EndEPG: + Last = true; + return m_Client->Respond(215, "End of EPG data"); + } + return false; +} + +// --- cLSTCHandler ----------------------------------------------------------- + +class cLSTCHandler +{ +private: + cConnectionVTP *m_Client; + const cChannel *m_Channel; + char *m_Option; + int m_Errno; + char *m_Error; + bool m_Traverse; +public: + cLSTCHandler(cConnectionVTP *Client, const char *Option); + ~cLSTCHandler(); + bool Next(bool &Last); +}; + +cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_Channel(NULL), + m_Option(NULL), + m_Errno(0), + m_Error(NULL), + m_Traverse(false) +{ + if (!Channels.Lock(false, 500)) { + m_Errno = 451; + m_Error = strdup("Channels are being modified - try again"); + } else if (*Option) { + if (isnumber(Option)) { + m_Channel = Channels.GetByNumber(strtol(Option, NULL, 10)); + if (m_Channel == NULL) { + m_Errno = 501; + asprintf(&m_Error, "Channel \"%s\" not defined", Option); + return; + } + } else { + int i = 1; + m_Traverse = true; + m_Option = strdup(Option); + while (i <= Channels.MaxNumber()) { + m_Channel = Channels.GetByNumber(i, 1); + if (strcasestr(m_Channel->Name(), Option) != NULL) + break; + i = m_Channel->Number() + 1; + } + + if (i > Channels.MaxNumber()) { + m_Errno = 501; + asprintf(&m_Error, "Channel \"%s\" not defined", Option); + return; + } + } + } else if (Channels.MaxNumber() >= 1) { + m_Channel = Channels.GetByNumber(1, 1); + m_Traverse = true; + } else { + m_Errno = 550; + m_Error = strdup("No channels defined"); + } +} + +cLSTCHandler::~cLSTCHandler() +{ + Channels.Unlock(); + if (m_Error != NULL) + free(m_Error); + if (m_Option != NULL) + free(m_Option); +} + +bool cLSTCHandler::Next(bool &Last) +{ + if (m_Error != NULL) { + Last = true; + cString str(m_Error, true); + m_Error = NULL; + return m_Client->Respond(m_Errno, *str); + } + + int number; + char *buffer; + + number = m_Channel->Number(); + buffer = strdup(*m_Channel->ToText()); + buffer[strlen(buffer) - 1] = '\0'; // remove \n + cString str(buffer, true); + + Last = true; + if (m_Traverse) { + int i = m_Channel->Number() + 1; + while (i <= Channels.MaxNumber()) { + m_Channel = Channels.GetByNumber(i, 1); + if (m_Channel != NULL) { + if (m_Option == NULL || strcasestr(m_Channel->Name(), + m_Option) != NULL) + break; + i = m_Channel->Number() + 1; + } else { + m_Errno = 501; + asprintf(&m_Error, "Channel \"%d\" not found", i); + } + } + + if (i < Channels.MaxNumber()) + Last = false; + } + + return m_Client->Respond(Last ? 250 : -250, "%d %s", number, buffer); +} + +// --- cLSTTHandler ----------------------------------------------------------- + +class cLSTTHandler +{ +private: + cConnectionVTP *m_Client; + cTimer *m_Timer; + int m_Index; + int m_Errno; + char *m_Error; + bool m_Traverse; +public: + cLSTTHandler(cConnectionVTP *Client, const char *Option); + ~cLSTTHandler(); + bool Next(bool &Last); +}; + +cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_Timer(NULL), + m_Index(0), + m_Errno(0), + m_Error(NULL), + m_Traverse(false) +{ + if (*Option) { + if (isnumber(Option)) { + m_Timer = Timers.Get(strtol(Option, NULL, 10) - 1); + if (m_Timer == NULL) { + m_Errno = 501; + asprintf(&m_Error, "Timer \"%s\" not defined", Option); + } + } else { + m_Errno = 501; + asprintf(&m_Error, "Error in timer number \"%s\"", Option); + } + } else if (Timers.Count()) { + m_Traverse = true; + m_Index = 0; + m_Timer = Timers.Get(m_Index); + if (m_Timer == NULL) { + m_Errno = 501; + asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1); + } + } else { + m_Errno = 550; + m_Error = strdup("No timers defined"); + } +} + +cLSTTHandler::~cLSTTHandler() +{ + if (m_Error != NULL) + free(m_Error); +} + +bool cLSTTHandler::Next(bool &Last) +{ + if (m_Error != NULL) { + Last = true; + cString str(m_Error, true); + m_Error = NULL; + return m_Client->Respond(m_Errno, *str); + } + + bool result; + char *buffer; + Last = !m_Traverse || m_Index >= Timers.Count() - 1; + buffer = strdup(*m_Timer->ToText()); + buffer[strlen(buffer) - 1] = '\0'; // strip \n + result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Timer->Index() + 1, + buffer); + free(buffer); + + if (m_Traverse && !Last) { + m_Timer = Timers.Get(++m_Index); + if (m_Timer == NULL) { + m_Errno = 501; + asprintf(&m_Error, "Timer \"%d\" not found", m_Index + 1); + } + } + return result; +} + +// --- cConnectionVTP --------------------------------------------------------- + +cConnectionVTP::cConnectionVTP(void): + cServerConnection("VTP"), + m_LiveSocket(NULL), + m_LiveStreamer(NULL), + m_LastCommand(NULL), + m_NoTSPIDS(false), + m_LSTEHandler(NULL), + m_LSTCHandler(NULL), + m_LSTTHandler(NULL) +{ +} + +cConnectionVTP::~cConnectionVTP() +{ + if (m_LastCommand != NULL) + free(m_LastCommand); + delete m_LiveStreamer; + delete m_LiveSocket; + delete m_LSTTHandler; + delete m_LSTCHandler; + delete m_LSTEHandler; +} + +void cConnectionVTP::Welcome(void) +{ Respond(220, "Welcome to Video Disk Recorder (VTP)"); } -void cConnectionVTP::Reject(void) { +void cConnectionVTP::Reject(void) +{ Respond(221, "Too many clients or client not allowed to connect"); cServerConnection::Reject(); } -void cConnectionVTP::Detach(void) { +void cConnectionVTP::Detach(void) +{ if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } -void cConnectionVTP::Attach(void) { +void cConnectionVTP::Attach(void) +{ if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } -bool cConnectionVTP::Command(char *Cmd) { - char *ep; - if ((ep = strchr(Cmd, ' ')) != NULL) - *(ep++) = '\0'; - else - ep = Cmd + strlen(Cmd); +bool cConnectionVTP::Command(char *Cmd) +{ + char *param = NULL; - if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(ep); - else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(ep); - else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(ep); - else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(ep); - else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(ep); - else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(ep); - else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(ep); - else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(ep); - else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(ep); - else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(ep); - else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(ep); + if (Cmd != NULL) { + if (m_LastCommand != NULL) { + esyslog("ERROR: streamdev: protocol violation (VTP) from %s:%d", + RemoteIp().c_str(), RemotePort()); + return false; + } + + if ((param = strchr(Cmd, ' ')) != NULL) + *(param++) = '\0'; + else + param = Cmd + strlen(Cmd); + m_LastCommand = strdup(Cmd); + } else { + Cmd = m_LastCommand; + param = NULL; + } + + if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(param); + //else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param); + else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param); + else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param); + + if (param == NULL) { + esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d", + __FILE__, __LINE__); + return false; + } + + if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param); + else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param); + else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param); + else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param); + else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param); + else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param); + else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param); + else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(param); + else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(param); + else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(param); + else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(param); // Commands adopted from SVDRP - else if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(ep); - else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(ep); - else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(ep); - else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(ep); - else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(ep); - else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(ep); - else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(ep); + //else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(param); + else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(param); + else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(param); + else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(param); else - return Respond(500, (std::string)"Unknown Command '" + Cmd + "'"); + return Respond(500, "Unknown Command \"%s\"", Cmd); } -bool cConnectionVTP::CmdCAPS(char *Opts) { - if (strcasecmp(Opts, "TSPIDS") == 0) - return Respond(220, (std::string)"Capability \"" + Opts + "\" accepted"); - return Respond(561, (std::string)"Capability \"" + Opts + "\" not known"); +bool cConnectionVTP::CmdCAPS(char *Opts) +{ + char *buffer; + + if (strcasecmp(Opts, "TS") == 0) { + m_NoTSPIDS = true; + return Respond(220, "Ignored, capability \"%s\" accepted for " + "compatibility", Opts); + } + + if (strcasecmp(Opts, "TSPIDS") == 0) { + m_NoTSPIDS = false; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + return Respond(561, "Capability \"%s\" not known", Opts); } -bool cConnectionVTP::CmdPROV(char *Opts) { +bool cConnectionVTP::CmdPROV(char *Opts) +{ const cChannel *chan; int prio; char *ep; @@ -103,14 +583,15 @@ bool cConnectionVTP::CmdPROV(char *Opts) { Opts = skipspace(ep); if ((chan = ChannelFromString(Opts)) == NULL) - return Respond(550, (std::string)"Undefined channel \"" + Opts + "\""); + return Respond(550, "Undefined channel \"%s\"", Opts); return GetDevice(chan, prio) != NULL ? Respond(220, "Channel available") : Respond(560, "Channel not available"); } -bool cConnectionVTP::CmdPORT(char *Opts) { +bool cConnectionVTP::CmdPORT(char *Opts) +{ uint id, dataport = 0; char dataip[20]; char *ep, *ipoffs; @@ -120,8 +601,8 @@ bool cConnectionVTP::CmdPORT(char *Opts) { if (ep == Opts || !isspace(*ep)) return Respond(500, "Use: PORT Id Destination"); - if (id >= si_Count) - return Respond(501, (std::string)"Wrong connection id " + (const char*)itoa(id)); + if (id != 0) + return Respond(501, "Wrong connection id %d", id); Opts = skipspace(ep); n = 0; @@ -148,26 +629,27 @@ bool cConnectionVTP::CmdPORT(char *Opts) { isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport); - m_DataSockets[id] = new cTBSocket(SOCK_STREAM); - if (!m_DataSockets[id]->Connect(dataip, dataport)) { + m_LiveSocket = new cTBSocket(SOCK_STREAM); + if (!m_LiveSocket->Connect(dataip, dataport)) { esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s", dataip, dataport, strerror(errno)); - DELETENULL(m_DataSockets[id]); + DELETENULL(m_LiveSocket); return Respond(551, "Couldn't open data connection"); } if (id == siLive) - m_LiveStreamer->Start(m_DataSockets[id]); + m_LiveStreamer->Start(m_LiveSocket); return Respond(220, "Port command ok, data connection opened"); } -bool cConnectionVTP::CmdTUNE(char *Opts) { +bool cConnectionVTP::CmdTUNE(char *Opts) +{ const cChannel *chan; cDevice *dev; if ((chan = ChannelFromString(Opts)) == NULL) - return Respond(550, (std::string)"Undefined channel \"" + Opts + "\""); + return Respond(550, "Undefined channel \"%s\"", Opts); if ((dev = GetDevice(chan, 0)) == NULL) return Respond(560, "Channel not available"); @@ -177,13 +659,14 @@ bool cConnectionVTP::CmdTUNE(char *Opts) { delete m_LiveStreamer; m_LiveStreamer = new cStreamdevLiveStreamer(1); - m_LiveStreamer->SetChannel(chan, stTSPIDS); + m_LiveStreamer->SetChannel(chan, m_NoTSPIDS ? stTS : stTSPIDS); m_LiveStreamer->SetDevice(dev); return Respond(220, "Channel tuned"); } -bool cConnectionVTP::CmdADDP(char *Opts) { +bool cConnectionVTP::CmdADDP(char *Opts) +{ int pid; char *end; @@ -192,11 +675,12 @@ bool cConnectionVTP::CmdADDP(char *Opts) { return Respond(500, "Use: ADDP Pid"); return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true) - ? Respond(220, (std::string)"Pid " + (const char*)itoa(pid) + " available") - : Respond(560, (std::string)"Pid " + (const char*)itoa(pid) + " not available"); + ? Respond(220, "Pid %d available", pid) + : Respond(560, "Pid %d not available", pid); } -bool cConnectionVTP::CmdDELP(char *Opts) { +bool cConnectionVTP::CmdDELP(char *Opts) +{ int pid; char *end; @@ -205,11 +689,12 @@ bool cConnectionVTP::CmdDELP(char *Opts) { return Respond(500, "Use: DELP Pid"); return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false) - ? Respond(220, (std::string)"Pid " + (const char*)itoa(pid) + " stopped") - : Respond(560, (std::string)"Pid " + (const char*)itoa(pid) + " not transferring"); + ? Respond(220, "Pid %d stopped", pid) + : Respond(560, "Pid %d not transferring", pid); } -bool cConnectionVTP::CmdADDF(char *Opts) { +bool cConnectionVTP::CmdADDF(char *Opts) +{ #if VDRVERSNUM >= 10300 int pid, tid, mask; char *ep; @@ -230,14 +715,15 @@ bool cConnectionVTP::CmdADDF(char *Opts) { return Respond(500, "Use: ADDF Pid Tid Mask"); return m_LiveStreamer->SetFilter(pid, tid, mask, true) - ? Respond(220, (std::string)"Filter " + (const char*)itoa(pid) + " transferring") - : Respond(560, (std::string)"Filter " + (const char*)itoa(pid) + " not available"); + ? Respond(220, "Filter %d transferring", pid) + : Respond(560, "Filter %d not available", pid); #else return Respond(500, "ADDF known but unimplemented with VDR < 1.3.0"); #endif } -bool cConnectionVTP::CmdDELF(char *Opts) { +bool cConnectionVTP::CmdDELF(char *Opts) +{ #if VDRVERSNUM >= 10307 int pid, tid, mask; char *ep; @@ -258,14 +744,15 @@ bool cConnectionVTP::CmdDELF(char *Opts) { return Respond(500, "Use: DELF Pid Tid Mask"); return m_LiveStreamer->SetFilter(pid, tid, mask, false) - ? Respond(220, (std::string)"Filter " + (const char*)itoa(pid) + " stopped") - : Respond(560, (std::string)"Filter " + (const char*)itoa(pid) + " not transferring"); + ? Respond(220, "Filter %d stopped", pid) + : Respond(560, "Filter %d not transferring", pid); #else return Respond(500, "DELF known but unimplemented with VDR < 1.3.0"); #endif } -bool cConnectionVTP::CmdABRT(char *Opts) { +bool cConnectionVTP::CmdABRT(char *Opts) +{ uint id; char *ep; @@ -273,23 +760,22 @@ bool cConnectionVTP::CmdABRT(char *Opts) { if (ep == Opts || (*ep != '\0' && *ep != ' ')) return Respond(500, "Use: ABRT Id"); - cTimeMs starttime; - if (id == siLive) - DELETENULL(m_LiveStreamer); + switch (id) { + case 0: DELETENULL(m_LiveStreamer); break; + } - Dprintf("ABRT took %ld ms\n", starttime.Elapsed()); - DELETENULL(m_DataSockets[id]); + DELETENULL(m_LiveSocket); return Respond(220, "Data connection closed"); } -bool cConnectionVTP::CmdQUIT(char *Opts) { - if (!Respond(221, "Video Disk Recorder closing connection")) - return false; +bool cConnectionVTP::CmdQUIT(char *Opts) +{ DeferClose(); - return true; + return Respond(221, "Video Disk Recorder closing connection"); } -bool cConnectionVTP::CmdSUSP(char *Opts) { +bool cConnectionVTP::CmdSUSP(char *Opts) +{ if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive()) return Respond(220, "Server is suspended"); else if (StreamdevServerSetup.SuspendMode == smOffer @@ -300,56 +786,124 @@ bool cConnectionVTP::CmdSUSP(char *Opts) { return Respond(550, "Client may not suspend server"); } -// Functions adopted from SVDRP -#define INIT_WRAPPER() bool _res = true -#define Reply(x...) _res &= ReplyWrapper(x) -#define EXIT_WRAPPER() return _res +// Functions extended from SVDRP -bool cConnectionVTP::ReplyWrapper(int Code, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - char *buffer; - vasprintf(&buffer, fmt, ap); - int npos; - if (buffer[npos = strlen(buffer)-1] == '\n') - buffer[npos] = '\0'; - bool res = Respond(Code, buffer); - free(buffer); - return res; +template +bool cConnectionVTP::CmdLSTX(cHandler *&Handler, char *Option) +{ + if (Option != NULL) { + delete Handler; + Handler = new cHandler(this, Option); + } + + bool last, result = Handler->Next(last); + if (!result || last) + DELETENULL(Handler); + + return result; } -bool cConnectionVTP::CmdLSTE(char *Option) { -#if VDRVERSNUM < 10300 - cMutexLock MutexLock; -#else - cSchedulesLock MutexLock; -#endif +bool cConnectionVTP::CmdLSTE(char *Option) +{ + return CmdLSTX(m_LSTEHandler, Option); +} + +bool cConnectionVTP::CmdLSTC(char *Option) +{ + return CmdLSTX(m_LSTCHandler, Option); +} + +bool cConnectionVTP::CmdLSTT(char *Option) +{ + return CmdLSTX(m_LSTTHandler, Option); +} + +// Functions adopted from SVDRP +#define INIT_WRAPPER() bool _res +#define Reply(c,m...) _res = Respond(c,m) +#define EXIT_WRAPPER() return _res + +bool cConnectionVTP::CmdMODT(const char *Option) +{ INIT_WRAPPER(); - /* we need to create a blocking copy of the socket here */ - int dupfd = dup(*this); - fcntl(dupfd, F_SETFL, fcntl(dupfd, F_GETFL) & ~O_NONBLOCK); -#if VDRVERSNUM < 10300 - const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); -#else - const cSchedules *Schedules = cSchedules::Schedules(MutexLock); -#endif - if (Schedules) { - FILE *f = fdopen(dupfd, "w"); - if (f) { - Schedules->Dump(f, "215-"); - fflush(f); - Reply(215, "End of EPG data"); - fclose(f); - } - else - Reply(451, "Can't open file connection"); - } - else - Reply(451, "Can't get EPG data"); + if (*Option) { + char *tail; + int n = strtol(Option, &tail, 10); + if (tail && tail != Option) { + tail = skipspace(tail); + cTimer *timer = Timers.Get(n - 1); + if (timer) { + cTimer t = *timer; + if (strcasecmp(tail, "ON") == 0) + t.SetFlags(tfActive); + else if (strcasecmp(tail, "OFF") == 0) + t.ClrFlags(tfActive); + else if (!t.Parse(tail)) { + Reply(501, "Error in timer settings"); + EXIT_WRAPPER(); + } + *timer = t; + Timers.SetModified(); + isyslog("timer %s modified (%s)", *timer->ToDescr(), + timer->HasFlags(tfActive) ? "active" : "inactive"); + Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + } else + Reply(501, "Timer \"%d\" not defined", n); + } else + Reply(501, "Error in timer number"); + } else + Reply(501, "Missing timer settings"); EXIT_WRAPPER(); } -bool cConnectionVTP::CmdLSTR(char *Option) { +bool cConnectionVTP::CmdNEWT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + cTimer *timer = new cTimer; + if (timer->Parse(Option)) { + cTimer *t = Timers.GetTimer(timer); + if (!t) { + Timers.Add(timer); + Timers.SetModified(); + isyslog("timer %s added", *timer->ToDescr()); + Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + EXIT_WRAPPER(); + } else + Reply(550, "Timer already defined: %d %s", t->Index() + 1, + *t->ToText()); + } else + Reply(501, "Error in timer settings"); + delete timer; + } else + Reply(501, "Missing timer settings"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdDELT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + if (isnumber(Option)) { + cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1); + if (timer) { + if (!timer->Recording()) { + isyslog("deleting timer %s", *timer->ToDescr()); + Timers.Del(timer); + Timers.SetModified(); + Reply(250, "Timer \"%s\" deleted", Option); + } else + Reply(550, "Timer \"%s\" is recording", Option); + } else + Reply(501, "Timer \"%s\" not defined", Option); + } else + Reply(501, "Error in timer number \"%s\"", Option); + } else + Reply(501, "Missing timer number"); + EXIT_WRAPPER(); +} + +/*bool cConnectionVTP::CmdLSTR(char *Option) { INIT_WRAPPER(); bool recordings = Recordings.Load(); Recordings.Sort(); @@ -403,140 +957,23 @@ bool cConnectionVTP::CmdDELR(char *Option) { else Reply(501, "Missing recording number"); EXIT_WRAPPER(); -} +}*/ -bool cConnectionVTP::CmdLSTT(char *Option) { - INIT_WRAPPER(); - if (*Option) { - if (isnumber(Option)) { - cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1); - if (timer) - Reply(250, "%d %s", timer->Index() + 1, (const char*)timer->ToText(true)); - else - Reply(501, "Timer \"%s\" not defined", Option); - } - else - Reply(501, "Error in timer number \"%s\"", Option); - } - else if (Timers.Count()) { - for (int i = 0; i < Timers.Count(); i++) { - cTimer *timer = Timers.Get(i); - if (timer) - Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, (const char*)timer->ToText(true)); - else - Reply(501, "Timer \"%d\" not found", i + 1); - } - } - else - Reply(550, "No timers defined"); - EXIT_WRAPPER(); -} - -bool cConnectionVTP::CmdMODT(char *Option) { - INIT_WRAPPER(); - if (*Option) { - char *tail; - int n = strtol(Option, &tail, 10); - if (tail && tail != Option) { - tail = skipspace(tail); - cTimer *timer = Timers.Get(n - 1); - if (timer) { - cTimer t = *timer; - if (strcasecmp(tail, "ON") == 0) -#if VDRVERSNUM < 10300 - t.SetActive(taActive); -#else - t.SetFlags(tfActive); -#endif - else if (strcasecmp(tail, "OFF") == 0) -#if VDRVERSNUM < 10300 - t.SetActive(taInactive); -#else - t.ClrFlags(tfActive); -#endif - else if (!t.Parse(tail)) { - Reply(501, "Error in timer settings"); - EXIT_WRAPPER(); - } - *timer = t; - Timers.Save(); -#if VDRVERSNUM < 10300 - isyslog("timer %d modified (%s)", timer->Index() + 1, - timer->Active() ? "active" : "inactive"); -#else - isyslog("timer %d modified (%s)", timer->Index() + 1, - timer->HasFlags(tfActive) ? "active" : "inactive"); -#endif - Reply(250, "%d %s", timer->Index() + 1, (const char*)timer->ToText(true)); - } - else - Reply(501, "Timer \"%d\" not defined", n); - } - else - Reply(501, "Error in timer number"); - } - else - Reply(501, "Missing timer settings"); - EXIT_WRAPPER(); -} - -bool cConnectionVTP::CmdNEWT(char *Option) { - INIT_WRAPPER(); - if (*Option) { - cTimer *timer = new cTimer; - if (timer->Parse(Option)) { - cTimer *t = Timers.GetTimer(timer); - if (!t) { - Timers.Add(timer); - Timers.Save(); - isyslog("timer %d added", timer->Index() + 1); - Reply(250, "%d %s", timer->Index() + 1, (const char*)timer->ToText(true)); - EXIT_WRAPPER(); - } - else - Reply(550, "Timer already defined: %d %s", t->Index() + 1, (const char*)t->ToText(true)); - } - else - Reply(501, "Error in timer settings"); - delete timer; - } - else - Reply(501, "Missing timer settings"); - EXIT_WRAPPER(); -} - -bool cConnectionVTP::CmdDELT(char *Option) { - INIT_WRAPPER(); - if (*Option) { - if (isnumber(Option)) { - cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1); - if (timer) { - if (!timer->Recording()) { - Timers.Del(timer); - Timers.Save(); - isyslog("timer %s deleted", Option); - Reply(250, "Timer \"%s\" deleted", Option); - } - else - Reply(550, "Timer \"%s\" is recording", Option); - } - else - Reply(501, "Timer \"%s\" not defined", Option); - } - else - Reply(501, "Error in timer number \"%s\"", Option); - } - else - Reply(501, "Missing timer number"); - EXIT_WRAPPER(); -} - -bool cConnectionVTP::Respond(int Code, const std::string &Message) { +bool cConnectionVTP::Respond(int Code, const char *Message, ...) +{ char *buffer; - bool result; - asprintf(&buffer, "%03d%c%s", Code < 0 ? -Code : Code, Code < 0 ? '-' : ' ', Message.c_str()); - result = cServerConnection::Respond(buffer); - free(buffer); - return result; + va_list ap; + va_start(ap, Message); + vasprintf(&buffer, Message, ap); + va_end(ap); + cString str(buffer, true); + + if (Code >= 0 && m_LastCommand != NULL) { + free(m_LastCommand); + m_LastCommand = NULL; + } + + return cServerConnection::Respond("%03d%c%s", Code >= 0, + Code < 0 ? -Code : Code, + Code < 0 ? '-' : ' ', buffer); } - diff --git a/server/connectionVTP.h b/server/connectionVTP.h index 4645b65..a0e6a9c 100644 --- a/server/connectionVTP.h +++ b/server/connectionVTP.h @@ -3,17 +3,31 @@ #include "server/connection.h" -class cDevice; class cTBSocket; class cStreamdevLiveStreamer; +class cLSTEHandler; +class cLSTCHandler; +class cLSTTHandler; class cConnectionVTP: public cServerConnection { + friend class cLSTEHandler; + private: - cTBSocket *m_DataSockets[si_Count]; + cTBSocket *m_LiveSocket; cStreamdevLiveStreamer *m_LiveStreamer; - // Members adopted from SVDRP + char *m_LastCommand; + bool m_NoTSPIDS; + + // Members adopted for SVDRP cRecordings Recordings; + cLSTEHandler *m_LSTEHandler; + cLSTCHandler *m_LSTCHandler; + cLSTTHandler *m_LSTTHandler; + +protected: + template + bool CmdLSTX(cHandler *&Handler, char *Option); public: cConnectionVTP(void); @@ -25,7 +39,7 @@ public: virtual void Detach(void); virtual void Attach(void); - bool Command(char *Cmd); + virtual bool Command(char *Cmd); bool CmdCAPS(char *Opts); bool CmdPROV(char *Opts); bool CmdPORT(char *Opts); @@ -38,17 +52,21 @@ public: bool CmdQUIT(char *Opts); bool CmdSUSP(char *Opts); - // Commands adopted from SVDRP - bool ReplyWrapper(int Code, const char *fmt, ...); + // Thread-safe implementations of SVDRP commands bool CmdLSTE(char *Opts); - bool CmdLSTR(char *Opts); - bool CmdDELR(char *Opts); + bool CmdLSTC(char *Opts); bool CmdLSTT(char *Opts); - bool CmdMODT(char *Opts); - bool CmdNEWT(char *Opts); - bool CmdDELT(char *Opts); - bool Respond(int Code, const std::string &Message); + // Commands adopted from SVDRP + bool CmdMODT(const char *Option); + bool CmdNEWT(const char *Option); + bool CmdDELT(const char *Option); + + //bool CmdLSTR(char *Opts); + //bool CmdDELR(char *Opts); + + bool Respond(int Code, const char *Message, ...) + __attribute__ ((format (printf, 3, 4))); }; #endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H diff --git a/server/server.c b/server/server.c index 60f184e..6c54451 100644 --- a/server/server.c +++ b/server/server.c @@ -1,5 +1,5 @@ /* - * $Id: server.c,v 1.2 2005/02/08 17:22:35 lordjaxom Exp $ + * $Id: server.c,v 1.3 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "server/server.h" @@ -14,102 +14,101 @@ cSVDRPhosts StreamdevHosts; -cStreamdevServer *cStreamdevServer::m_Instance = NULL; +cStreamdevServer *cStreamdevServer::m_Instance = NULL; +cList cStreamdevServer::m_Servers; +cList cStreamdevServer::m_Clients; -cStreamdevServer::cStreamdevServer(void) -#if VDRVERSNUM >= 10300 - : cThread("Streamdev: server") -#endif +cStreamdevServer::cStreamdevServer(void): + cThread("streamdev server"), + m_Active(false) { - m_Active = false; - - StreamdevHosts.Load(AddDirectory(cPlugin::ConfigDirectory(), - "streamdevhosts.conf"), true); + Start(); } -cStreamdevServer::~cStreamdevServer() { - if (m_Active) Stop(); +cStreamdevServer::~cStreamdevServer() +{ + Stop(); } -void cStreamdevServer::Init(void) { +void cStreamdevServer::Initialize(void) +{ if (m_Instance == NULL) { + if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP); + if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP); + m_Instance = new cStreamdevServer; - if (StreamdevServerSetup.StartVTPServer) - m_Instance->Register(new cComponentVTP); - if (StreamdevServerSetup.StartHTTPServer) - m_Instance->Register(new cComponentHTTP); - m_Instance->Start(); } } -void cStreamdevServer::Exit(void) { - if (m_Instance != NULL) { - m_Instance->Stop(); - DELETENULL(m_Instance); +void cStreamdevServer::Destruct(void) +{ + DELETENULL(m_Instance); +} + +void cStreamdevServer::Stop(void) +{ + if (m_Active) { + m_Active = false; + Cancel(3); } } -void cStreamdevServer::Stop(void) { - m_Active = false; - Cancel(3); -} - -void cStreamdevServer::Register(cServerComponent *Server) { +void cStreamdevServer::Register(cServerComponent *Server) +{ m_Servers.Add(Server); } -void cStreamdevServer::Action(void) { - cTBSelect select; - -#if VDRVERSNUM < 10300 - isyslog("Streamdev: Server thread started (pid=%d)", getpid()); -#endif - +void cStreamdevServer::Action(void) +{ m_Active = true; /* Initialize Server components, deleting those that failed */ for (cServerComponent *c = m_Servers.First(); c;) { cServerComponent *next = m_Servers.Next(c); - if (!c->Init()) + if (!c->Initialize()) m_Servers.Del(c); c = next; } - if (!m_Servers.Count()) { - esyslog("Streamdev: No server components registered, exiting"); + if (m_Servers.Count() == 0) { + esyslog("ERROR: no streamdev server activated, exiting"); m_Active = false; } + cTBSelect select; while (m_Active) { select.Clear(); /* Ask all Server components to register to the selector */ for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) - c->AddSelect(select); + select.Add(c->Socket(), false); /* Ask all Client connections to register to the selector */ for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) - s->AddSelect(select); + { + select.Add(s->Socket(), false); + if (s->HasData()) + select.Add(s->Socket(), true); + } - if (select.Select(1000) < 0) { - if (!m_Active) // Exit was requested while polling - continue; - esyslog("Streamdev: Fatal error, server exiting: %s", strerror(errno)); - m_Active = false; + if (select.Select() < 0) { + if (m_Active) // no exit was requested while polling + esyslog("fatal error, server exiting: %m"); + break; } /* Ask all Server components to act on signalled sockets */ - for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) { - cServerConnection *client; - if ((client = c->CanAct(select)) != NULL) { + for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){ + if (select.CanRead(c->Socket())) { + cServerConnection *client = c->Accept(); m_Clients.Add(client); if (m_Clients.Count() > StreamdevServerSetup.MaxClients) { - esyslog("Streamdev: Too many clients, rejecting %s:%d", + esyslog("streamdev: too many clients, rejecting %s:%d", client->RemoteIp().c_str(), client->RemotePort()); client->Reject(); } else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) { - esyslog("Streamdev: Client from %s:%d not allowed to connect", + esyslog("streamdev: client %s:%d not allowed to connect", client->RemoteIp().c_str(), client->RemotePort()); client->Reject(); } else @@ -119,10 +118,18 @@ void cStreamdevServer::Action(void) { /* Ask all Client connections to act on signalled sockets */ for (cServerConnection *s = m_Clients.First(); s;) { + bool result = true; + + if (select.CanWrite(s->Socket())) + result = s->Write(); + + if (result && select.CanRead(s->Socket())) + result = s->Read(); + cServerConnection *next = m_Clients.Next(s); - if (!s->CanAct(select)) { - isyslog("Streamdev: Closing connection to %s:%d", - s->RemoteIp().c_str(), s->RemotePort()); + if (!result) { + isyslog("streamdev: closing streamdev connection to %s:%d", + s->RemoteIp().c_str(), s->RemotePort()); s->Close(); m_Clients.Del(s); } @@ -130,19 +137,17 @@ void cStreamdevServer::Action(void) { } } - while (m_Clients.Count()) { - cServerConnection *client = m_Clients.First(); - client->Close(); - m_Clients.Del(client); + while (m_Clients.Count() > 0) { + cServerConnection *s = m_Clients.First(); + s->Close(); + m_Clients.Del(s); } - while (m_Servers.Count()) { - cServerComponent *server = m_Servers.First(); - server->Exit(); - m_Servers.Del(server); + while (m_Servers.Count() > 0) { + cServerComponent *c = m_Servers.First(); + c->Destruct(); + m_Servers.Del(c); } -#if VDRVERSNUM < 10300 - isyslog("Streamdev: Server thread stopped"); -#endif + m_Active = false; } diff --git a/server/server.h b/server/server.h index bf40f37..af574f5 100644 --- a/server/server.h +++ b/server/server.h @@ -1,5 +1,5 @@ /* - * $Id: server.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $ + * $Id: server.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEV_SERVER_H @@ -10,33 +10,36 @@ #include "server/component.h" #include "server/connection.h" +#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(), "streamdevhosts.conf")) + class cStreamdevServer: public cThread { private: bool m_Active; - cServerComponents m_Servers; - cServerConnections m_Clients; - - static cStreamdevServer *m_Instance; + static cStreamdevServer *m_Instance; + static cList m_Servers; + static cList m_Clients; protected: + void Stop(void); + virtual void Action(void); - void Stop(void); + static void Register(cServerComponent *Server); public: cStreamdevServer(void); virtual ~cStreamdevServer(); - void Register(cServerComponent *Server); - - static void Init(void); - static void Exit(void); + static void Initialize(void); + static void Destruct(void); static bool Active(void); }; -inline bool cStreamdevServer::Active(void) { - return m_Instance && m_Instance->m_Clients.Count() > 0; +inline bool cStreamdevServer::Active(void) +{ + return m_Instance != NULL + && m_Instance->m_Clients.Count() > 0; } extern cSVDRPhosts StreamdevHosts; diff --git a/server/setup.c b/server/setup.c index bd8fb67..2589fec 100644 --- a/server/setup.c +++ b/server/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $ + * $Id: setup.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #include @@ -72,7 +72,7 @@ void cStreamdevServerMenuSetupPage::Store(void) { || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) { restart = true; - cStreamdevServer::Exit(); + cStreamdevServer::Destruct(); } SetupStore("MaxClients", m_NewSetup.MaxClients); @@ -89,6 +89,6 @@ void cStreamdevServerMenuSetupPage::Store(void) { StreamdevServerSetup = m_NewSetup; if (restart) - cStreamdevServer::Init(); + cStreamdevServer::Initialize(); } diff --git a/server/streamer.c b/server/streamer.c index b64ffdd..582fc6a 100644 --- a/server/streamer.c +++ b/server/streamer.c @@ -1,5 +1,5 @@ /* - * $Id: streamer.c,v 1.13 2005/04/30 19:41:08 lordjaxom Exp $ + * $Id: streamer.c,v 1.14 2005/05/09 20:22:29 lordjaxom Exp $ */ #include @@ -16,7 +16,8 @@ // --- cStreamdevWriter ------------------------------------------------------- -cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer): +cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket, + cStreamdevStreamer *Streamer): cThread("streamdev-writer"), m_Streamer(Streamer), m_Socket(Socket), @@ -45,7 +46,7 @@ void cStreamdevWriter::Action(void) offset = 0; } - if (block) { + if (block != NULL) { sel.Clear(); sel.Add(*m_Socket, true); if (sel.Select(500) == -1) { @@ -130,8 +131,6 @@ void cStreamdevStreamer::Stop(void) void cStreamdevStreamer::Action(void) { - int max = 0; - m_Active = true; while (m_Active) { int got; diff --git a/streamdev-server.c b/streamdev-server.c index b09a85d..f0bfeac 100644 --- a/streamdev-server.c +++ b/streamdev-server.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: streamdev-server.c,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $ + * $Id: streamdev-server.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #include "streamdev-server.h" @@ -14,59 +14,73 @@ const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server"; -cPluginStreamdevServer::cPluginStreamdevServer(void) { +cPluginStreamdevServer::cPluginStreamdevServer(void) +{ } -cPluginStreamdevServer::~cPluginStreamdevServer() { - cStreamdevServer::Exit(); +cPluginStreamdevServer::~cPluginStreamdevServer() +{ } -const char *cPluginStreamdevServer::Description(void) { +const char *cPluginStreamdevServer::Description(void) +{ return tr(DESCRIPTION); } -bool cPluginStreamdevServer::Start(void) { +bool cPluginStreamdevServer::Start(void) +{ i18n_name = Name(); RegisterI18n(Phrases); - if (!StreamdevHosts.Load(STREAMDEVHOSTS, true, true)) { - esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTS); + if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) { + esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH); fprintf(stderr, "streamdev-server: error while loading %s\n"); - if (access(STREAMDEVHOSTS, F_OK) != 0) + if (access(STREAMDEVHOSTSPATH, F_OK) != 0) { fprintf(stderr, " Please install streamdevhosts.conf into the path " "printed above. Without it\n" - " no client will be able to access your streaming-" - "server. An example can be\n" - " found together with this plugin's sources.\n"); + " no client will be able to access your streaming-" + "server. An example can be\n" + " found together with this plugin's sources.\n"); + } return false; } - cStreamdevServer::Init(); + cStreamdevServer::Initialize(); - return true; + return true; } -bool cPluginStreamdevServer::Active(void) { +void cPluginStreamdevServer::Stop(void) +{ + cStreamdevServer::Destruct(); +} + +bool cPluginStreamdevServer::Active(void) +{ return cStreamdevServer::Active(); } -const char *cPluginStreamdevServer::MainMenuEntry(void) { +const char *cPluginStreamdevServer::MainMenuEntry(void) +{ if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) return tr("Suspend Live TV"); return NULL; } -cOsdObject *cPluginStreamdevServer::MainMenuAction(void) { +cOsdObject *cPluginStreamdevServer::MainMenuAction(void) +{ cControl::Launch(new cSuspendCtl); return NULL; } -cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void) { - return new cStreamdevServerMenuSetupPage; +cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void) +{ + return new cStreamdevServerMenuSetupPage; } -bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value) { - return StreamdevServerSetup.SetupParse(Name, Value); +bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value) +{ + return StreamdevServerSetup.SetupParse(Name, Value); } VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this! diff --git a/streamdev-server.h b/streamdev-server.h index 9362aac..a86a5cd 100644 --- a/streamdev-server.h +++ b/streamdev-server.h @@ -1,5 +1,5 @@ /* - * $Id: streamdev-server.h,v 1.1 2004/12/30 22:43:59 lordjaxom Exp $ + * $Id: streamdev-server.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ */ #ifndef VDR_STREAMDEVSERVER_H @@ -14,16 +14,18 @@ private: static const char *DESCRIPTION; public: - cPluginStreamdevServer(void); - virtual ~cPluginStreamdevServer(); - virtual const char *Version(void) { return VERSION; } - virtual const char *Description(void); - virtual bool Start(void); + cPluginStreamdevServer(void); + virtual ~cPluginStreamdevServer(); + + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void); + virtual bool Start(void); + virtual void Stop(void); virtual bool Active(void); - virtual const char *MainMenuEntry(void); - virtual cOsdObject *MainMenuAction(void); - virtual cMenuSetupPage *SetupMenu(void); - virtual bool SetupParse(const char *Name, const char *Value); + virtual const char *MainMenuEntry(void); + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); }; #endif // VDR_STREAMDEVSERVER_H