Logo Search packages:      
Sourcecode: vdr-plugin-streamdev version File versions  Download package

connectionVTP.c

/*
 *  $Id: connectionVTP.c,v 1.17 2008/03/13 16:01:18 schmirl Exp $
 */
 
#include "server/connectionVTP.h"
#include "server/livestreamer.h"
#include "server/suspend.h"
#include "setup.h"

#include <vdr/tools.h>
#include <tools/select.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>

/* 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: Requested action not taken
      551: Data connection not accepted
      560: Channel not available currently
      561: Capability not known
      562: Pid not available currently
      563: Recording not available (currently?)
*/

// --- 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)
{
      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);
                  if (channel != NULL) {
                        m_State = Event;
                        return m_Client->Respond(-215, "C %s %s", 
                                           *channel->GetChannelID().ToString(),
                                           channel->Name());
                  } else {
                        esyslog("ERROR: vdr streamdev: unable to find channel %s by ID",
                                       *m_Schedule->ChannelID().ToString());
                        m_State = EndChannel;
                        return Next(Last);
                  }
            } else {
                  m_State = EndEPG;
                  return Next(Last);
            }
            break;

      case Event:
            if (m_Event != NULL) {
                  m_State = Title;
#ifdef __FreeBSD__
                  return m_Client->Respond(-215, "E %u %d %d %X", m_Event->EventID(),
#else
                  return m_Client->Respond(-215, "E %u %ld %d %X", m_Event->EventID(),
#endif
                                           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())
#ifdef __FreeBSD__
                  return m_Client->Respond(-215, "V %d", m_Event->Vps());
#else
                  return m_Client->Respond(-215, "V %ld", m_Event->Vps());
#endif
            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_FilterSocket(NULL),
            m_FilterStreamer(NULL),
            m_LastCommand(NULL),
            m_StreamType(stTSPIDS),
            m_FiltersSupport(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_FilterStreamer;
      delete m_FilterSocket;
      delete m_LSTTHandler;
      delete m_LSTCHandler;
      delete m_LSTEHandler;
}

inline bool cConnectionVTP::Abort(void) const
{
      return m_LiveStreamer && m_LiveStreamer->Abort();
}

void cConnectionVTP::Welcome(void) 
{
      Respond(220, "Welcome to Video Disk Recorder (VTP)");
}

void cConnectionVTP::Reject(void)
{
      Respond(221, "Too many clients or client not allowed to connect");
      cServerConnection::Reject();
}

void cConnectionVTP::Detach(void) 
{
      if (m_LiveStreamer) m_LiveStreamer->Detach();
      if (m_FilterStreamer) m_FilterStreamer->Detach();
}

void cConnectionVTP::Attach(void) 
{
      if (m_LiveStreamer) m_LiveStreamer->Attach();
      if (m_FilterStreamer) m_FilterStreamer->Attach();
}

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();
      else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP();
      // Commands adopted from SVDRP
      //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, "Unknown Command \"%s\"", Cmd);
}

bool cConnectionVTP::CmdCAPS(char *Opts) 
{
      if (strcasecmp(Opts, "TS") == 0) {
            m_StreamType = stTS;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }

      if (strcasecmp(Opts, "TSPIDS") == 0) {
            m_StreamType = stTSPIDS;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }

      if (strcasecmp(Opts, "PS") == 0) {
            m_StreamType = stPS;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }

      if (strcasecmp(Opts, "PES") == 0) {
            m_StreamType = stPES;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }

      if (strcasecmp(Opts, "EXTERN") == 0) {
            m_StreamType = stExtern;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }

#if VDRVERSNUM >= 10300
      //
      // Deliver section filters data in separate, channel-independent data stream
      //
      if (strcasecmp(Opts, "FILTERS") == 0) {
            m_FiltersSupport = true;
            return Respond(220, "Capability \"%s\" accepted", Opts);
      }
#endif

      return Respond(561, "Capability \"%s\" not known", Opts);
}

bool cConnectionVTP::CmdPROV(char *Opts) 
{
      const cChannel *chan;
      int prio;
      char *ep;
      
      prio = strtol(Opts, &ep, 10);
      if (ep == Opts || !isspace(*ep))
            return Respond(501, "Use: PROV Priority Channel");

      Opts = skipspace(ep);
      if ((chan = ChannelFromString(Opts)) == NULL)
            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) 
{
      uint id, dataport = 0;
      char dataip[20];
      char *ep, *ipoffs;
      int n;

      id = strtoul(Opts, &ep, 10);
      if (ep == Opts || !isspace(*ep))
            return Respond(500, "Use: PORT Id Destination");
      
#if VDRVERSNUM >= 10300
      if (id != siLive && id != siLiveFilter)
            return Respond(501, "Wrong connection id %d", id);
#else
      if (id != siLive)
            return Respond(501, "Wrong connection id %d", id);
#endif
      
      Opts = skipspace(ep);
      n = 0;
      ipoffs = dataip;
      while ((ep = strchr(Opts, ',')) != NULL) {
            if (n < 4) {
                  memcpy(ipoffs, Opts, ep - Opts);
                  ipoffs += ep - Opts;
                  if (n < 3) *(ipoffs++) = '.';
            } else if (n == 4) {
                  *ep = 0;
                  dataport = strtoul(Opts, NULL, 10) << 8;
            } else
                  break;
            Opts = ep + 1;
            ++n;
      }
      *ipoffs = '\0';

      if (n != 5)
            return Respond(501, "Argument count invalid (must be 6 values)");

      dataport |= strtoul(Opts, NULL, 10);

      isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport);

#if VDRVERSNUM >= 10300
      if (id == siLiveFilter) {
            m_FiltersSupport = true;
            if(m_FilterStreamer)
                  m_FilterStreamer->Stop();
            delete m_FilterSocket;

            m_FilterSocket = new cTBSocket(SOCK_STREAM);
            if (!m_FilterSocket->Connect(dataip, dataport)) {
                  esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
                        dataip, dataport, strerror(errno));
                  DELETENULL(m_FilterSocket);
                  return Respond(551, "Couldn't open data connection");
            }

            if(!m_FilterStreamer)
                  m_FilterStreamer = new cStreamdevFilterStreamer;
            m_FilterStreamer->Start(m_FilterSocket);
            m_FilterStreamer->Activate(true);

            return Respond(220, "Port command ok, data connection opened");
      }
#endif

      if(m_LiveSocket && m_LiveStreamer)
            m_LiveStreamer->Stop();
      delete m_LiveSocket;

      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_LiveSocket);
            return Respond(551, "Couldn't open data connection");
      }

      if (!m_LiveSocket->SetDSCP())
            LOG_ERROR_STR("unable to set DSCP sockopt");
      if (m_LiveStreamer)
            m_LiveStreamer->Start(m_LiveSocket);

      return Respond(220, "Port command ok, data connection opened");
}

bool cConnectionVTP::CmdTUNE(char *Opts) 
{
      const cChannel *chan;
      cDevice *dev;
      
      if ((chan = ChannelFromString(Opts)) == NULL)
            return Respond(550, "Undefined channel \"%s\"", Opts);

      if ((dev = GetDevice(chan, 0)) == NULL)
            return Respond(560, "Channel not available");

      if (!dev->SwitchChannel(chan, false))
            return Respond(560, "Channel not available");

      delete m_LiveStreamer;
      m_LiveStreamer = new cStreamdevLiveStreamer(1);
      m_LiveStreamer->SetChannel(chan, m_StreamType);
      m_LiveStreamer->SetDevice(dev);
      if(m_LiveSocket)
            m_LiveStreamer->Start(m_LiveSocket);
      
#if VDRVERSNUM >= 10300
      if(m_FiltersSupport) {
            if(!m_FilterStreamer)
                  m_FilterStreamer = new cStreamdevFilterStreamer;
            m_FilterStreamer->SetDevice(dev);
            //m_FilterStreamer->SetChannel(chan);
      }
#endif

      return Respond(220, "Channel tuned");
}

bool cConnectionVTP::CmdADDP(char *Opts) 
{
      int pid;
      char *end;

      pid = strtoul(Opts, &end, 10);
      if (end == Opts || (*end != '\0' && *end != ' '))
            return Respond(500, "Use: ADDP Pid");

      return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true)
                  ? Respond(220, "Pid %d available", pid)
                  : Respond(560, "Pid %d not available", pid);
}

bool cConnectionVTP::CmdDELP(char *Opts) 
{
      int pid;
      char *end;

      pid = strtoul(Opts, &end, 10);
      if (end == Opts || (*end != '\0' && *end != ' '))
            return Respond(500, "Use: DELP Pid");

      return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false)
                  ? Respond(220, "Pid %d stopped", pid)
                  : Respond(560, "Pid %d not transferring", pid);
}

bool cConnectionVTP::CmdADDF(char *Opts) 
{
#if VDRVERSNUM >= 10300
      int pid, tid, mask;
      char *ep;

      if (m_FilterStreamer == NULL)
            return Respond(560, "Can't set filters without a filter stream");
      
      pid = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != ' '))
            return Respond(500, "Use: ADDF Pid Tid Mask");
      Opts = skipspace(ep);
      tid = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != ' '))
            return Respond(500, "Use: ADDF Pid Tid Mask");
      Opts = skipspace(ep);
      mask = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != '\0' && *ep != ' '))
            return Respond(500, "Use: ADDF Pid Tid Mask");

      return m_FilterStreamer->SetFilter(pid, tid, mask, true)
                  ? 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) 
{
#if VDRVERSNUM >= 10307
      int pid, tid, mask;
      char *ep;
      
      if (m_FilterStreamer == NULL)
            return Respond(560, "Can't delete filters without a stream");
      
      pid = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != ' '))
            return Respond(500, "Use: DELF Pid Tid Mask");
      Opts = skipspace(ep);
      tid = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != ' '))
            return Respond(500, "Use: DELF Pid Tid Mask");
      Opts = skipspace(ep);
      mask = strtol(Opts, &ep, 10);
      if (ep == Opts || (*ep != '\0' && *ep != ' '))
            return Respond(500, "Use: DELF Pid Tid Mask");

      m_FilterStreamer->SetFilter(pid, tid, mask, false);
      return Respond(220, "Filter %d stopped", pid);
#else
      return Respond(500, "DELF known but unimplemented with VDR < 1.3.0");
#endif
}

bool cConnectionVTP::CmdABRT(char *Opts) 
{
      uint id;
      char *ep;

      id = strtoul(Opts, &ep, 10);
      if (ep == Opts || (*ep != '\0' && *ep != ' '))
            return Respond(500, "Use: ABRT Id");

      switch (id) {
      case siLive: 
            DELETENULL(m_LiveStreamer); 
            DELETENULL(m_LiveSocket);
            break;
#if VDRVERSNUM >= 10300
      case siLiveFilter:
            DELETENULL(m_FilterStreamer);
            DELETENULL(m_FilterSocket);
            break;
#endif
      default:
            return Respond(501, "Wrong connection id %d", id);
            break;

      }

      return Respond(220, "Data connection closed");
}

bool cConnectionVTP::CmdQUIT(void) 
{
      DeferClose();
      return Respond(221, "Video Disk Recorder closing connection");
}

bool cConnectionVTP::CmdSUSP(void) 
{
      if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive())
            return Respond(220, "Server is suspended");
      else if (StreamdevServerSetup.SuspendMode == smOffer 
                  && StreamdevServerSetup.AllowSuspend) {
            cControl::Launch(new cSuspendCtl);
            return Respond(220, "Server is suspended");
      } else
            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 = false;
      if (Handler != NULL)
            result = Handler->Next(last);
      else
            esyslog("ERROR: vdr streamdev: Handler in LSTX command is NULL");
      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
#define Reply(c,m...) _res = Respond(c,m)
#define EXIT_WRAPPER() 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::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();
  if (*Option) {
     if (isnumber(Option)) {
        cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
        if (recording) {
           if (recording->Summary()) {
              char *summary = strdup(recording->Summary());
              Reply(250, "%s", strreplace(summary,'\n','|'));
              free(summary);
              }
           else
              Reply(550, "No summary availabe");
           }
        else
           Reply(550, "Recording \"%s\" not found", Option);
        }
     else
        Reply(501, "Error in recording number \"%s\"", Option);
     }
  else if (recordings) {
     cRecording *recording = Recordings.First();
     while (recording) {
           Reply(recording == Recordings.Last() ? 250 : -250, "%d %s", recording->Index() + 1, recording->Title(' ', true));
           recording = Recordings.Next(recording);
           }
     }
  else
     Reply(550, "No recordings available");
      EXIT_WRAPPER();
}

bool cConnectionVTP::CmdDELR(char *Option) {
      INIT_WRAPPER();
  if (*Option) {
     if (isnumber(Option)) {
        cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
        if (recording) {
           if (recording->Delete())
              Reply(250, "Recording \"%s\" deleted", Option);
           else
              Reply(554, "Error while deleting recording!");
           }
        else
           Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before deleting)");
        }
     else
        Reply(501, "Error in recording number \"%s\"", Option);
     }
  else
     Reply(501, "Missing recording number");
      EXIT_WRAPPER();
}*/

bool cConnectionVTP::Respond(int Code, const char *Message, ...)
{
      char *buffer;
      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);
}

Generated by  Doxygen 1.6.0   Back to index