summaryrefslogtreecommitdiff
path: root/server/connectionVTP.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/connectionVTP.c')
-rw-r--r--server/connectionVTP.c945
1 files changed, 691 insertions, 254 deletions
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;
+
+ }
+ }
+ }
+}
+
+cLSTEHandler::~cLSTEHandler()
+{
+ delete m_SchedulesLock;
+ if (m_Error != NULL)
+ free(m_Error);
+}
+
+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);
}
-cConnectionVTP::~cConnectionVTP() {
- if (m_LiveStreamer != NULL) delete m_LiveStreamer;
+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;
+}
- for (int idx = 0; idx < si_Count; ++idx)
- if (m_DataSockets[idx] != NULL) delete m_DataSockets[idx];
+// --- 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)
+{
}
-void cConnectionVTP::Welcome(void) {
+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);
-
- 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);
+bool cConnectionVTP::Command(char *Cmd)
+{
+ char *param = NULL;
+
+ 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 extended from SVDRP
+
+template<class cHandler>
+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)
+{
+ 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 = true
-#define Reply(x...) _res &= ReplyWrapper(x)
+#define INIT_WRAPPER() bool _res
+#define Reply(c,m...) _res = Respond(c,m)
#define EXIT_WRAPPER() return _res
-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;
+bool cConnectionVTP::CmdMODT(const 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)
+ 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::CmdLSTE(char *Option) {
-#if VDRVERSNUM < 10300
- cMutexLock MutexLock;
-#else
- cSchedulesLock MutexLock;
-#endif
+bool cConnectionVTP::CmdNEWT(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) {
+ 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) {
+/*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);
}
-