From 473a29ef8a4518008c3a969ea62984a8807d129e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Wed, 8 Nov 2017 00:32:03 +0100 Subject: [PATCH] [httpd] More refactoring, reduce code duplication in the httpd_xxx modules --- src/cache.c | 17 +- src/cache.h | 2 +- src/httpd.c | 41 +++ src/httpd.h | 46 ++- src/httpd_daap.c | 613 ++++++++++++++++++-------------------- src/httpd_daap.h | 2 +- src/httpd_dacp.c | 701 +++++++++++++++++++++----------------------- src/httpd_jsonapi.c | 122 ++------ src/httpd_oauth.c | 99 ++----- src/httpd_rsp.c | 205 +++++-------- 10 files changed, 844 insertions(+), 1004 deletions(-) diff --git a/src/cache.c b/src/cache.c index 8d99ab3c..7e49716e 100644 --- a/src/cache.c +++ b/src/cache.c @@ -47,13 +47,14 @@ #include "commands.h" -#define CACHE_VERSION 2 +#define CACHE_VERSION 3 struct cache_arg { char *query; // daap query char *ua; // user agent + int is_remote; int msec; char *path; // artwork path @@ -139,6 +140,7 @@ cache_create_tables(void) " id INTEGER PRIMARY KEY NOT NULL," \ " query VARCHAR(4096) UNIQUE NOT NULL," \ " user_agent VARCHAR(1024)," \ + " is_remote INTEGER DEFAULT 0," \ " msec INTEGER DEFAULT 0," \ " timestamp INTEGER DEFAULT 0" \ ");" @@ -605,7 +607,7 @@ cache_daap_reply_add(const char *query, struct evbuffer *evbuf) static enum command_state cache_daap_query_add(void *arg, int *retval) { -#define Q_TMPL "INSERT OR REPLACE INTO queries (user_agent, query, msec, timestamp) VALUES ('%q', '%q', %d, %" PRIi64 ");" +#define Q_TMPL "INSERT OR REPLACE INTO queries (user_agent, is_remote, query, msec, timestamp) VALUES ('%q', %d, '%q', %d, %" PRIi64 ");" #define Q_CLEANUP "DELETE FROM queries WHERE id NOT IN (SELECT id FROM queries ORDER BY timestamp DESC LIMIT 20);" struct cache_arg *cmdarg; struct timeval delay = { 60, 0 }; @@ -631,7 +633,7 @@ cache_daap_query_add(void *arg, int *retval) remove_tag(cmdarg->query, "session-id"); remove_tag(cmdarg->query, "revision-number"); - query = sqlite3_mprintf(Q_TMPL, cmdarg->ua, cmdarg->query, cmdarg->msec, (int64_t)time(NULL)); + query = sqlite3_mprintf(Q_TMPL, cmdarg->ua, cmdarg->is_remote, cmdarg->query, cmdarg->msec, (int64_t)time(NULL)); if (!query) { DPRINTF(E_LOG, L_CACHE, "Out of memory making query string.\n"); @@ -809,7 +811,7 @@ cache_daap_update_cb(int fd, short what, void *arg) return; } - ret = sqlite3_prepare_v2(g_db_hdl, "SELECT id, user_agent, query FROM queries;", -1, &stmt, 0); + ret = sqlite3_prepare_v2(g_db_hdl, "SELECT id, user_agent, is_remote, query FROM queries;", -1, &stmt, 0); if (ret != SQLITE_OK) { DPRINTF(E_LOG, L_CACHE, "Error preparing for cache update: %s\n", sqlite3_errmsg(g_db_hdl)); @@ -818,9 +820,9 @@ cache_daap_update_cb(int fd, short what, void *arg) while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { - query = strdup((char *)sqlite3_column_text(stmt, 2)); + query = strdup((char *)sqlite3_column_text(stmt, 3)); - evbuf = daap_reply_build(query, (char *)sqlite3_column_text(stmt, 1)); + evbuf = daap_reply_build(query, (char *)sqlite3_column_text(stmt, 1), sqlite3_column_int(stmt, 2)); if (!evbuf) { DPRINTF(E_LOG, L_CACHE, "Error building DAAP reply for query: %s\n", query); @@ -1329,7 +1331,7 @@ cache_daap_get(struct evbuffer *evbuf, const char *query) } void -cache_daap_add(const char *query, const char *ua, int msec) +cache_daap_add(const char *query, const char *ua, int is_remote, int msec) { struct cache_arg *cmdarg; @@ -1345,6 +1347,7 @@ cache_daap_add(const char *query, const char *ua, int msec) cmdarg->query = strdup(query); cmdarg->ua = strdup(ua); + cmdarg->is_remote = is_remote; cmdarg->msec = msec; commands_exec_async(cmdbase, cache_daap_query_add, cmdarg); diff --git a/src/cache.h b/src/cache.h index c732f9f8..3e7aa8ae 100644 --- a/src/cache.h +++ b/src/cache.h @@ -16,7 +16,7 @@ int cache_daap_get(struct evbuffer *evbuf, const char *query); void -cache_daap_add(const char *query, const char *ua, int msec); +cache_daap_add(const char *query, const char *ua, int is_remote, int msec); int cache_daap_threshold(void); diff --git a/src/httpd.c b/src/httpd.c index 1766f6ce..2c1205f5 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -253,6 +253,7 @@ httpd_fixup_uri(struct evhttp_request *req) } */ + /* --------------------------- REQUEST HELPERS ------------------------------ */ static void @@ -877,6 +878,46 @@ httpd_uri_parse(const char *uri) return NULL; } +struct httpd_request * +httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map) +{ + struct httpd_request *hreq; + struct evkeyvalq *headers; + int i; + int ret; + + CHECK_NULL(L_HTTPD, hreq = calloc(1, sizeof(struct httpd_request))); + + // Note req is allowed to be NULL + hreq->req = req; + hreq->uri_parsed = uri_parsed; + hreq->query = &(uri_parsed->ev_query); + + if (req && !user_agent) + { + headers = evhttp_request_get_input_headers(req); + hreq->user_agent = evhttp_find_header(headers, "User-Agent"); + } + else + hreq->user_agent = user_agent; + + // Find a handler for the path + for (i = 0; uri_map[i].handler; i++) + { + ret = regexec(&uri_map[i].preg, uri_parsed->path, 0, NULL, 0); + if (ret == 0) + { + hreq->handler = uri_map[i].handler; + return hreq; // Success + } + } + + // Handler not found, that's an error + free(hreq); + + return NULL; +} + /* Thread: httpd */ void httpd_stream_file(struct evhttp_request *req, int id) diff --git a/src/httpd.h b/src/httpd.h index c666efeb..34bbe6c7 100644 --- a/src/httpd.h +++ b/src/httpd.h @@ -2,10 +2,11 @@ #ifndef __HTTPD_H__ #define __HTTPD_H__ +#include +#include #include #include #include -#include enum httpd_send_flags { @@ -36,6 +37,40 @@ struct httpd_uri_parsed char *path_parts[7]; }; +/* + * A collection of pointers to request data that the reply handlers may need. + * Also has the function pointer to the reply handler and a pointer to a reply + * evbuffer. + */ +struct httpd_request { + // User-agent (if available) + const char *user_agent; + // The parsed request URI given to us by httpd_uri_parse + struct httpd_uri_parsed *uri_parsed; + // Shortcut to &uri_parsed->ev_query + struct evkeyvalq *query; + // http request struct (if available) + struct evhttp_request *req; + // A pointer to extra data that the module handling the request might need + void *extra_data; + + // Reply evbuffer + struct evbuffer *reply; + + // A pointer to the handler that will process the request + int (*handler)(struct httpd_request *hreq); +}; + +/* + * Maps a regex of the request path to a handler of the request + */ +struct httpd_uri_map +{ + char *regexp; + int (*handler)(struct httpd_request *hreq); + regex_t preg; +}; + /* * Helper to free the parsed uri struct */ @@ -48,6 +83,15 @@ httpd_uri_free(struct httpd_uri_parsed *parsed); struct httpd_uri_parsed * httpd_uri_parse(const char *uri); +/* + * Parse a request into the httpd_request struct. It can later be freed with + * free(), unless the module has allocated something to *extra_data. Note that + * the pointers in the returned struct are only valid as long as the inputs are + * still valid. If req is not null, then we will find the user-agent from the + * request headers, except if provided as an argument to this function. + */ +struct httpd_request * +httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent, struct httpd_uri_map *uri_map); void httpd_stream_file(struct evhttp_request *req, int id); diff --git a/src/httpd_daap.c b/src/httpd_daap.c index 5ae42de2..edceded3 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -93,37 +92,12 @@ enum daap_reply_result struct daap_session { int id; - char *user_agent; time_t mtime; bool is_remote; struct daap_session *next; }; -// Will be filled out by daap_request_parse() -struct daap_request { - // User-agent - const char *user_agent; - // Was the request made by a remote, e.g. Apple Remote - bool is_remote; - // The parsed request URI given to us by httpd.c - struct httpd_uri_parsed *uri_parsed; - // Shortcut to &uri_parsed->ev_query - struct evkeyvalq *query; - // http request struct - null when the request is from daap_reply_build(), i.e. the cache - struct evhttp_request *req; - // Pointer to the session matching the request - struct daap_session *session; - // A pointer to the handler that will process the request - enum daap_reply_result (*handler)(struct evbuffer *reply, struct daap_request *dreq); -}; - -struct uri_map { - regex_t preg; - char *regexp; - enum daap_reply_result (*handler)(struct evbuffer *reply, struct daap_request *dreq); -}; - struct daap_update_request { struct evhttp_request *req; @@ -155,16 +129,14 @@ static int current_rev; static struct daap_update_request *update_requests; static struct timeval daap_update_refresh_tv = { DAAP_UPDATE_REFRESH, 0 }; -/* Forward declaration of handlers */ -static struct uri_map daap_handlers[]; /* -------------------------- SESSION HANDLING ------------------------------ */ static void daap_session_free(struct daap_session *s) { - if (s->user_agent) - free(s->user_agent); + if (!s) + return; free(s); } @@ -186,7 +158,7 @@ daap_session_remove(struct daap_session *s) if (!ptr) { - DPRINTF(E_LOG, L_DAAP, "Error: Request to remove non-existent session. BUG!\n"); + DPRINTF(E_LOG, L_DAAP, "Error: Request to remove non-existent or ad-hoc session. BUG!\n"); return; } @@ -241,20 +213,13 @@ daap_session_cleanup(void) } static struct daap_session * -daap_session_add(const char *user_agent, bool is_remote, int request_session_id) +daap_session_add(bool is_remote, int request_session_id) { struct daap_session *s; daap_session_cleanup(); - s = calloc(1, sizeof(struct daap_session)); - if (!s) - { - DPRINTF(E_LOG, L_DAAP, "Out of memory for DAAP session\n"); - return NULL; - } - - memset(s, 0, sizeof(struct daap_session)); + CHECK_NULL(L_DAAP, s = calloc(1, sizeof(struct daap_session))); if (request_session_id) { @@ -274,9 +239,6 @@ daap_session_add(const char *user_agent, bool is_remote, int request_session_id) s->mtime = time(NULL); - if (user_agent) - s->user_agent = strdup(user_agent); - s->is_remote = is_remote; if (daap_sessions) @@ -506,11 +468,12 @@ daap_sort_finalize(struct sort_ctx *ctx) * filter if the SELECT is not from the files table. */ static void -user_agent_filter(struct query_params *qp, struct daap_request *dreq) +user_agent_filter(struct query_params *qp, struct httpd_request *hreq) { + struct daap_session *s = hreq->extra_data; char *filter; - if (dreq->is_remote) + if (s->is_remote) { // This makes sure 1) the SELECT is from files, 2) that the Remote query // contained extended_media_kind:1, which characterise the queries we want @@ -539,7 +502,7 @@ user_agent_filter(struct query_params *qp, struct daap_request *dreq) } static void -query_params_set(struct query_params *qp, int *sort_headers, struct daap_request *dreq, enum query_type type) +query_params_set(struct query_params *qp, int *sort_headers, struct httpd_request *hreq, enum query_type type) { const char *param; char *ptr; @@ -552,7 +515,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct daap_request memset(qp, 0, sizeof(struct query_params)); - param = evhttp_find_header(dreq->query, "index"); + param = evhttp_find_header(hreq->query, "index"); if (param) { if (param[0] == '-') /* -n, last n entries */ @@ -598,7 +561,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct daap_request qp->idx_type = I_SUB; qp->sort = S_NONE; - param = evhttp_find_header(dreq->query, "sort"); + param = evhttp_find_header(hreq->query, "sort"); if (param) { if (strcmp(param, "name") == 0) @@ -619,7 +582,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct daap_request if (sort_headers) { *sort_headers = 0; - param = evhttp_find_header(dreq->query, "include-sort-headers"); + param = evhttp_find_header(hreq->query, "include-sort-headers"); if (param && (strcmp(param, "1") == 0)) { *sort_headers = 1; @@ -627,9 +590,9 @@ query_params_set(struct query_params *qp, int *sort_headers, struct daap_request } } - param = evhttp_find_header(dreq->query, "query"); + param = evhttp_find_header(hreq->query, "query"); if (!param) - param = evhttp_find_header(dreq->query, "filter"); + param = evhttp_find_header(hreq->query, "filter"); if (param) { @@ -646,7 +609,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct daap_request qp->type = type; - user_agent_filter(qp, dreq); + user_agent_filter(qp, hreq); } static int @@ -711,31 +674,31 @@ parse_meta(const struct dmap_field ***out_meta, const char *param) } static void -daap_reply_send(struct evhttp_request *req, struct evbuffer *reply, enum daap_reply_result result) +daap_reply_send(struct httpd_request *hreq, enum daap_reply_result result) { switch (result) { case DAAP_REPLY_LOGOUT: - httpd_send_reply(req, 204, "Logout Successful", reply, 0); + httpd_send_reply(hreq->req, 204, "Logout Successful", hreq->reply, 0); break; case DAAP_REPLY_NO_CONTENT: - httpd_send_reply(req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); break; case DAAP_REPLY_OK: - httpd_send_reply(req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); break; case DAAP_REPLY_OK_NO_GZIP: case DAAP_REPLY_ERROR: - httpd_send_reply(req, HTTP_OK, "OK", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); break; case DAAP_REPLY_FORBIDDEN: - httpd_send_error(req, 403, "Forbidden"); + httpd_send_error(hreq->req, 403, "Forbidden"); break; case DAAP_REPLY_BAD_REQUEST: - httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); + httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request"); break; case DAAP_REPLY_SERVUNAVAIL: - httpd_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(hreq->req, HTTP_SERVUNAVAIL, "Internal Server Error"); break; case DAAP_REPLY_NO_CONNECTION: case DAAP_REPLY_NONE: @@ -744,73 +707,10 @@ daap_reply_send(struct evhttp_request *req, struct evbuffer *reply, enum daap_re } } -static struct daap_request * -daap_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed, const char *user_agent) -{ - struct daap_request *dreq; - struct evkeyvalq *headers; - const char *param; - int32_t id; - int ret; - int i; - - CHECK_NULL(L_DAAP, dreq = calloc(1, sizeof(struct daap_request))); - - dreq->req = req; - dreq->uri_parsed = uri_parsed; - dreq->query = &(uri_parsed->ev_query); - - if (user_agent) - dreq->user_agent = user_agent; - else if (req && (headers = evhttp_request_get_input_headers(req))) - dreq->user_agent = evhttp_find_header(headers, "User-Agent"); - - // Find a handler for the path - for (i = 0; daap_handlers[i].handler; i++) - { - ret = regexec(&daap_handlers[i].preg, uri_parsed->path, 0, NULL, 0); - if (ret == 0) - { - dreq->handler = daap_handlers[i].handler; - break; - } - } - - if (!dreq->handler) - { - DPRINTF(E_LOG, L_DAAP, "Unrecognized path '%s' in DAAP request: '%s'\n", uri_parsed->path, uri_parsed->uri); - goto error; - } - - // Check if we have a session - param = evhttp_find_header(dreq->query, "session-id"); - if (param) - { - ret = safe_atoi32(param, &id); - if (ret < 0) - { - DPRINTF(E_LOG, L_DAAP, "Invalid session id in DAAP request: '%s'\n", uri_parsed->uri); - goto error; - } - - dreq->session = daap_session_get(id); - } - - // Are we making a reply for a remote? - param = evhttp_find_header(dreq->query, "pairing-guid"); - dreq->is_remote = (param || (dreq->session && dreq->session->is_remote)); - - return dreq; - - error: - free(dreq); - - return NULL; -} - static int -daap_request_authorize(struct daap_request *dreq) +daap_request_authorize(struct httpd_request *hreq) { + struct daap_session *session = hreq->extra_data; const char *param; char *passwd; int ret; @@ -818,16 +718,16 @@ daap_request_authorize(struct daap_request *dreq) if (cfg_getbool(cfg_getsec(cfg, "general"), "promiscuous_mode")) return 0; - param = evhttp_find_header(dreq->query, "session-id"); + param = evhttp_find_header(hreq->query, "session-id"); if (param) { - if (!dreq->session) + if (!session) { - DPRINTF(E_LOG, L_DAAP, "DAAP session not found: '%s'\n", dreq->uri_parsed->uri); + DPRINTF(E_LOG, L_DAAP, "DAAP session not found: '%s'\n", hreq->uri_parsed->uri); return -1; } - dreq->session->mtime = time(NULL); + session->mtime = time(NULL); return 0; } @@ -836,17 +736,17 @@ daap_request_authorize(struct daap_request *dreq) return 0; // If no valid session then we may need to authenticate - if ((strcmp(dreq->uri_parsed->path, "/server-info") == 0) - || (strcmp(dreq->uri_parsed->path, "/login") == 0) - || (strcmp(dreq->uri_parsed->path, "/logout") == 0) - || (strcmp(dreq->uri_parsed->path, "/content-codes") == 0) - || (strncmp(dreq->uri_parsed->path, "/databases/1/items/", strlen("/databases/1/items/")) == 0)) + if ((strcmp(hreq->uri_parsed->path, "/server-info") == 0) + || (strcmp(hreq->uri_parsed->path, "/login") == 0) + || (strcmp(hreq->uri_parsed->path, "/logout") == 0) + || (strcmp(hreq->uri_parsed->path, "/content-codes") == 0) + || (strncmp(hreq->uri_parsed->path, "/databases/1/items/", strlen("/databases/1/items/")) == 0)) return 0; // No authentication DPRINTF(E_DBG, L_DAAP, "Checking authentication for library\n"); // We don't care about the username - ret = httpd_basic_auth(dreq->req, NULL, passwd, cfg_getstr(cfg_getsec(cfg, "library"), "name")); + ret = httpd_basic_auth(hreq->req, NULL, passwd, cfg_getstr(cfg_getsec(cfg, "library"), "name")); if (ret != 0) { DPRINTF(E_LOG, L_DAAP, "Unsuccessful library authentication\n"); @@ -860,10 +760,10 @@ daap_request_authorize(struct daap_request *dreq) /* --------------------------- REPLY HANDLERS ------------------------------- */ /* Note that some handlers can be called without a connection (needed for */ /* cache regeneration), while others cannot. Those that cannot should check */ -/* that dreq->req is not a null pointer. */ +/* that hreq->req is not a null pointer. */ static enum daap_reply_result -daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_server_info(struct httpd_request *hreq) { struct evbuffer *content; struct evkeyvalq *headers; @@ -874,7 +774,7 @@ daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) int mpro; int apro; - if (!dreq->req) + if (!hreq->req) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_server_info() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; @@ -889,7 +789,7 @@ daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) mpro = 2 << 16 | 10; apro = 3 << 16 | 12; - headers = evhttp_request_get_input_headers(dreq->req); + headers = evhttp_request_get_input_headers(hreq->req); if (headers && (clientver = evhttp_find_header(headers, "Client-DAAP-Version"))) { if (strcmp(clientver, "1.0") == 0) @@ -916,7 +816,7 @@ daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) /* Sub-optimal user-agent sniffing to solve the problem that iTunes 12.1 * does not work if we announce support for groups. */ - if (dreq->user_agent && (strncmp(dreq->user_agent, "iTunes", strlen("iTunes")) == 0)) + if (hreq->user_agent && (strncmp(hreq->user_agent, "iTunes", strlen("iTunes")) == 0)) dmap_add_short(content, "asgr", 0); // daap.supportsgroups (1=artists, 2=albums, 3=both) else dmap_add_short(content, "asgr", 3); // daap.supportsgroups (1=artists, 2=albums, 3=both) @@ -959,9 +859,9 @@ daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) // Create container len = evbuffer_get_length(content); - dmap_add_container(reply, "msrv", len); + dmap_add_container(hreq->reply, "msrv", len); - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, content)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, content)); evbuffer_free(content); @@ -969,7 +869,7 @@ daap_reply_server_info(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_reply_content_codes(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_content_codes(struct httpd_request *hreq) { const struct dmap_field *dmap_fields; size_t len; @@ -982,36 +882,37 @@ daap_reply_content_codes(struct evbuffer *reply, struct daap_request *dreq) for (i = 0; i < nfields; i++) len += 8 + 12 + 10 + 8 + strlen(dmap_fields[i].desc); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, len + 8)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, len + 8)); - dmap_add_container(reply, "mccr", len); - dmap_add_int(reply, "mstt", 200); + dmap_add_container(hreq->reply, "mccr", len); + dmap_add_int(hreq->reply, "mstt", 200); for (i = 0; i < nfields; i++) { len = 12 + 10 + 8 + strlen(dmap_fields[i].desc); - dmap_add_container(reply, "mdcl", len); - dmap_add_string(reply, "mcnm", dmap_fields[i].tag); /* 12 */ - dmap_add_string(reply, "mcna", dmap_fields[i].desc); /* 8 + strlen(desc) */ - dmap_add_short(reply, "mcty", dmap_fields[i].type); /* 10 */ + dmap_add_container(hreq->reply, "mdcl", len); + dmap_add_string(hreq->reply, "mcnm", dmap_fields[i].tag); /* 12 */ + dmap_add_string(hreq->reply, "mcna", dmap_fields[i].desc); /* 8 + strlen(desc) */ + dmap_add_short(hreq->reply, "mcty", dmap_fields[i].type); /* 10 */ } return DAAP_REPLY_OK; } static enum daap_reply_result -daap_reply_login(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_login(struct httpd_request *hreq) { + struct daap_session *adhoc = hreq->extra_data; + struct daap_session *session; struct pairing_info pi; const char *param; - bool is_remote; int request_session_id; int ret; - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 32)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); - is_remote = (param = evhttp_find_header(dreq->query, "pairing-guid")); + param = evhttp_find_header(hreq->query, "pairing-guid"); if (param && !cfg_getbool(cfg_getsec(cfg, "general"), "promiscuous_mode")) { if (strlen(param) < 3) @@ -1036,13 +937,13 @@ daap_reply_login(struct evbuffer *reply, struct daap_request *dreq) } else { - if (dreq->user_agent) - DPRINTF(E_INFO, L_DAAP, "Client '%s' logging in\n", dreq->user_agent); + if (hreq->user_agent) + DPRINTF(E_INFO, L_DAAP, "Client '%s' logging in\n", hreq->user_agent); else DPRINTF(E_INFO, L_DAAP, "Client (unknown user-agent) logging in\n"); } - param = evhttp_find_header(dreq->query, "request-session-id"); + param = evhttp_find_header(hreq->query, "request-session-id"); if (param) { ret = safe_atoi32(param, &request_session_id); @@ -1055,35 +956,35 @@ daap_reply_login(struct evbuffer *reply, struct daap_request *dreq) else request_session_id = 0; - dreq->session = daap_session_add(dreq->user_agent, is_remote, request_session_id); - if (!dreq->session) + session = daap_session_add(adhoc->is_remote, request_session_id); + if (!session) { - dmap_error_make(reply, "mlog", "Could not start session"); + dmap_error_make(hreq->reply, "mlog", "Could not start session"); return DAAP_REPLY_ERROR; } - dmap_add_container(reply, "mlog", 24); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "mlid", dreq->session->id); /* 12 */ + dmap_add_container(hreq->reply, "mlog", 24); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "mlid", session->id); /* 12 */ return DAAP_REPLY_OK; } static enum daap_reply_result -daap_reply_logout(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_logout(struct httpd_request *hreq) { - if (!dreq->session) + if (!hreq->extra_data) return DAAP_REPLY_FORBIDDEN; - daap_session_remove(dreq->session); + daap_session_remove(hreq->extra_data); - dreq->session = NULL; + hreq->extra_data = NULL; return DAAP_REPLY_LOGOUT; } static enum daap_reply_result -daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_update(struct httpd_request *hreq) { struct daap_update_request *ur; struct evhttp_connection *evcon; @@ -1091,13 +992,13 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) int reqd_rev; int ret; - if (!dreq->req) + if (!hreq->req) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_update() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; } - param = evhttp_find_header(dreq->query, "revision-number"); + param = evhttp_find_header(hreq->query, "revision-number"); if (!param) { DPRINTF(E_DBG, L_DAAP, "Missing revision-number in client update request\n"); @@ -1111,18 +1012,18 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Parameter revision-number not an integer\n"); - dmap_error_make(reply, "mupd", "Invalid request"); + dmap_error_make(hreq->reply, "mupd", "Invalid request"); return DAAP_REPLY_ERROR; } if (reqd_rev == 1) /* Or revision is not valid */ { - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 32)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); /* Send back current revision */ - dmap_add_container(reply, "mupd", 24); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "musr", current_rev); /* 12 */ + dmap_add_container(hreq->reply, "mupd", 24); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "musr", current_rev); /* 12 */ return DAAP_REPLY_OK; } @@ -1133,7 +1034,7 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Out of memory for update request\n"); - dmap_error_make(reply, "mupd", "Out of memory"); + dmap_error_make(hreq->reply, "mupd", "Out of memory"); return DAAP_REPLY_ERROR; } @@ -1149,14 +1050,14 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Out of memory for update request event\n"); - dmap_error_make(reply, "mupd", "Could not register timer"); + dmap_error_make(hreq->reply, "mupd", "Could not register timer"); update_free(ur); return DAAP_REPLY_ERROR; } } /* NOTE: we may need to keep reqd_rev in there too */ - ur->req = dreq->req; + ur->req = hreq->req; ur->next = update_requests; update_requests = ur; @@ -1164,7 +1065,7 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) /* If the connection fails before we have an update to push out * to the client, we need to know. */ - evcon = evhttp_request_get_connection(dreq->req); + evcon = evhttp_request_get_connection(hreq->req); if (evcon) evhttp_connection_set_closecb(evcon, update_fail_cb, ur); @@ -1172,14 +1073,14 @@ daap_reply_update(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_reply_activity(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_activity(struct httpd_request *hreq) { /* That's so nice, thanks for letting us know */ return DAAP_REPLY_NO_CONTENT; } static enum daap_reply_result -daap_reply_dblist(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_dblist(struct httpd_request *hreq) { struct evbuffer *content; struct evbuffer *item; @@ -1194,7 +1095,7 @@ daap_reply_dblist(struct evbuffer *reply, struct daap_request *dreq) CHECK_NULL(L_DAAP, content = evbuffer_new()); CHECK_NULL(L_DAAP, item = evbuffer_new()); CHECK_ERR(L_DAAP, evbuffer_expand(item, 512)); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 1024)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 1024)); // Add db entry for library with dbid = 1 dmap_add_int(item, "miid", 1); @@ -1237,14 +1138,14 @@ daap_reply_dblist(struct evbuffer *reply, struct daap_request *dreq) // Create container len = evbuffer_get_length(content); - dmap_add_container(reply, "avdb", len + 53); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_char(reply, "muty", 0); /* 9 */ - dmap_add_int(reply, "mtco", 2); /* 12 */ - dmap_add_int(reply, "mrco", 2); /* 12 */ - dmap_add_container(reply, "mlcl", len); /* 8 */ + dmap_add_container(hreq->reply, "avdb", len + 53); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_char(hreq->reply, "muty", 0); /* 9 */ + dmap_add_int(hreq->reply, "mtco", 2); /* 12 */ + dmap_add_int(hreq->reply, "mrco", 2); /* 12 */ + dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, content)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, content)); evbuffer_free(item); evbuffer_free(content); @@ -1253,13 +1154,14 @@ daap_reply_dblist(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, int playlist) +daap_reply_songlist_generic(struct httpd_request *hreq, int playlist) { struct query_params qp; struct db_media_file_info dbmfi; struct evbuffer *song; struct evbuffer *songlist; struct evkeyvalq *headers; + struct daap_session *s; const struct dmap_field **meta; struct sort_ctx *sctx; const char *param; @@ -1275,28 +1177,35 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i DPRINTF(E_DBG, L_DAAP, "Fetching song list for playlist %d\n", playlist); + s = hreq->extra_data; + if (!s) + { + DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_songlist_generic() called with NULL session (playlist %d)\n", playlist); + return DAAP_REPLY_ERROR; + } + if (playlist != -1) { // Songs in playlist tag = "apso"; - query_params_set(&qp, &sort_headers, dreq, Q_PLITEMS); + query_params_set(&qp, &sort_headers, hreq, Q_PLITEMS); qp.id = playlist; } else { // Songs in database tag = "adbs"; - query_params_set(&qp, &sort_headers, dreq, Q_ITEMS); + query_params_set(&qp, &sort_headers, hreq, Q_ITEMS); } CHECK_NULL(L_DAAP, songlist = evbuffer_new()); CHECK_NULL(L_DAAP, song = evbuffer_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 61)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(songlist, 4096)); CHECK_ERR(L_DAAP, evbuffer_expand(song, 512)); - param = evhttp_find_header(dreq->query, "meta"); + param = evhttp_find_header(hreq->query, "meta"); if (!param) { DPRINTF(E_DBG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1326,14 +1235,14 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); free(meta); - dmap_error_make(reply, tag, "Could not start query"); + dmap_error_make(hreq->reply, tag, "Could not start query"); goto error; } client_codecs = NULL; - if (!dreq->is_remote && dreq->req) + if (!s->is_remote && hreq->req) { - headers = evhttp_request_get_input_headers(dreq->req); + headers = evhttp_request_get_input_headers(hreq->req); client_codecs = evhttp_find_header(headers, "Accept-Codecs"); } @@ -1349,13 +1258,13 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i transcode = 0; } - else if (dreq->is_remote) + else if (s->is_remote) { transcode = 1; } else if (!last_codectype || (strcmp(last_codectype, dbmfi.codectype) != 0)) { - transcode = transcode_needed(dreq->user_agent, client_codecs, dbmfi.codectype); + transcode = transcode_needed(hreq->user_agent, client_codecs, dbmfi.codectype); free(last_codectype); last_codectype = strdup(dbmfi.codectype); @@ -1393,13 +1302,13 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i if (ret == -100) { - dmap_error_make(reply, tag, "Out of memory"); + dmap_error_make(hreq->reply, tag, "Out of memory"); goto error; } else if (ret < 0) { DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); - dmap_error_make(reply, tag, "Error fetching query results"); + dmap_error_make(hreq->reply, tag, "Error fetching query results"); goto error; } @@ -1408,25 +1317,25 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i if (sort_headers) { daap_sort_finalize(sctx); - dmap_add_container(reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); + dmap_add_container(hreq->reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); } else - dmap_add_container(reply, tag, len + 53); + dmap_add_container(hreq->reply, tag, len + 53); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_char(reply, "muty", 0); /* 9 */ - dmap_add_int(reply, "mtco", qp.results); /* 12 */ - dmap_add_int(reply, "mrco", nsongs); /* 12 */ - dmap_add_container(reply, "mlcl", len); /* 8 */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_char(hreq->reply, "muty", 0); /* 9 */ + dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ + dmap_add_int(hreq->reply, "mrco", nsongs); /* 12 */ + dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, songlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, songlist)); if (sort_headers) { len = evbuffer_get_length(sctx->headerlist); - dmap_add_container(reply, "mshl", len); /* 8 */ + dmap_add_container(hreq->reply, "mshl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, sctx->headerlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); } daap_sort_context_free(sctx); @@ -1446,29 +1355,29 @@ daap_reply_songlist_generic(struct evbuffer *reply, struct daap_request *dreq, i } static enum daap_reply_result -daap_reply_dbsonglist(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_dbsonglist(struct httpd_request *hreq) { - return daap_reply_songlist_generic(reply, dreq, -1); + return daap_reply_songlist_generic(hreq, -1); } static enum daap_reply_result -daap_reply_plsonglist(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_plsonglist(struct httpd_request *hreq) { int playlist; int ret; - ret = safe_atoi32(dreq->uri_parsed->path_parts[3], &playlist); + ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist); if (ret < 0) { - dmap_error_make(reply, "apso", "Invalid playlist ID"); + dmap_error_make(hreq->reply, "apso", "Invalid playlist ID"); return DAAP_REPLY_ERROR; } - return daap_reply_songlist_generic(reply, dreq, playlist); + return daap_reply_songlist_generic(hreq, playlist); } static enum daap_reply_result -daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_playlists(struct httpd_request *hreq) { struct query_params qp; struct db_playlist_info dbpli; @@ -1494,24 +1403,24 @@ daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) cfg_radiopl = cfg_getbool(cfg_getsec(cfg, "library"), "radio_playlists"); - ret = safe_atoi32(dreq->uri_parsed->path_parts[1], &database); + ret = safe_atoi32(hreq->uri_parsed->path_parts[1], &database); if (ret < 0) { - dmap_error_make(reply, "aply", "Invalid database ID"); + dmap_error_make(hreq->reply, "aply", "Invalid database ID"); return DAAP_REPLY_ERROR; } - query_params_set(&qp, NULL, dreq, Q_PL); + query_params_set(&qp, NULL, hreq, Q_PL); if (qp.sort == S_NONE) qp.sort = S_PLAYLIST; CHECK_NULL(L_DAAP, playlistlist = evbuffer_new()); CHECK_NULL(L_DAAP, playlist = evbuffer_new()); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 61)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(playlistlist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(playlist, 128)); - param = evhttp_find_header(dreq->query, "meta"); + param = evhttp_find_header(hreq->query, "meta"); if (!param) { DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1524,7 +1433,7 @@ daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n"); - dmap_error_make(reply, "aply", "Failed to parse query"); + dmap_error_make(hreq->reply, "aply", "Failed to parse query"); goto error; } @@ -1534,7 +1443,7 @@ daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); free(meta); - dmap_error_make(reply, "aply", "Could not start query"); + dmap_error_make(hreq->reply, "aply", "Could not start query"); goto error; } @@ -1651,26 +1560,26 @@ daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) if (ret == -100) { - dmap_error_make(reply, "aply", "Out of memory"); + dmap_error_make(hreq->reply, "aply", "Out of memory"); goto error; } else if (ret < 0) { DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); - dmap_error_make(reply, "aply", "Error fetching query results"); + dmap_error_make(hreq->reply, "aply", "Error fetching query results"); goto error; } /* Add header to evbuf, add playlistlist to evbuf */ len = evbuffer_get_length(playlistlist); - dmap_add_container(reply, "aply", len + 53); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_char(reply, "muty", 0); /* 9 */ - dmap_add_int(reply, "mtco", qp.results); /* 12 */ - dmap_add_int(reply,"mrco", npls); /* 12 */ - dmap_add_container(reply, "mlcl", len); + dmap_add_container(hreq->reply, "aply", len + 53); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_char(hreq->reply, "muty", 0); /* 9 */ + dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ + dmap_add_int(hreq->reply,"mrco", npls); /* 12 */ + dmap_add_container(hreq->reply, "mlcl", len); - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, playlistlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, playlistlist)); evbuffer_free(playlist); evbuffer_free(playlistlist); @@ -1687,7 +1596,7 @@ daap_reply_playlists(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_groups(struct httpd_request *hreq) { struct query_params qp; struct db_group_info dbgri; @@ -1709,14 +1618,14 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) int i; int ret; - param = evhttp_find_header(dreq->query, "group-type"); + param = evhttp_find_header(hreq->query, "group-type"); if (strcmp(param, "artists") == 0) { // Request from Remote may have the form: // groups?meta=dmap.xxx,dma...&type=music&group-type=artists&sort=album&include-sort-headers=1&query=('...')&session-id=... // Note: Since grouping by artist and sorting by album is crazy we override tag = "agar"; - query_params_set(&qp, &sort_headers, dreq, Q_GROUP_ARTISTS); + query_params_set(&qp, &sort_headers, hreq, Q_GROUP_ARTISTS); qp.sort = S_ARTIST; } else @@ -1725,7 +1634,7 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) // groups?meta=dmap.xxx,dma...&type=music&group-type=albums&sort=artist&include-sort-headers=0&query=('...'))&session-id=... // Sort may also be 'album' tag = "agal"; - query_params_set(&qp, &sort_headers, dreq, Q_GROUP_ALBUMS); + query_params_set(&qp, &sort_headers, hreq, Q_GROUP_ALBUMS); if (qp.sort == S_NONE) qp.sort = S_ALBUM; } @@ -1733,11 +1642,11 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) CHECK_NULL(L_DAAP, grouplist = evbuffer_new()); CHECK_NULL(L_DAAP, group = evbuffer_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 61)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 61)); CHECK_ERR(L_DAAP, evbuffer_expand(grouplist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(group, 128)); - param = evhttp_find_header(dreq->query, "meta"); + param = evhttp_find_header(hreq->query, "meta"); if (!param) { DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1750,7 +1659,7 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n"); - dmap_error_make(reply, tag, "Failed to parse query"); + dmap_error_make(hreq->reply, tag, "Failed to parse query"); goto error; } @@ -1760,7 +1669,7 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); free(meta); - dmap_error_make(reply, tag, "Could not start query"); + dmap_error_make(hreq->reply, tag, "Could not start query"); goto error; } @@ -1850,13 +1759,13 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) if (ret == -100) { - dmap_error_make(reply, tag, "Out of memory"); + dmap_error_make(hreq->reply, tag, "Out of memory"); goto error; } else if (ret < 0) { DPRINTF(E_LOG, L_DAAP, "Error fetching results\n"); - dmap_error_make(reply, tag, "Error fetching query results"); + dmap_error_make(hreq->reply, tag, "Error fetching query results"); goto error; } @@ -1865,25 +1774,25 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) if (sort_headers) { daap_sort_finalize(sctx); - dmap_add_container(reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); + dmap_add_container(hreq->reply, tag, len + evbuffer_get_length(sctx->headerlist) + 61); } else - dmap_add_container(reply, tag, len + 53); + dmap_add_container(hreq->reply, tag, len + 53); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_char(reply, "muty", 0); /* 9 */ - dmap_add_int(reply, "mtco", qp.results); /* 12 */ - dmap_add_int(reply,"mrco", ngrp); /* 12 */ - dmap_add_container(reply, "mlcl", len); /* 8 */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_char(hreq->reply, "muty", 0); /* 9 */ + dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ + dmap_add_int(hreq->reply,"mrco", ngrp); /* 12 */ + dmap_add_container(hreq->reply, "mlcl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, grouplist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, grouplist)); if (sort_headers) { len = evbuffer_get_length(sctx->headerlist); - dmap_add_container(reply, "mshl", len); /* 8 */ + dmap_add_container(hreq->reply, "mshl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, sctx->headerlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); } daap_sort_context_free(sctx); @@ -1903,7 +1812,7 @@ daap_reply_groups(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_browse(struct httpd_request *hreq) { struct query_params qp; struct evbuffer *itemlist; @@ -1916,40 +1825,40 @@ daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) int nitems; int ret; - if (strcmp(dreq->uri_parsed->path_parts[3], "artists") == 0) + if (strcmp(hreq->uri_parsed->path_parts[3], "artists") == 0) { tag = "abar"; - query_params_set(&qp, &sort_headers, dreq, Q_BROWSE_ARTISTS); + query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_ARTISTS); qp.sort = S_ARTIST; } - else if (strcmp(dreq->uri_parsed->path_parts[3], "albums") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "albums") == 0) { tag = "abal"; - query_params_set(&qp, &sort_headers, dreq, Q_BROWSE_ALBUMS); + query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_ALBUMS); qp.sort = S_ALBUM; } - else if (strcmp(dreq->uri_parsed->path_parts[3], "genres") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "genres") == 0) { tag = "abgn"; - query_params_set(&qp, &sort_headers, dreq, Q_BROWSE_GENRES); + query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_GENRES); qp.sort = S_GENRE; } - else if (strcmp(dreq->uri_parsed->path_parts[3], "composers") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "composers") == 0) { tag = "abcp"; - query_params_set(&qp, &sort_headers, dreq, Q_BROWSE_COMPOSERS); + query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_COMPOSERS); qp.sort = S_COMPOSER; } else { - DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", dreq->uri_parsed->path_parts[3]); - dmap_error_make(reply, "abro", "Invalid browse type"); + DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", hreq->uri_parsed->path_parts[3]); + dmap_error_make(hreq->reply, "abro", "Invalid browse type"); return DAAP_REPLY_ERROR; } CHECK_NULL(L_DAAP, itemlist = evbuffer_new()); CHECK_NULL(L_DAAP, sctx = daap_sort_context_new()); - CHECK_ERR(L_DAAP, evbuffer_expand(reply, 52)); + CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 52)); CHECK_ERR(L_DAAP, evbuffer_expand(itemlist, 1024)); // Just a starting alloc, it'll expand as needed ret = db_query_start(&qp); @@ -1957,7 +1866,7 @@ daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Could not start query\n"); - dmap_error_make(reply, "abro", "Could not start query"); + dmap_error_make(hreq->reply, "abro", "Could not start query"); goto error; } @@ -1985,7 +1894,7 @@ daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) { DPRINTF(E_LOG, L_DAAP, "Error fetching/building results\n"); - dmap_error_make(reply, "abro", "Error fetching/building query results"); + dmap_error_make(hreq->reply, "abro", "Error fetching/building query results"); goto error; } @@ -1993,24 +1902,24 @@ daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) if (sort_headers) { daap_sort_finalize(sctx); - dmap_add_container(reply, "abro", len + evbuffer_get_length(sctx->headerlist) + 52); + dmap_add_container(hreq->reply, "abro", len + evbuffer_get_length(sctx->headerlist) + 52); } else - dmap_add_container(reply, "abro", len + 44); + dmap_add_container(hreq->reply, "abro", len + 44); - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "mtco", qp.results); /* 12 */ - dmap_add_int(reply, "mrco", nitems); /* 12 */ - dmap_add_container(reply, tag, len); /* 8 */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "mtco", qp.results); /* 12 */ + dmap_add_int(hreq->reply, "mrco", nitems); /* 12 */ + dmap_add_container(hreq->reply, tag, len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, itemlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, itemlist)); if (sort_headers) { len = evbuffer_get_length(sctx->headerlist); - dmap_add_container(reply, "mshl", len); /* 8 */ + dmap_add_container(hreq->reply, "mshl", len); /* 8 */ - CHECK_ERR(L_DAAP, evbuffer_add_buffer(reply, sctx->headerlist)); + CHECK_ERR(L_DAAP, evbuffer_add_buffer(hreq->reply, sctx->headerlist)); } daap_sort_context_free(sctx); @@ -2029,7 +1938,7 @@ daap_reply_browse(struct evbuffer *reply, struct daap_request *dreq) /* NOTE: We only handle artwork at the moment */ static enum daap_reply_result -daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_extra_data(struct httpd_request *hreq) { struct evkeyvalq *headers; char clen[32]; @@ -2041,22 +1950,22 @@ daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) int max_h; int ret; - if (!dreq->req) + if (!hreq->req) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_extra_data() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; } - ret = safe_atoi32(dreq->uri_parsed->path_parts[3], &id); + ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &id); if (ret < 0) { - DPRINTF(E_LOG, L_DAAP, "Could not convert id parameter to integer: '%s'\n", dreq->uri_parsed->path_parts[3]); + DPRINTF(E_LOG, L_DAAP, "Could not convert id parameter to integer: '%s'\n", hreq->uri_parsed->path_parts[3]); return DAAP_REPLY_BAD_REQUEST; } - if (evhttp_find_header(dreq->query, "mw") && evhttp_find_header(dreq->query, "mh")) + if (evhttp_find_header(hreq->query, "mw") && evhttp_find_header(hreq->query, "mh")) { - param = evhttp_find_header(dreq->query, "mw"); + param = evhttp_find_header(hreq->query, "mw"); ret = safe_atoi32(param, &max_w); if (ret < 0) { @@ -2064,7 +1973,7 @@ daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) return DAAP_REPLY_BAD_REQUEST; } - param = evhttp_find_header(dreq->query, "mh"); + param = evhttp_find_header(hreq->query, "mh"); ret = safe_atoi32(param, &max_h); if (ret < 0) { @@ -2080,12 +1989,12 @@ daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) max_h = 0; } - if (strcmp(dreq->uri_parsed->path_parts[2], "groups") == 0) - ret = artwork_get_group(reply, id, max_w, max_h); - else if (strcmp(dreq->uri_parsed->path_parts[2], "items") == 0) - ret = artwork_get_item(reply, id, max_w, max_h); + if (strcmp(hreq->uri_parsed->path_parts[2], "groups") == 0) + ret = artwork_get_group(hreq->reply, id, max_w, max_h); + else if (strcmp(hreq->uri_parsed->path_parts[2], "items") == 0) + ret = artwork_get_item(hreq->reply, id, max_w, max_h); - len = evbuffer_get_length(reply); + len = evbuffer_get_length(hreq->reply); switch (ret) { @@ -2099,12 +2008,12 @@ daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) default: if (len > 0) - evbuffer_drain(reply, len); + evbuffer_drain(hreq->reply, len); goto no_artwork; } - headers = evhttp_request_get_output_headers(dreq->req); + headers = evhttp_request_get_output_headers(hreq->req); evhttp_remove_header(headers, "Content-Type"); evhttp_add_header(headers, "Content-Type", ctype); snprintf(clen, sizeof(clen), "%ld", (long)len); @@ -2117,22 +2026,22 @@ daap_reply_extra_data(struct evbuffer *reply, struct daap_request *dreq) } static enum daap_reply_result -daap_stream(struct evbuffer *reply, struct daap_request *dreq) +daap_stream(struct httpd_request *hreq) { int id; int ret; - if (!dreq->req) + if (!hreq->req) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_stream() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; } - ret = safe_atoi32(dreq->uri_parsed->path_parts[3], &id); + ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &id); if (ret < 0) return DAAP_REPLY_BAD_REQUEST; - httpd_stream_file(dreq->req, id); + httpd_stream_file(hreq->req, id); return DAAP_REPLY_NONE; } @@ -2150,7 +2059,7 @@ static const struct dmap_field dmap_TST8 = { "test.long", "TST8", NULL, DMA static const struct dmap_field dmap_TST9 = { "test.string", "TST9", NULL, DMAP_TYPE_STRING }; static enum daap_reply_result -daap_reply_dmap_test(struct evbuffer *reply, struct daap_request *dreq) +daap_reply_dmap_test(struct httpd_request *hreq) { struct evbuffer *test; char buf[64]; @@ -2210,16 +2119,16 @@ daap_reply_dmap_test(struct evbuffer *reply, struct daap_request *dreq) dmap_add_field(test, &dmap_TST8, buf, 0); dmap_add_field(test, &dmap_TST9, buf, 0); - dmap_add_container(reply, dmap_TEST.tag, evbuffer_get_length(test)); + dmap_add_container(hreq->reply, dmap_TEST.tag, evbuffer_get_length(test)); - ret = evbuffer_add_buffer(reply, test); + ret = evbuffer_add_buffer(hreq->reply, test); evbuffer_free(test); if (ret < 0) { DPRINTF(E_LOG, L_DAAP, "Could not add test results to DMAP test reply\n"); - dmap_error_make(reply, dmap_TEST.tag, "Out of memory"); + dmap_error_make(hreq->reply, dmap_TEST.tag, "Out of memory"); return DAAP_REPLY_ERROR; } @@ -2227,7 +2136,7 @@ daap_reply_dmap_test(struct evbuffer *reply, struct daap_request *dreq) } #endif /* DMAP_TEST */ -static struct uri_map daap_handlers[] = +static struct httpd_uri_map daap_handlers[] = { { .regexp = "^/server-info$", @@ -2312,71 +2221,96 @@ static struct uri_map daap_handlers[] = void daap_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) { - struct daap_request *dreq; + struct httpd_request *hreq; struct evkeyvalq *headers; struct timespec start; struct timespec end; - struct evbuffer *reply; + struct daap_session session; + const char *param; + int32_t id; int ret; int msec; DPRINTF(E_DBG, L_DAAP, "DAAP request: '%s'\n", uri_parsed->uri); - dreq = daap_request_parse(req, uri_parsed, NULL); - if (!dreq) + hreq = httpd_request_parse(req, uri_parsed, NULL, daap_handlers); + if (!hreq) { + DPRINTF(E_LOG, L_DAAP, "Unrecognized path '%s' in DAAP request: '%s'\n", uri_parsed->path, uri_parsed->uri); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } - ret = daap_request_authorize(dreq); + // Check if we have a session and point hreq->extra_data to it + param = evhttp_find_header(hreq->query, "session-id"); + if (param) + { + ret = safe_atoi32(param, &id); + if (ret < 0) + DPRINTF(E_LOG, L_DAAP, "Ignoring non-numeric session id in DAAP request: '%s'\n", uri_parsed->uri); + else + hreq->extra_data = daap_session_get(id); + } + + ret = daap_request_authorize(hreq); if (ret < 0) { httpd_send_error(req, 403, "Forbidden"); - free(dreq); + free(hreq); return; } + // Create an ad-hoc session, which is a way of passing is_remote to the handler, even though no real session exists + if (!hreq->extra_data) + { + memset(&session, 0, sizeof(struct daap_session)); + session.is_remote = (evhttp_find_header(hreq->query, "pairing-guid") != NULL); + hreq->extra_data = &session; + } + // Set reply headers headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "Accept-Ranges", "bytes"); evhttp_add_header(headers, "DAAP-Server", "forked-daapd/" VERSION); - /* Content-Type for all replies, even the actual audio streaming. Note that - * video streaming will override this Content-Type with a more appropriate - * video/ Content-Type as expected by clients like Front Row. - */ + // Content-Type for all replies, even the actual audio streaming. Note that + // video streaming will override this Content-Type with a more appropriate + // video/ Content-Type as expected by clients like Front Row. evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); // Now we create the actual reply - CHECK_NULL(L_DAAP, reply = evbuffer_new()); + CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new()); // Try the cache - ret = cache_daap_get(reply, uri_parsed->uri); + ret = cache_daap_get(hreq->reply, uri_parsed->uri); if (ret == 0) { // The cache will return the data gzipped, so httpd_send_reply won't need to do it evhttp_add_header(headers, "Content-Encoding", "gzip"); - httpd_send_reply(req, HTTP_OK, "OK", reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply + httpd_send_reply(req, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply - evbuffer_free(reply); - free(dreq); + evbuffer_free(hreq->reply); + free(hreq); return; } - // Let the handler construct a reply and then send it (note that the reply may be an error) + // No dice, let's call the handler so it can construct a reply and then send it (note that the reply may be an error) clock_gettime(CLOCK_MONOTONIC, &start); - ret = dreq->handler(reply, dreq); - daap_reply_send(req, reply, ret); + + ret = hreq->handler(hreq); + + daap_reply_send(hreq, ret); + clock_gettime(CLOCK_MONOTONIC, &end); msec = (end.tv_sec * 1000 + end.tv_nsec / 1000000) - (start.tv_sec * 1000 + start.tv_nsec / 1000000); DPRINTF(E_DBG, L_DAAP, "DAAP request handled in %d milliseconds\n", msec); if (ret == DAAP_REPLY_OK && msec > cache_daap_threshold()) - cache_daap_add(uri_parsed->uri, dreq->user_agent, msec); + cache_daap_add(uri_parsed->uri, hreq->user_agent, ((struct daap_session *)hreq->extra_data)->is_remote, msec); - evbuffer_free(reply); - free(dreq); + evbuffer_free(hreq->reply); + free(hreq); } int @@ -2420,39 +2354,50 @@ daap_session_is_valid(int id) return session ? 1 : 0; } +// Thread: Cache struct evbuffer * -daap_reply_build(const char *uri, const char *user_agent) +daap_reply_build(const char *uri, const char *user_agent, int is_remote) { - struct daap_request *dreq; + struct httpd_request *hreq; struct httpd_uri_parsed *uri_parsed; struct evbuffer *reply; + struct daap_session session; int ret; DPRINTF(E_DBG, L_DAAP, "Building reply for DAAP request: '%s'\n", uri); + reply = NULL; + uri_parsed = httpd_uri_parse(uri); if (!uri_parsed) return NULL; - dreq = daap_request_parse(NULL, uri_parsed, user_agent); - if (!dreq) + hreq = httpd_request_parse(NULL, uri_parsed, user_agent, daap_handlers); + if (!hreq) { - httpd_uri_free(uri_parsed); - return NULL; + DPRINTF(E_LOG, L_DAAP, "Cannot build reply, unrecognized path '%s' in request: '%s'\n", uri_parsed->path, uri_parsed->uri); + goto out_free_uri; } - CHECK_NULL(L_DAAP, reply = evbuffer_new()); + memset(&session, 0, sizeof(struct daap_session)); + session.is_remote = (bool)is_remote; - ret = dreq->handler(reply, dreq); + hreq->extra_data = &session; + + CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new()); + + ret = hreq->handler(hreq); if (ret < 0) { - evbuffer_free(reply); - free(dreq); - httpd_uri_free(uri_parsed); - return NULL; + evbuffer_free(hreq->reply); + goto out_free_hreq; } - free(dreq); + reply = hreq->reply; + + out_free_hreq: + free(hreq); + out_free_uri: httpd_uri_free(uri_parsed); return reply; diff --git a/src/httpd_daap.h b/src/httpd_daap.h index fff99a85..53fb6e55 100644 --- a/src/httpd_daap.h +++ b/src/httpd_daap.h @@ -20,6 +20,6 @@ int daap_session_is_valid(int id); struct evbuffer * -daap_reply_build(const char *uri, const char *user_agent); +daap_reply_build(const char *uri, const char *user_agent, int is_remote); #endif /* !__HTTPD_DAAP_H__ */ diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 359e5914..77393671 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -53,24 +52,6 @@ /* httpd event base, from httpd.c */ extern struct event_base *evbase_httpd; -// Will be filled out by dacp_request_parse() -struct dacp_request { - // The parsed request URI given to us by httpd.c - struct httpd_uri_parsed *uri_parsed; - // Shortcut to &uri_parsed->ev_query - struct evkeyvalq *query; - // http request struct - struct evhttp_request *req; - // A pointer to the handler that will process the request - void (*handler)(struct evbuffer *reply, struct dacp_request *dreq); -}; - -struct uri_map { - regex_t preg; - char *regexp; - void (*handler)(struct evbuffer *reply, struct dacp_request *dreq); -}; - struct dacp_update_request { struct evhttp_request *req; @@ -86,8 +67,6 @@ struct dacp_prop_map { dacp_propset propset; }; -/* Forward declaration of handlers */ -static struct uri_map dacp_handlers[]; /* ---------------- FORWARD - PROPERTIES GETTERS AND SETTERS ---------------- */ @@ -561,45 +540,8 @@ speaker_enum_cb(uint64_t id, const char *name, int relvol, int absvol, struct sp dmap_add_int(evbuf, "cmvo", relvol); /* 12 */ } -static struct dacp_request * -dacp_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) -{ - struct dacp_request *dreq; - int i; - int ret; - - CHECK_NULL(L_DACP, dreq = calloc(1, sizeof(struct dacp_request))); - - dreq->req = req; - dreq->uri_parsed = uri_parsed; - dreq->query = &(uri_parsed->ev_query); - - for (i = 0; dacp_handlers[i].handler; i++) - { - ret = regexec(&dacp_handlers[i].preg, uri_parsed->path, 0, NULL, 0); - if (ret == 0) - { - dreq->handler = dacp_handlers[i].handler; - break; - } - } - - if (!dreq->handler) - { - DPRINTF(E_LOG, L_DACP, "Unrecognized path '%s' in DACP request: '%s'\n", uri_parsed->path, uri_parsed->uri); - goto error; - } - - return dreq; - - error: - free(dreq); - - return NULL; -} - static int -dacp_request_authorize(struct dacp_request *dreq) +dacp_request_authorize(struct httpd_request *hreq) { const char *param; int32_t id; @@ -608,7 +550,7 @@ dacp_request_authorize(struct dacp_request *dreq) if (cfg_getbool(cfg_getsec(cfg, "general"), "promiscuous_mode")) return 0; - param = evhttp_find_header(dreq->query, "session-id"); + param = evhttp_find_header(hreq->query, "session-id"); if (!param) { DPRINTF(E_LOG, L_DACP, "No session-id specified in request\n"); @@ -631,7 +573,7 @@ dacp_request_authorize(struct dacp_request *dreq) return 0; invalid: - httpd_send_error(dreq->req, 403, "Forbidden"); + httpd_send_error(hreq->req, 403, "Forbidden"); return -1; } @@ -1141,41 +1083,43 @@ dacp_propset_userrating(const char *value, struct evkeyvalq *query) /* --------------------------- REPLY HANDLERS ------------------------------- */ -static void -dacp_reply_ctrlint(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_ctrlint(struct httpd_request *hreq) { /* /ctrl-int */ - CHECK_ERR(L_DACP, evbuffer_expand(reply, 256)); + CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 256)); /* If tags are added or removed container sizes should be adjusted too */ - dmap_add_container(reply, "caci", 194); /* 8, unknown dacp container - size of content */ - dmap_add_int(reply, "mstt", 200); /* 12, dmap.status */ - dmap_add_char(reply, "muty", 0); /* 9, dmap.updatetype */ - dmap_add_int(reply, "mtco", 1); /* 12, dmap.specifiedtotalcount */ - dmap_add_int(reply, "mrco", 1); /* 12, dmap.returnedcount */ - dmap_add_container(reply, "mlcl", 141); /* 8, dmap.listing - size of content */ - dmap_add_container(reply, "mlit", 133); /* 8, dmap.listingitem - size of content */ - dmap_add_int(reply, "miid", 1); /* 12, dmap.itemid - database ID */ - dmap_add_char(reply, "cmik", 1); /* 9, unknown */ + dmap_add_container(hreq->reply, "caci", 194); /* 8, unknown dacp container - size of content */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12, dmap.status */ + dmap_add_char(hreq->reply, "muty", 0); /* 9, dmap.updatetype */ + dmap_add_int(hreq->reply, "mtco", 1); /* 12, dmap.specifiedtotalcount */ + dmap_add_int(hreq->reply, "mrco", 1); /* 12, dmap.returnedcount */ + dmap_add_container(hreq->reply, "mlcl", 141); /* 8, dmap.listing - size of content */ + dmap_add_container(hreq->reply, "mlit", 133); /* 8, dmap.listingitem - size of content */ + dmap_add_int(hreq->reply, "miid", 1); /* 12, dmap.itemid - database ID */ + dmap_add_char(hreq->reply, "cmik", 1); /* 9, unknown */ - dmap_add_int(reply, "cmpr", (2 << 16 | 2)); /* 12, dmcp.protocolversion */ - dmap_add_int(reply, "capr", (2 << 16 | 5)); /* 12, dacp.protocolversion */ + dmap_add_int(hreq->reply, "cmpr", (2 << 16 | 2)); /* 12, dmcp.protocolversion */ + dmap_add_int(hreq->reply, "capr", (2 << 16 | 5)); /* 12, dacp.protocolversion */ - dmap_add_char(reply, "cmsp", 1); /* 9, unknown */ - dmap_add_char(reply, "aeFR", 0x64); /* 9, unknown */ - dmap_add_char(reply, "cmsv", 1); /* 9, unknown */ - dmap_add_char(reply, "cass", 1); /* 9, unknown */ - dmap_add_char(reply, "caov", 1); /* 9, unknown */ - dmap_add_char(reply, "casu", 1); /* 9, unknown */ - dmap_add_char(reply, "ceSG", 1); /* 9, unknown */ - dmap_add_char(reply, "cmrl", 1); /* 9, unknown */ - dmap_add_long(reply, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */ + dmap_add_char(hreq->reply, "cmsp", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "aeFR", 0x64); /* 9, unknown */ + dmap_add_char(hreq->reply, "cmsv", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "cass", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "caov", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "casu", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "ceSG", 1); /* 9, unknown */ + dmap_add_char(hreq->reply, "cmrl", 1); /* 9, unknown */ + dmap_add_long(hreq->reply, "ceSX", (1 << 1 | 1)); /* 16, unknown dacp - lowest bit announces support for playqueue-contents/-edit */ - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); + + return 0; } -static void -dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_cue_play(struct httpd_request *hreq) { struct player_status status; const char *sort; @@ -1189,7 +1133,7 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) /* /cue?command=play&query=...&sort=...&index=N */ - param = evhttp_find_header(dreq->query, "clear-first"); + param = evhttp_find_header(hreq->query, "clear-first"); if (param) { ret = safe_atoi32(param, &clear); @@ -1205,18 +1149,18 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) player_get_status(&status); - cuequery = evhttp_find_header(dreq->query, "query"); + cuequery = evhttp_find_header(hreq->query, "query"); if (cuequery) { - sort = evhttp_find_header(dreq->query, "sort"); + sort = evhttp_find_header(hreq->query, "sort"); ret = dacp_queueitem_add(cuequery, NULL, sort, 0, 0); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); - dmap_send_error(dreq->req, "cacr", "Could not build song queue"); - return; + dmap_send_error(hreq->req, "cacr", "Could not build song queue"); + return -1; } } else if (status.status != PLAY_STOPPED) @@ -1224,12 +1168,12 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) player_playback_stop(); } - param = evhttp_find_header(dreq->query, "dacp.shufflestate"); + param = evhttp_find_header(hreq->query, "dacp.shufflestate"); if (param) dacp_propset_shufflestate(param, NULL); pos = 0; - param = evhttp_find_header(dreq->query, "index"); + param = evhttp_find_header(hreq->query, "index"); if (param) { ret = safe_atou32(param, &pos); @@ -1238,10 +1182,10 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) } /* If selection was from Up Next queue or history queue (command will be playnow), then index is relative */ - if ((param = evhttp_find_header(dreq->query, "command")) && (strcmp(param, "playnow") == 0)) + if ((param = evhttp_find_header(hreq->query, "command")) && (strcmp(param, "playnow") == 0)) { /* If mode parameter is -1 or 1, the index is relative to the history queue, otherwise to the Up Next queue */ - param = evhttp_find_header(dreq->query, "mode"); + param = evhttp_find_header(hreq->query, "mode"); if (param && ((strcmp(param, "-1") == 0) || (strcmp(param, "1") == 0))) { /* Play from history queue */ @@ -1255,16 +1199,16 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not start playback from history\n"); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } } else { DPRINTF(E_LOG, L_DACP, "Could not start playback from history\n"); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } } else @@ -1278,8 +1222,8 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not fetch item from queue: pos=%d, now playing=%d\n", pos, status.item_id); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } } } @@ -1290,8 +1234,8 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not fetch item from queue: pos=%d\n", pos); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } } @@ -1301,23 +1245,25 @@ dacp_reply_cue_play(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not start playback\n"); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } player_get_status(&status); - CHECK_ERR(L_DACP, evbuffer_expand(reply, 64)); + CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 64)); - dmap_add_container(reply, "cacr", 24); /* 8 + len */ - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "miid", status.id);/* 12 */ + dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "miid", status.id);/* 12 */ - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); + + return 0; } -static void -dacp_reply_cue_clear(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_cue_clear(struct httpd_request *hreq) { /* /cue?command=clear */ @@ -1325,49 +1271,51 @@ dacp_reply_cue_clear(struct evbuffer *reply, struct dacp_request *dreq) db_queue_clear(0); - CHECK_ERR(L_DACP, evbuffer_expand(reply, 64)); + CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 64)); - dmap_add_container(reply, "cacr", 24); /* 8 + len */ - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "miid", 0); /* 12 */ + dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "miid", 0); /* 12 */ - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); + + return 0; } -static void -dacp_reply_cue(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_cue(struct httpd_request *hreq) { const char *param; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "command"); + param = evhttp_find_header(hreq->query, "command"); if (!param) { DPRINTF(E_LOG, L_DACP, "No command in cue request\n"); - dmap_send_error(dreq->req, "cacr", "No command in cue request"); - return; + dmap_send_error(hreq->req, "cacr", "No command in cue request"); + return -1; } if (strcmp(param, "clear") == 0) - dacp_reply_cue_clear(reply, dreq); + return dacp_reply_cue_clear(hreq); else if (strcmp(param, "play") == 0) - dacp_reply_cue_play(reply, dreq); + return dacp_reply_cue_play(hreq); else { DPRINTF(E_LOG, L_DACP, "Unknown cue command %s\n", param); - dmap_send_error(dreq->req, "cacr", "Unknown command in cue request"); - return; + dmap_send_error(hreq->req, "cacr", "Unknown command in cue request"); + return -1; } } -static void -dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playspec(struct httpd_request *hreq) { struct player_status status; const char *param; @@ -1383,19 +1331,18 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) * With our DAAP implementation, container-spec is the playlist ID and container-item-spec/item-spec is the song ID */ - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; /* Check for shuffle */ - shuffle = evhttp_find_header(dreq->query, "dacp.shufflestate"); + shuffle = evhttp_find_header(hreq->query, "dacp.shufflestate"); /* Playlist ID */ - param = evhttp_find_header(dreq->query, "container-spec"); + param = evhttp_find_header(hreq->query, "container-spec"); if (!param) { DPRINTF(E_LOG, L_DACP, "No container-spec in playspec request\n"); - goto out_fail; } @@ -1403,7 +1350,6 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (!param) { DPRINTF(E_LOG, L_DACP, "Malformed container-spec parameter in playspec request\n"); - goto out_fail; } param++; @@ -1416,19 +1362,17 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Couldn't convert container-spec to an integer in playspec (%s)\n", param); - goto out_fail; } if (!shuffle) { /* Start song ID */ - if ((param = evhttp_find_header(dreq->query, "item-spec"))) + if ((param = evhttp_find_header(hreq->query, "item-spec"))) plid = 0; // This is a podcast/audiobook - just play a single item, not a playlist - else if (!(param = evhttp_find_header(dreq->query, "container-item-spec"))) + else if (!(param = evhttp_find_header(hreq->query, "container-item-spec"))) { DPRINTF(E_LOG, L_DACP, "No container-item-spec/item-spec in playspec request\n"); - goto out_fail; } @@ -1436,7 +1380,6 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (!param) { DPRINTF(E_LOG, L_DACP, "Malformed container-item-spec/item-spec parameter in playspec request\n"); - goto out_fail; } param++; @@ -1449,7 +1392,6 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Couldn't convert container-item-spec/item-spec to an integer in playspec (%s)\n", param); - goto out_fail; } } @@ -1473,7 +1415,6 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue from playlist %d\n", plid); - goto out_fail; } @@ -1496,42 +1437,45 @@ dacp_reply_playspec(struct evbuffer *reply, struct dacp_request *dreq) if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not start playback\n"); - goto out_fail; } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); - return; + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + return 0; out_fail: - httpd_send_error(dreq->req, 500, "Internal Server Error"); + httpd_send_error(hreq->req, 500, "Internal Server Error"); + + return -1; } -static void -dacp_reply_pause(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_pause(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; player_playback_pause(); /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playpause(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playpause(struct httpd_request *hreq) { struct player_status status; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; player_get_status(&status); if (status.status == PLAY_PLAYING) @@ -1545,31 +1489,33 @@ dacp_reply_playpause(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after pause\n"); - httpd_send_error(dreq->req, 500, "Internal Server Error"); - return; + httpd_send_error(hreq->req, 500, "Internal Server Error"); + return -1; } } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_nextitem(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_nextitem(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; ret = player_playback_next(); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player returned an error for nextitem\n"); - httpd_send_error(dreq->req, 500, "Internal Server Error"); - return; + httpd_send_error(hreq->req, 500, "Internal Server Error"); + return -1; } ret = player_playback_start(); @@ -1577,30 +1523,32 @@ dacp_reply_nextitem(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after nextitem\n"); - httpd_send_error(dreq->req, 500, "Internal Server Error"); - return; + httpd_send_error(hreq->req, 500, "Internal Server Error"); + return -1; } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_previtem(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_previtem(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; ret = player_playback_prev(); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player returned an error for previtem\n"); - httpd_send_error(dreq->req, 500, "Internal Server Error"); - return; + httpd_send_error(hreq->req, 500, "Internal Server Error"); + return -1; } ret = player_playback_start(); @@ -1608,61 +1556,69 @@ dacp_reply_previtem(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Player returned an error for start after previtem\n"); - httpd_send_error(dreq->req, 500, "Internal Server Error"); - return; + httpd_send_error(hreq->req, 500, "Internal Server Error"); + return -1; } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_beginff(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_beginff(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; /* TODO */ /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_beginrew(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_beginrew(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; /* TODO */ /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playresume(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playresume(struct httpd_request *hreq) { int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; /* TODO */ /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playqueuecontents(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueuecontents(struct httpd_request *hreq) { struct evbuffer *songlist; struct evbuffer *playlists; @@ -1680,14 +1636,14 @@ dacp_reply_playqueuecontents(struct evbuffer *reply, struct dacp_request *dreq) /* /ctrl-int/1/playqueue-contents?span=50&session-id=... */ - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n"); span = 50; /* Default */ - param = evhttp_find_header(dreq->query, "span"); + param = evhttp_find_header(hreq->query, "span"); if (param) { ret = safe_atoi32(param, &span); @@ -1696,7 +1652,7 @@ dacp_reply_playqueuecontents(struct evbuffer *reply, struct dacp_request *dreq) } CHECK_NULL(L_DACP, songlist = evbuffer_new()); - CHECK_ERR(L_DACP, evbuffer_expand(reply, 128)); + CHECK_ERR(L_DACP, evbuffer_expand(hreq->reply, 128)); player_get_status(&status); @@ -1789,41 +1745,43 @@ dacp_reply_playqueuecontents(struct evbuffer *reply, struct dacp_request *dreq) /* Final construction of reply */ playlist_length = evbuffer_get_length(playlists); - dmap_add_container(reply, "ceQR", 79 + playlist_length + songlist_length); /* size of entire container */ - dmap_add_int(reply, "mstt", 200); /* 12, dmap.status */ - dmap_add_int(reply, "mtco", abs(span)); /* 12 */ - dmap_add_int(reply, "mrco", count); /* 12 */ - dmap_add_char(reply, "ceQu", 0); /* 9 */ - dmap_add_container(reply, "mlcl", 8 + playlist_length + songlist_length); /* 8 */ - dmap_add_container(reply, "ceQS", playlist_length); /* 8 */ + dmap_add_container(hreq->reply, "ceQR", 79 + playlist_length + songlist_length); /* size of entire container */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12, dmap.status */ + dmap_add_int(hreq->reply, "mtco", abs(span)); /* 12 */ + dmap_add_int(hreq->reply, "mrco", count); /* 12 */ + dmap_add_char(hreq->reply, "ceQu", 0); /* 9 */ + dmap_add_container(hreq->reply, "mlcl", 8 + playlist_length + songlist_length); /* 8 */ + dmap_add_container(hreq->reply, "ceQS", playlist_length); /* 8 */ - CHECK_ERR(L_DACP, evbuffer_add_buffer(reply, playlists)); - CHECK_ERR(L_DACP, evbuffer_add_buffer(reply, songlist)); + CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, playlists)); + CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, songlist)); evbuffer_free(playlists); evbuffer_free(songlist); - dmap_add_char(reply, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */ - dmap_add_char(reply, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */ + dmap_add_char(hreq->reply, "apsm", status.shuffle); /* 9, daap.playlistshufflemode - not part of mlcl container */ + dmap_add_char(hreq->reply, "aprm", status.repeat); /* 9, daap.playlistrepeatmode - not part of mlcl container */ - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); - return; + return 0; error: DPRINTF(E_LOG, L_DACP, "Database error in dacp_reply_playqueuecontents\n"); evbuffer_free(songlist); - dmap_send_error(dreq->req, "ceQR", "Database error"); + dmap_send_error(hreq->req, "ceQR", "Database error"); + + return -1; } -static void -dacp_reply_playqueueedit_clear(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueueedit_clear(struct httpd_request *hreq) { const char *param; struct player_status status; - param = evhttp_find_header(dreq->query, "mode"); + param = evhttp_find_header(hreq->query, "mode"); /* * The mode parameter contains the playlist to be cleared. @@ -1838,15 +1796,17 @@ dacp_reply_playqueueedit_clear(struct evbuffer *reply, struct dacp_request *dreq db_queue_clear(status.item_id); } - dmap_add_container(reply, "cacr", 24); /* 8 + len */ - dmap_add_int(reply, "mstt", 200); /* 12 */ - dmap_add_int(reply, "miid", 0); /* 12 */ + dmap_add_container(hreq->reply, "cacr", 24); /* 8 + len */ + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ + dmap_add_int(hreq->reply, "miid", 0); /* 12 */ - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); + + return 0; } -static void -dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueueedit_add(struct httpd_request *hreq) { //?command=add&query='dmap.itemid:156'&sort=album&mode=3&session-id=100 // -> mode=3: add to playqueue position 0 (play next) @@ -1872,7 +1832,7 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) mode = 1; - param = evhttp_find_header(dreq->query, "mode"); + param = evhttp_find_header(hreq->query, "mode"); if (param) { ret = safe_atoi32(param, &mode); @@ -1880,8 +1840,8 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Invalid mode value in playqueue-edit request\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } } @@ -1894,16 +1854,16 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) if (mode == 2) player_shuffle_set(1); - editquery = evhttp_find_header(dreq->query, "query"); + editquery = evhttp_find_header(hreq->query, "query"); if (!editquery) { DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } - sort = evhttp_find_header(dreq->query, "sort"); + sort = evhttp_find_header(hreq->query, "sort"); // if sort param is missing and an album or artist is added to the queue, set sort to "album" if (!sort && (strstr(editquery, "daap.songalbumid:") || strstr(editquery, "daap.songartistid:"))) @@ -1912,9 +1872,9 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) } // only use queryfilter if mode is not equal 0 (add to up next), 3 (play next) or 5 (add to up next) - queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : evhttp_find_header(dreq->query, "queuefilter"); + queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : evhttp_find_header(hreq->query, "queuefilter"); - querymodifier = evhttp_find_header(dreq->query, "query-modifier"); + querymodifier = evhttp_find_header(hreq->query, "query-modifier"); if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); @@ -1928,8 +1888,8 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Invalid playlist id in request: %s\n", editquery); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid); @@ -1940,8 +1900,8 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } if (ret > 0) @@ -1973,16 +1933,18 @@ dacp_reply_playqueueedit_add(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not start playback\n"); - dmap_send_error(dreq->req, "cacr", "Playback failed to start"); - return; + dmap_send_error(hreq->req, "cacr", "Playback failed to start"); + return -1; } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playqueueedit_move(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueueedit_move(struct httpd_request *hreq) { /* * Handles the move command. @@ -1998,7 +1960,7 @@ dacp_reply_playqueueedit_move(struct evbuffer *reply, struct dacp_request *dreq) int src; int dst; - param = evhttp_find_header(dreq->query, "edit-params"); + param = evhttp_find_header(hreq->query, "edit-params"); if (param) { ret = safe_atoi32(strchr(param, ':') + 1, &src); @@ -2006,8 +1968,8 @@ dacp_reply_playqueueedit_move(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-from value in playqueue-edit request\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } ret = safe_atoi32(strchr(param, ',') + 1, &dst); @@ -2015,8 +1977,8 @@ dacp_reply_playqueueedit_move(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Invalid edit-params move-to value in playqueue-edit request\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } player_get_status(&status); @@ -2024,11 +1986,13 @@ dacp_reply_playqueueedit_move(struct evbuffer *reply, struct dacp_request *dreq) } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playqueueedit_remove(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueueedit_remove(struct httpd_request *hreq) { /* * Handles the remove command. @@ -2040,7 +2004,7 @@ dacp_reply_playqueueedit_remove(struct evbuffer *reply, struct dacp_request *dre int item_index; int ret; - param = evhttp_find_header(dreq->query, "items"); + param = evhttp_find_header(hreq->query, "items"); if (param) { ret = safe_atoi32(param, &item_index); @@ -2048,8 +2012,8 @@ dacp_reply_playqueueedit_remove(struct evbuffer *reply, struct dacp_request *dre { DPRINTF(E_LOG, L_DACP, "Invalid edit-params remove item value in playqueue-edit request\n"); - dmap_send_error(dreq->req, "cacr", "Invalid request"); - return; + dmap_send_error(hreq->req, "cacr", "Invalid request"); + return -1; } player_get_status(&status); @@ -2058,11 +2022,13 @@ dacp_reply_playqueueedit_remove(struct evbuffer *reply, struct dacp_request *dre } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_playqueueedit(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playqueueedit(struct httpd_request *hreq) { const char *param; int ret; @@ -2110,40 +2076,40 @@ dacp_reply_playqueueedit(struct evbuffer *reply, struct dacp_request *dreq) -> remove song on position 1 from the playqueue */ - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "command"); + param = evhttp_find_header(hreq->query, "command"); if (!param) { DPRINTF(E_LOG, L_DACP, "No command in playqueue-edit request\n"); - dmap_send_error(dreq->req, "cmst", "Invalid request"); - return; + dmap_send_error(hreq->req, "cmst", "Invalid request"); + return -1; } if (strcmp(param, "clear") == 0) - dacp_reply_playqueueedit_clear(reply, dreq); + return dacp_reply_playqueueedit_clear(hreq); else if (strcmp(param, "playnow") == 0) - dacp_reply_cue_play(reply, dreq); + return dacp_reply_cue_play(hreq); else if (strcmp(param, "add") == 0) - dacp_reply_playqueueedit_add(reply, dreq); + return dacp_reply_playqueueedit_add(hreq); else if (strcmp(param, "move") == 0) - dacp_reply_playqueueedit_move(reply, dreq); + return dacp_reply_playqueueedit_move(hreq); else if (strcmp(param, "remove") == 0) - dacp_reply_playqueueedit_remove(reply, dreq); + return dacp_reply_playqueueedit_remove(hreq); else { DPRINTF(E_LOG, L_DACP, "Unknown playqueue-edit command %s\n", param); - dmap_send_error(dreq->req, "cmst", "Invalid request"); - return; + dmap_send_error(hreq->req, "cmst", "Invalid request"); + return -1; } } -static void -dacp_reply_playstatusupdate(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_playstatusupdate(struct httpd_request *hreq) { struct dacp_update_request *ur; struct evhttp_connection *evcon; @@ -2151,17 +2117,17 @@ dacp_reply_playstatusupdate(struct evbuffer *reply, struct dacp_request *dreq) int reqd_rev; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "revision-number"); + param = evhttp_find_header(hreq->query, "revision-number"); if (!param) { DPRINTF(E_LOG, L_DACP, "Missing revision-number in update request\n"); - dmap_send_error(dreq->req, "cmst", "Invalid request"); - return; + dmap_send_error(hreq->req, "cmst", "Invalid request"); + return -1; } ret = safe_atoi32(param, &reqd_rev); @@ -2169,19 +2135,19 @@ dacp_reply_playstatusupdate(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Parameter revision-number not an integer\n"); - dmap_send_error(dreq->req, "cmst", "Invalid request"); - return; + dmap_send_error(hreq->req, "cmst", "Invalid request"); + return -1; } if ((reqd_rev == 0) || (reqd_rev == 1)) { - ret = make_playstatusupdate(reply); + ret = make_playstatusupdate(hreq->reply); if (ret < 0) - httpd_send_error(dreq->req, 500, "Internal Server Error"); + httpd_send_error(hreq->req, 500, "Internal Server Error"); else - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); - return; + return ret; } /* Else, just let the request hang until we have changes to push back */ @@ -2190,11 +2156,11 @@ dacp_reply_playstatusupdate(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Out of memory for update request\n"); - dmap_send_error(dreq->req, "cmst", "Out of memory"); - return; + dmap_send_error(hreq->req, "cmst", "Out of memory"); + return -1; } - ur->req = dreq->req; + ur->req = hreq->req; ur->next = update_requests; update_requests = ur; @@ -2202,13 +2168,15 @@ dacp_reply_playstatusupdate(struct evbuffer *reply, struct dacp_request *dreq) /* If the connection fails before we have an update to push out * to the client, we need to know. */ - evcon = evhttp_request_get_connection(dreq->req); + evcon = evhttp_request_get_connection(hreq->req); if (evcon) evhttp_connection_set_closecb(evcon, update_fail_cb, ur); + + return 0; } -static void -dacp_reply_nowplayingartwork(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_nowplayingartwork(struct httpd_request *hreq) { char clen[32]; struct evkeyvalq *headers; @@ -2220,52 +2188,44 @@ dacp_reply_nowplayingartwork(struct evbuffer *reply, struct dacp_request *dreq) int max_h; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "mw"); + param = evhttp_find_header(hreq->query, "mw"); if (!param) { DPRINTF(E_LOG, L_DACP, "Request for artwork without mw parameter\n"); - - httpd_send_error(dreq->req, HTTP_BADREQUEST, "Bad Request"); - return; + goto error; } ret = safe_atoi32(param, &max_w); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not convert mw parameter to integer\n"); - - httpd_send_error(dreq->req, HTTP_BADREQUEST, "Bad Request"); - return; + goto error; } - param = evhttp_find_header(dreq->query, "mh"); + param = evhttp_find_header(hreq->query, "mh"); if (!param) { DPRINTF(E_LOG, L_DACP, "Request for artwork without mh parameter\n"); - - httpd_send_error(dreq->req, HTTP_BADREQUEST, "Bad Request"); - return; + goto error; } ret = safe_atoi32(param, &max_h); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not convert mh parameter to integer\n"); - - httpd_send_error(dreq->req, HTTP_BADREQUEST, "Bad Request"); - return; + goto error; } ret = player_now_playing(&id); if (ret < 0) goto no_artwork; - ret = artwork_get_item(reply, id, max_w, max_h); - len = evbuffer_get_length(reply); + ret = artwork_get_item(hreq->reply, id, max_w, max_h); + len = evbuffer_get_length(hreq->reply); switch (ret) { @@ -2279,26 +2239,31 @@ dacp_reply_nowplayingartwork(struct evbuffer *reply, struct dacp_request *dreq) default: if (len > 0) - evbuffer_drain(reply, len); + evbuffer_drain(hreq->reply, len); goto no_artwork; } - headers = evhttp_request_get_output_headers(dreq->req); + headers = evhttp_request_get_output_headers(hreq->req); evhttp_remove_header(headers, "Content-Type"); evhttp_add_header(headers, "Content-Type", ctype); snprintf(clen, sizeof(clen), "%ld", (long)len); evhttp_add_header(headers, "Content-Length", clen); - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, HTTPD_SEND_NO_GZIP); - return; + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); + return 0; no_artwork: - httpd_send_error(dreq->req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(hreq->req, HTTP_NOTFOUND, "Not Found"); + return 0; + + error: + httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request"); + return -1; } -static void -dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_getproperty(struct httpd_request *hreq) { struct player_status status; const struct dacp_prop_map *dpm; @@ -2311,17 +2276,17 @@ dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) size_t len; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "properties"); + param = evhttp_find_header(hreq->query, "properties"); if (!param) { DPRINTF(E_WARN, L_DACP, "Invalid DACP getproperty request, no properties\n"); - dmap_send_error(dreq->req, "cmgt", "Invalid request"); - return; + dmap_send_error(hreq->req, "cmgt", "Invalid request"); + return -1; } propstr = strdup(param); @@ -2329,8 +2294,8 @@ dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not duplicate properties parameter; out of memory\n"); - dmap_send_error(dreq->req, "cmgt", "Out of memory"); - return; + dmap_send_error(hreq->req, "cmgt", "Out of memory"); + return -1; } proplist = evbuffer_new(); @@ -2338,7 +2303,7 @@ dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not allocate evbuffer for properties list\n"); - dmap_send_error(dreq->req, "cmgt", "Out of memory"); + dmap_send_error(hreq->req, "cmgt", "Out of memory"); goto out_free_propstr; } @@ -2351,7 +2316,7 @@ dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) { DPRINTF(E_LOG, L_DACP, "Could not fetch queue_item for item-id %d\n", status.item_id); - dmap_send_error(dreq->req, "cmgt", "Server error"); + dmap_send_error(hreq->req, "cmgt", "Server error"); goto out_free_proplist; } } @@ -2379,34 +2344,36 @@ dacp_reply_getproperty(struct evbuffer *reply, struct dacp_request *dreq) free_queue_item(queue_item, 0); len = evbuffer_get_length(proplist); - dmap_add_container(reply, "cmgt", 12 + len); - dmap_add_int(reply, "mstt", 200); /* 12 */ + dmap_add_container(hreq->reply, "cmgt", 12 + len); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ - CHECK_ERR(L_DACP, evbuffer_add_buffer(reply, proplist)); + CHECK_ERR(L_DACP, evbuffer_add_buffer(hreq->reply, proplist)); evbuffer_free(proplist); - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); - return; + return 0; out_free_proplist: evbuffer_free(proplist); out_free_propstr: free(propstr); + + return -1; } -static void -dacp_reply_setproperty(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_setproperty(struct httpd_request *hreq) { const struct dacp_prop_map *dpm; struct evkeyval *param; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; /* Known properties: * dacp.shufflestate 0/1 @@ -2417,7 +2384,7 @@ dacp_reply_setproperty(struct evbuffer *reply, struct dacp_request *dreq) /* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */ - TAILQ_FOREACH(param, dreq->query, next) + TAILQ_FOREACH(param, hreq->query, next) { dpm = dacp_find_prop(param->key, strlen(param->key)); @@ -2428,43 +2395,47 @@ dacp_reply_setproperty(struct evbuffer *reply, struct dacp_request *dreq) } if (dpm->propset) - dpm->propset(param->value, dreq->query); + dpm->propset(param->value, hreq->query); else DPRINTF(E_WARN, L_DACP, "No setter method for DACP property %s\n", dpm->desc); } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static void -dacp_reply_getspeakers(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_getspeakers(struct httpd_request *hreq) { struct evbuffer *spklist; size_t len; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; CHECK_NULL(L_DACP, spklist = evbuffer_new()); player_speaker_enumerate(speaker_enum_cb, spklist); len = evbuffer_get_length(spklist); - dmap_add_container(reply, "casp", 12 + len); - dmap_add_int(reply, "mstt", 200); /* 12 */ + dmap_add_container(hreq->reply, "casp", 12 + len); + dmap_add_int(hreq->reply, "mstt", 200); /* 12 */ - evbuffer_add_buffer(reply, spklist); + evbuffer_add_buffer(hreq->reply, spklist); evbuffer_free(spklist); - httpd_send_reply(dreq->req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(hreq->req, HTTP_OK, "OK", hreq->reply, 0); + + return 0; } -static void -dacp_reply_setspeakers(struct evbuffer *reply, struct dacp_request *dreq) +static int +dacp_reply_setspeakers(struct httpd_request *hreq) { const char *param; const char *ptr; @@ -2473,17 +2444,17 @@ dacp_reply_setspeakers(struct evbuffer *reply, struct dacp_request *dreq) int i; int ret; - ret = dacp_request_authorize(dreq); + ret = dacp_request_authorize(hreq); if (ret < 0) - return; + return -1; - param = evhttp_find_header(dreq->query, "speaker-id"); + param = evhttp_find_header(hreq->query, "speaker-id"); if (!param) { DPRINTF(E_LOG, L_DACP, "Missing speaker-id parameter in DACP setspeakers request\n"); - httpd_send_error(dreq->req, HTTP_BADREQUEST, "Bad Request"); - return; + httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request"); + return -1; } if (strlen(param) == 0) @@ -2497,14 +2468,7 @@ dacp_reply_setspeakers(struct evbuffer *reply, struct dacp_request *dreq) while ((ptr = strchr(ptr + 1, ','))) nspk++; - ids = calloc((nspk + 1), sizeof(uint64_t)); - if (!ids) - { - DPRINTF(E_LOG, L_DACP, "Out of memory for speaker ids\n"); - - httpd_send_error(dreq->req, HTTP_SERVUNAVAIL, "Internal Server Error"); - return; - } + CHECK_NULL(L_DACP, ids = calloc((nspk + 1), sizeof(uint64_t))); param--; i = 1; @@ -2547,18 +2511,20 @@ dacp_reply_setspeakers(struct evbuffer *reply, struct dacp_request *dreq) /* Password problem */ if (ret == -2) - httpd_send_error(dreq->req, 902, ""); + httpd_send_error(hreq->req, 902, ""); else - httpd_send_error(dreq->req, 500, "Internal Server Error"); + httpd_send_error(hreq->req, 500, "Internal Server Error"); - return; + return -1; } /* 204 No Content is the canonical reply */ - httpd_send_reply(dreq->req, HTTP_NOCONTENT, "No Content", reply, HTTPD_SEND_NO_GZIP); + httpd_send_reply(hreq->req, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); + + return 0; } -static struct uri_map dacp_handlers[] = +static struct httpd_uri_map dacp_handlers[] = { { .regexp = "^/ctrl-int$", @@ -2644,15 +2610,16 @@ static struct uri_map dacp_handlers[] = void dacp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) { - struct dacp_request *dreq; - struct evbuffer *reply; + struct httpd_request *hreq; struct evkeyvalq *headers; DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", uri_parsed->uri); - dreq = dacp_request_parse(req, uri_parsed); - if (!dreq) + hreq = httpd_request_parse(req, uri_parsed, NULL, dacp_handlers); + if (!hreq) { + DPRINTF(E_LOG, L_DACP, "Unrecognized path '%s' in DACP request: '%s'\n", uri_parsed->path, uri_parsed->uri); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -2662,12 +2629,12 @@ dacp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) /* Content-Type for all DACP replies; can be overriden as needed */ evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); - CHECK_NULL(L_DACP, reply = evbuffer_new()); + CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new()); - dreq->handler(reply, dreq); + hreq->handler(hreq); - evbuffer_free(reply); - free(dreq); + evbuffer_free(hreq->reply); + free(hreq); } int diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index b39d9918..c5465ee9 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -49,26 +49,6 @@ # include "spotify.h" #endif -struct json_request { - // The parsed request URI given to us by httpd.c - struct httpd_uri_parsed *uri_parsed; - // Shortcut to &uri_parsed->ev_query - struct evkeyvalq *query; - // http request struct - struct evhttp_request *req; - // A pointer to the handler that will process the request - int (*handler)(struct evbuffer *reply, struct json_request *jreq); -}; - -struct uri_map -{ - regex_t preg; - char *regexp; - int (*handler)(struct evbuffer *reply, struct json_request *jreq); -}; - -/* Forward declaration of handlers */ -static struct uri_map adm_handlers[]; /* -------------------------------- HELPERS --------------------------------- */ @@ -147,44 +127,6 @@ pairing_get(struct evbuffer *evbuf) return 0; } -static struct json_request * -json_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) -{ - struct json_request *jreq; - int ret; - int i; - - CHECK_NULL(L_WEB, jreq = calloc(1, sizeof(struct json_request))); - - jreq->req = req; - jreq->uri_parsed = uri_parsed; - jreq->query = &(uri_parsed->ev_query); - - // Find a handler for the path - for (i = 0; adm_handlers[i].handler; i++) - { - ret = regexec(&adm_handlers[i].preg, uri_parsed->path, 0, NULL, 0); - if (ret == 0) - { - jreq->handler = adm_handlers[i].handler; - break; - } - } - - if (!jreq->handler) - { - DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in JSON api request: '%s'\n", uri_parsed->path, uri_parsed->uri); - goto error; - } - - return jreq; - - error: - free(jreq); - - return NULL; -} - /* --------------------------- REPLY HANDLERS ------------------------------- */ @@ -199,7 +141,7 @@ json_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_pars * } */ static int -jsonapi_reply_config(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_config(struct httpd_request *hreq) { json_object *jreply; json_object *buildopts; @@ -229,7 +171,7 @@ jsonapi_reply_config(struct evbuffer *reply, struct json_request *jreq) } json_object_object_add(jreply, "buildoptions", buildopts); - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -250,7 +192,7 @@ jsonapi_reply_config(struct evbuffer *reply, struct json_request *jreq) *} */ static int -jsonapi_reply_library(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_library(struct httpd_request *hreq) { struct query_params qp; struct filecount_info fci; @@ -276,9 +218,7 @@ jsonapi_reply_library(struct evbuffer *reply, struct json_request *jreq) is_scanning = library_is_scanning(); - // Build json response - CHECK_NULL(L_WEB, jreply = json_object_new_object()); json_object_object_add(jreply, "artists", json_object_new_int(artists)); @@ -287,7 +227,7 @@ jsonapi_reply_library(struct evbuffer *reply, struct json_request *jreq) json_object_object_add(jreply, "db_playtime", json_object_new_int64((fci.length / 1000))); json_object_object_add(jreply, "updating", json_object_new_boolean(is_scanning)); - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -298,7 +238,7 @@ jsonapi_reply_library(struct evbuffer *reply, struct json_request *jreq) * Endpoint to trigger a library rescan */ static int -jsonapi_reply_update(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_update(struct httpd_request *hreq) { library_rescan(); return 0; @@ -315,7 +255,7 @@ jsonapi_reply_update(struct evbuffer *reply, struct json_request *jreq) * } */ static int -jsonapi_reply_spotify(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_spotify(struct httpd_request *hreq) { json_object *jreply; @@ -354,7 +294,7 @@ jsonapi_reply_spotify(struct evbuffer *reply, struct json_request *jreq) json_object_object_add(jreply, "enabled", json_object_new_boolean(false)); #endif - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -362,7 +302,7 @@ jsonapi_reply_spotify(struct evbuffer *reply, struct json_request *jreq) } static int -jsonapi_reply_spotify_login(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_spotify_login(struct httpd_request *hreq) { #ifdef HAVE_SPOTIFY_H struct evbuffer *in_evbuf; @@ -376,7 +316,7 @@ jsonapi_reply_spotify_login(struct evbuffer *reply, struct json_request *jreq) DPRINTF(E_DBG, L_WEB, "Received Spotify login request\n"); - in_evbuf = evhttp_request_get_input_buffer(jreq->req); + in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(in_evbuf); if (!request) @@ -418,7 +358,7 @@ jsonapi_reply_spotify_login(struct evbuffer *reply, struct json_request *jreq) json_object_object_add(jreply, "errors", errors); } - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -436,18 +376,18 @@ jsonapi_reply_spotify_login(struct evbuffer *reply, struct json_request *jreq) * If request is a POST request, tries to pair the active remote with the given pin. */ static int -jsonapi_reply_pairing(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_pairing(struct httpd_request *hreq) { - if (evhttp_request_get_command(jreq->req) == EVHTTP_REQ_POST) + if (evhttp_request_get_command(hreq->req) == EVHTTP_REQ_POST) { - return pairing_kickoff(jreq->req); + return pairing_kickoff(hreq->req); } - return pairing_get(reply); + return pairing_get(hreq->reply); } static int -jsonapi_reply_lastfm(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_lastfm(struct httpd_request *hreq) { json_object *jreply; bool enabled = false; @@ -463,7 +403,7 @@ jsonapi_reply_lastfm(struct evbuffer *reply, struct json_request *jreq) json_object_object_add(jreply, "enabled", json_object_new_boolean(enabled)); json_object_object_add(jreply, "scrobbling_enabled", json_object_new_boolean(scrobbling_enabled)); - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -474,7 +414,7 @@ jsonapi_reply_lastfm(struct evbuffer *reply, struct json_request *jreq) * Endpoint to log into LastFM */ static int -jsonapi_reply_lastfm_login(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_lastfm_login(struct httpd_request *hreq) { #ifdef LASTFM struct evbuffer *in_evbuf; @@ -488,7 +428,7 @@ jsonapi_reply_lastfm_login(struct evbuffer *reply, struct json_request *jreq) DPRINTF(E_DBG, L_WEB, "Received LastFM login request\n"); - in_evbuf = evhttp_request_get_input_buffer(jreq->req); + in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(in_evbuf); if (!request) { @@ -532,7 +472,7 @@ jsonapi_reply_lastfm_login(struct evbuffer *reply, struct json_request *jreq) json_object_object_add(jreply, "errors", errors); } - CHECK_ERRNO(L_WEB, evbuffer_add_printf(reply, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); @@ -544,7 +484,7 @@ jsonapi_reply_lastfm_login(struct evbuffer *reply, struct json_request *jreq) } static int -jsonapi_reply_lastfm_logout(struct evbuffer *reply, struct json_request *jreq) +jsonapi_reply_lastfm_logout(struct httpd_request *hreq) { #ifdef LASTFM lastfm_logout(); @@ -552,7 +492,7 @@ jsonapi_reply_lastfm_logout(struct evbuffer *reply, struct json_request *jreq) return 0; } -static struct uri_map adm_handlers[] = +static struct httpd_uri_map adm_handlers[] = { { .regexp = "^/api/config", .handler = jsonapi_reply_config }, { .regexp = "^/api/library", .handler = jsonapi_reply_library }, @@ -572,8 +512,7 @@ static struct uri_map adm_handlers[] = void jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) { - struct json_request *jreq; - struct evbuffer *reply; + struct httpd_request *hreq; struct evkeyvalq *headers; int ret; @@ -585,9 +524,11 @@ jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) return; } - jreq = json_request_parse(req, uri_parsed); - if (!jreq) + hreq = httpd_request_parse(req, uri_parsed, NULL, adm_handlers); + if (!hreq) { + DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in JSON api request: '%s'\n", uri_parsed->path, uri_parsed->uri); + httpd_send_error(req, HTTP_BADREQUEST, "Bad Request"); return; } @@ -595,9 +536,9 @@ jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) headers = evhttp_request_get_output_headers(req); evhttp_add_header(headers, "DAAP-Server", "forked-daapd/" VERSION); - CHECK_NULL(L_WEB, reply = evbuffer_new()); + CHECK_NULL(L_WEB, hreq->reply = evbuffer_new()); - ret = jreq->handler(reply, jreq); + ret = hreq->handler(hreq); if (ret < 0) { httpd_send_error(req, 500, "Internal Server Error"); @@ -606,11 +547,11 @@ jsonapi_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) evhttp_add_header(headers, "Content-Type", "application/json"); - httpd_send_reply(req, HTTP_OK, "OK", reply, 0); + httpd_send_reply(req, HTTP_OK, "OK", hreq->reply, 0); error: - evbuffer_free(reply); - free(jreq); + evbuffer_free(hreq->reply); + free(hreq); } int @@ -654,4 +595,3 @@ jsonapi_deinit(void) for (i = 0; adm_handlers[i].handler; i++) regfree(&adm_handlers[i].preg); } - diff --git a/src/httpd_oauth.c b/src/httpd_oauth.c index 931694ad..8fea8113 100644 --- a/src/httpd_oauth.c +++ b/src/httpd_oauth.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -37,72 +36,12 @@ # include "spotify.h" #endif -struct oauth_request { - // The parsed request URI given to us by httpd.c - struct httpd_uri_parsed *uri_parsed; - // Shortcut to &uri_parsed->ev_query - struct evkeyvalq *query; - // http request struct - struct evhttp_request *req; - // A pointer to the handler that will process the request - void (*handler)(struct oauth_request *oreq); -}; - -struct uri_map { - regex_t preg; - char *regexp; - void (*handler)(struct oauth_request *oreq); -}; - -/* Forward declaration of handlers */ -static struct uri_map oauth_handlers[]; - - -/* ------------------------------- HELPERS ---------------------------------- */ - -static struct oauth_request * -oauth_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) -{ - struct oauth_request *oreq; - int i; - int ret; - - CHECK_NULL(L_WEB, oreq = calloc(1, sizeof(struct oauth_request))); - - oreq->req = req; - oreq->uri_parsed = uri_parsed; - oreq->query = &(uri_parsed->ev_query); - - for (i = 0; oauth_handlers[i].handler; i++) - { - ret = regexec(&oauth_handlers[i].preg, uri_parsed->path, 0, NULL, 0); - if (ret == 0) - { - oreq->handler = oauth_handlers[i].handler; - break; - } - } - - if (!oreq->handler) - { - DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in OAuth request: '%s'\n", uri_parsed->path, uri_parsed->uri); - goto error; - } - - return oreq; - - error: - free(oreq); - - return NULL; -} - /* --------------------------- REPLY HANDLERS ------------------------------- */ #ifdef HAVE_SPOTIFY_H -static void -oauth_reply_spotify(struct oauth_request *oreq) +static int +oauth_reply_spotify(struct httpd_request *hreq) { char redirect_uri[256]; char *errmsg; @@ -112,28 +51,32 @@ oauth_reply_spotify(struct oauth_request *oreq) httpd_port = cfg_getint(cfg_getsec(cfg, "library"), "port"); snprintf(redirect_uri, sizeof(redirect_uri), "http://forked-daapd.local:%d/oauth/spotify", httpd_port); - ret = spotify_oauth_callback(oreq->query, redirect_uri, &errmsg); + ret = spotify_oauth_callback(hreq->query, redirect_uri, &errmsg); if (ret < 0) { - DPRINTF(E_LOG, L_WEB, "Could not parse Spotify OAuth callback: '%s'\n", oreq->uri_parsed->uri); - httpd_send_error(oreq->req, HTTP_INTERNAL, errmsg); + DPRINTF(E_LOG, L_WEB, "Could not parse Spotify OAuth callback: '%s'\n", hreq->uri_parsed->uri); + httpd_send_error(hreq->req, HTTP_INTERNAL, errmsg); free(errmsg); - return; + return -1; } - httpd_redirect_to_admin(oreq->req); + httpd_redirect_to_admin(hreq->req); + + return 0; } #else -static void -oauth_reply_spotify(struct oauth_request *oreq) +static int +oauth_reply_spotify(struct httpd_request *hreq) { DPRINTF(E_LOG, L_HTTPD, "This version of forked-daapd was built without support for Spotify\n"); - httpd_send_error(oreq->req, HTTP_NOTFOUND, "This version of forked-daapd was built without support for Spotify"); + httpd_send_error(hreq->req, HTTP_NOTFOUND, "This version of forked-daapd was built without support for Spotify"); + + return -1; } #endif -static struct uri_map oauth_handlers[] = +static struct httpd_uri_map oauth_handlers[] = { { .regexp = "^/oauth/spotify$", @@ -151,20 +94,22 @@ static struct uri_map oauth_handlers[] = void oauth_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) { - struct oauth_request *oreq; + struct httpd_request *hreq; DPRINTF(E_LOG, L_WEB, "OAuth request: '%s'\n", uri_parsed->uri); - oreq = oauth_request_parse(req, uri_parsed); - if (!oreq) + hreq = httpd_request_parse(req, uri_parsed, NULL, oauth_handlers); + if (!hreq) { + DPRINTF(E_LOG, L_WEB, "Unrecognized path '%s' in OAuth request: '%s'\n", uri_parsed->path, uri_parsed->uri); + httpd_send_error(req, HTTP_NOTFOUND, NULL); return; } - oreq->handler(oreq); + hreq->handler(hreq); - free(oreq); + free(hreq); } int diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index bb495fc0..d1951625 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -52,24 +51,6 @@ #define F_DETAILED (1 << 3) #define F_ALWAYS (F_FULL | F_BROWSE | F_ID | F_DETAILED) -// Will be filled out by rsp_request_parse() -struct rsp_request { - // The parsed request URI given to us by httpd.c - struct httpd_uri_parsed *uri_parsed; - // Shortcut to &uri_parsed->ev_query - struct evkeyvalq *query; - // http request struct - struct evhttp_request *req; - // A pointer to the handler that will process the request - void (*handler)(struct rsp_request *rreq); -}; - -struct uri_map { - regex_t preg; - char *regexp; - void (*handler)(struct rsp_request *rreq); -}; - struct field_map { char *field; size_t offset; @@ -133,9 +114,6 @@ static const struct field_map rsp_fields[] = { NULL, 0, 0 } }; -/* Forward declaration of handlers */ -static struct uri_map rsp_handlers[]; - /* -------------------------------- HELPERS --------------------------------- */ @@ -226,31 +204,31 @@ rsp_send_error(struct evhttp_request *req, char *errmsg) } static int -query_params_set(struct query_params *qp, struct rsp_request *rreq) +query_params_set(struct query_params *qp, struct httpd_request *hreq) { const char *param; int ret; qp->offset = 0; - param = evhttp_find_header(rreq->query, "offset"); + param = evhttp_find_header(hreq->query, "offset"); if (param) { ret = safe_atoi32(param, &qp->offset); if (ret < 0) { - rsp_send_error(rreq->req, "Invalid offset"); + rsp_send_error(hreq->req, "Invalid offset"); return -1; } } qp->limit = 0; - param = evhttp_find_header(rreq->query, "limit"); + param = evhttp_find_header(hreq->query, "limit"); if (param) { ret = safe_atoi32(param, &qp->limit); if (ret < 0) { - rsp_send_error(rreq->req, "Invalid limit"); + rsp_send_error(hreq->req, "Invalid limit"); return -1; } } @@ -262,7 +240,7 @@ query_params_set(struct query_params *qp, struct rsp_request *rreq) qp->sort = S_NONE; - param = evhttp_find_header(rreq->query, "query"); + param = evhttp_find_header(hreq->query, "query"); if (param) { DPRINTF(E_DBG, L_RSP, "RSP browse query filter: %s\n", param); @@ -300,46 +278,8 @@ rsp_send_reply(struct evhttp_request *req, mxml_node_t *reply) evbuffer_free(evbuf); } -static struct rsp_request * -rsp_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) -{ - struct rsp_request *rreq; - int ret; - int i; - - CHECK_NULL(L_DAAP, rreq = calloc(1, sizeof(struct rsp_request))); - - rreq->req = req; - rreq->uri_parsed = uri_parsed; - rreq->query = &(uri_parsed->ev_query); - - // Find a handler for the path - for (i = 0; rsp_handlers[i].handler; i++) - { - ret = regexec(&rsp_handlers[i].preg, uri_parsed->path, 0, NULL, 0); - if (ret == 0) - { - rreq->handler = rsp_handlers[i].handler; - break; - } - } - - if (!rreq->handler) - { - DPRINTF(E_LOG, L_RSP, "Unrecognized path '%s' in RSP request: '%s'\n", uri_parsed->path, uri_parsed->uri); - goto error; - } - - return rreq; - - error: - free(rreq); - - return NULL; -} - static int -rsp_request_authorize(struct rsp_request *rreq) +rsp_request_authorize(struct httpd_request *hreq) { char *passwd; int ret; @@ -354,7 +294,7 @@ rsp_request_authorize(struct rsp_request *rreq) DPRINTF(E_DBG, L_RSP, "Checking authentication for library\n"); // We don't care about the username - ret = httpd_basic_auth(rreq->req, NULL, passwd, cfg_getstr(cfg_getsec(cfg, "library"), "name")); + ret = httpd_basic_auth(hreq->req, NULL, passwd, cfg_getstr(cfg_getsec(cfg, "library"), "name")); if (ret != 0) { DPRINTF(E_LOG, L_RSP, "Unsuccessful library authentication\n"); @@ -367,8 +307,8 @@ rsp_request_authorize(struct rsp_request *rreq) /* --------------------------- REPLY HANDLERS ------------------------------- */ -static void -rsp_reply_info(struct rsp_request *rreq) +static int +rsp_reply_info(struct httpd_request *hreq) { mxml_node_t *reply; mxml_node_t *status; @@ -418,11 +358,13 @@ rsp_reply_info(struct rsp_request *rreq) node = mxmlNewElement(info, "name"); mxmlNewText(node, 0, library); - rsp_send_reply(rreq->req, reply); + rsp_send_reply(hreq->req, reply); + + return 0; } -static void -rsp_reply_db(struct rsp_request *rreq) +static int +rsp_reply_db(struct httpd_request *hreq) { struct query_params qp; struct db_playlist_info dbpli; @@ -445,8 +387,8 @@ rsp_reply_db(struct rsp_request *rreq) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); - rsp_send_error(rreq->req, "Could not start query"); - return; + rsp_send_error(hreq->req, "Could not start query"); + return -1; } /* We'd use mxmlNewXML(), but then we can't put any attributes @@ -495,8 +437,8 @@ rsp_reply_db(struct rsp_request *rreq) mxmlDelete(reply); db_query_end(&qp); - rsp_send_error(rreq->req, "Error fetching query results"); - return; + rsp_send_error(hreq->req, "Error fetching query results"); + return -1; } /* HACK @@ -509,11 +451,13 @@ rsp_reply_db(struct rsp_request *rreq) db_query_end(&qp); - rsp_send_reply(rreq->req, reply); + rsp_send_reply(hreq->req, reply); + + return 0; } -static void -rsp_reply_playlist(struct rsp_request *rreq) +static int +rsp_reply_playlist(struct httpd_request *hreq) { struct query_params qp; struct db_media_file_info dbmfi; @@ -536,11 +480,11 @@ rsp_reply_playlist(struct rsp_request *rreq) memset(&qp, 0, sizeof(struct query_params)); - ret = safe_atoi32(rreq->uri_parsed->path_parts[2], &qp.id); + ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &qp.id); if (ret < 0) { - rsp_send_error(rreq->req, "Invalid playlist ID"); - return; + rsp_send_error(hreq->req, "Invalid playlist ID"); + return -1; } if (qp.id == 0) @@ -549,7 +493,7 @@ rsp_reply_playlist(struct rsp_request *rreq) qp.type = Q_PLITEMS; mode = F_FULL; - param = evhttp_find_header(rreq->query, "type"); + param = evhttp_find_header(hreq->query, "type"); if (param) { if (strcasecmp(param, "full") == 0) @@ -564,20 +508,20 @@ rsp_reply_playlist(struct rsp_request *rreq) DPRINTF(E_LOG, L_RSP, "Unknown browse mode %s\n", param); } - ret = query_params_set(&qp, rreq); + ret = query_params_set(&qp, hreq); if (ret < 0) - return; + return -1; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); - rsp_send_error(rreq->req, "Could not start query"); + rsp_send_error(hreq->req, "Could not start query"); if (qp.filter) free(qp.filter); - return; + return -1; } if (qp.offset > qp.results) @@ -612,7 +556,7 @@ rsp_reply_playlist(struct rsp_request *rreq) /* Items block (all items) */ while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { - headers = evhttp_request_get_input_headers(rreq->req); + headers = evhttp_request_get_input_headers(hreq->req); ua = evhttp_find_header(headers, "User-Agent"); client_codecs = evhttp_find_header(headers, "Accept-Codecs"); @@ -683,8 +627,8 @@ rsp_reply_playlist(struct rsp_request *rreq) mxmlDelete(reply); db_query_end(&qp); - rsp_send_error(rreq->req, "Error fetching query results"); - return; + rsp_send_error(hreq->req, "Error fetching query results"); + return -1; } /* HACK @@ -697,11 +641,13 @@ rsp_reply_playlist(struct rsp_request *rreq) db_query_end(&qp); - rsp_send_reply(rreq->req, reply); + rsp_send_reply(hreq->req, reply); + + return 0; } -static void -rsp_reply_browse(struct rsp_request *rreq) +static int +rsp_reply_browse(struct httpd_request *hreq) { struct query_params qp; char *browse_item; @@ -714,43 +660,43 @@ rsp_reply_browse(struct rsp_request *rreq) memset(&qp, 0, sizeof(struct query_params)); - if (strcmp(rreq->uri_parsed->path_parts[3], "artist") == 0) + if (strcmp(hreq->uri_parsed->path_parts[3], "artist") == 0) qp.type = Q_BROWSE_ARTISTS; - else if (strcmp(rreq->uri_parsed->path_parts[3], "genre") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "genre") == 0) qp.type = Q_BROWSE_GENRES; - else if (strcmp(rreq->uri_parsed->path_parts[3], "album") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "album") == 0) qp.type = Q_BROWSE_ALBUMS; - else if (strcmp(rreq->uri_parsed->path_parts[3], "composer") == 0) + else if (strcmp(hreq->uri_parsed->path_parts[3], "composer") == 0) qp.type = Q_BROWSE_COMPOSERS; else { - DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", rreq->uri_parsed->path_parts[3]); + DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", hreq->uri_parsed->path_parts[3]); - rsp_send_error(rreq->req, "Unsupported browse type"); - return; + rsp_send_error(hreq->req, "Unsupported browse type"); + return -1; } - ret = safe_atoi32(rreq->uri_parsed->path_parts[2], &qp.id); + ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &qp.id); if (ret < 0) { - rsp_send_error(rreq->req, "Invalid playlist ID"); - return; + rsp_send_error(hreq->req, "Invalid playlist ID"); + return -1; } - ret = query_params_set(&qp, rreq); + ret = query_params_set(&qp, hreq); if (ret < 0) - return; + return -1; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); - rsp_send_error(rreq->req, "Could not start query"); + rsp_send_error(hreq->req, "Could not start query"); if (qp.filter) free(qp.filter); - return; + return -1; } if (qp.offset > qp.results) @@ -798,8 +744,8 @@ rsp_reply_browse(struct rsp_request *rreq) mxmlDelete(reply); db_query_end(&qp); - rsp_send_error(rreq->req, "Error fetching query results"); - return; + rsp_send_error(hreq->req, "Error fetching query results"); + return -1; } /* HACK @@ -812,24 +758,31 @@ rsp_reply_browse(struct rsp_request *rreq) db_query_end(&qp); - rsp_send_reply(rreq->req, reply); + rsp_send_reply(hreq->req, reply); + + return 0; } -static void -rsp_stream(struct rsp_request *rreq) +static int +rsp_stream(struct httpd_request *hreq) { int id; int ret; - ret = safe_atoi32(rreq->uri_parsed->path_parts[2], &id); + ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &id); if (ret < 0) - httpd_send_error(rreq->req, HTTP_BADREQUEST, "Bad Request"); - else - httpd_stream_file(rreq->req, id); + { + httpd_send_error(hreq->req, HTTP_BADREQUEST, "Bad Request"); + return -1; + } + + httpd_stream_file(hreq->req, id); + + return 0; } -static struct uri_map rsp_handlers[] = +static struct httpd_uri_map rsp_handlers[] = { { .regexp = "^/rsp/info$", @@ -863,29 +816,31 @@ static struct uri_map rsp_handlers[] = void rsp_request(struct evhttp_request *req, struct httpd_uri_parsed *uri_parsed) { - struct rsp_request *rreq; + struct httpd_request *hreq; int ret; DPRINTF(E_DBG, L_RSP, "RSP request: '%s'\n", uri_parsed->uri); - rreq = rsp_request_parse(req, uri_parsed); - if (!rreq) + hreq = httpd_request_parse(req, uri_parsed, NULL, rsp_handlers); + if (!hreq) { + DPRINTF(E_LOG, L_RSP, "Unrecognized path '%s' in RSP request: '%s'\n", uri_parsed->path, uri_parsed->uri); + rsp_send_error(req, "Server error"); return; } - ret = rsp_request_authorize(rreq); + ret = rsp_request_authorize(hreq); if (ret < 0) { rsp_send_error(req, "Access denied"); - free(rreq); + free(hreq); return; } - rreq->handler(rreq); + hreq->handler(hreq); - free(rreq); + free(hreq); } int