- added TS compatibility mode

This commit is contained in:
lordjaxom 2005-05-09 20:22:29 +00:00
parent 3eec47314d
commit 450c8fd4a7
18 changed files with 1055 additions and 601 deletions

View File

@ -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 #ifndef VDR_STREAMDEV_COMMON_H
@ -50,9 +50,6 @@ const cChannel *ChannelFromString(const char *String, int *Apid = NULL);
#define BUFOVERTIME 5000 #define BUFOVERTIME 5000
#define BUFOVERCOUNT 100 #define BUFOVERCOUNT 100
#define STREAMDEVHOSTS (const char*)AddDirectory(cPlugin::ConfigDirectory(), \
"streamdevhosts.conf")
#define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno)) #define POLLFAIL esyslog("Streamdev: Polling failed: %s", strerror(errno))
#define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno)) #define WRITEFAIL esyslog("Streamdev: Writing failed: %s", strerror(errno))
#define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno)) #define READFAIL esyslog("Streamdev: Reading failed: %s", strerror(errno))

View File

@ -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/component.h"
#include "server/connection.h" #include "server/connection.h"
#include <vdr/tools.h>
#include <string.h>
#include <errno.h>
cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
uint ListenPort) { uint ListenPort):
m_Protocol = Protocol; m_Protocol(Protocol),
m_ListenIp = ListenIp; m_ListenIp(ListenIp),
m_ListenPort = ListenPort; m_ListenPort(ListenPort)
{
} }
cServerComponent::~cServerComponent() { cServerComponent::~cServerComponent()
{
} }
bool cServerComponent::Init(void) { bool cServerComponent::Initialize(void)
{
if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) { if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) {
esyslog("Streamdev: Couldn't listen (%s) %s:%d: %s", m_Protocol, m_ListenIp, esyslog("Streamdev: Couldn't listen (%s) %s:%d: %m",
m_ListenPort, strerror(errno)); m_Protocol, m_ListenIp, m_ListenPort);
return false; return false;
} }
isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort); isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort);
return true; return true;
} }
void cServerComponent::Exit(void) { void cServerComponent::Destruct(void)
{
m_Listen.Close(); m_Listen.Close();
} }
cServerConnection *cServerComponent::CanAct(const cTBSelect &Select) { cServerConnection *cServerComponent::Accept(void)
if (Select.CanRead(m_Listen)) { {
cServerConnection *client = NewConnection(); cServerConnection *client = NewClient();
if (client->Accept(m_Listen)) { if (client->Accept(m_Listen)) {
isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol, isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol,
client->RemoteIp().c_str(), client->RemotePort()); client->RemoteIp().c_str(), client->RemotePort());
return client; return client;
} else { } else {
esyslog("Streamdev: Couldn't accept (%s): %s", m_Protocol, esyslog("Streamdev: Couldn't accept (%s): %m", m_Protocol);
strerror(errno)); delete client;
delete client;
}
} }
return NULL; return NULL;
} }

View File

@ -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 #ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
@ -22,29 +22,30 @@ private:
const char *m_ListenIp; const char *m_ListenIp;
uint m_ListenPort; uint m_ListenPort;
protected:
/* Returns a new connection object for Accept() */
virtual cServerConnection *NewClient(void) = 0;
public: public:
cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort); cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort);
virtual ~cServerComponent(); virtual ~cServerComponent();
/* Starts listening on the specified Port, override if you want to do things /* Starts listening on the specified Port, override if you want to do things
different */ different */
virtual bool Init(void); virtual bool Initialize(void);
/* Stops listening, override if you want to do things different */ /* 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 */ /* Adds the listening socket to the Select object */
virtual void AddSelect(cTBSelect &Select) const { Select.Add(m_Listen); } virtual void Add(cTBSelect &Select) const { Select.Add(m_Listen); }
/* Accepts the connection on a NewConnection() object and calls the /* Accepts the connection on a NewClient() object and calls the
Welcome() on it, override if you want to do things different */ Welcome() on it, override if you want to do things different */
virtual cServerConnection *CanAct(const cTBSelect &Select); virtual cServerConnection *Accept(void);
/* Returns a new connection object for CanAct */
virtual cServerConnection *NewConnection(void) const = 0;
};
class cServerComponents: public cList<cServerComponent> {
}; };
#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H #endif // VDR_STREAMDEV_SERVERS_COMPONENT_H

View File

@ -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" #include "server/componentHTTP.h"
@ -7,9 +7,12 @@
#include "server/setup.h" #include "server/setup.h"
cComponentHTTP::cComponentHTTP(void): cComponentHTTP::cComponentHTTP(void):
cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP, cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP,
StreamdevServerSetup.HTTPServerPort) { StreamdevServerSetup.HTTPServerPort)
{
} }
cComponentHTTP::~cComponentHTTP() { cServerConnection *cComponentHTTP::NewClient(void)
{
return new cConnectionHTTP;
} }

View File

@ -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 #ifndef VDR_STREAMDEV_HTTPSERVER_H
#define VDR_STREAMDEV_HTTPSERVER_H #define VDR_STREAMDEV_HTTPSERVER_H
#include "server/component.h" #include "server/component.h"
#include "server/connectionHTTP.h"
#include <tools/socket.h>
#include <tools/select.h>
class cComponentHTTP: public cServerComponent { class cComponentHTTP: public cServerComponent {
protected:
virtual cServerConnection *NewClient(void);
public: public:
cComponentHTTP(void); cComponentHTTP(void);
~cComponentHTTP(void);
virtual cServerConnection *NewConnection(void) const;
}; };
inline cServerConnection *cComponentHTTP::NewConnection(void) const {
return new cConnectionHTTP;
}
#endif // VDR_STREAMDEV_HTTPSERVER_H #endif // VDR_STREAMDEV_HTTPSERVER_H

View File

@ -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" #include "server/componentVTP.h"
@ -7,9 +7,12 @@
#include "server/setup.h" #include "server/setup.h"
cComponentVTP::cComponentVTP(void): cComponentVTP::cComponentVTP(void):
cServerComponent("VTP", StreamdevServerSetup.VTPBindIP, cServerComponent("VTP", StreamdevServerSetup.VTPBindIP,
StreamdevServerSetup.VTPServerPort) { StreamdevServerSetup.VTPServerPort)
{
} }
cComponentVTP::~cComponentVTP() { cServerConnection *cComponentVTP::NewClient(void)
{
return new cConnectionVTP;
} }

View File

@ -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 #ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H
#define VDR_STREAMDEV_SERVERS_SERVERVTP_H #define VDR_STREAMDEV_SERVERS_SERVERVTP_H
#include "server/component.h" #include "server/component.h"
#include "server/connectionVTP.h"
#include <tools/socket.h>
#include <tools/select.h>
class cComponentVTP: public cServerComponent { class cComponentVTP: public cServerComponent {
private: protected:
cTBSocket m_Listen; virtual cServerConnection *NewClient(void);
public: public:
cComponentVTP(void); cComponentVTP(void);
virtual ~cComponentVTP();
virtual cServerConnection *NewConnection(void) const;
}; };
inline cServerConnection *cComponentVTP::NewConnection(void) const {
return new cConnectionVTP;
}
#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H #endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H

View File

@ -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" #include "server/connection.h"
@ -11,53 +11,79 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
cServerConnection::cServerConnection(const char *Protocol) { cServerConnection::cServerConnection(const char *Protocol):
m_RdBytes = 0; m_Protocol(Protocol),
m_WrBytes = 0; m_DeferClose(false),
m_WrOffs = 0; m_Pending(false),
m_DeferClose = false; m_ReadBytes(0),
m_Protocol = Protocol; m_WriteBytes(0),
m_WriteIndex(0)
{
} }
cServerConnection::~cServerConnection() { cServerConnection::~cServerConnection()
{
} }
bool cServerConnection::CanAct(const cTBSelect &Select) { bool cServerConnection::Read(void)
if (Select.CanRead(*this)) { {
int b; int b;
if ((b = Read(m_RdBuf + m_RdBytes, sizeof(m_RdBuf) - m_RdBytes - 1)) < 0) { if ((b = cTBSocket::Read(m_ReadBuffer + m_ReadBytes,
esyslog("Streamdev: Read from client (%s) %s:%d failed: %s", m_Protocol, sizeof(m_ReadBuffer) - m_ReadBytes - 1)) < 0) {
RemoteIp().c_str(), RemotePort(), strerror(errno)); esyslog("ERROR: read from client (%s) %s:%d failed: %m",
return false; m_Protocol, RemoteIp().c_str(), RemotePort());
} 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();
} }
if (Select.CanWrite(*this)) { if (b == 0) {
int b; isyslog("client (%s) %s:%d has closed connection",
if ((b = Write(m_WrBuf + m_WrOffs, m_WrBytes - m_WrOffs)) < 0) { m_Protocol, RemoteIp().c_str(), RemotePort());
esyslog("Streamdev: Write to client (%s) %s:%d failed: %s", m_Protocol, return false;
RemoteIp().c_str(), RemotePort(), strerror(errno));
return false;
}
m_WrOffs += b;
if (m_WrOffs == m_WrBytes) {
m_WrBytes = 0;
m_WrOffs = 0;
}
} }
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) if (m_DeferClose)
return false; return false;
Flushed(); Flushed();
@ -65,42 +91,33 @@ bool cServerConnection::CanAct(const cTBSelect &Select) {
return true; return true;
} }
bool cServerConnection::ParseBuffer(void) { bool cServerConnection::Respond(const char *Message, bool Last, ...)
char *ep; {
bool res; 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) { if (m_WriteBytes + length + 2 > sizeof(m_WriteBuffer)) {
*ep = '\0'; esyslog("ERROR: streamdev: output buffer overflow (%s) for %s:%d",
if (ep > m_RdBuf && *(ep-1) == '\015') m_Protocol, RemoteIp().c_str(), RemotePort());
*(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());
return false; return false;
} }
Dprintf("OUT: |%s|\n", Message.c_str()); Dprintf("OUT: |%s|\n", buffer);
memcpy(m_WrBuf + m_WrBytes, Message.c_str(), Message.size()); memcpy(m_WriteBuffer + m_WriteBytes, buffer, length);
free(buffer);
m_WrBytes += Message.size(); m_WriteBytes += length;
m_WrBuf[m_WrBytes++] = '\015'; m_WriteBuffer[m_WriteBytes++] = '\015';
m_WrBuf[m_WrBytes++] = '\012'; m_WriteBuffer[m_WriteBytes++] = '\012';
m_Pending = !Last;
return true; return true;
} }
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) { cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
{
cDevice *device = NULL; cDevice *device = NULL;
/*Dprintf("+ Statistics:\n"); /*Dprintf("+ Statistics:\n");

View File

@ -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 #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
#define VDR_STREAMDEV_SERVER_CONNECTION_H #define VDR_STREAMDEV_SERVER_CONNECTION_H
#include "tools/socket.h" #include "tools/socket.h"
#include "tools/select.h"
#include "common.h" #include "common.h"
class cChannel; class cChannel;
@ -16,18 +14,32 @@ class cDevice;
/* Basic capabilities of a straight text-based protocol, most functions /* Basic capabilities of a straight text-based protocol, most functions
virtual to support more complicated protocols */ virtual to support more complicated protocols */
class cServerConnection: public cListObject, public cTBSocket { class cServerConnection: public cListObject, public cTBSocket
{
private: private:
char m_RdBuf[8192];
uint m_RdBytes;
char m_WrBuf[8192];
uint m_WrBytes;
uint m_WrOffs;
const char *m_Protocol; 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: public:
/* If you derive, specify a short string such as HTTP for Protocol, which /* 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 */ /* Gets called if the client has been rejected by the core */
virtual void Reject(void) { DeferClose(); } virtual void Reject(void) { DeferClose(); }
/* Adds itself to the Select object, if data can be received or if data is /* Get the client socket's file number */
to be sent. Override if necessary */ virtual int Socket(void) const { return (int)*this; }
virtual void AddSelect(cTBSelect &Select) const;
/* Receives incoming data and calls ParseBuffer on it. Also writes queued /* Determine if there is data to send or any command pending */
output data if possible. Override if necessary */ virtual bool HasData(void) const;
virtual bool CanAct(const cTBSelect &Select);
/* Called by CanAct(), parses the input buffer for full lines (terminated /* Gets called by server when the socket can accept more data. Writes
either by '\012' or '\015\012') and calls Command on them, if any */ the buffer filled up by Respond(). Calls Command(NULL) if there is a
virtual bool ParseBuffer(void); 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 */ /* Gets called by server when there is incoming data to read. Calls
virtual bool Command(char *Cmd) = 0; Command() for each line. Returns false in case of an error, or if
the connection shall be closed and removed by the server */
/* Will put Message into the response queue, which will be sent in the next virtual bool Read(void);
server cycle. Note that Message will be line-terminated by Respond */
bool Respond(const std::string &Message);
/* Will make the socket close after sending all queued output data */ /* Will make the socket close after sending all queued output data */
void DeferClose(void) { m_DeferClose = true; } void DeferClose(void) { m_DeferClose = true; }
@ -73,15 +82,9 @@ public:
virtual void Attach(void) = 0; virtual void Attach(void) = 0;
}; };
class cServerConnections: public cList<cServerConnection> { inline bool cServerConnection::HasData(void) const
}; {
return m_WriteBytes > 0 || m_Pending || m_DeferClose;
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);
} }
#endif // VDR_STREAMDEV_SERVER_CONNECTION_H #endif // VDR_STREAMDEV_SERVER_CONNECTION_H

View File

@ -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 <ctype.h> #include <ctype.h>
@ -45,7 +45,8 @@ bool cConnectionHTTP::Command(char *Cmd)
return false; // ??? shouldn't happen return false; // ??? shouldn't happen
} }
bool cConnectionHTTP::ProcessRequest(void) { bool cConnectionHTTP::ProcessRequest(void)
{
Dprintf("process\n"); Dprintf("process\n");
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) { if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
switch (m_Job) { switch (m_Job) {
@ -71,7 +72,7 @@ bool cConnectionHTTP::ProcessRequest(void) {
if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) { if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) {
return Respond("HTTP/1.0 200 OK") return Respond("HTTP/1.0 200 OK")
&& Respond("Content-Type: audio/mpeg") && Respond("Content-Type: audio/mpeg")
&& Respond((std::string)"icy-name: " + m_Channel->Name()) && Respond("icy-name: %s", true, m_Channel->Name())
&& Respond(""); && Respond("");
} else { } else {
return Respond("HTTP/1.0 200 OK") return Respond("HTTP/1.0 200 OK")
@ -90,7 +91,8 @@ bool cConnectionHTTP::ProcessRequest(void) {
return Respond("HTTP/1.0 400 Bad Request"); return Respond("HTTP/1.0 400 Bad Request");
} }
void cConnectionHTTP::Flushed(void) { void cConnectionHTTP::Flushed(void)
{
std::string line; std::string line;
if (m_Status != hsBody) if (m_Status != hsBody)
@ -132,7 +134,7 @@ void cConnectionHTTP::Flushed(void) {
} }
line += "</li>"; line += "</li>";
} }
if (!Respond(line)) if (!Respond(line.c_str()))
DeferClose(); DeferClose();
m_ListChannel = Channels.Next(m_ListChannel); m_ListChannel = Channels.Next(m_ListChannel);
break; 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 char *sp = Opts.c_str(), *ptr = sp, *ep;
const cChannel *chan; const cChannel *chan;
int apid = 0, pos; int apid = 0, pos;
@ -193,33 +196,3 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts) {
return true; return true;
} }
#if 0
bool cHTTPConnection::Listing(void) {
cChannel *chan;
cTBString line;
Respond(200, "OK");
Respond("Content-Type: text/html");
Respond("");
Respond("<html><head><title>VDR Channel Listing</title></head>");
Respond("<body><ul>");
for (chan = Channels.First(); chan != NULL; chan = Channels.Next(chan)) {
if (chan->GroupSep() && !*chan->Name())
continue;
if (chan->GroupSep())
line.Format("<li>--- %s ---</li>", chan->Name());
else
line.Format("<li><a href=\"http://%s:%d/%s\">%s</a></li>",
(const char*)m_Socket.LocalIp(), StreamdevServerSetup.HTTPServerPort,
chan->GetChannelID().ToString(), chan->Name());
Respond(line);
}
Respond("</ul></body></html>");
m_DeferClose = true;
return true;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -3,17 +3,31 @@
#include "server/connection.h" #include "server/connection.h"
class cDevice;
class cTBSocket; class cTBSocket;
class cStreamdevLiveStreamer; class cStreamdevLiveStreamer;
class cLSTEHandler;
class cLSTCHandler;
class cLSTTHandler;
class cConnectionVTP: public cServerConnection { class cConnectionVTP: public cServerConnection {
friend class cLSTEHandler;
private: private:
cTBSocket *m_DataSockets[si_Count]; cTBSocket *m_LiveSocket;
cStreamdevLiveStreamer *m_LiveStreamer; cStreamdevLiveStreamer *m_LiveStreamer;
// Members adopted from SVDRP char *m_LastCommand;
bool m_NoTSPIDS;
// Members adopted for SVDRP
cRecordings Recordings; cRecordings Recordings;
cLSTEHandler *m_LSTEHandler;
cLSTCHandler *m_LSTCHandler;
cLSTTHandler *m_LSTTHandler;
protected:
template<class cHandler>
bool CmdLSTX(cHandler *&Handler, char *Option);
public: public:
cConnectionVTP(void); cConnectionVTP(void);
@ -25,7 +39,7 @@ public:
virtual void Detach(void); virtual void Detach(void);
virtual void Attach(void); virtual void Attach(void);
bool Command(char *Cmd); virtual bool Command(char *Cmd);
bool CmdCAPS(char *Opts); bool CmdCAPS(char *Opts);
bool CmdPROV(char *Opts); bool CmdPROV(char *Opts);
bool CmdPORT(char *Opts); bool CmdPORT(char *Opts);
@ -38,17 +52,21 @@ public:
bool CmdQUIT(char *Opts); bool CmdQUIT(char *Opts);
bool CmdSUSP(char *Opts); bool CmdSUSP(char *Opts);
// Commands adopted from SVDRP // Thread-safe implementations of SVDRP commands
bool ReplyWrapper(int Code, const char *fmt, ...);
bool CmdLSTE(char *Opts); bool CmdLSTE(char *Opts);
bool CmdLSTR(char *Opts); bool CmdLSTC(char *Opts);
bool CmdDELR(char *Opts);
bool CmdLSTT(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 #endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H

View File

@ -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" #include "server/server.h"
@ -14,102 +14,101 @@
cSVDRPhosts StreamdevHosts; cSVDRPhosts StreamdevHosts;
cStreamdevServer *cStreamdevServer::m_Instance = NULL; cStreamdevServer *cStreamdevServer::m_Instance = NULL;
cList<cServerComponent> cStreamdevServer::m_Servers;
cList<cServerConnection> cStreamdevServer::m_Clients;
cStreamdevServer::cStreamdevServer(void) cStreamdevServer::cStreamdevServer(void):
#if VDRVERSNUM >= 10300 cThread("streamdev server"),
: cThread("Streamdev: server") m_Active(false)
#endif
{ {
m_Active = false; Start();
StreamdevHosts.Load(AddDirectory(cPlugin::ConfigDirectory(),
"streamdevhosts.conf"), true);
} }
cStreamdevServer::~cStreamdevServer() { cStreamdevServer::~cStreamdevServer()
if (m_Active) Stop(); {
Stop();
} }
void cStreamdevServer::Init(void) { void cStreamdevServer::Initialize(void)
{
if (m_Instance == NULL) { if (m_Instance == NULL) {
if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP);
if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);
m_Instance = new cStreamdevServer; 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) { void cStreamdevServer::Destruct(void)
if (m_Instance != NULL) { {
m_Instance->Stop(); DELETENULL(m_Instance);
DELETENULL(m_Instance); }
void cStreamdevServer::Stop(void)
{
if (m_Active) {
m_Active = false;
Cancel(3);
} }
} }
void cStreamdevServer::Stop(void) { void cStreamdevServer::Register(cServerComponent *Server)
m_Active = false; {
Cancel(3);
}
void cStreamdevServer::Register(cServerComponent *Server) {
m_Servers.Add(Server); m_Servers.Add(Server);
} }
void cStreamdevServer::Action(void) { void cStreamdevServer::Action(void)
cTBSelect select; {
#if VDRVERSNUM < 10300
isyslog("Streamdev: Server thread started (pid=%d)", getpid());
#endif
m_Active = true; m_Active = true;
/* Initialize Server components, deleting those that failed */ /* Initialize Server components, deleting those that failed */
for (cServerComponent *c = m_Servers.First(); c;) { for (cServerComponent *c = m_Servers.First(); c;) {
cServerComponent *next = m_Servers.Next(c); cServerComponent *next = m_Servers.Next(c);
if (!c->Init()) if (!c->Initialize())
m_Servers.Del(c); m_Servers.Del(c);
c = next; c = next;
} }
if (!m_Servers.Count()) { if (m_Servers.Count() == 0) {
esyslog("Streamdev: No server components registered, exiting"); esyslog("ERROR: no streamdev server activated, exiting");
m_Active = false; m_Active = false;
} }
cTBSelect select;
while (m_Active) { while (m_Active) {
select.Clear(); select.Clear();
/* Ask all Server components to register to the selector */ /* Ask all Server components to register to the selector */
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) 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 */ /* Ask all Client connections to register to the selector */
for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) 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 (select.Select() < 0) {
if (!m_Active) // Exit was requested while polling if (m_Active) // no exit was requested while polling
continue; esyslog("fatal error, server exiting: %m");
esyslog("Streamdev: Fatal error, server exiting: %s", strerror(errno)); break;
m_Active = false;
} }
/* Ask all Server components to act on signalled sockets */ /* Ask all Server components to act on signalled sockets */
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) { for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
cServerConnection *client; if (select.CanRead(c->Socket())) {
if ((client = c->CanAct(select)) != NULL) { cServerConnection *client = c->Accept();
m_Clients.Add(client); m_Clients.Add(client);
if (m_Clients.Count() > StreamdevServerSetup.MaxClients) { 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->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) { } 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->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();
} else } else
@ -119,10 +118,18 @@ void cStreamdevServer::Action(void) {
/* Ask all Client connections to act on signalled sockets */ /* Ask all Client connections to act on signalled sockets */
for (cServerConnection *s = m_Clients.First(); s;) { 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); cServerConnection *next = m_Clients.Next(s);
if (!s->CanAct(select)) { if (!result) {
isyslog("Streamdev: Closing connection to %s:%d", isyslog("streamdev: closing streamdev connection to %s:%d",
s->RemoteIp().c_str(), s->RemotePort()); s->RemoteIp().c_str(), s->RemotePort());
s->Close(); s->Close();
m_Clients.Del(s); m_Clients.Del(s);
} }
@ -130,19 +137,17 @@ void cStreamdevServer::Action(void) {
} }
} }
while (m_Clients.Count()) { while (m_Clients.Count() > 0) {
cServerConnection *client = m_Clients.First(); cServerConnection *s = m_Clients.First();
client->Close(); s->Close();
m_Clients.Del(client); m_Clients.Del(s);
} }
while (m_Servers.Count()) { while (m_Servers.Count() > 0) {
cServerComponent *server = m_Servers.First(); cServerComponent *c = m_Servers.First();
server->Exit(); c->Destruct();
m_Servers.Del(server); m_Servers.Del(c);
} }
#if VDRVERSNUM < 10300 m_Active = false;
isyslog("Streamdev: Server thread stopped");
#endif
} }

View File

@ -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 #ifndef VDR_STREAMDEV_SERVER_H
@ -10,33 +10,36 @@
#include "server/component.h" #include "server/component.h"
#include "server/connection.h" #include "server/connection.h"
#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(), "streamdevhosts.conf"))
class cStreamdevServer: public cThread { class cStreamdevServer: public cThread {
private: private:
bool m_Active; bool m_Active;
cServerComponents m_Servers; static cStreamdevServer *m_Instance;
cServerConnections m_Clients; static cList<cServerComponent> m_Servers;
static cList<cServerConnection> m_Clients;
static cStreamdevServer *m_Instance;
protected: protected:
void Stop(void);
virtual void Action(void); virtual void Action(void);
void Stop(void); static void Register(cServerComponent *Server);
public: public:
cStreamdevServer(void); cStreamdevServer(void);
virtual ~cStreamdevServer(); virtual ~cStreamdevServer();
void Register(cServerComponent *Server); static void Initialize(void);
static void Destruct(void);
static void Init(void);
static void Exit(void);
static bool Active(void); static bool Active(void);
}; };
inline bool cStreamdevServer::Active(void) { inline bool cStreamdevServer::Active(void)
return m_Instance && m_Instance->m_Clients.Count() > 0; {
return m_Instance != NULL
&& m_Instance->m_Clients.Count() > 0;
} }
extern cSVDRPhosts StreamdevHosts; extern cSVDRPhosts StreamdevHosts;

View File

@ -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 <vdr/menuitems.h> #include <vdr/menuitems.h>
@ -72,7 +72,7 @@ void cStreamdevServerMenuSetupPage::Store(void) {
|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) { || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) {
restart = true; restart = true;
cStreamdevServer::Exit(); cStreamdevServer::Destruct();
} }
SetupStore("MaxClients", m_NewSetup.MaxClients); SetupStore("MaxClients", m_NewSetup.MaxClients);
@ -89,6 +89,6 @@ void cStreamdevServerMenuSetupPage::Store(void) {
StreamdevServerSetup = m_NewSetup; StreamdevServerSetup = m_NewSetup;
if (restart) if (restart)
cStreamdevServer::Init(); cStreamdevServer::Initialize();
} }

View File

@ -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 <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
@ -16,7 +16,8 @@
// --- cStreamdevWriter ------------------------------------------------------- // --- cStreamdevWriter -------------------------------------------------------
cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer): cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket,
cStreamdevStreamer *Streamer):
cThread("streamdev-writer"), cThread("streamdev-writer"),
m_Streamer(Streamer), m_Streamer(Streamer),
m_Socket(Socket), m_Socket(Socket),
@ -45,7 +46,7 @@ void cStreamdevWriter::Action(void)
offset = 0; offset = 0;
} }
if (block) { if (block != NULL) {
sel.Clear(); sel.Clear();
sel.Add(*m_Socket, true); sel.Add(*m_Socket, true);
if (sel.Select(500) == -1) { if (sel.Select(500) == -1) {
@ -130,8 +131,6 @@ void cStreamdevStreamer::Stop(void)
void cStreamdevStreamer::Action(void) void cStreamdevStreamer::Action(void)
{ {
int max = 0;
m_Active = true; m_Active = true;
while (m_Active) { while (m_Active) {
int got; int got;

View File

@ -3,7 +3,7 @@
* *
* See the README file for copyright information and how to reach the author. * 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" #include "streamdev-server.h"
@ -14,59 +14,73 @@
const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server"; const char *cPluginStreamdevServer::DESCRIPTION = "VDR Streaming Server";
cPluginStreamdevServer::cPluginStreamdevServer(void) { cPluginStreamdevServer::cPluginStreamdevServer(void)
{
} }
cPluginStreamdevServer::~cPluginStreamdevServer() { cPluginStreamdevServer::~cPluginStreamdevServer()
cStreamdevServer::Exit(); {
} }
const char *cPluginStreamdevServer::Description(void) { const char *cPluginStreamdevServer::Description(void)
{
return tr(DESCRIPTION); return tr(DESCRIPTION);
} }
bool cPluginStreamdevServer::Start(void) { bool cPluginStreamdevServer::Start(void)
{
i18n_name = Name(); i18n_name = Name();
RegisterI18n(Phrases); RegisterI18n(Phrases);
if (!StreamdevHosts.Load(STREAMDEVHOSTS, true, true)) { if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTS); esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
fprintf(stderr, "streamdev-server: error while loading %s\n"); 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 " fprintf(stderr, " Please install streamdevhosts.conf into the path "
"printed above. Without it\n" "printed above. Without it\n"
" no client will be able to access your streaming-" " no client will be able to access your streaming-"
"server. An example can be\n" "server. An example can be\n"
" found together with this plugin's sources.\n"); " found together with this plugin's sources.\n");
}
return false; 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(); return cStreamdevServer::Active();
} }
const char *cPluginStreamdevServer::MainMenuEntry(void) { const char *cPluginStreamdevServer::MainMenuEntry(void)
{
if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive()) if (StreamdevServerSetup.SuspendMode == smOffer && !cSuspendCtl::IsActive())
return tr("Suspend Live TV"); return tr("Suspend Live TV");
return NULL; return NULL;
} }
cOsdObject *cPluginStreamdevServer::MainMenuAction(void) { cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
{
cControl::Launch(new cSuspendCtl); cControl::Launch(new cSuspendCtl);
return NULL; return NULL;
} }
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void) { cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
return new cStreamdevServerMenuSetupPage; {
return new cStreamdevServerMenuSetupPage;
} }
bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value) { bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
return StreamdevServerSetup.SetupParse(Name, Value); {
return StreamdevServerSetup.SetupParse(Name, Value);
} }
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this! VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!

View File

@ -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 #ifndef VDR_STREAMDEVSERVER_H
@ -14,16 +14,18 @@ private:
static const char *DESCRIPTION; static const char *DESCRIPTION;
public: public:
cPluginStreamdevServer(void); cPluginStreamdevServer(void);
virtual ~cPluginStreamdevServer(); virtual ~cPluginStreamdevServer();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void); virtual const char *Version(void) { return VERSION; }
virtual bool Start(void); virtual const char *Description(void);
virtual bool Start(void);
virtual void Stop(void);
virtual bool Active(void); virtual bool Active(void);
virtual const char *MainMenuEntry(void); virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void); virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value); virtual bool SetupParse(const char *Name, const char *Value);
}; };
#endif // VDR_STREAMDEVSERVER_H #endif // VDR_STREAMDEVSERVER_H