aboutsummaryrefslogtreecommitdiffstats
path: root/src/input_curl.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-03-02 20:40:31 +0100
committerMax Kellermann <max@duempel.org>2009-03-02 20:40:31 +0100
commit36d24fb7eae3a7a761cb1659d10464f65ecdddf9 (patch)
tree51c8166fb9723f891a58122166077dcb7fd04db8 /src/input_curl.c
parent2e51365ea4b636560d234b0b937a6c8dfb528c10 (diff)
downloadmpd-36d24fb7eae3a7a761cb1659d10464f65ecdddf9.tar.gz
mpd-36d24fb7eae3a7a761cb1659d10464f65ecdddf9.tar.xz
mpd-36d24fb7eae3a7a761cb1659d10464f65ecdddf9.zip
input: moved plugins to ./src/input/
Create a sub directory for input plugins.
Diffstat (limited to 'src/input_curl.c')
-rw-r--r--src/input_curl.c958
1 files changed, 0 insertions, 958 deletions
diff --git a/src/input_curl.c b/src/input_curl.c
deleted file mode 100644
index 337e438af..000000000
--- a/src/input_curl.c
+++ /dev/null
@@ -1,958 +0,0 @@
-/* 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 "input_plugin.h"
-#include "conf.h"
-#include "config.h"
-#include "tag.h"
-#include "icy_metadata.h"
-
-#include <assert.h>
-#include <sys/select.h>
-#include <string.h>
-#include <errno.h>
-
-#include <curl/curl.h>
-#include <glib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "input_curl"
-
-/** rewinding is possible after up to 64 kB */
-static const off_t max_rewind_size = 64 * 1024;
-
-/**
- * Buffers created by input_curl_writefunction().
- */
-struct buffer {
- /** 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 */
- GQueue *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;
-
- /** limited list of old buffers, for rewinding */
- GQueue *rewind;
-
- /** error message provided by libcurl */
- char error[CURL_ERROR_SIZE];
-
- /** parser for icy-metadata */
- struct icy_metadata icy_metadata;
-
- /** the stream name from the icy-name response header */
- char *meta_name;
-
- /** the tag object ready to be requested via
- input_stream_tag() */
- struct tag *tag;
-};
-
-/** 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();
-}
-
-static void
-buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct buffer *buffer = data;
-
- assert(buffer->consumed <= buffer->size);
-
- g_free(data);
-}
-
-/* g_queue_clear() was introduced in GLib 2.14 */
-#if GLIB_CHECK_VERSION(2,14,0)
-#define g_queue_clear(q) do { g_queue_free(q); q = g_queue_new(); } while (0)
-#endif
-
-/**
- * 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;
-
- g_queue_foreach(c->buffers, buffer_free_callback, NULL);
- g_queue_clear(c->buffers);
-
- if (c->rewind != NULL) {
- g_queue_foreach(c->rewind, buffer_free_callback, NULL);
- g_queue_clear(c->rewind);
- }
-}
-
-/**
- * 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;
-
- if (c->tag != NULL)
- tag_free(c->tag);
- g_free(c->meta_name);
-
- input_curl_easy_free(c);
-
- if (c->multi != NULL)
- curl_multi_cleanup(c->multi);
-
- g_queue_free(c->buffers);
- if (c->rewind != NULL)
- g_queue_free(c->rewind);
-
- g_free(c->url);
- g_free(c);
-}
-
-static struct tag *
-input_curl_tag(struct input_stream *is)
-{
- struct input_curl *c = is->data;
- struct tag *tag = c->tag;
-
- c->tag = NULL;
- return tag;
-}
-
-static bool
-input_curl_multi_info_read(struct input_stream *is)
-{
- struct input_curl *c = is->data;
- CURLMsg *msg;
- int msgs_in_queue;
-
- while ((msg = curl_multi_info_read(c->multi,
- &msgs_in_queue)) != NULL) {
- if (msg->msg == CURLMSG_DONE) {
- c->eof = true;
- is->ready = true;
-
- if (msg->data.result != CURLE_OK) {
- g_warning("curl failed: %s\n", c->error);
- is->error = -1;
- return false;
- }
- }
- }
-
- return true;
-}
-
-/**
- * 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,
- };
-
- assert(!c->eof);
-
- 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;
-}
-
-/**
- * Mark a part of the buffer object as consumed.
- */
-static struct buffer *
-consume_buffer(struct buffer *buffer, size_t length, GQueue *rewind_buffers)
-{
- assert(buffer != NULL);
- assert(buffer->consumed < buffer->size);
-
- buffer->consumed += length;
- if (buffer->consumed < buffer->size)
- return buffer;
-
- assert(buffer->consumed == buffer->size);
-
- if (rewind_buffers != NULL)
- /* append this buffer to the rewind buffer list */
- g_queue_push_tail(rewind_buffers, buffer);
- else
- g_free(buffer);
-
- return NULL;
-}
-
-static size_t
-read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
- void *dest0, size_t length,
- GQueue *rewind_buffers)
-{
- struct buffer *buffer = g_queue_pop_head(buffers);
- uint8_t *dest = dest0;
- size_t nbytes = 0;
-
- assert(buffer->size > 0);
- assert(buffer->consumed < buffer->size);
-
- if (length > buffer->size - buffer->consumed)
- length = buffer->size - buffer->consumed;
-
- while (true) {
- size_t chunk;
-
- chunk = icy_data(icy_metadata, length);
- if (chunk > 0) {
- memcpy(dest, buffer->data + buffer->consumed,
- chunk);
- buffer = consume_buffer(buffer, chunk, rewind_buffers);
-
- nbytes += chunk;
- dest += chunk;
- length -= chunk;
-
- if (length == 0)
- break;
-
- assert(buffer != NULL);
- }
-
- chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed,
- length);
- if (chunk > 0) {
- buffer = consume_buffer(buffer, chunk, rewind_buffers);
-
- length -= chunk;
-
- if (length == 0)
- break;
-
- assert(buffer != NULL);
- }
- }
-
- if (buffer != NULL)
- g_queue_push_head(buffers, buffer);
-
- return nbytes;
-}
-
-static void
-copy_icy_tag(struct input_curl *c)
-{
- struct tag *tag = icy_tag(&c->icy_metadata);
-
- if (tag == NULL)
- return;
-
- if (c->tag != NULL)
- tag_free(c->tag);
-
- if (c->meta_name != NULL && !tag_has_type(tag, TAG_ITEM_NAME))
- tag_add_item(tag, TAG_ITEM_NAME, c->meta_name);
-
- c->tag = tag;
-}
-
-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;
- GQueue *rewind_buffers;
- size_t nbytes = 0;
- char *dest = ptr;
-
-#ifndef NDEBUG
- if (c->rewind != NULL &&
- (!g_queue_is_empty(c->rewind) || is->offset == 0)) {
- off_t offset = 0;
- struct buffer *buffer;
-
- for (GList *list = g_queue_peek_head_link(c->rewind);
- list != NULL; list = g_list_next(list)) {
- buffer = list->data;
- offset += buffer->consumed;
- assert(offset <= is->offset);
- }
-
- buffer = g_queue_peek_head(c->buffers);
- if (buffer != NULL)
- offset += buffer->consumed;
-
- assert(offset == is->offset);
- }
-#endif
-
- /* fill the buffer */
-
- while (!c->eof && g_queue_is_empty(c->buffers)) {
- int running_handles;
- bool bret;
-
- 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;
- is->ready = true;
- return 0;
- }
-
- bret = input_curl_multi_info_read(is);
- if (!bret)
- return 0;
- }
-
- /* send buffer contents */
-
- if (c->rewind != NULL &&
- (!g_queue_is_empty(c->rewind) || is->offset == 0))
- /* at the beginning or already writing the rewind
- buffer list */
- rewind_buffers = c->rewind;
- else
- /* we don't need the rewind buffers anymore */
- rewind_buffers = NULL;
-
- while (size > 0 && !g_queue_is_empty(c->buffers)) {
- size_t copy = read_from_buffer(&c->icy_metadata, c->buffers,
- dest + nbytes, size,
- rewind_buffers);
-
- nbytes += copy;
- size -= copy;
- }
-
- if (icy_defined(&c->icy_metadata))
- copy_icy_tag(c);
-
- is->offset += (off_t)nbytes;
-
-#ifndef NDEBUG
- if (rewind_buffers != NULL) {
- off_t offset = 0;
- struct buffer *buffer;
-
- for (GList *list = g_queue_peek_head_link(c->rewind);
- list != NULL; list = g_list_next(list)) {
- buffer = list->data;
- offset += buffer->consumed;
- assert(offset <= is->offset);
- }
-
- buffer = g_queue_peek_head(c->buffers);
- if (buffer != NULL)
- offset += buffer->consumed;
-
- assert(offset == is->offset);
- }
-#endif
-
- if (rewind_buffers != NULL && is->offset > max_rewind_size) {
- /* drop the rewind buffer, it has grown too large */
-
- g_queue_foreach(c->rewind, buffer_free_callback, NULL);
- g_queue_clear(c->rewind);
- }
-
- return nbytes;
-}
-
-static void
-input_curl_close(struct input_stream *is)
-{
- input_curl_free(is);
-}
-
-static bool
-input_curl_eof(G_GNUC_UNUSED struct input_stream *is)
-{
- struct input_curl *c = is->data;
-
- return c->eof && g_queue_is_empty(c->buffers);
-}
-
-static int
-input_curl_buffer(struct input_stream *is)
-{
- struct input_curl *c = is->data;
- CURLMcode mcode;
- int running_handles;
- bool ret;
-
- c->buffered = false;
-
- if (!is->ready && !c->eof)
- /* not ready yet means the caller is waiting in a busy
- loop; relax that by calling select() on the
- socket */
- input_curl_select(c);
-
- do {
- mcode = curl_multi_perform(c->multi, &running_handles);
- } while (mcode == CURLM_CALL_MULTI_PERFORM &&
- g_queue_is_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;
- is->ready = true;
- return -1;
- }
-
- ret = input_curl_multi_info_read(is);
- if (!ret)
- return -1;
-
- 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;
- struct input_curl *c = is->data;
- const char *header = ptr, *end, *value;
- char name[64];
-
- size *= nmemb;
- end = header + size;
-
- value = memchr(header, ':', size);
- if (value == NULL || (size_t)(value - header) >= sizeof(name))
- return size;
-
- memcpy(name, header, value - header);
- name[value - header] = 0;
-
- /* skip the colon */
-
- ++value;
-
- /* strip the value */
-
- while (value < end && g_ascii_isspace(*value))
- ++value;
-
- while (end > value && g_ascii_isspace(end[-1]))
- --end;
-
- if (strcasecmp(name, "accept-ranges") == 0) {
- /* a stream with icy-metadata is not seekable */
- if (!icy_defined(&c->icy_metadata))
- is->seekable = true;
- } else if (strcasecmp(name, "content-length") == 0) {
- char buffer[64];
-
- if ((size_t)(end - header) >= sizeof(buffer))
- return size;
-
- memcpy(buffer, value, end - value);
- buffer[end - value] = 0;
-
- is->size = is->offset + g_ascii_strtoull(buffer, NULL, 10);
- } else if (strcasecmp(name, "content-type") == 0) {
- g_free(is->mime);
- is->mime = g_strndup(value, end - value);
- } else if (strcasecmp(name, "icy-name") == 0 ||
- strcasecmp(name, "ice-name") == 0 ||
- strcasecmp(name, "x-audiocast-name") == 0) {
- g_free(c->meta_name);
- c->meta_name = g_strndup(value, end - value);
-
- if (c->tag != NULL)
- tag_free(c->tag);
-
- c->tag = tag_new();
- tag_add_item(c->tag, TAG_ITEM_NAME, c->meta_name);
- } else if (strcasecmp(name, "icy-metaint") == 0) {
- char buffer[64];
- size_t icy_metaint;
-
- if ((size_t)(end - header) >= sizeof(buffer) ||
- icy_defined(&c->icy_metadata))
- return size;
-
- memcpy(buffer, value, end - value);
- buffer[end - value] = 0;
-
- icy_metaint = g_ascii_strtoull(buffer, NULL, 10);
- g_debug("icy-metaint=%zu", icy_metaint);
-
- if (icy_metaint > 0) {
- icy_start(&c->icy_metadata, icy_metaint);
-
- /* a stream with icy-metadata is not
- seekable */
- is->seekable = false;
-
- if (c->rewind != NULL) {
- /* rewinding with icy-metadata is too
- hairy for me .. */
- assert(g_queue_is_empty(c->rewind));
-
- g_queue_free(c->rewind);
- c->rewind = NULL;
- }
- }
- }
-
- 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);
- g_queue_push_tail(c->buffers, buffer);
-
- 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;
- const char *proxy_host;
- const char *proxy_port;
- const char *proxy_user;
- const char *proxy_pass;
-
- 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_USERAGENT,
- "Music Player Daemon " VERSION);
- 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);
- curl_easy_setopt(c->easy, CURLOPT_FOLLOWLOCATION, 1);
- curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5);
- curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
- curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
-
- proxy_host = config_get_string(CONF_HTTP_PROXY_HOST, NULL);
- proxy_port = config_get_string(CONF_HTTP_PROXY_PORT, NULL);
-
- if (proxy_host != NULL) {
- char *proxy_host_str;
-
- if (proxy_port == NULL) {
- proxy_host_str = g_strdup(proxy_host);
- } else {
- proxy_host_str =
- g_strconcat(proxy_host, ":", proxy_port, NULL);
- }
- curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy_host_str);
- g_free(proxy_host_str);
- }
-
- proxy_user = config_get_string(CONF_HTTP_PROXY_USER, NULL);
- proxy_pass = config_get_string(CONF_HTTP_PROXY_PASSWORD, NULL);
-
- if ((proxy_user != NULL) && (proxy_pass != NULL)) {
- char *proxy_auth_str =
- g_strconcat(proxy_user, ":", proxy_pass, NULL);
- curl_easy_setopt(c->easy, CURLOPT_PROXYUSERPWD, proxy_auth_str);
- g_free(proxy_auth_str);
- }
-
- 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);
-
- if (mcode != CURLM_OK) {
- g_warning("curl_multi_perform() failed: %s\n",
- curl_multi_strerror(mcode));
- return false;
- }
-
- return true;
-}
-
-static bool
-input_curl_can_rewind(struct input_stream *is)
-{
- struct input_curl *c = is->data;
- struct buffer *buffer;
-
- if (c->rewind == NULL)
- return false;
-
- if (!g_queue_is_empty(c->rewind))
- /* the rewind buffer hasn't been wiped yet */
- return true;
-
- if (g_queue_is_empty(c->buffers))
- /* there are no buffers at all - cheap rewind not
- possible */
- return false;
-
- /* rewind is possible if this is the very first buffer of the
- resource */
- buffer = (struct buffer*)g_queue_peek_head(c->buffers);
- return (off_t)buffer->consumed == is->offset;
-}
-
-static void
-input_curl_rewind(struct input_stream *is)
-{
- struct input_curl *c = is->data;
-#ifndef NDEBUG
- off_t offset = 0;
-#endif
-
- assert(c->rewind != NULL);
-
- /* rewind the current buffer */
-
- if (!g_queue_is_empty(c->buffers)) {
- struct buffer *buffer =
- (struct buffer*)g_queue_peek_head(c->buffers);
-#ifndef NDEBUG
- offset += buffer->consumed;
-#endif
- buffer->consumed = 0;
- }
-
- /* reset and move all rewind buffers back to the regular buffer list */
-
- while (!g_queue_is_empty(c->rewind)) {
- struct buffer *buffer =
- (struct buffer*)g_queue_pop_tail(c->rewind);
-#ifndef NDEBUG
- offset += buffer->consumed;
-#endif
- buffer->consumed = 0;
- g_queue_push_head(c->buffers, buffer);
- }
-
- assert(offset == is->offset);
-
- is->offset = 0;
-
- /* rewind the icy_metadata object */
-
- icy_reset(&c->icy_metadata);
-}
-
-static bool
-input_curl_seek(struct input_stream *is, off_t offset, int whence)
-{
- struct input_curl *c = is->data;
- bool ret;
-
- assert(is->ready);
-
- if (whence == SEEK_SET && offset == 0) {
- if (is->offset == 0)
- /* no-op */
- return true;
-
- if (input_curl_can_rewind(is)) {
- /* we have enough rewind buffers left */
- input_curl_rewind(is);
- return true;
- }
- }
-
- if (!is->seekable)
- return false;
-
- /* calculate the absolute offset */
-
- switch (whence) {
- case SEEK_SET:
- break;
-
- case SEEK_CUR:
- offset += is->offset;
- break;
-
- case SEEK_END:
- if (is->size < 0)
- /* stream size is not known */
- return false;
-
- offset += is->size;
- break;
-
- default:
- return false;
- }
-
- if (offset < 0)
- return false;
-
- /* check if we can fast-forward the buffer */
-
- while (offset > is->offset && !g_queue_is_empty(c->buffers)) {
- GQueue *rewind_buffers;
- struct buffer *buffer;
- size_t length;
-
- if (c->rewind != NULL &&
- (!g_queue_is_empty(c->rewind) || is->offset == 0))
- /* at the beginning or already writing the rewind
- buffer list */
- rewind_buffers = c->rewind;
- else
- /* we don't need the rewind buffers anymore */
- rewind_buffers = NULL;
-
- buffer = (struct buffer *)g_queue_pop_head(c->buffers);
-
- length = buffer->size - buffer->consumed;
- if (offset - is->offset < (off_t)length)
- length = offset - is->offset;
-
- buffer = consume_buffer(buffer, length, rewind_buffers);
- if (buffer != NULL)
- g_queue_push_head(c->buffers, buffer);
-
- is->offset += length;
- }
-
- if (offset == is->offset)
- return true;
-
- /* close the old connection and open a new one */
-
- input_curl_easy_free(c);
-
- is->offset = offset;
- if (is->offset == is->size) {
- /* seek to EOF: simulate empty result; avoid
- triggering a "416 Requested Range Not Satisfiable"
- response */
- c->eof = true;
- return true;
- }
-
- ret = input_curl_easy_init(is);
- if (!ret)
- return false;
-
- /* send the "Range" header */
-
- if (is->offset > 0) {
- c->range = g_strdup_printf("%lld-", (long long)is->offset);
- curl_easy_setopt(c->easy, CURLOPT_RANGE, c->range);
- }
-
- ret = input_curl_send_request(c);
- if (!ret)
- return false;
-
- return input_curl_multi_info_read(is);
-}
-
-static bool
-input_curl_open(struct input_stream *is, const char *url)
-{
- struct input_curl *c;
- bool ret;
-
- if (strncmp(url, "http://", 7) != 0)
- return false;
-
- c = g_new0(struct input_curl, 1);
- c->url = g_strdup(url);
- c->buffers = g_queue_new();
- c->rewind = g_queue_new();
-
- is->plugin = &input_plugin_curl;
- 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;
- }
-
- icy_clear(&c->icy_metadata);
- c->tag = NULL;
-
- 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;
- }
-
- ret = input_curl_multi_info_read(is);
- if (!ret) {
- input_curl_free(is);
- return false;
- }
-
- return true;
-}
-
-const struct input_plugin input_plugin_curl = {
- .open = input_curl_open,
- .close = input_curl_close,
- .tag = input_curl_tag,
- .buffer = input_curl_buffer,
- .read = input_curl_read,
- .eof = input_curl_eof,
- .seek = input_curl_seek,
-};