diff options
Diffstat (limited to '')
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/inputStream.c | 18 | ||||
-rw-r--r-- | src/inputStream_http.c | 1049 | ||||
-rw-r--r-- | src/inputStream_http_auth.h | 102 | ||||
-rw-r--r-- | src/input_curl.c | 493 | ||||
-rw-r--r-- | src/input_curl.h (renamed from src/inputStream_http.h) | 22 | ||||
-rw-r--r-- | src/ls.c | 2 |
7 files changed, 525 insertions, 1171 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7e2386dc8..12e690cf7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,8 +49,6 @@ mpd_headers = \ decoder/_ogg_common.h \ inputStream.h \ inputStream_file.h \ - inputStream_http.h \ - inputStream_http_auth.h \ client.h \ list.h \ dlist.h \ @@ -132,7 +130,6 @@ mpd_SOURCES = \ decoder_list.c \ inputStream.c \ inputStream_file.c \ - inputStream_http.c \ client.c \ ioops.c \ list.c \ @@ -246,8 +243,14 @@ mpd_SOURCES += zeroconf.c endif +if HAVE_CURL +mpd_SOURCES += input_curl.c +endif + + mpd_CFLAGS = $(MPD_CFLAGS) mpd_CPPFLAGS = \ + $(CURL_CFLAGS) \ $(AO_CFLAGS) $(ALSA_CFLAGS) \ $(SHOUT_CFLAGS) \ $(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \ @@ -258,6 +261,7 @@ mpd_CPPFLAGS = \ $(FFMPEG_CFLAGS) \ $(GLIB_CFLAGS) mpd_LDADD = $(MPD_LIBS) \ + $(CURL_LIBS) \ $(AO_LIBS) $(ALSA_LIBS) \ $(SHOUT_LIBS) \ $(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \ diff --git a/src/inputStream.c b/src/inputStream.c index 1c5e027e9..b293cb1cd 100644 --- a/src/inputStream.c +++ b/src/inputStream.c @@ -17,20 +17,29 @@ */ #include "inputStream.h" +#include "config.h" #include "inputStream_file.h" -#include "inputStream_http.h" + +#ifdef HAVE_CURL +#include "input_curl.h" +#endif #include <stdlib.h> void initInputStream(void) { inputStream_initFile(); - inputStream_initHttp(); +#ifdef HAVE_CURL + input_curl_global_init(); +#endif } void input_stream_global_finish(void) { +#ifdef HAVE_CURL + input_curl_global_finish(); +#endif } int openInputStream(struct input_stream *inStream, char *url) @@ -46,8 +55,11 @@ int openInputStream(struct input_stream *inStream, char *url) if (inputStream_fileOpen(inStream, url) == 0) return 0; - if (inputStream_httpOpen(inStream, url) == 0) + +#ifdef HAVE_CURL + if (input_curl_open(inStream, url)) return 0; +#endif return -1; } diff --git a/src/inputStream_http.c b/src/inputStream_http.c deleted file mode 100644 index bbde11825..000000000 --- a/src/inputStream_http.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* 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 "inputStream_http_auth.h" - -#include "utils.h" -#include "log.h" -#include "conf.h" -#include "os_compat.h" -#include "ringbuf.h" -#include "condition.h" - -enum conn_state { /* only written by io thread, read by both */ - CONN_STATE_NEW, /* just (re)initialized */ - CONN_STATE_REDIRECT, /* redirect */ - CONN_STATE_CONNECTED, /* connected to the socket */ - CONN_STATE_REQUESTED, /* sent HTTP request */ - CONN_STATE_RESP_HEAD, /* reading HTTP response header */ - CONN_STATE_PREBUFFER, /* prebuffering data stream */ - CONN_STATE_BUFFER, /* buffering data stream */ - CONN_STATE_BUFFER_FULL, /* reading actual data stream */ - CONN_STATE_CLOSED /* it's over, time to die */ -}; - -/* used by all HTTP header matching */ -#define match(s) !strncasecmp(cur, s, (offset = sizeof(s) - 1)) - -#define assert_state(st) assert(data->state == st) -#define assert_state2(s1,s2) assert((data->state == s1) || (data->state == s2)) - -enum conn_action { /* only written by control thread, read by both */ - CONN_ACTION_NONE, - CONN_ACTION_CLOSE, - CONN_ACTION_DOSEEK -}; - -#define HTTP_BUFFER_SIZE_DEFAULT 131072 -#define HTTP_PREBUFFER_SIZE_DEFAULT (HTTP_BUFFER_SIZE_DEFAULT >> 2) -#define HTTP_REDIRECT_MAX 10 - -static char *proxy_host; -static char *proxy_port; -static char *proxy_user; -static char *proxy_password; -static size_t buffer_size = HTTP_BUFFER_SIZE_DEFAULT; -static size_t prebuffer_size = HTTP_PREBUFFER_SIZE_DEFAULT; - -struct http_data { - int fd; - enum conn_state state; - - /* { we may have a non-multithreaded HTTP discipline in the future */ - enum conn_action action; - int pipe_fds[2]; - - pthread_t io_thread; - struct ringbuf *rb; - - struct condition full_cond; - struct condition empty_cond; - struct condition action_cond; - /* } */ - - int nr_redirect; - size_t icy_metaint; - size_t icy_offset; - char *host; - char *path; - char *port; - char *proxy_auth; - char *http_auth; -}; - -static int awaken_buffer_task(struct http_data *data); - -static void init_http_data(struct http_data *data) -{ - data->fd = -1; - data->action = CONN_ACTION_NONE; - data->state = CONN_STATE_NEW; - init_async_pipe(data->pipe_fds); - - data->proxy_auth = proxy_host ? - proxy_auth_string(proxy_user, proxy_password) : - NULL; - data->http_auth = NULL; - data->host = NULL; - data->path = NULL; - data->port = NULL; - data->nr_redirect = 0; - data->icy_metaint = 0; - data->icy_offset = 0; - data->rb = ringbuf_create(buffer_size); - - cond_init(&data->action_cond); - cond_init(&data->full_cond); - cond_init(&data->empty_cond); -} - -static struct http_data *new_http_data(void) -{ - struct http_data *ret = xmalloc(sizeof(struct http_data)); - init_http_data(ret); - return ret; -} - -static void free_http_data(struct http_data * data) -{ - if (data->host) free(data->host); - if (data->path) free(data->path); - if (data->port) free(data->port); - if (data->proxy_auth) free(data->proxy_auth); - if (data->http_auth) free(data->http_auth); - - cond_destroy(&data->action_cond); - cond_destroy(&data->full_cond); - cond_destroy(&data->empty_cond); - - xclose(data->pipe_fds[0]); - xclose(data->pipe_fds[1]); - ringbuf_free(data->rb); - free(data); -} - -static int parse_url(struct http_data * data, char *url) -{ - char *colon; - char *slash; - char *at; - int len; - char *cur = url; - size_t offset; - - if (!match("http://")) - return -1; - - cur = url + offset; - colon = strchr(cur, ':'); - at = strchr(cur, '@'); - - if (data->http_auth) { - free(data->http_auth); - data->http_auth = NULL; - } - - if (at) { - char *user; - char *passwd; - - if (colon && colon < at) { - user = xmalloc(colon - cur + 1); - memcpy(user, cur, colon - cur); - user[colon - cur] = '\0'; - - passwd = xmalloc(at - colon); - memcpy(passwd, colon + 1, at - colon - 1); - passwd[at - colon - 1] = '\0'; - } else { - user = xmalloc(at - cur + 1); - memcpy(user, cur, at - cur); - user[at - cur] = '\0'; - - passwd = xstrdup(""); - } - - data->http_auth = http_auth_string(user, passwd); - - free(user); - free(passwd); - - cur = at + 1; - colon = strchr(cur, ':'); - } - - slash = strchr(cur, '/'); - - if (slash && colon && slash <= colon) - return -1; - - /* fetch the host portion */ - if (colon) - len = colon - cur + 1; - else if (slash) - len = slash - cur + 1; - else - len = strlen(cur) + 1; - - if (len <= 1) - return -1; - - if (data->host) - free(data->host); - data->host = xmalloc(len); - memcpy(data->host, cur, len - 1); - data->host[len - 1] = '\0'; - if (data->port) - free(data->port); - /* 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"); - } - - if (data->path) - free(data->path); - /* fetch the path */ - data->path = proxy_host ? xstrdup(url) : xstrdup(slash ? slash : "/"); - - return 0; -} - -/* triggers an action and waits for completion */ -static int trigger_action(struct http_data *data, - enum conn_action action, - int nonblocking) -{ - int ret = -1; - - assert(!pthread_equal(data->io_thread, pthread_self())); - cond_enter(&data->action_cond); - if (data->action != CONN_ACTION_NONE) - goto out; - data->action = action; - if (awaken_buffer_task(data)) { - /* DEBUG("wokeup from cond_wait to trigger action\n"); */ - } else if (xwrite(data->pipe_fds[1], "", 1) != 1) { - ERROR(__FILE__ ": pipe full, couldn't trigger action\n"); - data->action = CONN_ACTION_NONE; - goto out; - } - if (nonblocking) - cond_timedwait(&data->action_cond, 1); - else - cond_wait(&data->action_cond); - ret = 0; -out: - cond_leave(&data->action_cond); - return ret; -} - -static int take_action(struct http_data *data) -{ - assert(pthread_equal(data->io_thread, pthread_self())); - - cond_enter(&data->action_cond); - switch (data->action) { - case CONN_ACTION_NONE: - cond_leave(&data->action_cond); - return 0; - case CONN_ACTION_DOSEEK: - data->state = CONN_STATE_NEW; - break; - case CONN_ACTION_CLOSE: - data->state = CONN_STATE_CLOSED; - } - xclose(data->fd); - data->fd = -1; - data->action = CONN_ACTION_NONE; - cond_signal_sync(&data->action_cond); - cond_leave(&data->action_cond); - return 1; -} - -static int err_close(struct http_data *data) -{ - assert(pthread_equal(data->io_thread, pthread_self())); - xclose(data->fd); - data->state = CONN_STATE_CLOSED; - return -1; -} - -/* returns -1 on error, 0 on success (and sets dest) */ -static int my_getaddrinfo(struct addrinfo **dest, - const char *host, const char *port) -{ - struct addrinfo hints; - int error; - - 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 ((error = getaddrinfo(host, port, &hints, dest))) { - DEBUG(__FILE__ ": Error getting address info for %s:%s: %s\n", - host, port, gai_strerror(error)); - return -1; - } - return 0; -} - -/* returns the fd we connected to, or -1 on error */ -static int my_connect_addrs(struct addrinfo *ans) -{ - int fd; - struct addrinfo *ap; - - /* loop through possible addresses */ - for (ap = ans; ap != NULL; ap = ap->ai_next) { - fd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); - if (fd < 0) { - DEBUG(__FILE__ ": unable to get socket: %s\n", - strerror(errno)); - continue; - } - - set_nonblocking(fd); - if (connect(fd, ap->ai_addr, ap->ai_addrlen) >= 0 - || errno == EINPROGRESS) - return fd; /* success */ - DEBUG(__FILE__ ": unable to connect: %s\n", strerror(errno)); - xclose(fd); /* failed, get the next one */ - } - return -1; -} - -static int init_connection(struct http_data *data) -{ - struct addrinfo *ans = NULL; - - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state2(CONN_STATE_NEW, CONN_STATE_REDIRECT); - - if ((proxy_host ? my_getaddrinfo(&ans, proxy_host, proxy_port) : - my_getaddrinfo(&ans, data->host, data->port)) < 0) - return -1; - - assert(data->fd < 0); - data->fd = my_connect_addrs(ans); - freeaddrinfo(ans); - - if (data->fd < 0) - return -1; /* failed */ - data->state = CONN_STATE_CONNECTED; - return 0; -} - -#define my_nfds(d) ((d->fd > d->pipe_fds[0] ? d->fd : d->pipe_fds[0]) + 1) - -static int pipe_notified(struct http_data * data, fd_set *rfds) -{ - char buf; - int fd = data->pipe_fds[0]; - - assert(pthread_equal(data->io_thread, pthread_self())); - return FD_ISSET(fd, rfds) && (xread(fd, &buf, 1) == 1); -} - -enum await_result { - AWAIT_READY, - AWAIT_ACTION_PENDING, - AWAIT_ERROR -}; - -static enum await_result socket_error_or_ready(int fd) -{ - int ret; - int error = 0; - socklen_t error_len = sizeof(int); - - ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &error_len); - return (ret < 0 || error) ? AWAIT_ERROR : AWAIT_READY; -} - -static enum await_result await_sendable(struct http_data *data) -{ - fd_set rfds, wfds; - - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state(CONN_STATE_CONNECTED); - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_SET(data->pipe_fds[0], &rfds); - FD_SET(data->fd, &wfds); - - if (select(my_nfds(data), &rfds, &wfds, NULL, NULL) <= 0) - return AWAIT_ERROR; - if (pipe_notified(data, &rfds)) return AWAIT_ACTION_PENDING; - return socket_error_or_ready(data->fd); -} - -static enum await_result await_recvable(struct http_data *data) -{ - fd_set rfds; - - assert(pthread_equal(data->io_thread, pthread_self())); - - FD_ZERO(&rfds); - FD_SET(data->pipe_fds[0], &rfds); - FD_SET(data->fd, &rfds); - - if (select(my_nfds(data), &rfds, NULL, NULL, NULL) <= 0) - return AWAIT_ERROR; - if (pipe_notified(data, &rfds)) return AWAIT_ACTION_PENDING; - return socket_error_or_ready(data->fd); -} - -static void await_buffer_space(struct http_data *data) -{ - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state(CONN_STATE_BUFFER_FULL); - cond_wait(&data->full_cond); - if (ringbuf_write_space(data->rb) > 0) - data->state = CONN_STATE_BUFFER; - /* else spurious wakeup or action triggered ... */ -} - -static void feed_starved(struct http_data *data) -{ - assert(pthread_equal(data->io_thread, pthread_self())); - cond_signal_async(&data->empty_cond); -} - -static int starved_wait(struct http_data *data, const long sec) -{ - assert(!pthread_equal(data->io_thread, pthread_self())); - return cond_timedwait(&data->empty_cond, sec); -} - -static int awaken_buffer_task(struct http_data *data) -{ - assert(!pthread_equal(data->io_thread, pthread_self())); - - return ! cond_signal_async(&data->full_cond); -} - -static ssize_t buffer_data(InputStream *is) -{ - struct iovec vec[2]; - ssize_t r; - struct http_data *data = (struct http_data *)is->data; - - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state2(CONN_STATE_BUFFER, CONN_STATE_PREBUFFER); - - if (!ringbuf_get_write_vector(data->rb, vec)) { - data->state = CONN_STATE_BUFFER_FULL; - return 0; - } - r = readv(data->fd, vec, vec[1].iov_len ? 2 : 1); - if (r > 0) { - size_t buflen; - - ringbuf_write_advance(data->rb, r); - buflen = ringbuf_read_space(data->rb); - if (buflen == 0 || buflen < data->icy_metaint) - data->state = CONN_STATE_PREBUFFER; - else if (buflen >= prebuffer_size) - data->state = CONN_STATE_BUFFER; - if (data->state == CONN_STATE_BUFFER) - feed_starved(data); - return r; - } else if (r < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - is->error = errno; - } - err_close(data); - return r; -} - -/* - * This requires the socket to be writable beforehand (determined via - * select(2)). This does NOT retry or continue if we can't write the - * HTTP header in one shot. One reason for this is laziness, I don't - * want to have to store the header when recalling this function, but - * the other reason is practical, too: if we can't send a small HTTP - * request without blocking, the connection is pathetic anyways and we - * should just stop - * - * Returns -1 on error, 0 on success - */ -static int send_request(InputStream * is) -{ - struct http_data *data = (struct http_data *) is->data; - int length; - ssize_t nbytes; - char request[2048]; /* todo(?): write item-at-a-time and cork */ - - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state(CONN_STATE_CONNECTED); - length = snprintf(request, sizeof(request), - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Connection: close\r\n" - "User-Agent: " PACKAGE_NAME "/" PACKAGE_VERSION "\r\n" - "Range: bytes=%ld-\r\n" - "%s" /* authorization */ - "Icy-Metadata:1\r\n" - "\r\n", - data->path, - data->host, - is->offset, - data->proxy_auth ? data->proxy_auth : - (data->http_auth ? data->http_auth : "")); - if (length < 0 || length >= (int)sizeof(request)) - return err_close(data); - nbytes = write(data->fd, request, (size_t)length); - if (nbytes < 0 || nbytes != (ssize_t)length) - return err_close(data); - data->state = CONN_STATE_REQUESTED; - return 0; -} - -/* handles parsing of the first line of the HTTP response */ -static int parse_response_code(InputStream * is, const char *response) -{ - size_t offset; - const char *cur = response; - - is->seekable = 0; - if (match("HTTP/1.0 ")) { - return atoi(cur + offset); - } else if (match("HTTP/1.1 ")) { - is->seekable = 1; - return atoi(cur + offset); - } else if (match("ICY 200 OK")) { - return 200; - } else if (match("ICY 400 Server Full")) { - return 400; - } else if (match("ICY 404")) - return 404; - return 0; -} - -static int leading_space(int c) -{ - return (c == ' ' || c == '\t'); -} - -static int parse_header_dup(char **dst, char *cur) -{ - char *eol; - size_t len; - - if (!(eol = strstr(cur, "\r\n"))) - return -1; - *eol = '\0'; - while (leading_space(*cur)) - cur++; - len = strlen(cur) + 1; - *dst = xrealloc(*dst, len); - memcpy(*dst, cur, len); - *eol = '\r'; - return 0; -} - -static int parse_redirect(InputStream * is, char *response, const char *needle) -{ - char *url = NULL; - char *cur = strstr(response, "\r\n"); - size_t offset; - struct http_data *data = (struct http_data *) is->data; - int ret; - - while (cur && cur != needle) { - assert(cur < needle); - if (match("\r\nLocation:")) - goto found; - cur = strstr(cur + 2, "\r\n"); - } - return -1; -found: - if (parse_header_dup(&url, cur + offset) < 0) - return -1; - ret = parse_url(data, url); - free(url); - if (!ret && data->nr_redirect < HTTP_REDIRECT_MAX) { - data->nr_redirect++; - xclose(data->fd); - data->fd = -1; - data->state = CONN_STATE_REDIRECT; - is->ready = 1; - return 0; /* success */ - } - return -1; -} - -static int parse_headers(InputStream * is, char *response, const char *needle) -{ - struct http_data *data = (struct http_data *) is->data; - char *cur = strstr(response, "\r\n"); - size_t offset; - long tmp; - - data->icy_metaint = 0; - data->icy_offset = 0; - if (is->mime) { - free(is->mime); - is->mime = NULL; - } - if (is->metaName) { - free(is->metaName); - is->metaName = NULL; - } - is->size = 0; - - while (cur && cur != needle) { - assert(cur < needle); - if (match("\r\nContent-Length:")) { - if ((tmp = atol(cur + offset)) >= 0) - is->size = tmp; - } else if (match("\r\nicy-metaint:")) { - if ((tmp = atol(cur + offset)) >= 0) - data->icy_metaint = tmp; - } else if (match("\r\nicy-name:") || - match("\r\nice-name:") || - match("\r\nx-audiocast-name:")) { - if (parse_header_dup(&is->metaName, cur + offset) < 0) - return -1; - DEBUG(__FILE__": metaName: %s\n", is->metaName); - } else if (match("\r\nContent-Type:")) { - if (parse_header_dup(&is->mime, cur + offset) < 0) - return -1; - } - cur = strstr(cur + 2, "\r\n"); - } - return 0; -} - -/* Returns -1 on error, 0 on success */ -static int recv_response(InputStream * is) -{ - struct http_data *data = (struct http_data *) is->data; - char *needle; - char response[2048]; - const size_t response_max = sizeof(response) - 1; - ssize_t r; - ssize_t peeked; - - assert(pthread_equal(data->io_thread, pthread_self())); - assert_state2(CONN_STATE_RESP_HEAD, CONN_STATE_REQUESTED); - do { - r = recv(data->fd, response, response_max, MSG_PEEK); - } while (r < 0 && errno == EINTR); - if (r <= 0) - return err_close(data); /* EOF */ - response[r] = '\0'; - if (!(needle = strstr(response, "\r\n\r\n"))) { - if ((size_t)r == response_max) - return err_close(data); - /* response too small, try again */ - data->state = CONN_STATE_RESP_HEAD; - return -1; - } - - switch (parse_response_code(is, response)) { - case 200: /* OK */ - case 206: /* Partial Content */ - break; - case 301: /* Moved Permanently */ - case 302: /* Moved Temporarily */ - if (parse_redirect(is, response, needle) == 0) - return 0; /* success, reconnect */ - default: - return err_close(data); - } - - parse_headers(is, response, needle); - if (is->size <= 0) - is->seekable = 0; - needle += sizeof("\r\n\r\n") - 1; - peeked = needle - response; - assert(peeked <= r); - do { - r = recv(data->fd, response, peeked, 0); - } while (r < 0 && errno == EINTR); - assert(r == peeked && "r != peeked"); - - ringbuf_writer_reset(data->rb); - data->state = CONN_STATE_PREBUFFER; - is->ready = 1; - - return 0; -} - -static void * http_io_task(void *arg) -{ - InputStream *is = (InputStream *) arg; - struct http_data *data = (struct http_data *) is->data; - - cond_enter(&data->full_cond); - while (1) { - take_action(data); - switch (data->state) { - case CONN_STATE_NEW: - case CONN_STATE_REDIRECT: - init_connection(data); - break; - case CONN_STATE_CONNECTED: - switch (await_sendable(data)) { - case AWAIT_READY: send_request(is); break; - case AWAIT_ACTION_PENDING: break; - case AWAIT_ERROR: goto err; - } - break; - case CONN_STATE_REQUESTED: - case CONN_STATE_RESP_HEAD: - switch (await_recvable(data)) { - case AWAIT_READY: recv_response(is); break; - case AWAIT_ACTION_PENDING: break; - case AWAIT_ERROR: goto err; - } - break; - case CONN_STATE_PREBUFFER: - case CONN_STATE_BUFFER: - switch (await_recvable(data)) { - case AWAIT_READY: buffer_data(is); break; - case AWAIT_ACTION_PENDING: break; - case AWAIT_ERROR: goto err; - } - break; - case CONN_STATE_BUFFER_FULL: - await_buffer_space(data); - break; - case CONN_STATE_CLOSED: goto closed; - } - } -err: - err_close(data); -closed: - assert_state(CONN_STATE_CLOSED); - cond_leave(&data->full_cond); - return NULL; -} - -int inputStream_httpBuffer(mpd_unused InputStream *is) -{ - return 0; -} - -int inputStream_httpOpen(InputStream * is, char *url) -{ - struct http_data *data = new_http_data(); - pthread_attr_t attr; - - is->seekable = 0; - is->data = data; - if (parse_url(data, url) < 0) { - free_http_data(data); - return -1; - } - - is->seekFunc = inputStream_httpSeek; - is->closeFunc = inputStream_httpClose; - is->readFunc = inputStream_httpRead; - is->atEOFFunc = inputStream_httpAtEOF; - is->bufferFunc = inputStream_httpBuffer; - - pthread_attr_init(&attr); - if (pthread_create(&data->io_thread, &attr, http_io_task, is)) - FATAL("failed to spawn http_io_task: %s", strerror(errno)); - - cond_enter(&data->empty_cond); /* httpClose will leave this */ - return 0; -} - -int inputStream_httpSeek(InputStream * is, long offset, int whence) -{ - struct http_data *data = (struct http_data *)is->data; - long old_offset = is->offset; - long diff; - - if (!is->seekable) { - is->error = ESPIPE; - return -1; - } - assert(is->size > 0); - - switch (whence) { - case SEEK_SET: - is->offset = offset; - break; - case SEEK_CUR: - is->offset += offset; - break; - case SEEK_END: - is->offset = is->size + offset; - break; - default: - is->error = EINVAL; - return -1; - } - - diff = is->offset - old_offset; - if (!diff) - return 0; /* nothing to seek */ - if (diff > 0) { /* seek forward if we've already buffered it */ - long avail = (long)ringbuf_read_space(data->rb); - if (avail >= diff) { - ringbuf_read_advance(data->rb, diff); - return 0; - } - } - trigger_action(data, CONN_ACTION_DOSEEK, 0); - return 0; -} - -static void parse_icy_metadata(InputStream * is, char *metadata, size_t size) -{ - char *r = NULL; - char *cur; - size_t offset; - - assert(size); - metadata[size] = '\0'; - cur = strtok_r(metadata, ";", &r); - while (cur) { - if (match("StreamTitle=")) { - if (is->metaTitle) - free(is->metaTitle); - if (cur[offset] == '\'') - offset++; - if (r[-2] == '\'') - r[-2] = '\0'; - is->metaTitle = xstrdup(cur + offset); - DEBUG(__FILE__ ": metaTitle: %s\n", is->metaTitle); - return; - } - cur = strtok_r(NULL, ";", &r); - } -} - -static size_t read_with_metadata(InputStream *is, unsigned char *ptr, - ssize_t len) -{ - struct http_data *data = (struct http_data *) is->data; - size_t readed = 0; - size_t r; - size_t to_read; - assert(data->icy_metaint > 0); - - while (len > 0) { - if (ringbuf_read_space(data->rb) < data->icy_metaint) - break; - if (data->icy_offset >= data->icy_metaint) { - unsigned char metabuf[(UCHAR_MAX << 4) + 1]; - size_t metalen; - r = ringbuf_read(data->rb, metabuf, 1); - assert(r == 1 && "failed to read"); - awaken_buffer_task(data); - metalen = *(metabuf); - metalen <<= 4; - if (metalen) { - r = ringbuf_read(data->rb, metabuf, metalen); - assert(r == metalen && "short metadata read"); - parse_icy_metadata(is, (char*)metabuf, metalen); - } - data->icy_offset = 0; - } - to_read = len; - if (to_read > (data->icy_metaint - data->icy_offset)) - to_read = data->icy_metaint - data->icy_offset; - if (!(r = ringbuf_read(data->rb, ptr, to_read))) - break; - awaken_buffer_task(data); - len -= r; - ptr += r; - readed += r; - data->icy_offset += r; - } - return readed; -} - -size_t inputStream_httpRead(InputStream * is, void *_ptr, size_t size) -{ - struct http_data *data = (struct http_data *) is->data; - size_t len = size; - size_t r; - unsigned char *ptr = _ptr, *ptr0 = _ptr; - long tries = len / 128; /* try harder for bigger reads */ - -retry: - switch (data->state) { - case CONN_STATE_NEW: - case CONN_STATE_REDIRECT: - case CONN_STATE_CONNECTED: - case CONN_STATE_REQUESTED: - case CONN_STATE_RESP_HEAD: - case CONN_STATE_PREBUFFER: - if ((starved_wait(data, 1) == 0) || (tries-- > 0)) - goto retry; /* success */ - return 0; - case CONN_STATE_BUFFER: - case CONN_STATE_BUFFER_FULL: - break; - case CONN_STATE_CLOSED: - if (!ringbuf_read_space(data->rb)) - return 0; - } - - while (1) { - if (data->icy_metaint > 0) - r = read_with_metadata(is, ptr, len); - else /* easy, no metadata to worry about */ - r = ringbuf_read(data->rb, ptr, len); - assert(r <= len); - if (r) { - awaken_buffer_task(data); - is->offset += r; - ptr += r; - len -= r; - } - if (!len || (--tries < 0) || - (data->state == CONN_STATE_CLOSED && - !ringbuf_read_space(data->rb))) - break; - starved_wait(data, 1); - } - return (ptr - ptr0) / size; -} - -int inputStream_httpClose(InputStream * is) -{ - struct http_data *data = (struct http_data *) is->data; - - /* - * The cancellation routines in pthreads suck (and - * are probably unportable) and using signal handlers - * between threads is _definitely_ unportable. - */ - while (data->state != CONN_STATE_CLOSED) - trigger_action(data, CONN_ACTION_CLOSE, 1); - pthread_join(data->io_thread, NULL); - cond_leave(&data->empty_cond); - free_http_data(data); - return 0; -} - -int inputStream_httpAtEOF(InputStream * is) -{ - struct http_data *data = (struct http_data *) is->data; - if (data->state == CONN_STATE_CLOSED && !ringbuf_read_space(data->rb)) - return 1; - return 0; -} - -void inputStream_initHttp(void) -{ - ConfigParam *param = getConfigParam(CONF_HTTP_PROXY_HOST); - char *test; - if (param) { - proxy_host = param->value; - - param = getConfigParam(CONF_HTTP_PROXY_PORT); - - if (!param) { - FATAL("%s specified but not %s\n", CONF_HTTP_PROXY_HOST, - CONF_HTTP_PROXY_PORT); - } - proxy_port = param->value; - - param = getConfigParam(CONF_HTTP_PROXY_USER); - - if (param) { - proxy_user = 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); - } - - proxy_password = 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) { - long tmp = strtol(param->value, &test, 10); - if (*test != '\0' || tmp <= 0) { - FATAL("\"%s\" specified for %s at line %i is not a " - "positive integer\n", - param->value, CONF_HTTP_BUFFER_SIZE, param->line); - } - - buffer_size = tmp * 1024; - } - if (buffer_size < 4096) - FATAL(CONF_HTTP_BUFFER_SIZE" must be >= 4KB\n"); - - param = getConfigParam(CONF_HTTP_PREBUFFER_SIZE); - - if (param) { - long tmp = strtol(param->value, &test, 10); - if (*test != '\0' || tmp <= 0) { - FATAL("\"%s\" specified for %s at line %i is not a " - "positive integer\n", - param->value, CONF_HTTP_PREBUFFER_SIZE, - param->line); - } - - prebuffer_size = tmp * 1024; - } - - if (prebuffer_size > buffer_size) - prebuffer_size = buffer_size; - assert(buffer_size > 0 && "http buffer_size too small"); - assert(prebuffer_size > 0 && "http prebuffer_size too small"); -} - diff --git a/src/inputStream_http_auth.h b/src/inputStream_http_auth.h deleted file mode 100644 index 6aed1f36e..000000000 --- a/src/inputStream_http_auth.h +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 - */ - -/* This file is only included by inputStream_http.c */ -#ifndef INPUT_STREAM_HTTP_AUTH_H -#define INPUT_STREAM_HTTP_AUTH_H - -#include "utils.h" - -#include <string.h> - -/* 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; - - static const 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 *auth_string(const char *header, - const char *user, const 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 proxy_auth_string(x, y) auth_string(PROXY_AUTH_HEADER, x, y) -#define http_auth_string(x, y) auth_string(HTTP_AUTH_HEADER, x, y) - -#endif /* INPUT_STREAM_HTTP_AUTH_H */ diff --git a/src/input_curl.c b/src/input_curl.c new file mode 100644 index 000000000..3c32d7118 --- /dev/null +++ b/src/input_curl.c @@ -0,0 +1,493 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann <max@duempel.org> + * 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 "input_curl.h" +#include "inputStream.h" +#include "gcc.h" +#include "dlist.h" + +#include <assert.h> +#include <sys/select.h> +#include <string.h> +#include <errno.h> + +#include <curl/curl.h> +#include <glib.h> + +/** + * Buffers created by input_curl_writefunction(). + */ +struct buffer { + struct list_head siblings; + + /** size of the payload */ + size_t size; + + /** how much has been consumed yet? */ + size_t consumed; + + /** the payload */ + unsigned char data[sizeof(long)]; +}; + +struct input_curl { + /* some buffers which were passed to libcurl, which we have + too free */ + char *url, *range; + struct curl_slist *request_headers; + + /** the curl handles */ + CURL *easy; + CURLM *multi; + + /** list of buffers, where input_curl_writefunction() appends + to, and input_curl_read() reads from them */ + struct list_head buffers; + + /** has something been added to the buffers list? */ + bool buffered; + + /** did libcurl tell us the we're at the end of the response body? */ + bool eof; +}; + +/** libcurl should accept "ICY 200 OK" */ +static struct curl_slist *http_200_aliases; + +void input_curl_global_init(void) +{ + CURLcode code = curl_global_init(CURL_GLOBAL_ALL); + if (code != CURLE_OK) + g_warning("curl_global_init() failed: %s\n", + curl_easy_strerror(code)); + + http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK"); +} + +void input_curl_global_finish(void) +{ + curl_slist_free_all(http_200_aliases); + + curl_global_cleanup(); +} + +/** + * Frees the current "libcurl easy" handle, and everything associated + * with it. + */ +static void +input_curl_easy_free(struct input_curl *c) +{ + if (c->easy != NULL) { + curl_multi_remove_handle(c->multi, c->easy); + curl_easy_cleanup(c->easy); + c->easy = NULL; + } + + curl_slist_free_all(c->request_headers); + c->request_headers = NULL; + + g_free(c->range); + c->range = NULL; + + while (!list_empty(&c->buffers)) { + struct buffer *buffer = (struct buffer *)c->buffers.next; + list_del(&buffer->siblings); + + g_free(buffer); + } +} + +/** + * Frees this stream (but not the input_stream struct itself). + */ +static void +input_curl_free(struct input_stream *is) +{ + struct input_curl *c = is->data; + + input_curl_easy_free(c); + + if (c->multi != NULL) + curl_multi_cleanup(c->multi); + + g_free(c->url); + g_free(c); +} + +/** + * Wait for the libcurl socket. + * + * @return -1 on error, 0 if no data is available yet, 1 if data is + * available + */ +static int +input_curl_select(struct input_curl *c) +{ + fd_set rfds, wfds, efds; + int max_fd, ret; + CURLMcode mcode; + /* XXX hard coded timeout value.. */ + struct timeval timeout = { + .tv_sec = 1, + .tv_usec = 0, + }; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + mcode = curl_multi_fdset(c->multi, &rfds, &wfds, &efds, &max_fd); + if (mcode != CURLM_OK) { + g_warning("curl_multi_fdset() failed: %s\n", + curl_multi_strerror(mcode)); + return -1; + } + + assert(max_fd >= 0); + + ret = select(max_fd + 1, &rfds, &wfds, &efds, &timeout); + if (ret < 0) + g_warning("select() failed: %s\n", strerror(errno)); + + return ret; +} + +static size_t +read_from_buffer(struct buffer *buffer, void *dest, size_t length) +{ + assert(buffer->size > 0); + assert(buffer->consumed < buffer->size); + + if (length > buffer->size - buffer->consumed) + length = buffer->size - buffer->consumed; + + memcpy(dest, buffer->data + buffer->consumed, length); + + buffer->consumed += length; + if (buffer->consumed == buffer->size) { + list_del(&buffer->siblings); + g_free(buffer); + } + + return length; +} + +static size_t +input_curl_read(struct input_stream *is, void *ptr, size_t size) +{ + struct input_curl *c = is->data; + CURLMcode mcode = CURLM_CALL_MULTI_PERFORM; + size_t nbytes = 0; + char *dest = ptr; + + /* fill the buffer */ + + while (!c->eof && list_empty(&c->buffers)) { + int running_handles; + + if (mcode != CURLM_CALL_MULTI_PERFORM) { + /* if we're still here, there is no input yet + - wait for input */ + int ret = input_curl_select(c); + if (ret <= 0) + /* no data yet or error */ + return 0; + } + + mcode = curl_multi_perform(c->multi, &running_handles); + if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) { + g_warning("curl_multi_perform() failed: %s\n", + curl_multi_strerror(mcode)); + c->eof = true; + return 0; + } + + c->eof = running_handles == 0; + } + + /* send buffer contents */ + + while (size > 0 && !list_empty(&c->buffers)) { + struct buffer *buffer = (struct buffer *)c->buffers.next; + size_t copy = read_from_buffer(buffer, dest + nbytes, size); + + nbytes += copy; + size -= copy; + } + + is->offset += (off_t)nbytes; + return nbytes; +} + +static int +input_curl_close(struct input_stream *is) +{ + input_curl_free(is); + return 0; +} + +static int +input_curl_eof(mpd_unused struct input_stream *is) +{ + struct input_curl *c = is->data; + + return c->eof && list_empty(&c->buffers); +} + +static int +input_curl_buffer(mpd_unused struct input_stream *is) +{ + struct input_curl *c = is->data; + CURLMcode mcode; + int running_handles; + + c->buffered = false; + + do { + mcode = curl_multi_perform(c->multi, &running_handles); + } while (mcode == CURLM_CALL_MULTI_PERFORM && list_empty(&c->buffers)); + + if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) { + g_warning("curl_multi_perform() failed: %s\n", + curl_multi_strerror(mcode)); + c->eof = true; + return -1; + } + + c->eof = running_handles == 0; + + return c->buffered; +} + +/** called by curl when new data is available */ +static size_t +input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct input_stream *is = stream; + const char *header = ptr, *end, *colon; + char name[64]; + + size *= nmemb; + end = header + size; + + colon = memchr(header, ':', size); + if (colon == NULL || (size_t)(colon - header) >= sizeof(name)) + return size; + + memcpy(name, header, colon - header); + name[colon - header] = 0; + + if (strcasecmp(name, "accept-ranges") == 0) + is->seekable = true; + else if (strcasecmp(name, "content-length") == 0) { + char value[64]; + + header = colon + 1; + while (header < end && header[0] == ' ') + ++header; + + if ((size_t)(end - header) >= sizeof(value)) + return size; + + memcpy(value, header, end - header); + value[end - header] = 0; + + is->size = is->offset + g_ascii_strtoull(value, NULL, 10); + } + + return size; +} + +/** called by curl when new data is available */ +static size_t +input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct input_stream *is = stream; + struct input_curl *c = is->data; + struct buffer *buffer; + + size *= nmemb; + if (size == 0) + return 0; + + buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size); + buffer->size = size; + buffer->consumed = 0; + memcpy(buffer->data, ptr, size); + list_add_tail(&buffer->siblings, &c->buffers); + + c->buffered = true; + is->ready = true; + + return size; +} + +static bool +input_curl_easy_init(struct input_stream *is) +{ + struct input_curl *c = is->data; + CURLcode code; + CURLMcode mcode; + + c->eof = false; + + c->easy = curl_easy_init(); + if (c->easy == NULL) { + g_warning("curl_easy_init() failed\n"); + return false; + } + + mcode = curl_multi_add_handle(c->multi, c->easy); + if (mcode != CURLM_OK) + return false; + + curl_easy_setopt(c->easy, CURLOPT_HEADERFUNCTION, + input_curl_headerfunction); + curl_easy_setopt(c->easy, CURLOPT_WRITEHEADER, is); + curl_easy_setopt(c->easy, CURLOPT_WRITEFUNCTION, + input_curl_writefunction); + curl_easy_setopt(c->easy, CURLOPT_WRITEDATA, is); + curl_easy_setopt(c->easy, CURLOPT_HTTP200ALIASES, http_200_aliases); + + code = curl_easy_setopt(c->easy, CURLOPT_URL, c->url); + if (code != CURLE_OK) + return false; + + c->request_headers = NULL; + c->request_headers = curl_slist_append(c->request_headers, + "Icy-Metadata: 1"); + curl_easy_setopt(c->easy, CURLOPT_HTTPHEADER, c->request_headers); + + return true; +} + +static bool +input_curl_send_request(struct input_curl *c) +{ + CURLMcode mcode; + int running_handles; + + do { + mcode = curl_multi_perform(c->multi, &running_handles); + } while (mcode == CURLM_CALL_MULTI_PERFORM); + + c->eof = running_handles == 0; + + if (mcode != CURLM_OK) { + g_warning("curl_multi_perform() failed: %s\n", + curl_multi_strerror(mcode)); + return false; + } + + return true; +} + +static int +input_curl_seek(struct input_stream *is, mpd_unused long offset, + mpd_unused int whence) +{ + struct input_curl *c = is->data; + bool ret; + + if (!is->seekable) + return -1; + + /* calculate the absolute offset */ + + switch (whence) { + case SEEK_SET: + is->offset = (off_t)offset; + break; + + case SEEK_CUR: + is->offset += (off_t)offset; + break; + + case SEEK_END: + is->offset = (off_t)is->size + (off_t)offset; + break; + + default: + return -1; + } + + if (is->offset < 0) + return -1; + + /* close the old connection and open a new one */ + + input_curl_easy_free(c); + + ret = input_curl_easy_init(is); + if (!ret) + return -1; + + /* send the "Range" header */ + + if (is->offset > 0) { + c->range = g_strdup_printf("%ld-", is->offset); /* XXX 64 bit safety */ + curl_easy_setopt(c->easy, CURLOPT_RANGE, c->range); + } + + ret = input_curl_send_request(c); + if (!ret) + return -1; + + return 0; +} + +bool input_curl_open(struct input_stream *is, char *url) +{ + struct input_curl *c; + bool ret; + + c = g_new0(struct input_curl, 1); + c->url = g_strdup(url); + INIT_LIST_HEAD(&c->buffers); + + is->data = c; + + c->multi = curl_multi_init(); + if (c->multi == NULL) { + g_warning("curl_multi_init() failed\n"); + + input_curl_free(is); + return false; + } + + ret = input_curl_easy_init(is); + if (!ret) { + input_curl_free(is); + return false; + } + + ret = input_curl_send_request(c); + if (!ret) { + input_curl_free(is); + return false; + } + + is->seekFunc = input_curl_seek; + is->closeFunc = input_curl_close; + is->readFunc = input_curl_read; + is->atEOFFunc = input_curl_eof; + is->bufferFunc = input_curl_buffer; + + return true; +} diff --git a/src/inputStream_http.h b/src/input_curl.h index 01d70c1eb..9ced70f7f 100644 --- a/src/inputStream_http.h +++ b/src/input_curl.h @@ -1,5 +1,5 @@ /* the Music Player Daemon (MPD) - * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * Copyright (C) 2008 Max Kellermann <max@duempel.org> * This project's homepage is: http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -16,23 +16,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef INPUT_STREAM_HTTP_H -#define INPUT_STREAM_HTTP_H +#ifndef MPD_INPUT_CURL_H +#define MPD_INPUT_CURL_H -#include "inputStream.h" +#include <stdbool.h> -void inputStream_initHttp(void); +struct input_stream; -int inputStream_httpOpen(InputStream * inStream, char *filename); +void input_curl_global_init(void); -int inputStream_httpSeek(InputStream * inStream, long offset, int whence); +void input_curl_global_finish(void); -size_t inputStream_httpRead(InputStream * inStream, void *ptr, size_t size); - -int inputStream_httpClose(InputStream * inStream); - -int inputStream_httpAtEOF(InputStream * inStream); - -int inputStream_httpBuffer(InputStream * inStream); +bool input_curl_open(struct input_stream *is, char *url); #endif @@ -27,7 +27,9 @@ #include "os_compat.h" static const char *remoteUrlPrefixes[] = { +#ifdef HAVE_CURL "http://", +#endif NULL }; |