/* * svdrpclient.c: SERIES plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "svdrpclient.h" //*************************************************************************** // SVDRP Client //*************************************************************************** cSvdrpClient::cSvdrpClient(const char* aIp, int aPort) { port = aPort; ip = aIp ? ::strdup(aIp) : 0; bufSize = BUFSIZ; buffer = (char*)malloc(bufSize); } cSvdrpClient::~cSvdrpClient(void) { close(); free(ip); free(buffer); } //*************************************************************************** // Connect //*************************************************************************** #include #include #include #include #include #include #include #include #include int cSvdrpClient::connect() { if (!ip) { tell(0, "SVDRPCL: No server IP specified"); return -1; } struct hostent* hostInfo; struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); long remoteAddr; if (!isdigit(ip[1])) { // map hostname to ip if (hostInfo = ::gethostbyname(ip)) memcpy((char*)&remoteAddr, hostInfo->h_addr, hostInfo->h_length); else if ((remoteAddr = inet_addr(ip)) == INADDR_NONE) return -1; memcpy(&server_addr.sin_addr, &remoteAddr, sizeof(struct in_addr)); } else if (!::inet_aton(ip, &server_addr.sin_addr)) { tell(0, "SVDRPCL: Invalid server IP '%s'", ip); return -1; } int sock = ::socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { tell(0, "SVDRPCL: Error creating socket for connection to %s: %m", ip); return -1; } // set nonblocking int flags = ::fcntl(sock, F_GETFL, 0); if (flags < 0 || ::fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { tell(0, "SVDRPCL: Unable to use nonblocking I/O for %s: %m", ip); return -1; } if (::connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { if (errno != EINPROGRESS) { tell(0, "SVDRPCL: connect to %s:%hu failed: %m", ip, port); return -1; } int result; fd_set fds; struct timeval tv; cTimeMs starttime; int timeout = 10 * 1000; do { FD_ZERO(&fds); FD_SET(sock, &fds); tv.tv_usec = (timeout % 1000) * 1000; tv.tv_sec = timeout / 1000; result = ::select(sock + 1, 0, &fds, 0, &tv); } while (result == -1 && errno == EINTR && (timeout = 10 * 1000 - starttime.Elapsed()) > 100); if (!result) // timeout { result = -1; errno = ETIMEDOUT; } else if (result == 1) // check socket for errors { int error; socklen_t size = sizeof(error); result = ::getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &size); if (result == 0 && error != 0) { result = -1; errno = error; } } if (result != 0) { tell(0, "SVDRPCL: Error connecting to %s:%hu: %m", ip, port); ::close(sock); return -1; } } return sock; } int cSvdrpClient::open() { if (file.IsOpen()) return 0; int fd = connect(); if (fd < 0) return -1; if (!file.Open(fd)) { ::close(fd); return -1; } // check for greeting cList greeting; if (receive(&greeting) != 220) { tell(0, "SVDRPCL: did not receive greeting from %s. Closing...", ip); abort(); return -1; } const char* msg = 0; if (greeting.First() && greeting.First()->Text()) msg = greeting.First()->Text(); tell(0, "SVDRPCL: connected to %s:%hu '%s'", ip, port, msg); return 0; } void cSvdrpClient::close() { if (!file.IsOpen()) return; if (send("QUIT\r\n", false)) receive(); file.Close(); } void cSvdrpClient::abort(void) { file.Close(); } int cSvdrpClient::send(const char* cmd, int reconnect) { if (!cmd) return false; if (reconnect && !file.IsOpen()) open(); if (!file.IsOpen()) { tell(0, "SVDRPCL: unable to send command to %s. Socket is closed", ip); return false; } int len = ::strlen(cmd); if (safe_write(file, cmd, len) < 0) { tell(0, "SVDRPCL: error while writing to %s: %m", ip); abort(); return false; } return true; } int cSvdrpClient::receive(cList* list) { int timeoutMs = 20 * 1000; // #TODO iconv !!! while (readLine(timeoutMs)) { char* tail; long int code = ::strtol(buffer, &tail, 10); if (tail - buffer == 3 && code >= 100 && code <= 999 && (*tail == ' ' || *tail == '-')) { if (list) { const char* s = buffer + 4; list->Add(new cLine(s)); } if (*tail == ' ') return code; } else { tell(0, "SVDRPCL: Unexpected reply from %s '%s'", ip, buffer); close(); break; } } if (list) list->Clear(); return 0; } int cSvdrpClient::readLine(int timeoutMs) { if (!file.IsOpen()) return false; int tail = 0; while (cFile::FileReady(file, timeoutMs)) { unsigned char c; int r = safe_read(file, &c, 1); if (r > 0) { if (c == '\n' || c == 0x00) { // line complete, make sure the string is terminated buffer[tail] = 0; return true; } else if ((c <= 0x1F || c == 0x7F) && c != 0x09) { // ignore control characters } else { if (tail >= bufSize - 1) { bufSize += BUFSIZ; buffer = (char*) realloc(buffer, bufSize); if (!buffer) { tell(0, "SVDRPCL: unable to increase buffer size to %d byte", bufSize); close(); return false; } } buffer[tail++] = c; } } else { tell(0, "SVDRPCL: lost connection '%s'", ip); buffer[0] = 0; abort(); return false; } } tell(0, "SVDRPCL: timeout waiting server reply '%s'", ip); buffer[0] = 0; abort(); return false; }