/lib/cmyth/libcmyth/socket.c
C | 2938 lines | 1763 code | 151 blank | 1024 comment | 331 complexity | 8813cc90c2d7b80fff3167cab409dc89 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0, GPL-2.0, LGPL-2.1
- /*
- * Copyright (C) 2004-2012, Eric Lund
- * http://www.mvpmc.org/
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /*
- * socket.c - functions to handle low level socket interactions with a
- * MythTV frontend.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <cmyth_local.h>
- #define __UNSIGNED "0123456789"
- #define __SIGNED "+-0123456789"
- #define __check_num(num) (strspn((num), __SIGNED) == strlen((num)))
- #define __check_unum(num) (strspn((num), __UNSIGNED) == strlen((num)))
- /*
- * cmyth_send_message(cmyth_conn_t conn, char *request)
- *
- * Scope: PRIVATE (mapped to __cmyth_send_message)
- *
- * Description
- *
- * Send a myth protocol on the socket indicated by 'conn'. The
- * message sent has the form:
- *
- * <length><request>
- *
- * Where <length> is the 8 character, space padded, left justified
- * ASCII representation of the number of bytes in the message
- * (including <length>) and <request> is the string specified in the
- * 'request' argument.
- *
- * Return Value:
- *
- * Success: 0
- *
- * Failure: -(errno)
- */
- int
- cmyth_send_message(cmyth_conn_t conn, char *request)
- {
- /*
- * For now this is unimplemented.
- */
- char *msg;
- int reqlen;
- int written = 0;
- int w;
- struct timeval tv;
- fd_set fds;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
- if (!conn) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
- __FUNCTION__);
- return -EBADF;
- }
- if (conn->conn_fd < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: not connected\n",
- __FUNCTION__);
- conn->conn_hang = 1;
- return -EBADF;
- }
- if (!request) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no request\n", __FUNCTION__);
- return -EINVAL;
- }
- reqlen = strlen(request);
- msg = malloc(9 + reqlen);
- if (!msg) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cannot allocate message buffer\n",
- __FUNCTION__);
- return -ENOMEM;
- }
- sprintf(msg, "%-8d%s", reqlen, request);
- cmyth_dbg(CMYTH_DBG_PROTO, "%s: sending message '%s'\n",
- __FUNCTION__, msg);
- reqlen += 8;
- do {
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(conn->conn_fd, &fds);
- if (select((int)conn->conn_fd+1, NULL, &fds, NULL, &tv) == 0) {
- conn->conn_hang = 1;
- return -ETIMEDOUT;
- } else {
- conn->conn_hang = 0;
- }
- w = send(conn->conn_fd, msg + written, reqlen - written, 0);
- if (w < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: write() failed (%d)\n",
- __FUNCTION__, errno);
- free(msg);
- conn->conn_hang = 1;
- return -errno;
- }
- written += w;
- } while (written < reqlen);
- free(msg);
- return 0;
- }
- /*
- * cmyth_rcv_length(cmyth_conn_t conn)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_length)
- *
- * Description
- *
- * Receive the <length> portion of a MythTV Protocol message
- * on the socket specified by 'conn'
- *
- * Return Value:
- *
- * Success: A length value > 0 and < 100000000
- *
- * Failure: -(errno)
- */
- int
- cmyth_rcv_length(cmyth_conn_t conn)
- {
- char buf[16];
- int rtot = 0;
- int r;
- int hangcount = 0;
- int ret;
- struct timeval tv;
- fd_set fds;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
- if (!conn) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
- __FUNCTION__);
- return -EBADF;
- }
- if (conn->conn_fd < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: not connected\n",
- __FUNCTION__);
- return -EBADF;
- }
- buf[8] ='\0';
- do {
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(conn->conn_fd, &fds);
- r = select((int)conn->conn_fd+1, &fds, NULL, NULL, &tv);
- if (r > 0) {
- conn->conn_hang = 0;
- r = recv(conn->conn_fd, &buf[rtot], 8 - rtot, 0);
- }
- if (r == 0) {
- conn->conn_hang = 1;
- if (++hangcount > 2)
- return -ETIMEDOUT;
- }
- if (r < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: read() failed (%d)\n",
- __FUNCTION__, errno);
- conn->conn_hang = 1;
- return -EBADF;
- }
- rtot += r;
- } while (rtot < 8);
- ret = atoi(buf);
- cmyth_dbg(CMYTH_DBG_PROTO, "%s: buffer is '%s' ret = %d\n",
- __FUNCTION__, buf, ret);
- return ret;
- }
- /*
- * cmyth_conn_refill(cmyth_conn_t conn, int len)
- *
- * Scope: PRIVATE (static)
- *
- * Description
- *
- * FIll up the buffer in the connection 'conn' with up to 'len' bytes
- * of data.
- *
- * Return Value:
- *
- * Success: 0
- *
- * Failure: -errno
- */
- static int
- cmyth_conn_refill(cmyth_conn_t conn, int len)
- {
- int r;
- int total = 0;
- unsigned char *p;
- struct timeval tv;
- fd_set fds;
- if (!conn) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
- __FUNCTION__);
- return -EINVAL;
- }
- if (!conn->conn_buf) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: connection has no buffer\n",
- __FUNCTION__);
- return -EINVAL;
- }
- if ((unsigned)len > conn->conn_buflen) {
- len = conn->conn_buflen;
- }
- p = conn->conn_buf;
- while (len > 0) {
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(conn->conn_fd, &fds);
- if ((r=select((int)conn->conn_fd+1, &fds, NULL, NULL, &tv)) == 0) {
- conn->conn_hang = 1;
- return -ETIMEDOUT;
- } else if (r > 0) {
- conn->conn_hang = 0;
- r = recv(conn->conn_fd, p, len, 0);
- }
- if (r <= 0) {
- if (total == 0) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: read failed (%d)\n",
- __FUNCTION__, errno);
- conn->conn_hang = 1;
- if ( r == 0 )
- return -1 * EBADF;
- else
- return -1 * errno;
- }
- /*
- * There were bytes read before the error, use them and
- * then report the error next time.
- */
- break;
- }
- total += r;
- len -= r;
- p += r;
- }
- conn->conn_pos = 0;
- conn->conn_len = total;
- return 0;
- }
- /*
- * cmyth_rcv_string(cmyth_conn_t conn, char *buf, int buflen, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_length)
- *
- * Description
- *
- * Receive a string token from a list of tokens in a MythTV Protocol
- * message. Tokens in MythTV Protocol messages are separated by the
- * string: []:[] or terminated by running out of message. Up to
- * 'count' Bytes will be consumed from the socket specified by 'conn'
- * (stopping when a separator is seen or 'count' is exhausted). Of
- * these, the first 'buflen' bytes will be copied into 'buf'. If
- * a full 'buflen' bytes is read, 'buf' will not be terminated with a
- * '\0', otherwise it will be. If an error is encountered and
- * 'err' is not NULL, an indication of the nature of the error will be
- * recorded by placing an error code in the location pointed to by
- * 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- */
- int
- cmyth_rcv_string(cmyth_conn_t conn, int *err, char *buf, int buflen, int count)
- {
- static char separator[] = "[]:[]";
- int consumed = 0;
- int placed = 0;
- char *state = separator;
- char *sep_start = NULL;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count < 0) {
- /*
- * Strings are terminated by either the next separator or the end of the payload. If
- * the last string requested in the payload is empty the count will be zero. In this case
- * we should return an empty string rather than an error.
- *
- * http://www.mythtv.org/wiki/Myth_Protocol#Packet_Data_Format
- */
- *err = EINVAL;
- return 0;
- }
- if (!conn) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
- __FUNCTION__);
- *err = EBADF;
- return 0;
- }
- if (conn->conn_fd < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: not connected\n",
- __FUNCTION__);
- *err = EBADF;
- return 0;
- }
- if (!conn->conn_buf) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection buffer\n",
- __FUNCTION__);
- *err = EBADF;
- return 0;
- }
- if (!buf) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no output buffer\n",
- __FUNCTION__);
- *err = EBADF;
- return 0;
- }
- while (1) {
- if (consumed >= count) {
- /*
- * We have consumed all the characters we were
- * asked to from the stream. Force a refill
- * on the next call, and return 'consumed'.
- */
- conn->conn_pos = conn->conn_len = 0;
- if (buflen > placed) {
- buf[placed] = '\0';
- }
- break;
- }
- if (conn->conn_pos >= conn->conn_len) {
- /*
- * We have run out of (or never had any) bytes
- * from the connection. Refill the buffer.
- */
- *err = cmyth_conn_refill(conn, count - consumed);
- if (*err < 0) {
- *err = -1 * (*err);
- break;
- }
- }
- if (sep_start && conn->conn_buf[conn->conn_pos] != (unsigned char)*state) {
- /*
- * Reset separator in case the current character does not match
- * the expected part of the separator. This needs to take place
- * before checking if the current character starts a new separator.
- * (To resolve issues with strings that look like [[]:[])
- */
- sep_start = NULL;
- state = separator;
- }
- if (conn->conn_buf[conn->conn_pos] == (unsigned char)*state) {
- /*
- * We matched the next (possibly first) step
- * of a separator, advance to the next.
- */
- if ((state == separator) && (placed < buflen)) {
- sep_start = &buf[placed];
- }
- ++state;
- }
- if (placed < buflen) {
- /*
- * This character goes in the output buffer,
- * put it there.
- */
- buf[placed++] = conn->conn_buf[conn->conn_pos];
- }
- ++conn->conn_pos;
- ++consumed;
- if (*state == '\0') {
- /*
- * Reached the end of a separator, terminate
- * the returned buffer at the beginning of the
- * separator (if it fell within the buffer)
- * and return.
- */
- if (sep_start) {
- *sep_start = '\0';
- } else if (buflen > placed) {
- buf[placed] = '\0';
- }
- break;
- }
- }
- cmyth_dbg(CMYTH_DBG_PROTO, "%s: string received '%s'\n",
- __FUNCTION__, buf);
- return consumed;
- }
- /*
- * cmyth_rcv_uint32(cmyth_conn_t conn, int *err, uint32_t *buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_uint32)
- *
- * Description
- *
- * Receive an unsigned (32 bit) integer token from a list of
- * tokens in a MythTV Protocol message. Tokens in MythTV Protocol
- * messages are separated by the string: []:[] or terminated by
- * running out of message. Up to 'count' Bytes will be consumed from
- * the socket specified by 'conn' (stopping when a separator is seen
- * or 'count' is exhausted). The unsigned long long integer value of
- * the token is placed in the location pointed to by 'buf'. If an
- * error is encountered and 'err' is not NULL, an indication of the
- * nature of the error will be recorded by placing an error code in
- * the location pointed to by 'err'. If all goes well, 'err' wil be
- * set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in an unsinged
- * long long integer
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_uint32(cmyth_conn_t conn, int *err, uint32_t *buf, int count)
- {
- char num[32];
- char *num_p = num;
- uint64_t val = 0;
- uint64_t limit = UINT32_MAX;
- int consumed;
- int tmp;
- *buf = 0;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- consumed = cmyth_rcv_string(conn, err, num, sizeof(num), count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_string() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- while (*num_p) {
- if (!isdigit(*num_p)) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: received illegal integer: '%s'\n",
- __FUNCTION__, num);
- *err = EINVAL;
- return consumed;
- }
- /*
- * If we are about to make 'val' bigger than 32bit,
- * it is ERANGE.
- */
- if (val > limit && *num_p > '5') {
- *err = ERANGE;
- return consumed;
- }
- val *= 10;
- val += ((*num_p) - '0');
- num_p++;
- }
- /*
- * Got a result, return it.
- */
- *buf = (uint32_t)val;
- return consumed;
- }
- /*
- * cmyth_rcv_int32(cmyth_conn_t conn, int *err, int32_t *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_int32)
- *
- * Description
- *
- * Receive a signed (32 bit) integer token from a list of
- * tokens in a MythTV Protocol message. Tokens in MythTV Protocol
- * messages are separated by the string: []:[] or terminated by
- * running out of message. Up to 'count' Bytes will be consumed from
- * the socket specified by 'conn' (stopping when a separator is seen
- * or 'count' is exhausted). The long long integer value of the token
- * is placed in the location pointed to by 'buf'. If an error is
- * encountered and 'err' is not NULL, an indication of the nature of
- * the error will be recorded by placing an error code in the location
- * pointed to by 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in a
- * long long integer
- *
- * EINVAL The token received is not numeric
- */
- int
- cmyth_rcv_int32(cmyth_conn_t conn, int *err, int32_t *buf, int count)
- {
- char num[32];
- char *num_p = num;
- uint64_t val = 0;
- int sign = 1;
- uint64_t limit = INT32_MAX;
- int consumed;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- consumed = cmyth_rcv_string(conn, err, num, sizeof(num), count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_string() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if (*num_p && (*num_p == '-')) {
- ++num_p;
- sign = -1;
- }
- while (*num_p) {
- if (!isdigit(*num_p)) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: received illegal integer: '%s'\n",
- __FUNCTION__, num);
- *err = EINVAL;
- return consumed;
- }
- val *= 10;
- val += ((*num_p) - '0');
- /*
- * Check and make sure we are still under the limit (this is
- * an absolute value limit, sign will be applied later).
- */
- if (val > limit) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: long out of range: '%s'\n",
- __FUNCTION__, num);
- *err = ERANGE;
- return consumed;
- }
- num_p++;
- }
- /*
- * Got a result, return it.
- */
- *buf = (int32_t)(sign * val);
- return consumed;
- }
- /*
- * cmyth_rcv_okay(cmyth_conn_t conn)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_okay)
- *
- * Description
- *
- * Receive an OK response on a connection.
- * This is here to easily handle simple acknowledgement from
- * the server.
- *
- * Return Value:
- *
- * Success: 0
- *
- * Failure: -(errno)
- */
- int
- cmyth_rcv_okay(cmyth_conn_t conn)
- {
- int count, consumed;
- char buf[3];
- char tmp[1024];
- int err, ret;
- count = cmyth_rcv_length(conn);
- if (count < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed\n",
- __FUNCTION__);
- return count;
- }
- consumed = cmyth_rcv_string(conn, &err, buf, sizeof(buf), count);
- if (err) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed\n",
- __FUNCTION__);
- return -err;
- }
- count -= consumed;
- cmyth_toupper_string(buf);
- ret = (strncmp(buf, "OK",2) == 0) ? 0 : -1;
- if (count > 0) {
- cmyth_dbg(CMYTH_DBG_INFO,
- "%s: did not consume everything\n",
- __FUNCTION__);
- while(count > 0 && err == 0) {
- consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count);
- count -= consumed;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: leftover data %s\n", __FUNCTION__, tmp);
- }
- }
- return ret;
- }
- /*
- * cmyth_rcv_feedback(cmyth_conn_t conn, char *fb)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_feedback)
- *
- * Description
- *
- * Receive user specified response on a connection.
- * This is here to easily handle simple acknowledgement from
- * the server.
- *
- * Return Value:
- *
- * Success: 0
- *
- * Failure: -(errno)
- */
- int
- cmyth_rcv_feedback(cmyth_conn_t conn, char *fb)
- {
- int count, consumed;
- char buf[8];
- char tmp[1024];
- int err, ret;
- count = cmyth_rcv_length(conn);
- if (count < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed\n",
- __FUNCTION__);
- return count;
- }
- consumed = cmyth_rcv_string(conn, &err, buf, sizeof(buf), count);
- if (err) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed\n",
- __FUNCTION__);
- return -err;
- }
- count -= consumed;
- ret = (strncmp(buf, fb, sizeof(fb)) == 0) ? 0 : -1;
- if (count > 0) {
- cmyth_dbg(CMYTH_DBG_INFO,
- "%s: did not consume everything\n",
- __FUNCTION__);
- while(count > 0 && err == 0) {
- consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count);
- count -= consumed;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: leftover data %s\n", __FUNCTION__, tmp);
- }
- }
- return ret;
- }
- /*
- * cmyth_rcv_version(cmyth_conn_t conn, unsigned long *vers)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_version)
- *
- * Description
- *
- * Receive an ACCEPT <version> or REJECT <version> response on a
- * connection. If 'vers' is non-NULL it points to the location where
- * the received version number should be placed. If it is NULL, this
- * routine will still read the version but it will throw it away.
- *
- * Return Value:
- *
- * Success: 0
- *
- * Failure: -(errno)
- */
- int
- cmyth_rcv_version(cmyth_conn_t conn, uint32_t *vers)
- {
- int len;
- int consumed;
- char buf[8];
- uint32_t tmp_vers;
- int err;
- if (!vers) {
- vers = &tmp_vers;
- }
- len = cmyth_rcv_length(conn);
- if (len < 0) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed\n",
- __FUNCTION__);
- return len;
- }
- consumed = cmyth_rcv_string(conn, &err, buf, sizeof(buf), len);
- if (err) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed\n",
- __FUNCTION__);
- return -err;
- }
- len -= consumed;
- /*
- * The string we just consumed was either "ACCEPT" or "REJECT". In
- * either case, the number following it is the correct version, and
- * we use it as an unsigned long.
- */
- consumed = cmyth_rcv_uint32(conn, &err, vers, len);
- if (consumed < len) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: did not consume everything %d < %d\n",
- __FUNCTION__, consumed, len);
- }
- return -err;
- }
- /*
- * cmyth_rcv_int8(cmyth_conn_t conn, int *err, int8_t *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_int8)
- *
- * Description
- *
- * Receive a byte (signed 8 bit) integer token from a list of tokens
- * in a MythTV Protocol message. Tokens in MythTV Protocol messages
- * are separated by the string: []:[] or terminated by running out of
- * message. Up to 'count' Bytes will be consumed from the socket
- * specified by 'conn' (stopping when a separator is seen or 'count'
- * is exhausted). The byte integer value of the token is placed in
- * the location pointed to by 'buf'. If an error is encountered and
- * 'err' is not NULL, an indication of the nature of the error will be
- * recorded by placing an error code in the location pointed to by
- * 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * Success / Failure: A value >=0 indicating the number of bytes
- * consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err' by this function:
- *
- * ERANGE The token received is too large to fit in a byte integer
- *
- * EINVAL The token received is not numeric
- */
- int
- cmyth_rcv_int8(cmyth_conn_t conn, int *err, int8_t *buf, int count)
- {
- int32_t val;
- int consumed;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- consumed = cmyth_rcv_int32(conn, err, &val, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_long() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if ((val > 127) || (val < -128)) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: value doesn't fit: '%"PRId32"'\n",
- __FUNCTION__, val);
- *err = ERANGE;
- return consumed;
- }
- *err = 0;
- *buf = (int8_t)val;
- return consumed;
- }
- /*
- * cmyth_rcv_int16(cmyth_conn_t conn, int *err, int16_t *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_int16)
- *
- * Description
- *
- * Receive a signed (16 bit) integer token from a list of tokens
- * in a MythTV Protocol message. Tokens in MythTV Protocol messages
- * are separated by the string: []:[] or terminated by running out of
- * message. Up to 'count' Bytes will be consumed from the socket
- * specified by 'conn' (stopping when a separator is seen or 'count'
- * is exhausted). The short integer value of the token is placed in
- * the location pointed to by 'buf'. If an error is encountered and
- * 'err' is not NULL, an indication of the nature of the error will be
- * recorded by placing an error code in the location pointed to by
- * 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in a short integer
- *
- * EINVAL The token received is not numeric
- */
- int
- cmyth_rcv_int16(cmyth_conn_t conn, int *err, int16_t *buf, int count)
- {
- int32_t val;
- int consumed;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- consumed = cmyth_rcv_int32(conn, err, &val, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_long() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if ((val > 32767) || (val < -32768)) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: value doesn't fit: '%"PRId32"'\n",
- __FUNCTION__, val);
- *err = ERANGE;
- return consumed;
- }
- *err = 0;
- *buf = (int16_t)val;
- return consumed;
- }
- /*
- * cmyth_rcv_old_int64(cmyth_conn_t conn, int *err, long long *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_long)
- *
- * Description
- *
- * Receive a long long (signed 64 bit) integer token from a list of tokens
- * in a MythTV Protocol message. Tokens in MythTV Protocol messages
- * are separated by the string: []:[] or terminated by running out of
- * message. Up to 'count' Bytes will be consumed from the socket
- * specified by 'conn' (stopping when a separator is seen or 'count'
- * is exhausted). The long integer value of the token is placed in
- * the location pointed to by 'buf'. If an error is encountered and
- * 'err' is not NULL, an indication of the nature of the error will be
- * recorded by placing an error code in the location pointed to by
- * 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in a long integer
- *
- * EINVAL The token received is not numeric
- */
- int
- cmyth_rcv_old_int64(cmyth_conn_t conn, int *err, int64_t *buf, int count)
- {
- int64_t val;
- int consumed;
- int tmp;
- uint32_t hi, lo;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- consumed = cmyth_rcv_int32(conn, err, (int32_t*)&hi, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_u_long() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- consumed += cmyth_rcv_int32(conn, err, (int32_t*)&lo, count-consumed);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_u_long() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- val = (((int64_t)hi) << 32) | ((int64_t)(lo & 0xffffffff));
- *err = 0;
- *buf = val;
- return consumed;
- }
- /*
- * cmyth_rcv_new_int64(cmyth_conn_t conn, int *err, long long *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_long)
- *
- * Description
- *
- * Receive a long long (signed 64 bit) integer token from a list of tokens
- * in a MythTV Protocol message. Tokens in MythTV Protocol messages
- * are separated by the string: []:[] or terminated by running out of
- * message. Up to 'count' Bytes will be consumed from the socket
- * specified by 'conn' (stopping when a separator is seen or 'count'
- * is exhausted). The long integer value of the token is placed in
- * the location pointed to by 'buf'. If an error is encountered and
- * 'err' is not NULL, an indication of the nature of the error will be
- * recorded by placing an error code in the location pointed to by
- * 'err'. If all goes well, 'err' wil be set to 0.
- *
- * As of protocol version 57, Myth now sends a single 64bit string instead
- * of 2 32bit strings when sending proginfo data. This does not seem to
- * apply uniformly though. For instance 'ANN FILETRANSFER' still uses
- * the old method
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in a long integer
- *
- * EINVAL The token received is not numeric
- */
- int
- cmyth_rcv_new_int64(cmyth_conn_t conn, int *err, int64_t *buf, int count,
- int forced)
- {
- char num[32];
- char *num_p = num;
- uint64_t val = 0;
- int sign = 1;
- uint64_t limit = INT64_MAX;
- int consumed;
- int tmp;
- /*
- * Between protocols 57 and 66, not all messages used the new
- * format for 64-bit values.
- */
- if ((conn->conn_version < 57) ||
- ((conn->conn_version < 66) && !forced)) {
- return cmyth_rcv_old_int64(conn, err, buf, count);
- }
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- consumed = cmyth_rcv_string(conn, err, num, sizeof(num), count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_string() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if (*num_p && (*num_p == '-')) {
- ++num_p;
- sign = -1;
- }
- while (*num_p) {
- if (!isdigit(*num_p)) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: received illegal integer: '%s'\n",
- __FUNCTION__, num);
- *err = EINVAL;
- return consumed;
- }
- val *= 10;
- val += ((*num_p) - '0');
- /*
- * Check and make sure we are still under the limit (this is
- * an absolute value limit, sign will be applied later).
- */
- if (val > limit) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: long long out of range: '%s'\n",
- __FUNCTION__, num);
- *err = ERANGE;
- return consumed;
- }
- num_p++;
- }
- /*
- * Got a result, return it.
- */
- *buf = (int64_t)(sign * val);
- return consumed;
- }
- /*
- * cmyth_rcv_uint8(cmyth_conn_t conn, int *err, uint8_t *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_uint8)
- *
- * Description
- *
- * Receive an unsigned byte (8 bit) integer token from a list of
- * tokens in a MythTV Protocol message. Tokens in MythTV Protocol
- * messages are separated by the string: []:[] or terminated by
- * running out of message. Up to 'count' Bytes will be consumed from
- * the socket specified by 'conn' (stopping when a separator is seen
- * or 'count' is exhausted). The unsigned byte integer value of the
- * token is placed in the location pointed to by 'buf'. If an error
- * is encountered and 'err' is not NULL, an indication of the nature
- * of the error will be recorded by placing an error code in the
- * location pointed to by 'err'. If all goes well, 'err' wil be set
- * to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in an
- # unsigned byte integer
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_uint8(cmyth_conn_t conn, int *err, uint8_t *buf, int count)
- {
- uint32_t val;
- int consumed;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- consumed = cmyth_rcv_uint32(conn, err, &val, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_ulong() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if (val > 255) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: value doesn't fit: '%llu'\n",
- __FUNCTION__, val);
- *err = ERANGE;
- return consumed;
- }
- *err = 0;
- *buf = (uint8_t)val;
- return consumed;
- }
- /*
- * cmyth_rcv_uint16(cmyth_conn_t conn, int *err, uint16_t *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_uint16)
- *
- * Description
- *
- * Receive an unsigned (16 bit) integer token from a list of
- * tokens in a MythTV Protocol message. Tokens in MythTV Protocol
- * messages are separated by the string: []:[] or terminated by
- * running out of message. Up to 'count' Bytes will be consumed from
- * the socket specified by 'conn' (stopping when a separator is seen
- * or 'count' is exhausted). The unsigned short integer value of the
- * token is placed in the location pointed to by 'buf'. If an error
- * is encountered and 'err' is not NULL, an indication of the nature
- * of the error will be recorded by placing an error code in the
- * location pointed to by 'err'. If all goes well, 'err' wil be set
- * to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received is too large to fit in an
- * unsinged short integer
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_uint16(cmyth_conn_t conn, int *err, uint16_t *buf, int count)
- {
- uint32_t val;
- int consumed;
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- consumed = cmyth_rcv_uint32(conn, err, &val, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_ulong() failed (%d)\n",
- __FUNCTION__, consumed);
- return consumed;
- }
- if (val > 65535) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: value doesn't fit: '%llu'\n",
- __FUNCTION__, val);
- *err = ERANGE;
- return consumed;
- }
- *err = 0;
- *buf = (uint16_t)val;
- return consumed;
- }
- /*
- * cmyth_rcv_timestamp(cmyth_conn_t conn, int *err, cmyth_timestamp_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_timestamp)
- *
- * Description
- *
- * Receive a timestamp in international format from a list of
- * tokens in a MythTV Protocol message. A time stamp is formatted
- * as follows:
- *
- * <YYYY>-<MM>-<DD>T<HH>:<MM>:<SS>
- *
- * Tokens in MythTV Protocol messages are separated by the string:
- * []:[] or terminated by running out of message. Up to 'count' Bytes
- * will be consumed from the socket specified by 'conn' (stopping when
- * a separator is seen or 'count' is exhausted). The timestamp
- * structure specified in 'buf' will be filled out. If an error is
- * encountered and 'err' is not NULL, an indication of the nature of
- * the error will be recorded by placing an error code in the location
- * pointed to by 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a timestamp
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_timestamp(cmyth_conn_t conn, int *err, cmyth_timestamp_t *ts,
- int count)
- {
- int consumed;
- char tbuf[CMYTH_TIMESTAMP_LEN + 1];
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- tbuf[CMYTH_TIMESTAMP_LEN] = '\0';
- consumed = cmyth_rcv_string(conn, err, tbuf,
- CMYTH_TIMESTAMP_LEN, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_string() failed (%d)\n",
- __FUNCTION__, *err);
- return consumed;
- }
- /*
- * Allow for the timestamp to be empty in the case of livetv
- */
- if ((strlen(tbuf) == 1) && (tbuf[0] = ' '))
- return consumed;
- if (strlen(tbuf) == 0)
- return consumed;
- if (*ts)
- ref_release(*ts);
- *ts = cmyth_timestamp_from_string(tbuf);
- if (*ts == NULL) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_timestamp_from_string() failed\n",
- __FUNCTION__);
- *err = -EINVAL;
- }
- return consumed;
- }
- /*
- * cmyth_rcv_datetime(cmyth_conn_t conn, int *err, cmyth_timestamp_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_datetime)
- *
- * Description
- *
- * Receive a datetime as an unsigned integer -- number of seconds
- * since Jan 1, 1970.
- *
- * Tokens in MythTV Protocol messages are separated by the string:
- * []:[] or terminated by running out of message. Up to 'count' Bytes
- * will be consumed from the socket specified by 'conn' (stopping when
- * a separator is seen or 'count' is exhausted). The timestamp
- * structure specified in 'buf' will be filled out. If an error is
- * encountered and 'err' is not NULL, an indication of the nature of
- * the error will be recorded by placing an error code in the location
- * pointed to by 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a datetime
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_datetime(cmyth_conn_t conn, int *err, cmyth_timestamp_t *ts,
- int count)
- {
- int consumed;
- char tbuf[CMYTH_INT32_LEN + 1];
- int tmp;
- if (!err) {
- err = &tmp;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- tbuf[CMYTH_INT32_LEN] = '\0';
- consumed = cmyth_rcv_string(conn, err, tbuf, CMYTH_INT32_LEN, count);
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_string() failed (%d)\n",
- __FUNCTION__, *err);
- return consumed;
- }
- if (*ts)
- ref_release(*ts);
- *ts = cmyth_timestamp_from_unixtime((time_t)atoi(tbuf));
- if (*ts == NULL) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_datetime_from_string() failed\n",
- __FUNCTION__);
- *err = -EINVAL;
- }
- return consumed;
- }
- static void
- cmyth_proginfo_parse_url(cmyth_proginfo_t p)
- {
- static const char service[]="myth://";
- char *host = NULL;
- char *port = NULL;
- char *path = NULL;
- char *placed = NULL;
- char *eaddr = NULL;
- int ip6 = 0;
- if (!p || ! p->proginfo_url ||
- (!strcmp(p->proginfo_url, "none")) ||
- (!strcmp(p->proginfo_url, " "))) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: proginfo or url was NULL, p = %p, url = %p\n",
- __FUNCTION__, p, p ? p->proginfo_url : NULL);
- return;
- }
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: url is: '%s'\n",
- __FUNCTION__, p->proginfo_url);
- path = p->proginfo_url;
- if (strncmp(p->proginfo_url, service, sizeof(service) - 1) == 0) {
- /*
- * The URL starts with myth://. The rest looks like
- * <host>:<port>/<filename>.
- */
- host = p->proginfo_url + strlen(service);
- /* JLB: ipv6 host looks like [...] */
- eaddr = strchr(host, '/');
- if (!eaddr) {
- eaddr = host + strlen(host) + 1;
- }
- if ((unsigned char)*host == '[') {
- placed = strchr(host,']');
- if (placed && placed < eaddr && (unsigned char)*(++placed) == ':') {
- ip6 = 1;
- port = placed;
- }
- else {
- goto out;
- }
- }
- else {
- port = strchr(host, ':');
- }
- if (!port || port > eaddr) {
- /*
- * This does not seem to be a proper URL, so
- * just assume it is a filename, and get out.
- */
- host = NULL;
- port = NULL;
- goto out;
- }
- port = port + 1;
- path = strchr(port, '/');
- if (!path) {
- /*
- * This does not seem to be a proper URL, so
- * just assume it is a filename, and get out.
- */
- goto out;
- }
- }
- out:
- if (host && port) {
- char tmp;
- if (ip6 == 1) {
- ++host;
- tmp = *(port - 2);
- *(port - 2) = '\0';
- if (p->proginfo_host)
- ref_release(p->proginfo_host);
- p->proginfo_host = ref_strdup(host);
- *(port - 2) = tmp;
- }
- else {
- tmp = *(port - 1);
- *(port - 1) = '\0';
- if (p->proginfo_host)
- ref_release(p->proginfo_host);
- p->proginfo_host = ref_strdup(host);
- *(port - 1) = tmp;
- }
- if (path) {
- tmp = *(path);
- *(path) = '\0';
- p->proginfo_port = atoi(port);
- *(path) = tmp;
- }
- else {
- p->proginfo_port = atoi(port);
- }
- } else {
- if (p->proginfo_host)
- ref_release(p->proginfo_host);
- p->proginfo_host = ref_strdup(p->proginfo_hostname);
- p->proginfo_port = 6543;
- }
- if (p->proginfo_pathname)
- ref_release(p->proginfo_pathname);
- p->proginfo_pathname = ref_strdup(path);
- }
- /*
- * cmyth_rcv_proginfo(cmyth_conn_t conn, cmyth_proginfo_t buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_proginfo)
- *
- * Description
- *
- * Receive a program information structure from a list of tokens in a
- * MythTV Protocol message. Tokens in MythTV Protocol messages are
- * separated by the string: []:[] or terminated by running out of
- * message. Up to 'count' Bytes will be consumed from the socket
- * specified by 'conn' (stopping when a separator is seen or 'count'
- * is exhausted). The proginfo structure specified in 'buf' will be
- * filled out. If an error is encountered and 'err' is not NULL, an
- * indication of the nature of the error will be recorded by placing
- * an error code in the location pointed to by 'err'. If all goes
- * well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a program
- * information structure
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_proginfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
- int count)
- {
- int consumed;
- int total = 0;
- char *failed = NULL;
- char tmp_str[32768];
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- tmp_str[sizeof(tmp_str) - 1] = '\0';
- buf->proginfo_version = conn->conn_version;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: VERSION IS %"PRIu32"\n",
- __FUNCTION__, buf->proginfo_version);
- /*
- * Get proginfo_title (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_title)
- ref_release(buf->proginfo_title);
- buf->proginfo_title = ref_strdup(tmp_str);
- /*
- * Get proginfo_subtitle (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_subtitle)
- ref_release(buf->proginfo_subtitle);
- buf->proginfo_subtitle = ref_strdup(tmp_str);
- /*
- * Get proginfo_description (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_description)
- ref_release(buf->proginfo_description);
- buf->proginfo_description = ref_strdup(tmp_str);
- if (buf->proginfo_version >= 67) {
- /*
- * Get season and episode (unsigned int)
- */
- consumed = cmyth_rcv_uint16(conn, err,
- &buf->proginfo_season, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort";
- goto fail;
- }
- consumed = cmyth_rcv_uint16(conn, err,
- &buf->proginfo_episode, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort";
- goto fail;
- }
- }
- /*
- * Get proginfo_category (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_category)
- ref_release(buf->proginfo_category);
- buf->proginfo_category = ref_strdup(tmp_str);
- /*
- * Get proginfo_chanId (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err,
- &buf->proginfo_chanId, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_chanstr (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_chanstr)
- ref_release(buf->proginfo_chanstr);
- buf->proginfo_chanstr = ref_strdup(tmp_str);
- /*
- * Get proginfo_chansign (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_chansign)
- ref_release(buf->proginfo_chansign);
- buf->proginfo_chansign = ref_strdup(tmp_str);
- /*
- * Get proginfo_channame (string) Version 1 or proginfo_chanicon
- * (string) Version 8.
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- /* FIXME: doesn't seem to match the dump? */
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: GOT TO ICON/NAME\n", __FUNCTION__);
- if (buf->proginfo_chanicon)
- ref_release(buf->proginfo_chanicon);
- if (buf->proginfo_channame)
- ref_release(buf->proginfo_channame);
- if (buf->proginfo_version >= 8) {
- buf->proginfo_chanicon = ref_strdup(tmp_str);
- /*
- * Simulate a channel name (Number and Callsign) for
- * compatibility.
- */
- sprintf(tmp_str,
- "%s %s", buf->proginfo_chanstr,
- buf->proginfo_chansign);
- buf->proginfo_channame = ref_strdup(tmp_str);
- } else { /* Assume version 1 */
- buf->proginfo_channame = ref_strdup(tmp_str);
- buf->proginfo_chanicon = ref_strdup("");
- }
- /*
- * Get proginfo_url (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_url)
- ref_release(buf->proginfo_url);
- buf->proginfo_url = ref_strdup(tmp_str);
- /*
- * Get proginfo_Length (long_long)
- */
- if (buf->proginfo_version < 57) {
- consumed = cmyth_rcv_old_int64(conn, err, &buf->proginfo_Length,
- count);
- } else {
- /*
- * Since protocol 57 mythbackend now sends a single 64 bit
- * integer rather than two 32 bit hi and lo integers for the
- * proginfo length.
- */
- consumed = cmyth_rcv_new_int64(conn, err, &buf->proginfo_Length,
- count, 1);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "rcv_64";
- goto fail;
- }
- /*
- * Get proginfo_start_ts (timestamp)
- */
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: GOT TO START_TS\n", __FUNCTION__);
- if (buf->proginfo_version >= 14) {
- consumed = cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_start_ts),
- count);
- }
- else {
- consumed = cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_start_ts),
- count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "proginfo_start_ts cmyth_rcv";
- goto fail;
- }
- /*
- * Get proginfo_end_ts (timestamp)
- */
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s: GOT TO END_TS\n", __FUNCTION__);
- if (buf->proginfo_version >= 14) {
- consumed = cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_end_ts), count);
- }
- else {
- consumed = cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_end_ts), count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- if (buf->proginfo_version < 57) {
- /*
- * Get proginfo_conflicting (ulong in Version 1, string in Version 8)
- */
- if (buf->proginfo_version >= 8) {
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_unknown_0)
- ref_release(buf->proginfo_unknown_0);
- buf->proginfo_unknown_0 = ref_strdup(tmp_str);
- } else { /* Assume version 1 */
- consumed = cmyth_rcv_uint32(conn, err,
- &buf->proginfo_conflicting, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- }
- /*
- * Get proginfo_recording (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_recording, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- }
- /*
- * Get proginfo_override (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_override, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_hostname (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_hostname)
- ref_release(buf->proginfo_hostname);
- buf->proginfo_hostname = ref_strdup(tmp_str);
- /*
- * Get proginfo_source_id (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_source_id, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_card_id (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_card_id, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_input_id (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_input_id, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_rec_priority (byte)
- */
- consumed = cmyth_rcv_int8(conn, err,
- &buf->proginfo_rec_priority, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_long";
- goto fail;
- }
- /*
- * Get proginfo_rec_status (byte)
- */
- consumed = cmyth_rcv_int8(conn, err, &buf->proginfo_rec_status, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_byte";
- goto fail;
- }
- /*
- * Get proginfo_record_id (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_record_id, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- /*
- * Get proginfo_rec_type (ubyte)
- */
- consumed = cmyth_rcv_uint8(conn, err, &buf->proginfo_rec_type, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ubyte";
- goto fail;
- }
- /*
- * Get proginfo_rec_dupin (ubyte)
- */
- consumed = cmyth_rcv_uint8(conn, err, &buf->proginfo_rec_dupin, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ubyte";
- goto fail;
- }
- if (buf->proginfo_version >= 8) {
- /*
- * Get proginfo_rec_dupmethod (ubyte)
- */
- consumed = cmyth_rcv_uint8(conn, err,
- &buf->proginfo_rec_dupmethod, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ubyte";
- goto fail;
- }
- }
- /*
- * Get proginfo_rec_start_ts (timestamp)
- */
- if (buf->proginfo_version >= 14) {
- consumed = cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_rec_start_ts),
- count);
- }
- else {
- consumed = cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_rec_start_ts),
- count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- /*
- * Get proginfo_rec_end_ts (timestamp)
- */
- if (buf->proginfo_version >= 14) {
- consumed = cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_rec_end_ts),
- count);
- }
- else {
- consumed = cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_rec_end_ts),
- count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- if (buf->proginfo_version < 57) {
- /*
- * Get proginfo_repeat (ubyte)
- */
- consumed = cmyth_rcv_uint8(conn, err, &buf->proginfo_repeat, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ubyte";
- goto fail;
- }
- }
- /*
- * Get proginfo_program_flags (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err, &buf->proginfo_program_flags,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- if (buf->proginfo_version >= 8) {
- /*
- * Get proginfo_recgroup (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_recgroup)
- ref_release(buf->proginfo_recgroup);
- buf->proginfo_recgroup = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 8 && buf->proginfo_version < 57) {
- /*
- * Get proginfo_chancommfree (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_chancommfree)
- ref_release(buf->proginfo_chancommfree);
- buf->proginfo_chancommfree = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 8) {
- /*
- * Get proginfo_chan_output_filters (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_chan_output_filters)
- ref_release(buf->proginfo_chan_output_filters);
- buf->proginfo_chan_output_filters = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 8) {
- /*
- * Get proginfo_seriesid (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_seriesid)
- ref_release(buf->proginfo_seriesid);
- buf->proginfo_seriesid = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 8) {
- /*
- * Get programid (string)
- */
- consumed = cmyth_rcv_string(conn, err, tmp_str,
- sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_programid)
- ref_release(buf->proginfo_programid);
- buf->proginfo_programid = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 67) {
- /*
- * Get inetref (string)
- */
- consumed = cmyth_rcv_string(conn, err, tmp_str,
- sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_inetref)
- ref_release(buf->proginfo_inetref);
- buf->proginfo_inetref = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 12) {
- /*
- * Get lastmodified (string)
- */
- if (buf->proginfo_version >= 14) {
- consumed =
- cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_lastmodified),
- count);
- }
- else {
- consumed =
- cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_lastmodified),
- count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- }
- if (buf->proginfo_version >= 12) {
- char stars[16];
- /*
- * Get stars (string)
- */
- consumed = cmyth_rcv_string(conn, err, tmp_str,
- sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_stars)
- ref_release(buf->proginfo_stars);
- snprintf(stars, sizeof(stars), "%3.1f", atof(tmp_str) * 4.0);
- buf->proginfo_stars = ref_strdup(stars);
- }
- if (buf->proginfo_version >= 12) {
- /*
- * Get original_air_date (string)
- */
- if ((buf->proginfo_version >= 14) & (buf->proginfo_version <=32)) {
- consumed =
- cmyth_rcv_datetime(conn, err,
- &(buf->proginfo_originalairdate),
- count);
- }
- else {
- consumed =
- cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_originalairdate),
- count);
- }
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- }
- if (buf->proginfo_version >= 15 && buf->proginfo_version < 57) {
- /*
- * Get has_air_date (ubyte)
- */
- consumed = cmyth_rcv_uint8(conn, err,
- &buf->proginfo_hasairdate, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ubyte";
- goto fail;
- }
- }
- if (buf->proginfo_version >= 18) {
- /*
- * Get playgroup (string)
- */
- consumed = cmyth_rcv_string(conn, err, tmp_str,
- sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_playgroup)
- ref_release(buf->proginfo_playgroup);
- buf->proginfo_playgroup = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 25) {
- /*
- * Get proginfo_recpriority_2 (byte)
- */
- consumed = cmyth_rcv_int8(conn, err,
- &buf->proginfo_recpriority_2,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_long";
- goto fail;
- }
- }
- if (buf->proginfo_version >= 31) {
- /*
- * Get proginfo_parentid (ulong)
- */
- consumed = cmyth_rcv_uint32(conn, err,
- &buf->proginfo_parentid, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ulong";
- goto fail;
- }
- }
- if (buf->proginfo_version >= 32) {
- /*
- * Get storagegroup (string)
- */
- consumed = cmyth_rcv_string(conn, err, tmp_str,
- sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_storagegroup)
- ref_release(buf->proginfo_storagegroup);
- buf->proginfo_storagegroup = ref_strdup(tmp_str);
- }
- if (buf->proginfo_version >= 35) {
- /*
- * Get audioproperties,videoproperties,subtitletype (ushort)
- */
- consumed = cmyth_rcv_uint16(conn, err,
- &buf->proginfo_audioproperties, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort audio";
- goto fail;
- }
- consumed = cmyth_rcv_uint16(conn, err,
- &buf->proginfo_videoproperties, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort video";
- goto fail;
- }
- consumed = cmyth_rcv_uint16(conn, err,
- &buf->proginfo_subtitletype, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort subtitletype";
- goto fail;
- }
- }
- /*
- * Get Year
- */
- if (buf->proginfo_version >= 43) {
- consumed = cmyth_rcv_uint16(conn, err, &buf->proginfo_year,
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_ushort proginfo_year";
- goto fail;
- }
- }
- cmyth_dbg(CMYTH_DBG_INFO, "%s: got recording info\n", __FUNCTION__);
- cmyth_proginfo_parse_url(buf);
- return total;
- fail:
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: %s() failed (%d) (count = %d)\n",
- __FUNCTION__, failed, *err, count);
- return total;
- }
- /*
- * cmyth_rcv_chaninfo(cmyth_conn_t conn, cmyth_proginfo_t buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_chaninfo)
- *
- * Description
- *
- * Receive a program information structure containing channel
- * information from a list of tokens in a MythTV Protocol message.
- * Channel information is a subset of program information containing
- * only the title, episode name (subtitle), description, category,
- * start and end timestamps, callsign, icon path (a pathname to the
- * channel icon found in filename), channel name, and channel id.
- * This is the information returned for each program in a program
- * guide when browsing on a recorder.
- *
- * Tokens in MythTV Protocol messages are separated by the string:
- * []:[] or terminated by running out of message. Up to 'count' Bytes
- * will be consumed from the socket specified by 'conn' (stopping when
- * a separator is seen or 'count' is exhausted). The proginfo
- * structure specified in 'buf' will be filled out. If an error is
- * encountered and 'err' is not NULL, an indication of the nature of
- * the error will be recorded by placing an error code in the location
- * pointed to by 'err'. If all goes well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a channel
- * information structure
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_chaninfo(cmyth_conn_t conn, int *err, cmyth_proginfo_t buf,
- int count)
- {
- int consumed;
- int total = 0;
- char *failed = NULL;
- char tmp_str[32768];
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- tmp_str[sizeof(tmp_str) - 1] = '\0';
- /*
- * Get proginfo_title (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_title)
- ref_release(buf->proginfo_title);
- buf->proginfo_title = ref_strdup(tmp_str);
- /*
- * Get proginfo_subtitle (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_subtitle)
- ref_release(buf->proginfo_subtitle);
- buf->proginfo_subtitle = ref_strdup(tmp_str);
- /*
- * Get proginfo_description (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_description)
- ref_release(buf->proginfo_description);
- buf->proginfo_description = ref_strdup(tmp_str);
- /*
- * Get proginfo_category (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_category)
- ref_release(buf->proginfo_category);
- buf->proginfo_category = ref_strdup(tmp_str);
- /*
- * Get proginfo_start_ts (timestamp)
- */
- consumed = cmyth_rcv_timestamp(conn, err,
- &(buf->proginfo_start_ts), count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- /*
- * Get proginfo_end_ts (timestamp)
- */
- consumed = cmyth_rcv_timestamp(conn, err, &(buf->proginfo_end_ts),
- count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_timestamp";
- goto fail;
- }
- /*
- * Get proginfo_chansign (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_chansign)
- ref_release(buf->proginfo_chansign);
- buf->proginfo_chansign = ref_strdup(tmp_str);
- /*
- * Get proginfo_url (string) (this is the channel icon path)
- *
- * XXX: This isn't a url, but what is it?
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- buf->proginfo_url = NULL;
- /*
- * Get proginfo_channame (string)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_channame)
- ref_release(buf->proginfo_channame);
- buf->proginfo_channame = ref_strdup(tmp_str);
- /*
- * Get proginfo_chanId (long)
- */
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_long";
- goto fail;
- }
- buf->proginfo_chanId = atoi(tmp_str);
- // get seriesid
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_seriesid)
- ref_release(buf->proginfo_seriesid);
- buf->proginfo_seriesid = ref_strdup(tmp_str);
- // get programid
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- if (buf->proginfo_programid)
- ref_release(buf->proginfo_programid);
- buf->proginfo_programid = ref_strdup(tmp_str);
- // get chanOutputFilters
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- // get repeat
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- // get airdate
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- // get stars
- consumed = cmyth_rcv_string(conn, err,
- tmp_str, sizeof(tmp_str) - 1, count);
- count -= consumed;
- total += consumed;
- if (*err) {
- failed = "cmyth_rcv_string";
- goto fail;
- }
- return total;
- fail:
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: %s() failed (%d) (count = %d)\n",
- __FUNCTION__, failed, *err, count);
- return total;
- }
- /*
- * cmyth_rcv_proglist(cmyth_conn_t conn, int *err, cmyth_proglist_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_proglist)
- *
- * Description
- *
- * Receive a program list from a list of tokens in a MythTV Protocol
- * message. Tokens in MythTV Protocol messages are separated by the
- * string: []:[] or terminated by running out of message. Up to
- * 'count' Bytes will be consumed from the socket specified by 'conn'
- * (stopping when a separator is seen or 'count' is exhausted). The
- * program list structure specified in 'buf' will be filled out. If
- * an error is encountered and 'err' is not NULL, an indication of the
- * nature of the error will be recorded by placing an error code in
- * the location pointed to by 'err'. If all goes well, 'err' wil be
- * set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a program list
- */
- int
- cmyth_rcv_proglist(cmyth_conn_t conn, int *err, cmyth_proglist_t buf,
- int count)
- {
- int tmp_err;
- int consumed = 0;
- int r;
- int c;
- cmyth_proginfo_t pi;
- int i;
- cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
- if (!err) {
- err = &tmp_err;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- if(!buf) {
- *err = EINVAL;
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL buffer\n", __FUNCTION__);
- return 0;
- }
- r = cmyth_rcv_int32(conn, err, &buf->proglist_count, count);
- consumed += r;
- if (*err) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_long() failed (%d)\n",
- __FUNCTION__, *err);
- return consumed;
- }
- count -= r;
- c = buf->proglist_count;
- buf->proglist_list = malloc(c * sizeof(cmyth_proginfo_t));
- if (!buf->proglist_list) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc() failed for list\n",
- __FUNCTION__);
- *err = ENOMEM;
- return consumed;
- }
- memset(buf->proglist_list, 0, c * sizeof(cmyth_proginfo_t));
- for (i = 0; i < c; ++i) {
- pi = cmyth_proginfo_create();
- if (!pi) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_proginfo_create() failed\n",
- __FUNCTION__);
- *err = ENOMEM;
- break;
- }
- r = cmyth_rcv_proginfo(conn, err, pi, count);
- consumed += r;
- count -= r;
- if (*err) {
- ref_release(pi);
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: cmyth_rcv_proginfo() failed (%d)\n",
- __FUNCTION__, *err);
- break;
- }
- buf->proglist_list[i] = pi;
- }
- return consumed;
- }
- /*
- * cmyth_rcv_keyframe(cmyth_conn_t conn, int *err, cmyth_keyframe_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_keyframe)
- *
- * Description
- *
- * Receive a keyframe description from a list of tokens in a MythTV
- * Protocol message. Tokens in MythTV Protocol messages are separated
- * by the string: []:[] or terminated by running out of message. Up
- * to 'count' Bytes will be consumed from the socket specified by
- * 'conn' (stopping when a separator is seen or 'count' is exhausted).
- * The keyframe structure specified in 'buf' will be filled out. If
- * an error is encountered and 'err' is not NULL, an indication of the
- * nature of the error will be recorded by placing an error code in
- * the location pointed to by 'err'. If all goes well, 'err' wil be
- * set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a keyframe
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_keyframe(cmyth_conn_t conn, int *err, cmyth_keyframe_t buf,
- int count)
- {
- int tmp_err;
- if (!err) {
- err = &tmp_err;
- }
- /*
- * For now this is unimplemented.
- */
- *err = ENOSYS;
- return 0;
- }
- /*
- * cmyth_rcv_freespace(cmyth_conn_t conn, cmyth_freespace_t buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_freespace)
- *
- * Description
- *
- * Receive a free space description from a list of tokens in a MythTV
- * Protocol message. Tokens in MythTV Protocol messages are separated
- * by the string: []:[] or terminated by running out of message. Up
- * to 'count' Bytes will be consumed from the socket specified by
- * 'conn' (stopping when a separator is seen or 'count' is exhausted).
- * The free space structure specified in 'buf' will be filled out. If
- * an error is encountered and 'err' is not NULL, an indication of the
- * nature of the error will be recorded by placing an error code in
- * the location pointed to by 'err'. If all goes well, 'err' wil be
- * set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a free space
- * description
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_freespace(cmyth_conn_t conn, int *err, cmyth_freespace_t buf,
- int count)
- {
- int tmp_err;
- if (!err) {
- err = &tmp_err;
- }
- /*
- * For now this is unimplemented.
- */
- *err = ENOSYS;
- return 0;
- }
- /*
- * cmyth_rcv_recorder(cmyth_conn_t conn, cmyth_recorder_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_recorder)
- *
- * Description
- *
- * Receive a recorder description from a list of tokens in a MythTV
- * Protocol message. Tokens in MythTV Protocol messages are separated
- * by the string: []:[] or terminated by running out of message. Up
- * to 'count' Bytes will be consumed from the socket specified by
- * 'conn' (stopping when a separator is seen or 'count' is exhausted).
- * The recorder structure specified in 'buf' will be filled out. If
- * an error is encountered and 'err' is not NULL, an indication of the
- * nature of the error will be recorded by placing an error code in
- * the location pointed to by 'err'. If all goes well, 'err' wil be
- * set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a recorder
- * description
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_recorder(cmyth_conn_t conn, int *err, cmyth_recorder_t buf,
- int count)
- {
- int tmp_err;
- if (!err) {
- err = &tmp_err;
- }
- /*
- * For now this is unimplemented.
- */
- *err = ENOSYS;
- return 0;
- }
- /*
- * cmyth_rcv_ringbuf(cmyth_conn_t conn, int *err, cmyth_ringbuf_t buf,
- * int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_ringbuf)
- *
- * Description
- *
- * Receive a ring buffer description from a list of tokens in a MythTV
- * Protocol message. Tokens in MythTV Protocol messages are separated
- * by the string: []:[] or terminated by running out of message. Up
- * to 'count' Bytes will be consumed from the socket specified by
- * 'conn' (stopping when a separator is seen or 'count' is exhausted).
- * The ring buffer structure specified in 'buf' will be filled out.
- * If an error is encountered and 'err' is not NULL, an indication of
- * the nature of the error will be recorded by placing an error code
- * in the location pointed to by 'err'. If all goes well, 'err' wil
- * be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- *
- * Error Codes:
- *
- * In addition to system call error codes, the following errors may be
- * placed in 'err':
- *
- * ERANGE The token received did not parse into a recorder
- * description
- *
- * EINVAL The token received is not numeric or is signed
- */
- int
- cmyth_rcv_ringbuf(cmyth_conn_t conn, int *err, cmyth_ringbuf_t buf, int count)
- {
- int tmp_err;
- if (!err) {
- err = &tmp_err;
- }
- /*
- * For now this is unimplemented.
- */
- *err = ENOSYS;
- return 0;
- }
- /*
- * cmyth_rcv_data(cmyth_conn_t conn, int *err, unsigned char *buf, int count)
- *
- * Scope: PRIVATE (mapped to __cmyth_rcv_data)
- *
- * Description
- *
- * Receive raw data from the socket specified by 'conn' and place it
- * in 'buf'. This function consumes 'count' bytes and places them in
- * 'buf'. If an error is encountered and 'err' is not NULL, an
- * indication of the nature of the error will be recorded by placing
- * an error code in the location pointed to by 'err'. If all goes
- * well, 'err' wil be set to 0.
- *
- * Return Value:
- *
- * A value >=0 indicating the number of bytes consumed.
- */
- int
- cmyth_rcv_data(cmyth_conn_t conn, int *err, unsigned char *buf, int count)
- {
- int r;
- int total = 0;
- unsigned char *p;
- int tmp_err;
- struct timeval tv;
- fd_set fds;
- if (!err) {
- err = &tmp_err;
- }
- if (count <= 0) {
- *err = EINVAL;
- return 0;
- }
- *err = 0;
- if (!conn) {
- cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
- __FUNCTION__);
- *err = EINVAL;
- return 0;
- }
- p = buf;
- while (count > 0) {
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- FD_ZERO(&fds);
- FD_SET(conn->conn_fd, &fds);
- if (select((int)conn->conn_fd+1, &fds, NULL, NULL, &tv) == 0) {
- conn->conn_hang = 1;
- return -ETIMEDOUT;
- } else {
- conn->conn_hang = 0;
- }
- r = recv(conn->conn_fd, p, count, 0);
- if (r < 0) {
- if (total == 0) {
- cmyth_dbg(CMYTH_DBG_ERROR,
- "%s: read failed (%d)\n",
- __FUNCTION__, errno);
- conn->conn_hang = 1;
- if (r == 0)
- *err = -1 * EBADF;
- else
- *err = -1 * errno;
- return 0;
- }
- /*
- * There were bytes read before the error, use them and
- * then report the error next time.
- */
- break;
- }
- total += r;
- count -= r;
- p += r;
- }
- return total;
- }
- void cmyth_toupper_string(char *str)
- {
- if (str) {
- int i;
- for ( i=0 ; i < sizeof(str) && str[i] != '\0' ; i++ ) {
- str[i] = toupper(str[i]);
- }
- }
- }