From 36d24fb7eae3a7a761cb1659d10464f65ecdddf9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 2 Mar 2009 20:40:31 +0100 Subject: input: moved plugins to ./src/input/ Create a sub directory for input plugins. --- Makefile.am | 16 +- src/input/archive_input_plugin.c | 77 ++++ src/input/archive_input_plugin.h | 24 + src/input/curl_input_plugin.c | 958 +++++++++++++++++++++++++++++++++++++++ src/input/curl_input_plugin.h | 28 ++ src/input/file_input_plugin.c | 129 ++++++ src/input/file_input_plugin.h | 24 + src/input/mms_input_plugin.c | 124 +++++ src/input/mms_input_plugin.h | 24 + src/input_archive.c | 77 ---- src/input_archive.h | 24 - src/input_curl.c | 958 --------------------------------------- src/input_curl.h | 28 -- src/input_file.c | 129 ------ src/input_file.h | 24 - src/input_mms.c | 124 ----- src/input_mms.h | 24 - src/input_stream.c | 8 +- 18 files changed, 1400 insertions(+), 1400 deletions(-) create mode 100644 src/input/archive_input_plugin.c create mode 100644 src/input/archive_input_plugin.h create mode 100644 src/input/curl_input_plugin.c create mode 100644 src/input/curl_input_plugin.h create mode 100644 src/input/file_input_plugin.c create mode 100644 src/input/file_input_plugin.h create mode 100644 src/input/mms_input_plugin.c create mode 100644 src/input/mms_input_plugin.h delete mode 100644 src/input_archive.c delete mode 100644 src/input_archive.h delete mode 100644 src/input_curl.c delete mode 100644 src/input_curl.h delete mode 100644 src/input_file.c delete mode 100644 src/input_file.h delete mode 100644 src/input_mms.c delete mode 100644 src/input_mms.h diff --git a/Makefile.am b/Makefile.am index 270eb48ed..c0941967a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,9 +69,9 @@ mpd_headers = \ src/decoder/_ogg_common.h \ src/input_plugin.h \ src/input_stream.h \ - src/input_file.h \ - src/input_curl.h \ - src/input_mms.h \ + src/input/file_input_plugin.h \ + src/input/curl_input_plugin.h \ + src/input/mms_input_plugin.h \ src/icy_metadata.h \ src/client.h \ src/listen.h \ @@ -138,7 +138,7 @@ mpd_headers = \ src/archive_api.h \ src/archive_internal.h \ src/archive_list.h \ - src/input_archive.h + src/input/archive_input_plugin.h src_mpd_SOURCES = \ $(mpd_headers) \ @@ -257,7 +257,7 @@ if ENABLE_ARCHIVE ARCHIVE_SRC += \ src/archive_api.c \ src/archive_list.c \ - src/input_archive.c + src/input/archive_input_plugin.c endif @@ -408,14 +408,14 @@ INPUT_LIBS = \ INPUT_SRC = \ src/input_stream.c \ - src/input_file.c + src/input/file_input_plugin.c if HAVE_CURL -INPUT_SRC += src/input_curl.c src/icy_metadata.c +INPUT_SRC += src/input/curl_input_plugin.c src/icy_metadata.c endif if ENABLE_MMS -INPUT_SRC += src/input_mms.c +INPUT_SRC += src/input/mms_input_plugin.c endif diff --git a/src/input/archive_input_plugin.c b/src/input/archive_input_plugin.c new file mode 100644 index 000000000..31f873aa2 --- /dev/null +++ b/src/input/archive_input_plugin.c @@ -0,0 +1,77 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Viliam Mateicka + * 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/archive_input_plugin.h" +#include "archive_api.h" +#include "archive_list.h" +#include "input_plugin.h" + +#include + +/** + * select correct archive plugin to handle the input stream + * may allow stacking of archive plugins. for example for handling + * tar.gz a gzip handler opens file (through inputfile stream) + * then it opens a tar handler and sets gzip inputstream as + * parent_stream so tar plugin fetches file data from gzip + * plugin and gzip fetches file from disk + */ +static bool +input_archive_open(struct input_stream *is, const char *pathname) +{ + const struct archive_plugin *arplug; + struct archive_file *file; + char *archive, *filename, *suffix, *pname; + bool opened; + + if (pathname[0] != '/') + return false; + + pname = g_strdup(pathname); + // archive_lookup will modify pname when true is returned + if (!archive_lookup(pname, &archive, &filename, &suffix)) { + g_debug("not an archive, lookup %s failed\n", pname); + g_free(pname); + return false; + } + + //check which archive plugin to use (by ext) + arplug = archive_plugin_from_suffix(suffix); + if (!arplug) { + g_warning("can't handle archive %s\n",archive); + g_free(pname); + return false; + } + + file = arplug->open(archive); + + //setup fileops + opened = arplug->open_stream(file, is, filename); + + if (!opened) { + g_warning("open inarchive file %s failed\n\n",filename); + } else { + is->ready = true; + } + g_free(pname); + return opened; +} + +const struct input_plugin input_plugin_archive = { + .open = input_archive_open, +}; diff --git a/src/input/archive_input_plugin.h b/src/input/archive_input_plugin.h new file mode 100644 index 000000000..8fc93b433 --- /dev/null +++ b/src/input/archive_input_plugin.h @@ -0,0 +1,24 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Viliam Mateicka + * 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 + */ + +#ifndef MPD_INPUT_ARCHIVE_H +#define MPD_INPUT_ARCHIVE_H + +extern const struct input_plugin input_plugin_archive; + +#endif diff --git a/src/input/curl_input_plugin.c b/src/input/curl_input_plugin.c new file mode 100644 index 000000000..b6d444a8a --- /dev/null +++ b/src/input/curl_input_plugin.c @@ -0,0 +1,958 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * 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_input_plugin.h" +#include "input_plugin.h" +#include "conf.h" +#include "config.h" +#include "tag.h" +#include "icy_metadata.h" + +#include +#include +#include +#include + +#include +#include + +#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, +}; diff --git a/src/input/curl_input_plugin.h b/src/input/curl_input_plugin.h new file mode 100644 index 000000000..5ae06f065 --- /dev/null +++ b/src/input/curl_input_plugin.h @@ -0,0 +1,28 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * 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 + */ + +#ifndef MPD_INPUT_CURL_H +#define MPD_INPUT_CURL_H + +extern const struct input_plugin input_plugin_curl; + +void input_curl_global_init(void); + +void input_curl_global_finish(void); + +#endif diff --git a/src/input/file_input_plugin.c b/src/input/file_input_plugin.c new file mode 100644 index 000000000..c857bbd65 --- /dev/null +++ b/src/input/file_input_plugin.c @@ -0,0 +1,129 @@ +/* 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 "input/file_input_plugin.h" +#include "input_plugin.h" + +#include +#include +#include +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "input_file" + +static bool +input_file_open(struct input_stream *is, const char *filename) +{ + int fd, ret; + struct stat st; + + if (filename[0] != '/') + return false; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + is->error = errno; + return false; + } + + is->seekable = true; + + ret = fstat(fd, &st); + if (ret < 0) { + is->error = errno; + close(fd); + return false; + } + + if (!S_ISREG(st.st_mode)) { + g_debug("Not a regular file: %s\n", filename); + is->error = EINVAL; + close(fd); + return false; + } + + is->size = st.st_size; + +#ifdef POSIX_FADV_SEQUENTIAL + posix_fadvise(fd, (off_t)0, is->size, POSIX_FADV_SEQUENTIAL); +#endif + + is->plugin = &input_plugin_file; + is->data = GINT_TO_POINTER(fd); + is->ready = true; + + return true; +} + +static bool +input_file_seek(struct input_stream *is, off_t offset, int whence) +{ + int fd = GPOINTER_TO_INT(is->data); + + offset = lseek(fd, offset, whence); + if (offset < 0) { + is->error = errno; + return false; + } + + is->offset = offset; + return true; +} + +static size_t +input_file_read(struct input_stream *is, void *ptr, size_t size) +{ + int fd = GPOINTER_TO_INT(is->data); + ssize_t nbytes; + + nbytes = read(fd, ptr, size); + if (nbytes < 0) { + is->error = errno; + g_debug("input_file_read: error reading: %s\n", + strerror(is->error)); + return 0; + } + + is->offset += nbytes; + return (size_t)nbytes; +} + +static void +input_file_close(struct input_stream *is) +{ + int fd = GPOINTER_TO_INT(is->data); + + close(fd); +} + +static bool +input_file_eof(struct input_stream *is) +{ + return is->offset >= is->size; +} + +const struct input_plugin input_plugin_file = { + .open = input_file_open, + .close = input_file_close, + .read = input_file_read, + .eof = input_file_eof, + .seek = input_file_seek, +}; diff --git a/src/input/file_input_plugin.h b/src/input/file_input_plugin.h new file mode 100644 index 000000000..c4233d071 --- /dev/null +++ b/src/input/file_input_plugin.h @@ -0,0 +1,24 @@ +/* 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 + */ + +#ifndef MPD_INPUT_FILE_H +#define MPD_INPUT_FILE_H + +extern const struct input_plugin input_plugin_file; + +#endif diff --git a/src/input/mms_input_plugin.c b/src/input/mms_input_plugin.c new file mode 100644 index 000000000..d449d058a --- /dev/null +++ b/src/input/mms_input_plugin.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * 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/mms_input_plugin.h" +#include "input_plugin.h" + +#include +#include + +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "jack" + +struct input_mms { + mmsx_t *mms; + + bool eof; +}; + +static bool +input_mms_open(struct input_stream *is, const char *url) +{ + struct input_mms *m; + + if (!g_str_has_prefix(url, "mms://") && + !g_str_has_prefix(url, "mmsh://") && + !g_str_has_prefix(url, "mmst://") && + !g_str_has_prefix(url, "mmsu://")) + return false; + + m = g_new(struct input_mms, 1); + m->mms = mmsx_connect(NULL, NULL, url, 128 * 1024); + if (m->mms == NULL) { + g_warning("mmsx_connect() failed"); + return false; + } + + /* XX is this correct? at least this selects the ffmpeg + decoder, which seems to work fine*/ + is->mime = g_strdup("audio/x-ms-wma"); + + is->plugin = &input_plugin_mms; + is->data = m; + is->ready = true; + return true; +} + +static size_t +input_mms_read(struct input_stream *is, void *ptr, size_t size) +{ + struct input_mms *m = is->data; + int ret; + + ret = mmsx_read(NULL, m->mms, ptr, size); + if (ret <= 0) { + if (ret < 0) { + is->error = errno; + g_warning("mmsx_read() failed: %s", g_strerror(errno)); + } + + m->eof = true; + return false; + } + + is->offset += ret; + + return (size_t)ret; +} + +static void +input_mms_close(struct input_stream *is) +{ + struct input_mms *m = is->data; + + mmsx_close(m->mms); + g_free(m); +} + +static bool +input_mms_eof(struct input_stream *is) +{ + struct input_mms *m = is->data; + + return m->eof; +} + +static int +input_mms_buffer(G_GNUC_UNUSED struct input_stream *is) +{ + return 0; +} + +static bool +input_mms_seek(G_GNUC_UNUSED struct input_stream *is, + G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence) +{ + return false; +} + +const struct input_plugin input_plugin_mms = { + .open = input_mms_open, + .close = input_mms_close, + .buffer = input_mms_buffer, + .read = input_mms_read, + .eof = input_mms_eof, + .seek = input_mms_seek, +}; diff --git a/src/input/mms_input_plugin.h b/src/input/mms_input_plugin.h new file mode 100644 index 000000000..db98ee397 --- /dev/null +++ b/src/input/mms_input_plugin.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * 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 + */ + +#ifndef INPUT_MMS_H +#define INPUT_MMS_H + +extern const struct input_plugin input_plugin_mms; + +#endif diff --git a/src/input_archive.c b/src/input_archive.c deleted file mode 100644 index 22ebde8d4..000000000 --- a/src/input_archive.c +++ /dev/null @@ -1,77 +0,0 @@ -/* the Music Player Daemon (MPD) - * Copyright (C) 2008 Viliam Mateicka - * 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_archive.h" -#include "archive_api.h" -#include "archive_list.h" -#include "input_plugin.h" - -#include - -/** - * select correct archive plugin to handle the input stream - * may allow stacking of archive plugins. for example for handling - * tar.gz a gzip handler opens file (through inputfile stream) - * then it opens a tar handler and sets gzip inputstream as - * parent_stream so tar plugin fetches file data from gzip - * plugin and gzip fetches file from disk - */ -static bool -input_archive_open(struct input_stream *is, const char *pathname) -{ - const struct archive_plugin *arplug; - struct archive_file *file; - char *archive, *filename, *suffix, *pname; - bool opened; - - if (pathname[0] != '/') - return false; - - pname = g_strdup(pathname); - // archive_lookup will modify pname when true is returned - if (!archive_lookup(pname, &archive, &filename, &suffix)) { - g_debug("not an archive, lookup %s failed\n", pname); - g_free(pname); - return false; - } - - //check which archive plugin to use (by ext) - arplug = archive_plugin_from_suffix(suffix); - if (!arplug) { - g_warning("can't handle archive %s\n",archive); - g_free(pname); - return false; - } - - file = arplug->open(archive); - - //setup fileops - opened = arplug->open_stream(file, is, filename); - - if (!opened) { - g_warning("open inarchive file %s failed\n\n",filename); - } else { - is->ready = true; - } - g_free(pname); - return opened; -} - -const struct input_plugin input_plugin_archive = { - .open = input_archive_open, -}; diff --git a/src/input_archive.h b/src/input_archive.h deleted file mode 100644 index 8fc93b433..000000000 --- a/src/input_archive.h +++ /dev/null @@ -1,24 +0,0 @@ -/* the Music Player Daemon (MPD) - * Copyright (C) 2008 Viliam Mateicka - * 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 - */ - -#ifndef MPD_INPUT_ARCHIVE_H -#define MPD_INPUT_ARCHIVE_H - -extern const struct input_plugin input_plugin_archive; - -#endif 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 - * 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 -#include -#include -#include - -#include -#include - -#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, -}; diff --git a/src/input_curl.h b/src/input_curl.h deleted file mode 100644 index 5ae06f065..000000000 --- a/src/input_curl.h +++ /dev/null @@ -1,28 +0,0 @@ -/* the Music Player Daemon (MPD) - * Copyright (C) 2008 Max Kellermann - * 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 - */ - -#ifndef MPD_INPUT_CURL_H -#define MPD_INPUT_CURL_H - -extern const struct input_plugin input_plugin_curl; - -void input_curl_global_init(void); - -void input_curl_global_finish(void); - -#endif diff --git a/src/input_file.c b/src/input_file.c deleted file mode 100644 index 5334d453e..000000000 --- a/src/input_file.c +++ /dev/null @@ -1,129 +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 "input_file.h" -#include "input_plugin.h" - -#include -#include -#include -#include -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "input_file" - -static bool -input_file_open(struct input_stream *is, const char *filename) -{ - int fd, ret; - struct stat st; - - if (filename[0] != '/') - return false; - - fd = open(filename, O_RDONLY); - if (fd < 0) { - is->error = errno; - return false; - } - - is->seekable = true; - - ret = fstat(fd, &st); - if (ret < 0) { - is->error = errno; - close(fd); - return false; - } - - if (!S_ISREG(st.st_mode)) { - g_debug("Not a regular file: %s\n", filename); - is->error = EINVAL; - close(fd); - return false; - } - - is->size = st.st_size; - -#ifdef POSIX_FADV_SEQUENTIAL - posix_fadvise(fd, (off_t)0, is->size, POSIX_FADV_SEQUENTIAL); -#endif - - is->plugin = &input_plugin_file; - is->data = GINT_TO_POINTER(fd); - is->ready = true; - - return true; -} - -static bool -input_file_seek(struct input_stream *is, off_t offset, int whence) -{ - int fd = GPOINTER_TO_INT(is->data); - - offset = lseek(fd, offset, whence); - if (offset < 0) { - is->error = errno; - return false; - } - - is->offset = offset; - return true; -} - -static size_t -input_file_read(struct input_stream *is, void *ptr, size_t size) -{ - int fd = GPOINTER_TO_INT(is->data); - ssize_t nbytes; - - nbytes = read(fd, ptr, size); - if (nbytes < 0) { - is->error = errno; - g_debug("input_file_read: error reading: %s\n", - strerror(is->error)); - return 0; - } - - is->offset += nbytes; - return (size_t)nbytes; -} - -static void -input_file_close(struct input_stream *is) -{ - int fd = GPOINTER_TO_INT(is->data); - - close(fd); -} - -static bool -input_file_eof(struct input_stream *is) -{ - return is->offset >= is->size; -} - -const struct input_plugin input_plugin_file = { - .open = input_file_open, - .close = input_file_close, - .read = input_file_read, - .eof = input_file_eof, - .seek = input_file_seek, -}; diff --git a/src/input_file.h b/src/input_file.h deleted file mode 100644 index c4233d071..000000000 --- a/src/input_file.h +++ /dev/null @@ -1,24 +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 - */ - -#ifndef MPD_INPUT_FILE_H -#define MPD_INPUT_FILE_H - -extern const struct input_plugin input_plugin_file; - -#endif diff --git a/src/input_mms.c b/src/input_mms.c deleted file mode 100644 index 34dce5b6e..000000000 --- a/src/input_mms.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2003-2009 The Music Player Daemon Project - * 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_mms.h" -#include "input_plugin.h" - -#include -#include - -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "jack" - -struct input_mms { - mmsx_t *mms; - - bool eof; -}; - -static bool -input_mms_open(struct input_stream *is, const char *url) -{ - struct input_mms *m; - - if (!g_str_has_prefix(url, "mms://") && - !g_str_has_prefix(url, "mmsh://") && - !g_str_has_prefix(url, "mmst://") && - !g_str_has_prefix(url, "mmsu://")) - return false; - - m = g_new(struct input_mms, 1); - m->mms = mmsx_connect(NULL, NULL, url, 128 * 1024); - if (m->mms == NULL) { - g_warning("mmsx_connect() failed"); - return false; - } - - /* XX is this correct? at least this selects the ffmpeg - decoder, which seems to work fine*/ - is->mime = g_strdup("audio/x-ms-wma"); - - is->plugin = &input_plugin_mms; - is->data = m; - is->ready = true; - return true; -} - -static size_t -input_mms_read(struct input_stream *is, void *ptr, size_t size) -{ - struct input_mms *m = is->data; - int ret; - - ret = mmsx_read(NULL, m->mms, ptr, size); - if (ret <= 0) { - if (ret < 0) { - is->error = errno; - g_warning("mmsx_read() failed: %s", g_strerror(errno)); - } - - m->eof = true; - return false; - } - - is->offset += ret; - - return (size_t)ret; -} - -static void -input_mms_close(struct input_stream *is) -{ - struct input_mms *m = is->data; - - mmsx_close(m->mms); - g_free(m); -} - -static bool -input_mms_eof(struct input_stream *is) -{ - struct input_mms *m = is->data; - - return m->eof; -} - -static int -input_mms_buffer(G_GNUC_UNUSED struct input_stream *is) -{ - return 0; -} - -static bool -input_mms_seek(G_GNUC_UNUSED struct input_stream *is, - G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence) -{ - return false; -} - -const struct input_plugin input_plugin_mms = { - .open = input_mms_open, - .close = input_mms_close, - .buffer = input_mms_buffer, - .read = input_mms_read, - .eof = input_mms_eof, - .seek = input_mms_seek, -}; diff --git a/src/input_mms.h b/src/input_mms.h deleted file mode 100644 index db98ee397..000000000 --- a/src/input_mms.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2003-2009 The Music Player Daemon Project - * 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 - */ - -#ifndef INPUT_MMS_H -#define INPUT_MMS_H - -extern const struct input_plugin input_plugin_mms; - -#endif diff --git a/src/input_stream.c b/src/input_stream.c index 794dd1980..73c7acc2a 100644 --- a/src/input_stream.c +++ b/src/input_stream.c @@ -19,18 +19,18 @@ #include "input_plugin.h" #include "config.h" -#include "input_file.h" +#include "input/file_input_plugin.h" #ifdef ENABLE_ARCHIVE -#include "input_archive.h" +#include "input/archive_input_plugin.h" #endif #ifdef HAVE_CURL -#include "input_curl.h" +#include "input/curl_input_plugin.h" #endif #ifdef ENABLE_MMS -#include "input_mms.h" +#include "input/mms_input_plugin.h" #endif #include -- cgit v1.2.3