diff options
Diffstat (limited to 'plugins/streamdev/streamdev-cvs/client/socket.c')
| -rw-r--r-- | plugins/streamdev/streamdev-cvs/client/socket.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/plugins/streamdev/streamdev-cvs/client/socket.c b/plugins/streamdev/streamdev-cvs/client/socket.c new file mode 100644 index 0000000..02f501d --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/client/socket.c @@ -0,0 +1,374 @@ +/* + * $Id: socket.c,v 1.12 2008/04/08 14:18:16 schmirl Exp $ + */ + +#include <tools/select.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <time.h> + +#define MINLOGREPEAT 10 //don't log connect failures too often (seconds) + +#include "client/socket.h" +#include "client/setup.h" +#include "common.h" + +cClientSocket ClientSocket; + +cClientSocket::cClientSocket(void) +{ + memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count); + Reset(); +} + +cClientSocket::~cClientSocket() +{ + Reset(); + if (IsOpen()) Quit(); +} + +void cClientSocket::Reset(void) +{ + for (int it = 0; it < si_Count; ++it) { + if (m_DataSockets[it] != NULL) + DELETENULL(m_DataSockets[it]); + } +} + +cTBSocket *cClientSocket::DataSocket(eSocketId Id) const { + return m_DataSockets[Id]; +} + +bool cClientSocket::Command(const std::string &Command, uint Expected, uint TimeoutMs) +{ + errno = 0; + + std::string pkt = Command + "\015\012"; + Dprintf("OUT: |%s|\n", Command.c_str()); + + cTimeMs starttime; + if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) { + esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(), + strerror(errno)); + Close(); + return false; + } + + uint64_t elapsed = starttime.Elapsed(); + if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs? + TimeoutMs -= elapsed; + return Expect(Expected, NULL, TimeoutMs); + } + + return true; +} + +bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) { + char *endptr; + int bufcount; + bool res; + + errno = 0; + + if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) { + esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(), + strerror(errno)); + Close(); + return false; + } + if (m_Buffer[bufcount - 1] == '\015') + --bufcount; + m_Buffer[bufcount] = '\0'; + Dprintf("IN: |%s|\n", m_Buffer); + + if (Result != NULL) + *Result = m_Buffer; + + res = strtoul(m_Buffer, &endptr, 10) == Expected; + return res; +} + +bool cClientSocket::CheckConnection(void) { + CMD_LOCK; + + if (IsOpen()) { + cTBSelect select; + + Dprintf("connection open\n"); + + // XXX+ check if connection is still alive (is there a better way?) + // There REALLY shouldn't be anything readable according to PROTOCOL here + // If there is, assume it's an eof signal (subseq. read would return 0) + select.Add(*this, false); + int res; + if ((res = select.Select(0)) == 0) { + Dprintf("select said nothing happened\n"); + return true; + } + Dprintf("closing connection (res was %d)", res); + Close(); + } + + if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){ + static time_t lastTime = 0; + if (time(NULL) - lastTime > MINLOGREPEAT) { + esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s", + (const char*)StreamdevClientSetup.RemoteIp, + StreamdevClientSetup.RemotePort, strerror(errno)); + lastTime = time(NULL); + } + return false; + } + + if (!Expect(220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d", + RemoteIp().c_str(), RemotePort()); + Close(); + return false; + } + + if (!Command("CAPS TSPIDS", 220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d", + RemoteIp().c_str(), RemotePort()); + Close(); + return false; + } + + const char *Filters = ""; + if(Command("CAPS FILTERS", 220)) + Filters = ",FILTERS"; + + isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s", + RemoteIp().c_str(), RemotePort(), Filters); + return true; +} + +bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " " + + (const char*)Channel->GetChannelID().ToString(); + if (!Command(command)) + return false; + + std::string buffer; + if (!Expect(220, &buffer)) { + if (buffer.substr(0, 3) != "560" && errno == 0) + esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s", + RemoteIp().c_str(), RemotePort(), Channel->Name()); + return false; + } + return true; +} + +bool cClientSocket::CreateDataConnection(eSocketId Id) { + cTBSocket listen(SOCK_STREAM); + + if (!CheckConnection()) return false; + + if (m_DataSockets[Id] != NULL) + DELETENULL(m_DataSockets[Id]); + + if (!listen.Listen(LocalIp(), 0, 1)) { + esyslog("ERROR: Streamdev: Couldn't create data connection: %s", + strerror(errno)); + return false; + } + + std::string command = (std::string)"PORT " + (const char*)itoa(Id) + " " + + LocalIp().c_str() + "," + + (const char*)itoa((listen.LocalPort() >> 8) & 0xff) + "," + + (const char*)itoa(listen.LocalPort() & 0xff); + size_t idx = 4; + while ((idx = command.find('.', idx + 1)) != (size_t)-1) + command[idx] = ','; + + CMD_LOCK; + + if (!Command(command, 220)) { + Dprintf("error: %m\n"); + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d", + RemoteIp().c_str(), RemotePort()); + return false; + } + + /* The server SHOULD do the following: + * - get PORT command + * - connect to socket + * - return 220 + */ + + m_DataSockets[Id] = new cTBSocket; + if (!m_DataSockets[Id]->Accept(listen)) { + esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s", + RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ", + errno == 0 ? "" : strerror(errno)); + DELETENULL(m_DataSockets[Id]); + return false; + } + + return true; +} + +bool cClientSocket::CloseDataConnection(eSocketId Id) { + //if (!CheckConnection()) return false; + + CMD_LOCK; + + if(Id == siLive || Id == siLiveFilter) + if (m_DataSockets[Id] != NULL) { + std::string command = (std::string)"ABRT " + (const char*)itoa(Id); + if (!Command(command, 220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't cleanly close data connection"); + //return false; + } + DELETENULL(m_DataSockets[Id]); + } + return true; +} + +bool cClientSocket::SetChannelDevice(const cChannel *Channel) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + std::string command = (std::string)"TUNE " + + (const char*)Channel->GetChannelID().ToString(); + if (!Command(command, 220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s", + RemoteIp().c_str(), RemotePort(), Channel->Name()); + return false; + } + return true; +} + +bool cClientSocket::SetPid(int Pid, bool On) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid); + if (!Command(command, 220)) { + if (errno == 0) + esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(), + LocalPort()); + return false; + } + return true; +} + +bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid) + + " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask); + if (!Command(command, 220)) { + if (errno == 0) + esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d", + Pid, Tid, Mask, LocalIp().c_str(), LocalPort()); + return false; + } + return true; +} + +bool cClientSocket::CloseDvr(void) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + if (m_DataSockets[siLive] != NULL) { + std::string command = (std::string)"ABRT " + (const char*)itoa(siLive); + if (!Command(command, 220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't cleanly close data connection"); + return false; + } + + DELETENULL(m_DataSockets[siLive]); + } + return true; +} + +bool cClientSocket::SynchronizeEPG(void) { + std::string buffer; + bool result; + FILE *epgfd; + + if (!CheckConnection()) return false; + + isyslog("Streamdev: Synchronizing EPG from server\n"); + + CMD_LOCK; + + if (!Command("LSTE")) + return false; + + if ((epgfd = tmpfile()) == NULL) { + esyslog("ERROR: Streamdev: Error while processing EPG data: %s", + strerror(errno)); + return false; + } + + while ((result = Expect(215, &buffer))) { + if (buffer[3] == ' ') break; + fputs(buffer.c_str() + 4, epgfd); + fputc('\n', epgfd); + } + + if (!result) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d", + RemoteIp().c_str(), RemotePort()); + fclose(epgfd); + return false; + } + + rewind(epgfd); + if (cSchedules::Read(epgfd)) + cSchedules::Cleanup(true); + else { + esyslog("ERROR: Streamdev: Parsing EPG data failed"); + fclose(epgfd); + return false; + } + fclose(epgfd); + return true; +} + +bool cClientSocket::Quit(void) { + bool res; + + if (!CheckConnection()) return false; + + if (!(res = Command("QUIT", 221))) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d", + RemoteIp().c_str(), RemotePort()); + } + Close(); + return res; +} + +bool cClientSocket::SuspendServer(void) { + if (!CheckConnection()) return false; + + CMD_LOCK; + + if (!Command("SUSP", 220)) { + if (errno == 0) + esyslog("ERROR: Streamdev: Couldn't suspend server"); + return false; + } + return true; +} |
