summaryrefslogtreecommitdiff
path: root/plugins/streamdev/streamdev-cvs/client/socket.c
diff options
context:
space:
mode:
authorkwacker <vdr@w-i-r.com>2010-04-11 13:46:11 +0200
committerkwacker <vdr@w-i-r.com>2010-04-11 13:46:11 +0200
commit9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88 (patch)
tree3a52de029f950dcd9f9856a53fd67abef8519e68 /plugins/streamdev/streamdev-cvs/client/socket.c
parent9cd931834ecadbf5efefdf484abb966e9248ebbb (diff)
downloadx-vdr-9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88.tar.gz
x-vdr-9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88.tar.bz2
Burn 0.2.0-beta3 und Streamdev mit Paches aktualisiert
Diffstat (limited to 'plugins/streamdev/streamdev-cvs/client/socket.c')
-rw-r--r--plugins/streamdev/streamdev-cvs/client/socket.c374
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;
+}