aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/src/inputStream_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/inputStream_http.c')
-rw-r--r--trunk/src/inputStream_http.c912
1 files changed, 912 insertions, 0 deletions
diff --git a/trunk/src/inputStream_http.c b/trunk/src/inputStream_http.c
new file mode 100644
index 000000000..3f18575dd
--- /dev/null
+++ b/trunk/src/inputStream_http.c
@@ -0,0 +1,912 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "inputStream_http.h"
+
+#include "utils.h"
+#include "log.h"
+#include "conf.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define HTTP_CONN_STATE_CLOSED 0
+#define HTTP_CONN_STATE_INIT 1
+#define HTTP_CONN_STATE_HELLO 2
+#define HTTP_CONN_STATE_OPEN 3
+#define HTTP_CONN_STATE_REOPEN 4
+
+#define HTTP_BUFFER_SIZE_DEFAULT 131072
+#define HTTP_PREBUFFER_SIZE_DEFAULT (HTTP_BUFFER_SIZE_DEFAULT >> 2)
+
+#define HTTP_REDIRECT_MAX 10
+
+static char *proxyHost;
+static char *proxyPort;
+static char *proxyUser;
+static char *proxyPassword;
+static int bufferSize = HTTP_BUFFER_SIZE_DEFAULT;
+static int prebufferSize = HTTP_PREBUFFER_SIZE_DEFAULT;
+
+typedef struct _InputStreemHTTPData {
+ char *host;
+ char *path;
+ char *port;
+ int sock;
+ int connState;
+ char *buffer;
+ size_t buflen;
+ int timesRedirected;
+ int icyMetaint;
+ int prebuffer;
+ int icyOffset;
+ char *proxyAuth;
+ char *httpAuth;
+} InputStreamHTTPData;
+
+void inputStream_initHttp(void)
+{
+ ConfigParam *param = getConfigParam(CONF_HTTP_PROXY_HOST);
+ char *test;
+
+ if (param) {
+ proxyHost = param->value;
+
+ param = getConfigParam(CONF_HTTP_PROXY_PORT);
+
+ if (!param) {
+ FATAL("%s specified but not %s", CONF_HTTP_PROXY_HOST,
+ CONF_HTTP_PROXY_PORT);
+ }
+ proxyPort = param->value;
+
+ param = getConfigParam(CONF_HTTP_PROXY_USER);
+
+ if (param) {
+ proxyUser = param->value;
+
+ param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
+
+ if (!param) {
+ FATAL("%s specified but not %s\n",
+ CONF_HTTP_PROXY_USER,
+ CONF_HTTP_PROXY_PASSWORD);
+ }
+
+ proxyPassword = param->value;
+ } else {
+ param = getConfigParam(CONF_HTTP_PROXY_PASSWORD);
+
+ if (param) {
+ FATAL("%s specified but not %s\n",
+ CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_USER);
+ }
+ }
+ } else if ((param = getConfigParam(CONF_HTTP_PROXY_PORT))) {
+ FATAL("%s specified but not %s, line %i\n",
+ CONF_HTTP_PROXY_PORT, CONF_HTTP_PROXY_HOST, param->line);
+ } else if ((param = getConfigParam(CONF_HTTP_PROXY_USER))) {
+ FATAL("%s specified but not %s, line %i\n",
+ CONF_HTTP_PROXY_USER, CONF_HTTP_PROXY_HOST, param->line);
+ } else if ((param = getConfigParam(CONF_HTTP_PROXY_PASSWORD))) {
+ FATAL("%s specified but not %s, line %i\n",
+ CONF_HTTP_PROXY_PASSWORD, CONF_HTTP_PROXY_HOST,
+ param->line);
+ }
+
+ param = getConfigParam(CONF_HTTP_BUFFER_SIZE);
+
+ if (param) {
+ bufferSize = strtol(param->value, &test, 10);
+
+ if (bufferSize <= 0 || *test != '\0') {
+ FATAL("\"%s\" specified for %s at line %i is not a "
+ "positive integer\n",
+ param->value, CONF_HTTP_BUFFER_SIZE, param->line);
+ }
+
+ bufferSize *= 1024;
+
+ if (prebufferSize > bufferSize)
+ prebufferSize = bufferSize;
+ }
+
+ param = getConfigParam(CONF_HTTP_PREBUFFER_SIZE);
+
+ if (param) {
+ prebufferSize = strtol(param->value, &test, 10);
+
+ if (prebufferSize <= 0 || *test != '\0') {
+ FATAL("\"%s\" specified for %s at line %i is not a "
+ "positive integer\n",
+ param->value, CONF_HTTP_PREBUFFER_SIZE,
+ param->line);
+ }
+
+ prebufferSize *= 1024;
+ }
+
+ if (prebufferSize > bufferSize)
+ prebufferSize = bufferSize;
+}
+
+/* base64 code taken from xmms */
+
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+
+static char *base64Dup(char *s)
+{
+ int i;
+ int len = strlen(s);
+ char *ret = xcalloc(BASE64_LENGTH(len) + 1, 1);
+ unsigned char *p = (unsigned char *)ret;
+
+ char tbl[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'
+ };
+
+ /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
+ for (i = 0; i < len; i += 3) {
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+ *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
+ *p++ = tbl[s[2] & 0x3f];
+ s += 3;
+ }
+ /* Pad the result if necessary... */
+ if (i == len + 1)
+ *(p - 1) = '=';
+ else if (i == len + 2)
+ *(p - 1) = *(p - 2) = '=';
+ /* ...and zero-terminate it. */
+ *p = '\0';
+
+ return ret;
+}
+
+static char *authString(char *header, char *user, char *password)
+{
+ char *ret = NULL;
+ int templen;
+ char *temp;
+ char *temp64;
+
+ if (!user || !password)
+ return NULL;
+
+ templen = strlen(user) + strlen(password) + 2;
+ temp = xmalloc(templen);
+ strcpy(temp, user);
+ strcat(temp, ":");
+ strcat(temp, password);
+ temp64 = base64Dup(temp);
+ free(temp);
+
+ ret = xmalloc(strlen(temp64) + strlen(header) + 3);
+ strcpy(ret, header);
+ strcat(ret, temp64);
+ strcat(ret, "\r\n");
+ free(temp64);
+
+ return ret;
+}
+
+#define PROXY_AUTH_HEADER "Proxy-Authorization: Basic "
+#define HTTP_AUTH_HEADER "Authorization: Basic "
+
+#define proxyAuthString(x, y) authString(PROXY_AUTH_HEADER, x, y)
+#define httpAuthString(x, y) authString(HTTP_AUTH_HEADER, x, y)
+
+static InputStreamHTTPData *newInputStreamHTTPData(void)
+{
+ InputStreamHTTPData *ret = xmalloc(sizeof(InputStreamHTTPData));
+
+ if (proxyHost) {
+ ret->proxyAuth = proxyAuthString(proxyUser, proxyPassword);
+ } else
+ ret->proxyAuth = NULL;
+
+ ret->httpAuth = NULL;
+ ret->host = NULL;
+ ret->path = NULL;
+ ret->port = NULL;
+ ret->connState = HTTP_CONN_STATE_CLOSED;
+ ret->timesRedirected = 0;
+ ret->icyMetaint = 0;
+ ret->prebuffer = 0;
+ ret->icyOffset = 0;
+ ret->buffer = xmalloc(bufferSize);
+
+ return ret;
+}
+
+static void freeInputStreamHTTPData(InputStreamHTTPData * data)
+{
+ if (data->host)
+ free(data->host);
+ if (data->path)
+ free(data->path);
+ if (data->port)
+ free(data->port);
+ if (data->proxyAuth)
+ free(data->proxyAuth);
+ if (data->httpAuth)
+ free(data->httpAuth);
+
+ free(data->buffer);
+
+ free(data);
+}
+
+static int parseUrl(InputStreamHTTPData * data, char *url)
+{
+ char *temp;
+ char *colon;
+ char *slash;
+ char *at;
+ int len;
+
+ if (strncmp("http://", url, strlen("http://")) != 0)
+ return -1;
+
+ temp = url + strlen("http://");
+
+ colon = strchr(temp, ':');
+ at = strchr(temp, '@');
+
+ if (data->httpAuth) {
+ free(data->httpAuth);
+ data->httpAuth = NULL;
+ }
+
+ if (at) {
+ char *user;
+ char *passwd;
+
+ if (colon && colon < at) {
+ user = xmalloc(colon - temp + 1);
+ memcpy(user, temp, colon - temp);
+ user[colon - temp] = '\0';
+
+ passwd = xmalloc(at - colon);
+ memcpy(passwd, colon + 1, at - colon - 1);
+ passwd[at - colon - 1] = '\0';
+ } else {
+ user = xmalloc(at - temp + 1);
+ memcpy(user, temp, at - temp);
+ user[at - temp] = '\0';
+
+ passwd = xstrdup("");
+ }
+
+ data->httpAuth = httpAuthString(user, passwd);
+
+ free(user);
+ free(passwd);
+
+ temp = at + 1;
+ colon = strchr(temp, ':');
+ }
+
+ slash = strchr(temp, '/');
+
+ if (slash && colon && slash <= colon)
+ return -1;
+
+ /* fetch the host portion */
+ if (colon)
+ len = colon - temp + 1;
+ else if (slash)
+ len = slash - temp + 1;
+ else
+ len = strlen(temp) + 1;
+
+ if (len <= 1)
+ return -1;
+
+ data->host = xmalloc(len);
+ memcpy(data->host, temp, len - 1);
+ data->host[len - 1] = '\0';
+ /* fetch the port */
+ if (colon && (!slash || slash != colon + 1)) {
+ len = strlen(colon) - 1;
+ if (slash)
+ len -= strlen(slash);
+ data->port = xmalloc(len + 1);
+ memcpy(data->port, colon + 1, len);
+ data->port[len] = '\0';
+ DEBUG(__FILE__ ": Port: %s\n", data->port);
+ } else {
+ data->port = xstrdup("80");
+ }
+
+ /* fetch the path */
+ if (proxyHost)
+ data->path = xstrdup(url);
+ else
+ data->path = xstrdup(slash ? slash : "/");
+
+ return 0;
+}
+
+static int initHTTPConnection(InputStream * inStream)
+{
+ char *connHost;
+ char *connPort;
+ struct addrinfo *ans = NULL;
+ struct addrinfo *ap = NULL;
+ struct addrinfo hints;
+ int error, flags;
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ /**
+ * Setup hints
+ */
+ hints.ai_flags = 0;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_addrlen = 0;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hints.ai_next = NULL;
+
+ if (proxyHost) {
+ connHost = proxyHost;
+ connPort = proxyPort;
+ } else {
+ connHost = data->host;
+ connPort = data->port;
+ }
+
+ error = getaddrinfo(connHost, connPort, &hints, &ans);
+ if (error) {
+ DEBUG(__FILE__ ": Error getting address info: %s\n",
+ gai_strerror(error));
+ return -1;
+ }
+
+ /* loop through possible addresses */
+ for (ap = ans; ap != NULL; ap = ap->ai_next) {
+ if ((data->sock = socket(ap->ai_family, ap->ai_socktype,
+ ap->ai_protocol)) < 0) {
+ DEBUG(__FILE__ ": unable to connect: %s\n",
+ strerror(errno));
+ freeaddrinfo(ans);
+ return -1;
+ }
+
+ flags = fcntl(data->sock, F_GETFL, 0);
+ fcntl(data->sock, F_SETFL, flags | O_NONBLOCK);
+
+ if (connect(data->sock, ap->ai_addr, ap->ai_addrlen) >= 0
+ || errno == EINPROGRESS) {
+ data->connState = HTTP_CONN_STATE_INIT;
+ data->buflen = 0;
+ freeaddrinfo(ans);
+ return 0; /* success */
+ }
+
+ /* failed, get the next one */
+
+ DEBUG(__FILE__ ": unable to connect: %s\n", strerror(errno));
+ close(data->sock);
+ }
+
+ freeaddrinfo(ans);
+ return -1; /* failed */
+}
+
+static int finishHTTPInit(InputStream * inStream)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ struct timeval tv;
+ fd_set writeSet;
+ fd_set errorSet;
+ int error;
+ socklen_t error_len = sizeof(int);
+ int ret;
+ int length;
+ char request[2048];
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&writeSet);
+ FD_ZERO(&errorSet);
+ FD_SET(data->sock, &writeSet);
+ FD_SET(data->sock, &errorSet);
+
+ ret = select(data->sock + 1, NULL, &writeSet, &errorSet, &tv);
+
+ if (ret == 0 || (ret < 0 && errno == EINTR))
+ return 0;
+
+ if (ret < 0) {
+ DEBUG(__FILE__ ": problem select'ing: %s\n", strerror(errno));
+ goto close_err;
+ }
+
+ getsockopt(data->sock, SOL_SOCKET, SO_ERROR, &error, &error_len);
+ if (error)
+ goto close_err;
+
+ /* deal with ICY metadata later, for now its fucking up stuff! */
+ length = snprintf(request, sizeof(request),
+ "GET %s HTTP/1.1\r\n" "Host: %s\r\n"
+ /*"Connection: close\r\n" */
+ "User-Agent: %s/%s\r\n"
+ "Range: bytes=%ld-\r\n"
+ "%s" /* authorization */
+ "Icy-Metadata:1\r\n"
+ "\r\n",
+ data->path, data->host,
+ PACKAGE_NAME, PACKAGE_VERSION,
+ inStream->offset,
+ data->proxyAuth ? data->proxyAuth :
+ (data->httpAuth ? data->httpAuth : ""));
+
+ if (length >= sizeof(request))
+ goto close_err;
+ ret = write(data->sock, request, length);
+ if (ret != length)
+ goto close_err;
+
+ data->connState = HTTP_CONN_STATE_HELLO;
+ return 0;
+
+close_err:
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ return -1;
+}
+
+static int getHTTPHello(InputStream * inStream)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ fd_set readSet;
+ struct timeval tv;
+ int ret;
+ char *needle;
+ char *cur = data->buffer;
+ int rc;
+ long readed;
+
+ FD_ZERO(&readSet);
+ FD_SET(data->sock, &readSet);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ ret = select(data->sock + 1, &readSet, NULL, NULL, &tv);
+
+ if (ret == 0 || (ret < 0 && errno == EINTR))
+ return 0;
+
+ if (ret < 0) {
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ close(data->sock);
+ data->buflen = 0;
+ return -1;
+ }
+
+ if (data->buflen >= bufferSize - 1) {
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ close(data->sock);
+ return -1;
+ }
+
+ readed = recv(data->sock, data->buffer + data->buflen,
+ bufferSize - 1 - data->buflen, 0);
+
+ if (readed < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+
+ if (readed <= 0) {
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ close(data->sock);
+ data->buflen = 0;
+ return -1;
+ }
+
+ data->buffer[data->buflen + readed] = '\0';
+ data->buflen += readed;
+
+ needle = strstr(data->buffer, "\r\n\r\n");
+
+ if (!needle)
+ return 0;
+
+ if (0 == strncmp(cur, "HTTP/1.0 ", 9)) {
+ inStream->seekable = 0;
+ rc = atoi(cur + 9);
+ } else if (0 == strncmp(cur, "HTTP/1.1 ", 9)) {
+ inStream->seekable = 1;
+ rc = atoi(cur + 9);
+ } else if (0 == strncmp(cur, "ICY 200 OK", 10)) {
+ inStream->seekable = 0;
+ rc = 200;
+ } else if (0 == strncmp(cur, "ICY 400 Server Full", 19))
+ rc = 400;
+ else if (0 == strncmp(cur, "ICY 404", 7))
+ rc = 404;
+ else {
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ return -1;
+ }
+
+ switch (rc) {
+ case 200:
+ case 206:
+ break;
+ case 301:
+ case 302:
+ cur = strstr(cur, "Location: ");
+ if (cur) {
+ char *url;
+ int curlen = 0;
+ cur += strlen("Location: ");
+ while (*(cur + curlen) != '\0'
+ && *(cur + curlen) != '\r') {
+ curlen++;
+ }
+ url = xmalloc(curlen + 1);
+ memcpy(url, cur, curlen);
+ url[curlen] = '\0';
+ ret = parseUrl(data, url);
+ free(url);
+ if (ret == 0 && data->timesRedirected <
+ HTTP_REDIRECT_MAX) {
+ data->timesRedirected++;
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_REOPEN;
+ data->buflen = 0;
+ return 0;
+ }
+ }
+ case 400:
+ case 401:
+ case 403:
+ case 404:
+ default:
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ data->buflen = 0;
+ return -1;
+ }
+
+ cur = strstr(data->buffer, "\r\n");
+ while (cur && cur != needle) {
+ if (0 == strncmp(cur, "\r\nContent-Length: ", 18)) {
+ if (!inStream->size)
+ inStream->size = atol(cur + 18);
+ } else if (0 == strncmp(cur, "\r\nicy-metaint:", 14)) {
+ data->icyMetaint = atoi(cur + 14);
+ } else if (0 == strncmp(cur, "\r\nicy-name:", 11) ||
+ 0 == strncmp(cur, "\r\nice-name:", 11)) {
+ int incr = 11;
+ char *temp = strstr(cur + incr, "\r\n");
+ if (!temp)
+ break;
+ *temp = '\0';
+ if (inStream->metaName)
+ free(inStream->metaName);
+ while (*(incr + cur) == ' ')
+ incr++;
+ inStream->metaName = xstrdup(cur + incr);
+ *temp = '\r';
+ DEBUG("inputStream_http: metaName: %s\n",
+ inStream->metaName);
+ } else if (0 == strncmp(cur, "\r\nx-audiocast-name:", 19)) {
+ int incr = 19;
+ char *temp = strstr(cur + incr, "\r\n");
+ if (!temp)
+ break;
+ *temp = '\0';
+ if (inStream->metaName)
+ free(inStream->metaName);
+ while (*(incr + cur) == ' ')
+ incr++;
+ inStream->metaName = xstrdup(cur + incr);
+ *temp = '\r';
+ DEBUG("inputStream_http: metaName: %s\n",
+ inStream->metaName);
+ } else if (0 == strncmp(cur, "\r\nContent-Type:", 15)) {
+ int incr = 15;
+ char *temp = strstr(cur + incr, "\r\n");
+ if (!temp)
+ break;
+ *temp = '\0';
+ if (inStream->mime)
+ free(inStream->mime);
+ while (*(incr + cur) == ' ')
+ incr++;
+ inStream->mime = xstrdup(cur + incr);
+ *temp = '\r';
+ }
+
+ cur = strstr(cur + 2, "\r\n");
+ }
+
+ if (inStream->size <= 0)
+ inStream->seekable = 0;
+
+ needle += 4; /* 4 == strlen("\r\n\r\n") */
+ data->buflen -= (needle - data->buffer);
+ /*fwrite(data->buffer, 1, data->buflen, stdout); */
+ memmove(data->buffer, needle, data->buflen);
+
+ data->connState = HTTP_CONN_STATE_OPEN;
+
+ data->prebuffer = 1;
+
+ return 0;
+}
+
+int inputStream_httpOpen(InputStream * inStream, char *url)
+{
+ InputStreamHTTPData *data = newInputStreamHTTPData();
+
+ inStream->data = data;
+
+ if (parseUrl(data, url) < 0) {
+ freeInputStreamHTTPData(data);
+ return -1;
+ }
+
+ if (initHTTPConnection(inStream) < 0) {
+ freeInputStreamHTTPData(data);
+ return -1;
+ }
+
+ inStream->seekFunc = inputStream_httpSeek;
+ inStream->closeFunc = inputStream_httpClose;
+ inStream->readFunc = inputStream_httpRead;
+ inStream->atEOFFunc = inputStream_httpAtEOF;
+ inStream->bufferFunc = inputStream_httpBuffer;
+
+ return 0;
+}
+
+int inputStream_httpSeek(InputStream * inStream, long offset, int whence)
+{
+ InputStreamHTTPData *data;
+
+ if (!inStream->seekable)
+ return -1;
+
+ switch (whence) {
+ case SEEK_SET:
+ inStream->offset = offset;
+ break;
+ case SEEK_CUR:
+ inStream->offset += offset;
+ break;
+ case SEEK_END:
+ inStream->offset = inStream->size + offset;
+ break;
+ default:
+ return -1;
+ }
+
+ data = (InputStreamHTTPData *)inStream->data;
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_REOPEN;
+ data->buflen = 0;
+
+ inputStream_httpBuffer(inStream);
+
+ return 0;
+}
+
+static void parseIcyMetadata(InputStream * inStream, char *metadata, int size)
+{
+ char *r;
+ char *s;
+ char *temp = xmalloc(size + 1);
+ memcpy(temp, metadata, size);
+ temp[size] = '\0';
+ s = strtok_r(temp, ";", &r);
+ while (s) {
+ if (0 == strncmp(s, "StreamTitle=", 12)) {
+ int cur = 12;
+ if (inStream->metaTitle)
+ free(inStream->metaTitle);
+ if (*(s + cur) == '\'')
+ cur++;
+ if (s[strlen(s) - 1] == '\'') {
+ s[strlen(s) - 1] = '\0';
+ }
+ inStream->metaTitle = xstrdup(s + cur);
+ DEBUG("inputStream_http: metaTitle: %s\n",
+ inStream->metaTitle);
+ }
+ s = strtok_r(NULL, ";", &r);
+ }
+ free(temp);
+}
+
+size_t inputStream_httpRead(InputStream * inStream, void *ptr, size_t size,
+ size_t nmemb)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ long tosend = 0;
+ long inlen = size * nmemb;
+ long maxToSend = data->buflen;
+
+ inputStream_httpBuffer(inStream);
+
+ switch (data->connState) {
+ case HTTP_CONN_STATE_OPEN:
+ if (data->prebuffer || data->buflen < data->icyMetaint)
+ return 0;
+
+ break;
+ case HTTP_CONN_STATE_CLOSED:
+ if (data->buflen)
+ break;
+ default:
+ return 0;
+ }
+
+ if (data->icyMetaint > 0) {
+ if (data->icyOffset >= data->icyMetaint) {
+ int metalen = *(data->buffer);
+ metalen <<= 4;
+ if (metalen < 0)
+ metalen = 0;
+ if (metalen + 1 > data->buflen) {
+ /* damn that's some fucking big metadata! */
+ if (bufferSize < metalen + 1) {
+ data->connState =
+ HTTP_CONN_STATE_CLOSED;
+ close(data->sock);
+ data->buflen = 0;
+ }
+ return 0;
+ }
+ if (metalen > 0) {
+ parseIcyMetadata(inStream, data->buffer + 1,
+ metalen);
+ }
+ data->buflen -= metalen + 1;
+ memmove(data->buffer, data->buffer + metalen + 1,
+ data->buflen);
+ data->icyOffset = 0;
+ }
+ maxToSend = data->icyMetaint - data->icyOffset;
+ maxToSend = maxToSend > data->buflen ? data->buflen : maxToSend;
+ }
+
+ if (data->buflen > 0) {
+ tosend = inlen > maxToSend ? maxToSend : inlen;
+ tosend = (tosend / size) * size;
+
+ memcpy(ptr, data->buffer, tosend);
+ /*fwrite(ptr,1,readed,stdout); */
+ data->buflen -= tosend;
+ data->icyOffset += tosend;
+ /*fwrite(data->buffer,1,readed,stdout); */
+ memmove(data->buffer, data->buffer + tosend, data->buflen);
+
+ inStream->offset += tosend;
+ }
+
+ return tosend / size;
+}
+
+int inputStream_httpClose(InputStream * inStream)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+
+ switch (data->connState) {
+ case HTTP_CONN_STATE_CLOSED:
+ break;
+ default:
+ close(data->sock);
+ }
+
+ freeInputStreamHTTPData(data);
+
+ return 0;
+}
+
+int inputStream_httpAtEOF(InputStream * inStream)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ switch (data->connState) {
+ case HTTP_CONN_STATE_CLOSED:
+ if (data->buflen == 0)
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int inputStream_httpBuffer(InputStream * inStream)
+{
+ InputStreamHTTPData *data = (InputStreamHTTPData *) inStream->data;
+ ssize_t readed = 0;
+
+ if (data->connState == HTTP_CONN_STATE_REOPEN) {
+ if (initHTTPConnection(inStream) < 0)
+ return -1;
+ }
+
+ if (data->connState == HTTP_CONN_STATE_INIT) {
+ if (finishHTTPInit(inStream) < 0)
+ return -1;
+ }
+
+ if (data->connState == HTTP_CONN_STATE_HELLO) {
+ if (getHTTPHello(inStream) < 0)
+ return -1;
+ }
+
+ switch (data->connState) {
+ case HTTP_CONN_STATE_OPEN:
+ case HTTP_CONN_STATE_CLOSED:
+ break;
+ default:
+ return -1;
+ }
+
+ if (data->buflen == 0 || data->buflen < data->icyMetaint) {
+ data->prebuffer = 1;
+ } else if (data->buflen > prebufferSize)
+ data->prebuffer = 0;
+
+ if (data->connState == HTTP_CONN_STATE_OPEN &&
+ data->buflen < bufferSize - 1) {
+ readed = read(data->sock, data->buffer + data->buflen,
+ (size_t) (bufferSize - 1 - data->buflen));
+
+ if (readed < 0 && (errno == EAGAIN || errno == EINTR)) {
+ readed = 0;
+ } else if (readed <= 0) {
+ close(data->sock);
+ data->connState = HTTP_CONN_STATE_CLOSED;
+ readed = 0;
+ }
+ /*fwrite(data->buffer+data->buflen,1,readed,stdout); */
+ data->buflen += readed;
+ }
+
+ if (data->buflen > prebufferSize)
+ data->prebuffer = 0;
+
+ return (readed ? 1 : 0);
+}