diff --git a/INSTALL b/INSTALL
index e0bc9e551..403f8d349 100644
--- a/INSTALL
+++ b/INSTALL
@@ -70,6 +70,10 @@ For Zeroconf support.
 libsamplerate - http://www.mega-nerd.com/SRC/
 For advanced samplerate conversions.
 
+libcurl - http://curl.haxx.se/
+For playing HTTP streams.
+
+
 Download
 --------
 
diff --git a/configure.ac b/configure.ac
index 37057a802..d79240ff9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -102,6 +102,17 @@ AC_ARG_ENABLE(un,
 	[enable_un=yes])
 
 
+dnl
+dnl input options
+dnl
+
+AC_ARG_ENABLE(curl,
+	AS_HELP_STRING([--disable-curl],
+		[enable support obtaining song data via HTTP (default: enable)]),
+	[enable_curl=$enableval],
+	[enable_curl=yes])
+
+
 dnl
 dnl audio output plugins
 dnl
@@ -316,6 +327,13 @@ case $host in
 		enable_osx=yes ;;
 esac
 
+if test x$enable_curl = xyes; then
+	PKG_CHECK_MODULES(CURL, [libcurl],
+		AC_DEFINE(HAVE_CURL, 1, [Define when libcurl is used for HTTP streaming]),
+		enable_curl=no)
+fi
+AM_CONDITIONAL(HAVE_CURL, test x$enable_curl = xyes)
+
 if test x$enable_shout_ogg = xyes || x$enable_shout_mp3 = xyes; then
 	enable_shout=yes
 	PKG_CHECK_MODULES([SHOUT], [shout],
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
similarity index 59%
rename from src/inputStream_http.h
rename to 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
diff --git a/src/ls.c b/src/ls.c
index b70952214..a456db040 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -27,7 +27,9 @@
 #include "os_compat.h"
 
 static const char *remoteUrlPrefixes[] = {
+#ifdef HAVE_CURL
 	"http://",
+#endif
 	NULL
 };