diff --git a/src/Makefile.am b/src/Makefile.am index 00c0f83a..07ade9a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -93,6 +93,7 @@ owntone_SOURCES = main.c \ $(MDNS_SRC) mdns.h \ remote_pairing.c remote_pairing.h \ httpd.c httpd.h httpd_internal.h \ + httpd_libevhttp.c \ httpd_rsp.c \ httpd_daap.c httpd_daap.h \ httpd_dacp.c \ diff --git a/src/httpd.c b/src/httpd.c index 7a474dd5..c0b3a6f0 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -296,7 +296,6 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd { ; struct evhttp_connection *evcon; - struct evkeyvalq *headers; struct httpd_uri_map *uri; int req_method; int ret; @@ -312,8 +311,9 @@ request_set(struct httpd_request *hreq, struct evhttp_request *req, struct httpd if (req) { - headers = evhttp_request_get_input_headers(req); - hreq->user_agent = evhttp_find_header(headers, "User-Agent"); + hreq->in_body = evhttp_request_get_input_buffer(req); + hreq->in_headers = evhttp_request_get_input_headers(req); + hreq->user_agent = evhttp_find_header(hreq->in_headers, "User-Agent"); evcon = evhttp_request_get_connection(req); if (evcon) diff --git a/src/httpd_artworkapi.c b/src/httpd_artworkapi.c index 0f81fa48..c6b8b427 100644 --- a/src/httpd_artworkapi.c +++ b/src/httpd_artworkapi.c @@ -40,20 +40,20 @@ request_process(struct httpd_request *hreq, uint32_t *max_w, uint32_t *max_h) *max_w = 0; *max_h = 0; - param = evhttp_find_header(hreq->query, "maxwidth"); + param = httpd_query_value_find(hreq->query, "maxwidth"); if (param) { ret = safe_atou32(param, max_w); if (ret < 0) - DPRINTF(E_LOG, L_WEB, "Invalid width in request: '%s'\n", hreq->uri_parsed->uri); + DPRINTF(E_LOG, L_WEB, "Invalid width in request: '%s'\n", hreq->uri); } - param = evhttp_find_header(hreq->query, "maxheight"); + param = httpd_query_value_find(hreq->query, "maxheight"); if (param) { ret = safe_atou32(param, max_h); if (ret < 0) - DPRINTF(E_LOG, L_WEB, "Invalid height in request: '%s'\n", hreq->uri_parsed->uri); + DPRINTF(E_LOG, L_WEB, "Invalid height in request: '%s'\n", hreq->uri); } return 0; @@ -62,14 +62,14 @@ request_process(struct httpd_request *hreq, uint32_t *max_w, uint32_t *max_h) static int response_process(struct httpd_request *hreq, int format) { - struct evkeyvalq *headers; + httpd_headers *headers; - headers = evhttp_request_get_output_headers(hreq->req); + headers = httpd_request_output_headers_get(hreq); if (format == ART_FMT_PNG) - evhttp_add_header(headers, "Content-Type", "image/png"); + httpd_header_add(headers, "Content-Type", "image/png"); else if (format == ART_FMT_JPEG) - evhttp_add_header(headers, "Content-Type", "image/jpeg"); + httpd_header_add(headers, "Content-Type", "image/jpeg"); else return HTTP_NOCONTENT; @@ -141,9 +141,9 @@ artworkapi_reply_group(struct httpd_request *hreq) static struct httpd_uri_map artworkapi_handlers[] = { - { EVHTTP_REQ_GET, "^/artwork/nowplaying$", artworkapi_reply_nowplaying }, - { EVHTTP_REQ_GET, "^/artwork/item/[[:digit:]]+$", artworkapi_reply_item }, - { EVHTTP_REQ_GET, "^/artwork/group/[[:digit:]]+$", artworkapi_reply_group }, + { HTTPD_METHOD_GET, "^/artwork/nowplaying$", artworkapi_reply_nowplaying }, + { HTTPD_METHOD_GET, "^/artwork/item/[[:digit:]]+$", artworkapi_reply_item }, + { HTTPD_METHOD_GET, "^/artwork/group/[[:digit:]]+$", artworkapi_reply_group }, { 0, NULL, NULL } }; diff --git a/src/httpd_daap.c b/src/httpd_daap.c index 49e40cb6..c8ff4ef6 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -286,7 +286,6 @@ static void update_refresh_cb(int fd, short event, void *arg) { struct daap_update_request *ur; - struct evhttp_connection *evcon; struct evbuffer *reply; ur = (struct daap_update_request *)arg; @@ -301,8 +300,7 @@ update_refresh_cb(int fd, short event, void *arg) dmap_add_int(reply, "mstt", 200); /* 12 */ dmap_add_int(reply, "musr", current_rev); /* 12 */ - evcon = evhttp_request_get_connection(ur->hreq->req); - evhttp_connection_set_closecb(evcon, NULL, NULL); + httpd_request_closecb_set(ur->hreq, NULL, NULL); httpd_send_reply(ur->hreq, HTTP_OK, "OK", reply, 0); @@ -310,20 +308,17 @@ update_refresh_cb(int fd, short event, void *arg) } static void -update_fail_cb(struct evhttp_connection *evcon, void *arg) +update_fail_cb(httpd_connection *conn, void *arg) { - struct evhttp_connection *evc; struct daap_update_request *ur; ur = (struct daap_update_request *)arg; DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n"); - evc = evhttp_request_get_connection(ur->hreq->req); - if (evc) - evhttp_connection_set_closecb(evc, NULL, NULL); + httpd_request_closecb_set(ur->hreq, NULL, NULL); - evhttp_request_free(ur->hreq->req); + httpd_request_backend_free(ur->hreq); // TODO check if still necessary update_remove(ur); } @@ -513,7 +508,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques memset(qp, 0, sizeof(struct query_params)); - param = evhttp_find_header(hreq->query, "index"); + param = httpd_query_value_find(hreq->query, "index"); if (param) { if (param[0] == '-') /* -n, last n entries */ @@ -559,7 +554,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques qp->idx_type = I_SUB; qp->sort = S_NONE; - param = evhttp_find_header(hreq->query, "sort"); + param = httpd_query_value_find(hreq->query, "sort"); if (param) { if (strcmp(param, "name") == 0) @@ -580,7 +575,7 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques if (sort_headers) { *sort_headers = 0; - param = evhttp_find_header(hreq->query, "include-sort-headers"); + param = httpd_query_value_find(hreq->query, "include-sort-headers"); if (param && (strcmp(param, "1") == 0)) { *sort_headers = 1; @@ -588,9 +583,9 @@ query_params_set(struct query_params *qp, int *sort_headers, struct httpd_reques } } - param = evhttp_find_header(hreq->query, "query"); + param = httpd_query_value_find(hreq->query, "query"); if (!param) - param = evhttp_find_header(hreq->query, "filter"); + param = httpd_query_value_find(hreq->query, "filter"); if (param) { @@ -721,7 +716,7 @@ daap_request_authorize(struct httpd_request *hreq) if (session->is_remote && (strcmp(hreq->uri_parsed->path, "/login") == 0)) return 0; - param = evhttp_find_header(hreq->query, "session-id"); + param = httpd_query_value_find(hreq->query, "session-id"); if (param) { if (session->id == 0) @@ -764,13 +759,12 @@ daap_request_authorize(struct httpd_request *hreq) /* --------------------------- 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 hreq->req is not a null pointer. */ +/* that httpd_request_connection_get(hreq) is not null. */ static enum daap_reply_result daap_reply_server_info(struct httpd_request *hreq) { struct evbuffer *content; - struct evkeyvalq *headers; char *name; char *passwd; const char *clientver; @@ -778,7 +772,7 @@ daap_reply_server_info(struct httpd_request *hreq) int mpro; int apro; - if (!hreq->req) + if (!httpd_request_connection_get(hreq)) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_server_info() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; @@ -793,8 +787,7 @@ daap_reply_server_info(struct httpd_request *hreq) mpro = 2 << 16 | 10; apro = 3 << 16 | 12; - headers = evhttp_request_get_input_headers(hreq->req); - if (headers && (clientver = evhttp_find_header(headers, "Client-DAAP-Version"))) + if (hreq->in_headers && (clientver = httpd_header_find(hreq->in_headers, "Client-DAAP-Version"))) { if (strcmp(clientver, "1.0") == 0) { @@ -917,7 +910,7 @@ daap_reply_login(struct httpd_request *hreq) CHECK_ERR(L_DAAP, evbuffer_expand(hreq->reply, 32)); - param = evhttp_find_header(hreq->query, "pairing-guid"); + param = httpd_query_value_find(hreq->query, "pairing-guid"); if (param && !net_peer_address_is_trusted(hreq->peer_address)) { if (strlen(param) < 3) @@ -948,7 +941,7 @@ daap_reply_login(struct httpd_request *hreq) DPRINTF(E_INFO, L_DAAP, "Client (unknown user-agent) logging in from %s\n", hreq->peer_address); } - param = evhttp_find_header(hreq->query, "request-session-id"); + param = httpd_query_value_find(hreq->query, "request-session-id"); if (param) { ret = safe_atoi32(param, &request_session_id); @@ -992,18 +985,17 @@ static enum daap_reply_result daap_reply_update(struct httpd_request *hreq) { struct daap_update_request *ur; - struct evhttp_connection *evcon; const char *param; int reqd_rev; int ret; - if (!hreq->req) + if (!httpd_request_connection_get(hreq)) { 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(hreq->query, "revision-number"); + param = httpd_query_value_find(hreq->query, "revision-number"); if (!param) { DPRINTF(E_DBG, L_DAAP, "Missing revision-number in client update request\n"); @@ -1070,9 +1062,7 @@ daap_reply_update(struct httpd_request *hreq) /* If the connection fails before we have an update to push out * to the client, we need to know. */ - evcon = evhttp_request_get_connection(hreq->req); - if (evcon) - evhttp_connection_set_closecb(evcon, update_fail_cb, ur); + httpd_request_closecb_set(hreq, update_fail_cb, ur); return DAAP_REPLY_NONE; } @@ -1165,7 +1155,6 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist) struct db_media_file_info dbmfi; struct evbuffer *song; struct evbuffer *songlist; - struct evkeyvalq *headers; struct daap_session *s; const struct dmap_field **meta = NULL; struct sort_ctx *sctx; @@ -1210,7 +1199,7 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist) CHECK_ERR(L_DAAP, evbuffer_expand(songlist, 4096)); CHECK_ERR(L_DAAP, evbuffer_expand(song, 512)); - param = evhttp_find_header(hreq->query, "meta"); + param = httpd_query_value_find(hreq->query, "meta"); if (!param) { DPRINTF(E_DBG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1239,10 +1228,9 @@ daap_reply_songlist_generic(struct httpd_request *hreq, int playlist) } client_codecs = NULL; - if (!s->is_remote && hreq->req) + if (!s->is_remote && hreq->in_headers) { - headers = evhttp_request_get_input_headers(hreq->req); - client_codecs = evhttp_find_header(headers, "Accept-Codecs"); + client_codecs = httpd_header_find(hreq->in_headers, "Accept-Codecs"); } nsongs = 0; @@ -1428,7 +1416,7 @@ daap_reply_playlists(struct httpd_request *hreq) CHECK_ERR(L_DAAP, evbuffer_expand(playlistlist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(playlist, 128)); - param = evhttp_find_header(hreq->query, "meta"); + param = httpd_query_value_find(hreq->query, "meta"); if (!param) { DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1626,7 +1614,7 @@ daap_reply_groups(struct httpd_request *hreq) int i; int ret; - param = evhttp_find_header(hreq->query, "group-type"); + param = httpd_query_value_find(hreq->query, "group-type"); if (param && strcmp(param, "artists") == 0) { // Request from Remote may have the form: @@ -1654,7 +1642,7 @@ daap_reply_groups(struct httpd_request *hreq) CHECK_ERR(L_DAAP, evbuffer_expand(grouplist, 1024)); CHECK_ERR(L_DAAP, evbuffer_expand(group, 128)); - param = evhttp_find_header(hreq->query, "meta"); + param = httpd_query_value_find(hreq->query, "meta"); if (!param) { DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n"); @@ -1947,7 +1935,7 @@ daap_reply_browse(struct httpd_request *hreq) static enum daap_reply_result daap_reply_extra_data(struct httpd_request *hreq) { - struct evkeyvalq *headers; + httpd_headers *headers; char clen[32]; const char *param; char *ctype; @@ -1957,7 +1945,7 @@ daap_reply_extra_data(struct httpd_request *hreq) int max_h; int ret; - if (!hreq->req) + if (!httpd_request_connection_get(hreq)) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_reply_extra_data() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; @@ -1970,9 +1958,9 @@ daap_reply_extra_data(struct httpd_request *hreq) return DAAP_REPLY_BAD_REQUEST; } - if (evhttp_find_header(hreq->query, "mw") && evhttp_find_header(hreq->query, "mh")) + if (httpd_query_value_find(hreq->query, "mw") && httpd_query_value_find(hreq->query, "mh")) { - param = evhttp_find_header(hreq->query, "mw"); + param = httpd_query_value_find(hreq->query, "mw"); ret = safe_atoi32(param, &max_w); if (ret < 0) { @@ -1980,7 +1968,7 @@ daap_reply_extra_data(struct httpd_request *hreq) return DAAP_REPLY_BAD_REQUEST; } - param = evhttp_find_header(hreq->query, "mh"); + param = httpd_query_value_find(hreq->query, "mh"); ret = safe_atoi32(param, &max_h); if (ret < 0) { @@ -2020,11 +2008,11 @@ daap_reply_extra_data(struct httpd_request *hreq) goto no_artwork; } - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_remove_header(headers, "Content-Type"); - evhttp_add_header(headers, "Content-Type", ctype); + headers = httpd_request_output_headers_get(hreq); + httpd_header_remove(headers, "Content-Type"); + httpd_header_add(headers, "Content-Type", ctype); snprintf(clen, sizeof(clen), "%ld", (long)len); - evhttp_add_header(headers, "Content-Length", clen); + httpd_header_add(headers, "Content-Length", clen); return DAAP_REPLY_OK_NO_GZIP; @@ -2038,7 +2026,7 @@ daap_stream(struct httpd_request *hreq) int id; int ret; - if (!hreq->req) + if (!httpd_request_connection_get(hreq)) { DPRINTF(E_LOG, L_DAAP, "Bug! daap_stream() cannot be called without an actual connection\n"); return DAAP_REPLY_NO_CONNECTION; @@ -2228,7 +2216,7 @@ static struct httpd_uri_map daap_handlers[] = static void daap_request(struct httpd_request *hreq) { - struct evkeyvalq *headers; + httpd_headers *headers; struct timespec start; struct timespec end; struct daap_session session; @@ -2248,7 +2236,7 @@ daap_request(struct httpd_request *hreq) } // Check if we have a session and point hreq->extra_data to it - param = evhttp_find_header(hreq->query, "session-id"); + param = httpd_query_value_find(hreq->query, "session-id"); if (param) { ret = safe_atoi32(param, &id); @@ -2262,7 +2250,7 @@ daap_request(struct httpd_request *hreq) if (!hreq->extra_data) { memset(&session, 0, sizeof(struct daap_session)); - session.is_remote = (evhttp_find_header(hreq->query, "pairing-guid") != NULL); + session.is_remote = (httpd_query_value_find(hreq->query, "pairing-guid") != NULL); hreq->extra_data = &session; } @@ -2273,13 +2261,13 @@ daap_request(struct httpd_request *hreq) } // Set reply headers - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(headers, "Accept-Ranges", "bytes"); - evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION); + headers = httpd_request_output_headers_get(hreq); + httpd_header_add(headers, "Accept-Ranges", "bytes"); + httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" 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. - evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); + httpd_header_add(headers, "Content-Type", "application/x-dmap-tagged"); // Now we create the actual reply CHECK_NULL(L_DAAP, hreq->reply = evbuffer_new()); @@ -2289,7 +2277,7 @@ daap_request(struct httpd_request *hreq) 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_header_add(headers, "Content-Encoding", "gzip"); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); // TODO not all want this reply evbuffer_free(hreq->reply); @@ -2389,7 +2377,7 @@ daap_deinit(void) { struct daap_session *s; struct daap_update_request *ur; - struct evhttp_connection *evcon; + httpd_connection *conn; for (s = daap_sessions; daap_sessions; s = daap_sessions) { @@ -2401,11 +2389,11 @@ daap_deinit(void) { update_requests = ur->next; - evcon = evhttp_request_get_connection(ur->hreq->req); - if (evcon) + conn = httpd_request_connection_get(ur->hreq); + if (conn) { - evhttp_connection_set_closecb(evcon, NULL, NULL); - evhttp_connection_free(evcon); + httpd_connection_closecb_set(conn, NULL, NULL); + httpd_connection_free(conn); } update_free(ur); diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 5c3bcd86..8e2aeb12 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -565,15 +564,13 @@ speaker_enum_cb(struct player_speaker_info *spk, void *arg) static int speaker_get(struct player_speaker_info *speaker_info, struct httpd_request *hreq, const char *req_name) { - struct evkeyvalq *headers; const char *remote; uint32_t active_remote; int ret; - headers = evhttp_request_get_input_headers(hreq->req); - remote = evhttp_find_header(headers, "Active-Remote"); + remote = httpd_header_find(hreq->in_headers, "Active-Remote"); - if (!headers || !remote || (safe_atou32(remote, &active_remote) < 0)) + if (!remote || (safe_atou32(remote, &active_remote) < 0)) { DPRINTF(E_LOG, L_DACP, "'%s' request from '%s' has invalid Active-Remote: '%s'\n", req_name, hreq->peer_address, remote); return -1; @@ -633,7 +630,7 @@ dacp_request_authorize(struct httpd_request *hreq) if (net_peer_address_is_trusted(hreq->peer_address)) return 0; - param = evhttp_find_header(hreq->query, "session-id"); + param = httpd_query_value_find(hreq->query, "session-id"); if (!param) { DPRINTF(E_LOG, L_DACP, "No session-id specified in request\n"); @@ -737,7 +734,6 @@ playstatusupdate_cb(int fd, short what, void *arg) struct dacp_update_request *ur; struct evbuffer *evbuf; struct evbuffer *update; - struct evhttp_connection *evcon; uint8_t *buf; size_t len; int ret; @@ -776,9 +772,7 @@ playstatusupdate_cb(int fd, short what, void *arg) { update_requests = ur->next; - evcon = evhttp_request_get_connection(ur->hreq->req); - if (evcon) - evhttp_connection_set_closecb(evcon, NULL, NULL); + httpd_request_closecb_set(ur->hreq, NULL, NULL); // Only copy buffer if we actually need to reuse it if (ur->next) @@ -822,19 +816,16 @@ dacp_playstatus_update_handler(short event_mask) } static void -update_fail_cb(struct evhttp_connection *evcon, void *arg) +update_fail_cb(httpd_connection *conn, void *arg) { struct dacp_update_request *ur; struct dacp_update_request *p; - struct evhttp_connection *evc; ur = (struct dacp_update_request *)arg; DPRINTF(E_DBG, L_DACP, "Update request: client closed connection\n"); - evc = evhttp_request_get_connection(ur->hreq->req); - if (evc) - evhttp_connection_set_closecb(evc, NULL, NULL); + httpd_request_closecb_set(ur->hreq, NULL, NULL); if (ur == update_requests) update_requests = ur->next; @@ -852,7 +843,7 @@ update_fail_cb(struct evhttp_connection *evcon, void *arg) p->next = ur->next; } - evhttp_request_free(ur->hreq->req); + httpd_request_backend_free(ur->hreq); // TODO check if still necessary free(ur); } @@ -978,7 +969,7 @@ dacp_propset_volume(const char *value, struct httpd_request *hreq) return; } - param = evhttp_find_header(hreq->query, "speaker-id"); + param = httpd_query_value_find(hreq->query, "speaker-id"); if (param) { ret = safe_atou64(param, &id); @@ -993,7 +984,7 @@ dacp_propset_volume(const char *value, struct httpd_request *hreq) return; } - param = evhttp_find_header(hreq->query, "include-speaker-id"); + param = httpd_query_value_find(hreq->query, "include-speaker-id"); if (param) { ret = safe_atou64(param, &id); @@ -1125,9 +1116,9 @@ dacp_propset_userrating(const char *value, struct httpd_request *hreq) return; } - param = evhttp_find_header(hreq->query, "item-spec"); // Remote + param = httpd_query_value_find(hreq->query, "item-spec"); // Remote if (!param) - param = evhttp_find_header(hreq->query, "song-spec"); // Retune + param = httpd_query_value_find(hreq->query, "song-spec"); // Retune if (!param) { @@ -1238,7 +1229,7 @@ dacp_reply_cue_play(struct httpd_request *hreq) /* /cue?command=play&query=...&sort=...&index=N */ - param = evhttp_find_header(hreq->query, "clear-first"); + param = httpd_query_value_find(hreq->query, "clear-first"); if (param) { ret = safe_atoi32(param, &clear); @@ -1254,10 +1245,10 @@ dacp_reply_cue_play(struct httpd_request *hreq) player_get_status(&status); - cuequery = evhttp_find_header(hreq->query, "query"); + cuequery = httpd_query_value_find(hreq->query, "query"); if (cuequery) { - sort = evhttp_find_header(hreq->query, "sort"); + sort = httpd_query_value_find(hreq->query, "sort"); ret = dacp_queueitem_add(cuequery, NULL, sort, 0, 0); if (ret < 0) @@ -1273,12 +1264,12 @@ dacp_reply_cue_play(struct httpd_request *hreq) player_playback_stop(); } - param = evhttp_find_header(hreq->query, "dacp.shufflestate"); + param = httpd_query_value_find(hreq->query, "dacp.shufflestate"); if (param) dacp_propset_shufflestate(param, NULL); pos = 0; - param = evhttp_find_header(hreq->query, "index"); + param = httpd_query_value_find(hreq->query, "index"); if (param) { ret = safe_atou32(param, &pos); @@ -1287,10 +1278,10 @@ dacp_reply_cue_play(struct httpd_request *hreq) } /* If selection was from Up Next queue or history queue (command will be playnow), then index is relative */ - if ((param = evhttp_find_header(hreq->query, "command")) && (strcmp(param, "playnow") == 0)) + if ((param = httpd_query_value_find(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(hreq->query, "mode"); + param = httpd_query_value_find(hreq->query, "mode"); if (param && ((strcmp(param, "-1") == 0) || (strcmp(param, "1") == 0))) { /* Play from history queue */ @@ -1397,7 +1388,7 @@ dacp_reply_cue(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "command"); + param = httpd_query_value_find(hreq->query, "command"); if (!param) { DPRINTF(E_LOG, L_DACP, "No command in cue request\n"); @@ -1463,10 +1454,10 @@ dacp_reply_playspec(struct httpd_request *hreq) return -1; /* Check for shuffle */ - shuffle = evhttp_find_header(hreq->query, "dacp.shufflestate"); + shuffle = httpd_query_value_find(hreq->query, "dacp.shufflestate"); /* Playlist ID */ - param = evhttp_find_header(hreq->query, "container-spec"); + param = httpd_query_value_find(hreq->query, "container-spec"); if (!param) { DPRINTF(E_LOG, L_DACP, "No container-spec in playspec request\n"); @@ -1495,9 +1486,9 @@ dacp_reply_playspec(struct httpd_request *hreq) if (!shuffle) { /* Start song ID */ - if ((param = evhttp_find_header(hreq->query, "item-spec"))) + if ((param = httpd_query_value_find(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(hreq->query, "container-item-spec"))) + else if (!(param = httpd_query_value_find(hreq->query, "container-item-spec"))) { DPRINTF(E_LOG, L_DACP, "No container-item-spec/item-spec in playspec request\n"); goto out_fail; @@ -1787,7 +1778,7 @@ dacp_reply_playqueuecontents(struct httpd_request *hreq) DPRINTF(E_DBG, L_DACP, "Fetching playqueue contents\n"); span = 50; /* Default */ - param = evhttp_find_header(hreq->query, "span"); + param = httpd_query_value_find(hreq->query, "span"); if (param) { ret = safe_atoi32(param, &span); @@ -1927,7 +1918,7 @@ dacp_reply_playqueueedit_clear(struct httpd_request *hreq) const char *param; struct player_status status; - param = evhttp_find_header(hreq->query, "mode"); + param = httpd_query_value_find(hreq->query, "mode"); /* * The mode parameter contains the playlist to be cleared. @@ -1978,7 +1969,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq) mode = 1; - param = evhttp_find_header(hreq->query, "mode"); + param = httpd_query_value_find(hreq->query, "mode"); if (param) { ret = safe_atoi32(param, &mode); @@ -2000,7 +1991,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq) if (mode == 2) player_shuffle_set(1); - editquery = evhttp_find_header(hreq->query, "query"); + editquery = httpd_query_value_find(hreq->query, "query"); if (!editquery) { DPRINTF(E_LOG, L_DACP, "Could not add song queue, DACP query missing\n"); @@ -2009,7 +2000,7 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq) return -1; } - sort = evhttp_find_header(hreq->query, "sort"); + sort = httpd_query_value_find(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:"))) @@ -2018,9 +2009,9 @@ dacp_reply_playqueueedit_add(struct httpd_request *hreq) } // 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(hreq->query, "queuefilter"); + queuefilter = (mode == 0 || mode == 3 || mode == 5) ? NULL : httpd_query_value_find(hreq->query, "queuefilter"); - querymodifier = evhttp_find_header(hreq->query, "query-modifier"); + querymodifier = httpd_query_value_find(hreq->query, "query-modifier"); if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); @@ -2106,7 +2097,7 @@ dacp_reply_playqueueedit_move(struct httpd_request *hreq) int src; int dst; - param = evhttp_find_header(hreq->query, "edit-params"); + param = httpd_query_value_find(hreq->query, "edit-params"); if (param) { ret = safe_atoi32(strchr(param, ':') + 1, &src); @@ -2150,7 +2141,7 @@ dacp_reply_playqueueedit_remove(struct httpd_request *hreq) int item_index; int ret; - param = evhttp_find_header(hreq->query, "items"); + param = httpd_query_value_find(hreq->query, "items"); if (param) { ret = safe_atoi32(param, &item_index); @@ -2226,7 +2217,7 @@ dacp_reply_playqueueedit(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "command"); + param = httpd_query_value_find(hreq->query, "command"); if (!param) { DPRINTF(E_LOG, L_DACP, "No command in playqueue-edit request\n"); @@ -2258,7 +2249,6 @@ static int dacp_reply_playstatusupdate(struct httpd_request *hreq) { struct dacp_update_request *ur; - struct evhttp_connection *evcon; const char *param; int reqd_rev; int ret; @@ -2267,7 +2257,7 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "revision-number"); + param = httpd_query_value_find(hreq->query, "revision-number"); if (!param) { DPRINTF(E_LOG, L_DACP, "Missing revision-number in update request\n"); @@ -2317,9 +2307,7 @@ dacp_reply_playstatusupdate(struct httpd_request *hreq) /* If the connection fails before we have an update to push out * to the client, we need to know. */ - evcon = evhttp_request_get_connection(hreq->req); - if (evcon) - evhttp_connection_set_closecb(evcon, update_fail_cb, ur); + httpd_request_closecb_set(hreq, update_fail_cb, ur); return 0; } @@ -2328,7 +2316,7 @@ static int dacp_reply_nowplayingartwork(struct httpd_request *hreq) { char clen[32]; - struct evkeyvalq *headers; + httpd_headers *headers; const char *param; char *ctype; size_t len; @@ -2341,7 +2329,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "mw"); + param = httpd_query_value_find(hreq->query, "mw"); if (!param) { DPRINTF(E_LOG, L_DACP, "Request for artwork without mw parameter\n"); @@ -2355,7 +2343,7 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq) goto error; } - param = evhttp_find_header(hreq->query, "mh"); + param = httpd_query_value_find(hreq->query, "mh"); if (!param) { DPRINTF(E_LOG, L_DACP, "Request for artwork without mh parameter\n"); @@ -2393,11 +2381,11 @@ dacp_reply_nowplayingartwork(struct httpd_request *hreq) goto no_artwork; } - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_remove_header(headers, "Content-Type"); - evhttp_add_header(headers, "Content-Type", ctype); + headers = httpd_request_output_headers_get(hreq); + httpd_header_remove(headers, "Content-Type"); + httpd_header_add(headers, "Content-Type", ctype); snprintf(clen, sizeof(clen), "%ld", (long)len); - evhttp_add_header(headers, "Content-Length", clen); + httpd_header_add(headers, "Content-Length", clen); httpd_send_reply(hreq, HTTP_OK, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); return 0; @@ -2429,7 +2417,7 @@ dacp_reply_getproperty(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "properties"); + param = httpd_query_value_find(hreq->query, "properties"); if (!param) { DPRINTF(E_WARN, L_DACP, "Invalid DACP getproperty request, no properties\n"); @@ -2513,11 +2501,27 @@ dacp_reply_getproperty(struct httpd_request *hreq) return -1; } +static void +setproperty_cb(const char *key, const char *val, void *arg) +{ + struct httpd_request *hreq = arg; + const struct dacp_prop_map *dpm = dacp_find_prop(key, strlen(key)); + + if (!dpm) + { + DPRINTF(E_SPAM, L_DACP, "Unknown DACP property %s\n", key); + return; + } + + if (dpm->propset) + dpm->propset(val, hreq); + else + DPRINTF(E_WARN, L_DACP, "No setter method for DACP property %s\n", dpm->desc); +} + static int dacp_reply_setproperty(struct httpd_request *hreq) { - const struct dacp_prop_map *dpm; - struct evkeyval *param; int ret; ret = dacp_request_authorize(hreq); @@ -2536,21 +2540,7 @@ dacp_reply_setproperty(struct httpd_request *hreq) /* /ctrl-int/1/setproperty?dacp.shufflestate=1&session-id=100 */ - TAILQ_FOREACH(param, hreq->query, next) - { - dpm = dacp_find_prop(param->key, strlen(param->key)); - - if (!dpm) - { - DPRINTF(E_SPAM, L_DACP, "Unknown DACP property %s\n", param->key); - continue; - } - - if (dpm->propset) - dpm->propset(param->value, hreq); - else - DPRINTF(E_WARN, L_DACP, "No setter method for DACP property %s\n", dpm->desc); - } + httpd_query_iterate(hreq->query, setproperty_cb, hreq); /* 204 No Content is the canonical reply */ httpd_send_reply(hreq, HTTP_NOCONTENT, "No Content", hreq->reply, HTTPD_SEND_NO_GZIP); @@ -2600,7 +2590,7 @@ dacp_reply_setspeakers(struct httpd_request *hreq) if (ret < 0) return -1; - param = evhttp_find_header(hreq->query, "speaker-id"); + param = httpd_query_value_find(hreq->query, "speaker-id"); if (!param) { DPRINTF(E_LOG, L_DACP, "Missing speaker-id parameter in DACP setspeakers request\n"); @@ -2865,7 +2855,7 @@ static struct httpd_uri_map dacp_handlers[] = static void dacp_request(struct httpd_request *hreq) { - struct evkeyvalq *headers; + httpd_headers *headers; DPRINTF(E_DBG, L_DACP, "DACP request: '%s'\n", hreq->uri); @@ -2877,10 +2867,10 @@ dacp_request(struct httpd_request *hreq) return; } - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION); + headers = httpd_request_output_headers_get(hreq); + httpd_header_add(headers, "DAAP-Server", PACKAGE_NAME "/" VERSION); /* Content-Type for all DACP replies; can be overriden as needed */ - evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged"); + httpd_header_add(headers, "Content-Type", "application/x-dmap-tagged"); CHECK_NULL(L_DACP, hreq->reply = evbuffer_new()); @@ -2951,17 +2941,17 @@ static void dacp_deinit(void) { struct dacp_update_request *ur; - struct evhttp_connection *evcon; + httpd_connection *conn; for (ur = update_requests; update_requests; ur = update_requests) { update_requests = ur->next; - evcon = evhttp_request_get_connection(ur->hreq->req); - if (evcon) + conn = httpd_request_connection_get(ur->hreq); + if (conn) { - evhttp_connection_set_closecb(evcon, NULL, NULL); - evhttp_connection_free(evcon); + httpd_connection_closecb_set(conn, NULL, NULL); + httpd_connection_free(conn); } free(ur); diff --git a/src/httpd_internal.h b/src/httpd_internal.h index 7be90a24..fe40fa35 100644 --- a/src/httpd_internal.h +++ b/src/httpd_internal.h @@ -7,6 +7,26 @@ #include #include +typedef struct evhttp_connection httpd_connection; +typedef struct evkeyvalq httpd_headers; +typedef struct evkeyvalq httpd_query; + +typedef void (*httpd_connection_closecb)(httpd_connection *conn, void *arg); +typedef void (*httpd_connection_chunkcb)(httpd_connection *conn, void *arg); +typedef void (*httpd_query_iteratecb)(const char *key, const char *val, void *arg); + +enum httpd_methods +{ + HTTPD_METHOD_GET = 1 << 0, + HTTPD_METHOD_POST = 1 << 1, + HTTPD_METHOD_HEAD = 1 << 2, + HTTPD_METHOD_PUT = 1 << 3, + HTTPD_METHOD_DELETE = 1 << 4, + HTTPD_METHOD_OPTIONS = 1 << 5, + HTTPD_METHOD_TRACE = 1 << 6, + HTTPD_METHOD_CONNECT = 1 << 7, + HTTPD_METHOD_PATCH = 1 << 8, +}; enum httpd_send_flags { @@ -49,7 +69,7 @@ struct httpd_request { // 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; + httpd_query *query; // http request struct (if available) struct evhttp_request *req; // Source IP address (ipv4 or ipv6) and port of the request (if available) @@ -58,6 +78,10 @@ struct httpd_request { // A pointer to extra data that the module handling the request might need void *extra_data; + // Request headers + httpd_headers *in_headers; + // Request body + struct evbuffer *in_body; // Reply evbuffer struct evbuffer *reply; @@ -103,7 +127,7 @@ struct httpd_module */ struct httpd_uri_map { - int method; + enum httpd_methods method; char *regexp; int (*handler)(struct httpd_request *hreq); void *preg; @@ -179,4 +203,70 @@ httpd_admin_check_auth(struct httpd_request *hreq); int httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm); + +/*-------------------------- WRAPPERS FOR EVHTTP -----------------------------*/ + +const char * +httpd_query_value_find(httpd_query *query, const char *key); + +void +httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg); + +const char * +httpd_header_find(httpd_headers *headers, const char *key); + +void +httpd_header_remove(httpd_headers *headers, const char *key); + +void +httpd_header_add(httpd_headers *headers, const char *key, const char *val); + +void +httpd_headers_clear(httpd_headers *headers); + +httpd_headers * +httpd_request_input_headers_get(struct httpd_request *hreq); + +httpd_headers * +httpd_request_output_headers_get(struct httpd_request *hreq); + +int +httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg); +/* +int +httpd_connection_peer_get(char **addr, uint16_t *port, struct httpd_connection *conn); +*/ +void +httpd_connection_free(httpd_connection *conn); + +httpd_connection * +httpd_request_connection_get(struct httpd_request *hreq); +/* +const char * +httpd_request_uri_get(struct httpd_request *hreq); + +int +httpd_request_peer_get(char **addr, uint16_t *port, struct httpd_request *hreq); +*/ +int +httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq); + +void +httpd_request_backend_free(struct httpd_request *hreq); + +int +httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg); +/* +void +httpd_reply_send(struct httpd_request *hreq, int code, const char *reason, struct evbuffer *evbuf) +*/ +void +httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason); + +void +httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg); + +void +httpd_reply_end_send(struct httpd_request *hreq); + #endif /* !__HTTPD_INTERNAL_H__ */ diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index fa2f2b29..d5755c7f 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -764,7 +764,7 @@ query_params_limit_set(struct query_params *query_params, struct httpd_request * query_params->limit = -1; query_params->offset = 0; - param = evhttp_find_header(hreq->query, "limit"); + param = httpd_query_value_find(hreq->query, "limit"); if (param) { query_params->idx_type = I_SUB; @@ -775,7 +775,7 @@ query_params_limit_set(struct query_params *query_params, struct httpd_request * return -1; } - param = evhttp_find_header(hreq->query, "offset"); + param = httpd_query_value_find(hreq->query, "offset"); if (param && safe_atoi32(param, &query_params->offset) < 0) { DPRINTF(E_LOG, L_WEB, "Invalid value for query parameter 'offset' (%s)\n", param); @@ -1062,7 +1062,6 @@ jsonapi_reply_settings_option_put(struct httpd_request *hreq) const char *optionname; struct settings_category *category; struct settings_option *option; - struct evbuffer *in_evbuf; json_object* request; int intval; bool boolval; @@ -1088,8 +1087,7 @@ jsonapi_reply_settings_option_put(struct httpd_request *hreq) return HTTP_NOTFOUND; } - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Missing request body for setting option '%s' (type %d)\n", optionname, option->type); @@ -1252,7 +1250,7 @@ jsonapi_reply_update(struct httpd_request *hreq) { const char *param; - param = evhttp_find_header(hreq->query, "scan_kind"); + param = httpd_query_value_find(hreq->query, "scan_kind"); library_rescan(db_scan_kind_enum(param)); return HTTP_NOCONTENT; @@ -1263,7 +1261,7 @@ jsonapi_reply_meta_rescan(struct httpd_request *hreq) { const char *param; - param = evhttp_find_header(hreq->query, "scan_kind"); + param = httpd_query_value_find(hreq->query, "scan_kind"); library_metarescan(db_scan_kind_enum(param)); return HTTP_NOCONTENT; @@ -1379,7 +1377,6 @@ static int jsonapi_reply_lastfm_login(struct httpd_request *hreq) { #ifdef LASTFM - struct evbuffer *in_evbuf; json_object *request; const char *user; const char *password; @@ -1390,8 +1387,7 @@ jsonapi_reply_lastfm_login(struct httpd_request *hreq) DPRINTF(E_DBG, L_WEB, "Received LastFM login request\n"); - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); @@ -1466,13 +1462,11 @@ jsonapi_reply_lastfm_logout(struct httpd_request *hreq) static int jsonapi_reply_pairing_pair(struct httpd_request *hreq) { - struct evbuffer *evbuf; json_object* request; const char* pin; int ret; - evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); @@ -1623,7 +1617,6 @@ static int jsonapi_reply_outputs_put_byid(struct httpd_request *hreq) { uint64_t output_id; - struct evbuffer *in_evbuf; json_object* request; bool selected; int volume; @@ -1638,8 +1631,7 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq) return HTTP_BADREQUEST; } - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); @@ -1741,12 +1733,10 @@ jsonapi_reply_outputs(struct httpd_request *hreq) static int jsonapi_reply_verification(struct httpd_request *hreq) { - struct evbuffer *in_evbuf; json_object* request; const char* message; - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); @@ -1769,15 +1759,13 @@ jsonapi_reply_verification(struct httpd_request *hreq) static int jsonapi_reply_outputs_set(struct httpd_request *hreq) { - struct evbuffer *in_evbuf; json_object *request; json_object *outputs; json_object *output_id; int nspk, i, ret; uint64_t *ids; - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); @@ -1902,11 +1890,11 @@ jsonapi_reply_player_play(struct httpd_request *hreq) const char *param; int ret; - if ((param = evhttp_find_header(hreq->query, "item_id"))) + if ((param = httpd_query_value_find(hreq->query, "item_id"))) { return play_item_with_id(param); } - else if ((param = evhttp_find_header(hreq->query, "position"))) + else if ((param = httpd_query_value_find(hreq->query, "position"))) { return play_item_at_position(param); } @@ -2033,8 +2021,8 @@ jsonapi_reply_player_seek(struct httpd_request *hreq) int seek_ms; int ret; - param_pos = evhttp_find_header(hreq->query, "position_ms"); - param_seek = evhttp_find_header(hreq->query, "seek_ms"); + param_pos = httpd_query_value_find(hreq->query, "position_ms"); + param_seek = httpd_query_value_find(hreq->query, "seek_ms"); if (!param_pos && !param_seek) return HTTP_BADREQUEST; @@ -2472,7 +2460,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) int ret = 0; - param_pos = evhttp_find_header(hreq->query, "position"); + param_pos = httpd_query_value_find(hreq->query, "position"); if (param_pos) { if (safe_atoi32(param_pos, &pos) < 0) @@ -2487,8 +2475,8 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) else pos = -1; - param_uris = evhttp_find_header(hreq->query, "uris"); - param_expression = evhttp_find_header(hreq->query, "expression"); + param_uris = httpd_query_value_find(hreq->query, "uris"); + param_expression = httpd_query_value_find(hreq->query, "expression"); if (!param_uris && !param_expression) { @@ -2498,7 +2486,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) } // if query parameter "clear" is "true", stop playback and clear the queue before adding new queue items - param = evhttp_find_header(hreq->query, "clear"); + param = httpd_query_value_find(hreq->query, "clear"); if (param && strcmp(param, "true") == 0) { player_playback_stop(); @@ -2506,7 +2494,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) } // if query parameter "shuffle" is present, update the shuffle state before adding new queue items - param = evhttp_find_header(hreq->query, "shuffle"); + param = httpd_query_value_find(hreq->query, "shuffle"); if (param) { shuffle = (strcmp(param, "true") == 0); @@ -2520,7 +2508,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) else { // This overrides the value specified in query - param = evhttp_find_header(hreq->query, "limit"); + param = httpd_query_value_find(hreq->query, "limit"); if (param && safe_atoi32(param, &limit) == 0) ret = queue_tracks_add_byexpression(param_expression, pos, limit, &total_count); else @@ -2540,10 +2528,10 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) return HTTP_INTERNAL; // If query parameter "playback" is "start", start playback after successfully adding new items - param = evhttp_find_header(hreq->query, "playback"); + param = httpd_query_value_find(hreq->query, "playback"); if (param && strcmp(param, "start") == 0) { - if ((param = evhttp_find_header(hreq->query, "playback_from_position"))) + if ((param = httpd_query_value_find(hreq->query, "playback_from_position"))) ret = (play_item_at_position(param) == HTTP_NOCONTENT) ? 0 : -1; else ret = player_playback_start(); @@ -2618,21 +2606,21 @@ jsonapi_reply_queue_tracks_update(struct httpd_request *hreq) ret = HTTP_OK; is_changed = false; - if ((param = evhttp_find_header(hreq->query, "new_position"))) + if ((param = httpd_query_value_find(hreq->query, "new_position"))) ret = update_pos(item_id, param, status.shuffle); - if ((param = evhttp_find_header(hreq->query, "title"))) + if ((param = httpd_query_value_find(hreq->query, "title"))) update_str(&is_changed, &queue_item->title, param); - if ((param = evhttp_find_header(hreq->query, "album"))) + if ((param = httpd_query_value_find(hreq->query, "album"))) update_str(&is_changed, &queue_item->album, param); - if ((param = evhttp_find_header(hreq->query, "artist"))) + if ((param = httpd_query_value_find(hreq->query, "artist"))) update_str(&is_changed, &queue_item->artist, param); - if ((param = evhttp_find_header(hreq->query, "album_artist"))) + if ((param = httpd_query_value_find(hreq->query, "album_artist"))) update_str(&is_changed, &queue_item->album_artist, param); - if ((param = evhttp_find_header(hreq->query, "composer"))) + if ((param = httpd_query_value_find(hreq->query, "composer"))) update_str(&is_changed, &queue_item->composer, param); - if ((param = evhttp_find_header(hreq->query, "genre"))) + if ((param = httpd_query_value_find(hreq->query, "genre"))) update_str(&is_changed, &queue_item->genre, param); - if ((param = evhttp_find_header(hreq->query, "artwork_url"))) + if ((param = httpd_query_value_find(hreq->query, "artwork_url"))) update_str(&is_changed, &queue_item->artwork_url, param); if (ret != HTTP_OK) @@ -2713,7 +2701,7 @@ jsonapi_reply_queue(struct httpd_request *hreq) if (status.shuffle) query_params.sort = S_SHUFFLE_POS; - param = evhttp_find_header(hreq->query, "id"); + param = httpd_query_value_find(hreq->query, "id"); if (param && strcmp(param, "now_playing") == 0) { query_params.filter = db_mprintf("id = %d", status.item_id); @@ -2724,10 +2712,10 @@ jsonapi_reply_queue(struct httpd_request *hreq) } else { - param = evhttp_find_header(hreq->query, "start"); + param = httpd_query_value_find(hreq->query, "start"); if (param && safe_atoi32(param, &start_pos) == 0) { - param = evhttp_find_header(hreq->query, "end"); + param = httpd_query_value_find(hreq->query, "end"); if (!param || safe_atoi32(param, &end_pos) != 0) { end_pos = start_pos + 1; @@ -2774,7 +2762,7 @@ jsonapi_reply_player_repeat(struct httpd_request *hreq) { const char *param; - param = evhttp_find_header(hreq->query, "state"); + param = httpd_query_value_find(hreq->query, "state"); if (!param) return HTTP_BADREQUEST; @@ -2800,7 +2788,7 @@ jsonapi_reply_player_shuffle(struct httpd_request *hreq) const char *param; bool shuffle; - param = evhttp_find_header(hreq->query, "state"); + param = httpd_query_value_find(hreq->query, "state"); if (!param) return HTTP_BADREQUEST; @@ -2816,7 +2804,7 @@ jsonapi_reply_player_consume(struct httpd_request *hreq) const char *param; bool consume; - param = evhttp_find_header(hreq->query, "state"); + param = httpd_query_value_find(hreq->query, "state"); if (!param) return HTTP_BADREQUEST; @@ -2895,7 +2883,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq) step = 0; // Parse and validate parameters - param_volume = evhttp_find_header(hreq->query, "volume"); + param_volume = httpd_query_value_find(hreq->query, "volume"); if (param_volume) { ret = safe_atoi32(param_volume, &volume); @@ -2903,7 +2891,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq) return HTTP_BADREQUEST; } - param_step = evhttp_find_header(hreq->query, "step"); + param_step = httpd_query_value_find(hreq->query, "step"); if (param_step) { ret = safe_atoi32(param_step, &step); @@ -2918,7 +2906,7 @@ jsonapi_reply_player_volume(struct httpd_request *hreq) return HTTP_BADREQUEST; } - param = evhttp_find_header(hreq->query, "output_id"); + param = httpd_query_value_find(hreq->query, "output_id"); if (param) { // Update volume for individual output @@ -2957,7 +2945,7 @@ jsonapi_reply_library_artists(struct httpd_request *hreq) return HTTP_NOTMODIFIED; media_kind = 0; - param = evhttp_find_header(hreq->query, "media_kind"); + param = httpd_query_value_find(hreq->query, "media_kind"); if (param) { media_kind = db_media_kind_enum(param); @@ -3106,7 +3094,7 @@ jsonapi_reply_library_albums(struct httpd_request *hreq) return HTTP_NOTMODIFIED; media_kind = 0; - param = evhttp_find_header(hreq->query, "media_kind"); + param = httpd_query_value_find(hreq->query, "media_kind"); if (param) { media_kind = db_media_kind_enum(param); @@ -3251,7 +3239,7 @@ jsonapi_reply_library_album_tracks_put_byid(struct httpd_request *hreq) if (ret < 0) return HTTP_INTERNAL; - param = evhttp_find_header(hreq->query, "play_count"); + param = httpd_query_value_find(hreq->query, "play_count"); if (!param) return HTTP_BADREQUEST; @@ -3327,7 +3315,6 @@ jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq) static int jsonapi_reply_library_tracks_put(struct httpd_request *hreq) { - struct evbuffer *in_evbuf; json_object *request = NULL; json_object *tracks; json_object *track = NULL; @@ -3337,8 +3324,7 @@ jsonapi_reply_library_tracks_put(struct httpd_request *hreq) int32_t track_id; int i; - in_evbuf = evhttp_request_get_input_buffer(hreq->req); - request = jparse_obj_from_evbuffer(in_evbuf); + request = jparse_obj_from_evbuffer(hreq->in_body); if (!request) { DPRINTF(E_LOG, L_WEB, "Failed to read json tracks request\n"); @@ -3417,7 +3403,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq) if (ret < 0) return HTTP_INTERNAL; - param = evhttp_find_header(hreq->query, "play_count"); + param = httpd_query_value_find(hreq->query, "play_count"); if (param) { if (strcmp(param, "increment") == 0) @@ -3435,7 +3421,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq) } } - param = evhttp_find_header(hreq->query, "rating"); + param = httpd_query_value_find(hreq->query, "rating"); if (param) { ret = safe_atou32(param, &val); @@ -3451,7 +3437,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq) } // Retreive marked tracks via "/api/search?type=tracks&expression=usermark+=+1" - param = evhttp_find_header(hreq->query, "usermark"); + param = httpd_query_value_find(hreq->query, "usermark"); if (param) { ret = safe_atou32(param, &val); @@ -3672,7 +3658,7 @@ jsonapi_reply_library_playlist_put(struct httpd_request *hreq) return HTTP_BADREQUEST; } - if ((param = evhttp_find_header(hreq->query, "query_limit"))) + if ((param = httpd_query_value_find(hreq->query, "query_limit"))) ret = playlist_attrib_query_limit_set(playlist_id, param); else ret = -1; @@ -3828,7 +3814,7 @@ jsonapi_reply_library_playlist_tracks_put_byid(struct httpd_request *hreq) if (ret < 0) return HTTP_INTERNAL; - param = evhttp_find_header(hreq->query, "play_count"); + param = httpd_query_value_find(hreq->query, "play_count"); if (!param) return HTTP_BADREQUEST; @@ -3857,7 +3843,7 @@ jsonapi_reply_queue_save(struct httpd_request *hreq) char *playlist_name = NULL; int ret = 0; - if ((param = evhttp_find_header(hreq->query, "name")) == NULL) + if ((param = httpd_query_value_find(hreq->query, "name")) == NULL) { DPRINTF(E_LOG, L_WEB, "Invalid argument, missing 'name'\n"); return HTTP_BADREQUEST; @@ -3914,7 +3900,7 @@ jsonapi_reply_library_browse(struct httpd_request *hreq) DPRINTF(E_DBG, L_WEB, "Browse query with type '%s'\n", browse_type); media_kind = 0; - param = evhttp_find_header(hreq->query, "media_kind"); + param = httpd_query_value_find(hreq->query, "media_kind"); if (param) { media_kind = db_media_kind_enum(param); @@ -4069,7 +4055,7 @@ jsonapi_reply_library_count(struct httpd_request *hreq) memset(&qp, 0, sizeof(struct query_params)); qp.type = Q_COUNT_ITEMS; - param_expression = evhttp_find_header(hreq->query, "expression"); + param_expression = httpd_query_value_find(hreq->query, "expression"); if (param_expression) { memset(&smartpl_expression, 0, sizeof(struct smartpl)); @@ -4123,7 +4109,7 @@ jsonapi_reply_library_files(struct httpd_request *hreq) int total; int ret; - param = evhttp_find_header(hreq->query, "directory"); + param = httpd_query_value_find(hreq->query, "directory"); directory_id = DIR_FILE; if (param) @@ -4215,7 +4201,7 @@ jsonapi_reply_library_add(struct httpd_request *hreq) const char *url; int ret; - url = evhttp_find_header(hreq->query, "url"); + url = httpd_query_value_find(hreq->query, "url"); if (!url) { DPRINTF(E_LOG, L_WEB, "Missing URL parameter for library add\n"); @@ -4528,9 +4514,9 @@ jsonapi_reply_search(struct httpd_request *hreq) reply = NULL; - param_type = evhttp_find_header(hreq->query, "type"); - param_query = evhttp_find_header(hreq->query, "query"); - param_expression = evhttp_find_header(hreq->query, "expression"); + param_type = httpd_query_value_find(hreq->query, "type"); + param_query = httpd_query_value_find(hreq->query, "query"); + param_expression = httpd_query_value_find(hreq->query, "expression"); if (!param_type || (!param_query && !param_expression)) { @@ -4540,7 +4526,7 @@ jsonapi_reply_search(struct httpd_request *hreq) } media_kind = 0; - param_media_kind = evhttp_find_header(hreq->query, "media_kind"); + param_media_kind = httpd_query_value_find(hreq->query, "media_kind"); if (param_media_kind) { media_kind = db_media_kind_enum(param_media_kind); @@ -4635,80 +4621,80 @@ jsonapi_reply_library_backup(struct httpd_request *hreq) static struct httpd_uri_map adm_handlers[] = { - { EVHTTP_REQ_GET, "^/api/config$", jsonapi_reply_config }, - { EVHTTP_REQ_GET, "^/api/settings$", jsonapi_reply_settings_get }, - { EVHTTP_REQ_GET, "^/api/settings/[A-Za-z0-9_]+$", jsonapi_reply_settings_category_get }, - { EVHTTP_REQ_GET, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_get }, - { EVHTTP_REQ_PUT, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_put }, - { EVHTTP_REQ_DELETE, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_delete }, - { EVHTTP_REQ_GET, "^/api/library$", jsonapi_reply_library }, - { EVHTTP_REQ_GET | - EVHTTP_REQ_PUT, "^/api/update$", jsonapi_reply_update }, - { EVHTTP_REQ_PUT, "^/api/rescan$", jsonapi_reply_meta_rescan }, - { EVHTTP_REQ_GET, "^/api/spotify-logout$", jsonapi_reply_spotify_logout }, - { EVHTTP_REQ_GET, "^/api/spotify$", jsonapi_reply_spotify }, - { EVHTTP_REQ_GET, "^/api/pairing$", jsonapi_reply_pairing_get }, - { EVHTTP_REQ_POST, "^/api/pairing$", jsonapi_reply_pairing_pair }, - { EVHTTP_REQ_POST, "^/api/lastfm-login$", jsonapi_reply_lastfm_login }, - { EVHTTP_REQ_GET, "^/api/lastfm-logout$", jsonapi_reply_lastfm_logout }, - { EVHTTP_REQ_GET, "^/api/lastfm$", jsonapi_reply_lastfm }, - { EVHTTP_REQ_POST, "^/api/verification$", jsonapi_reply_verification }, + { HTTPD_METHOD_GET, "^/api/config$", jsonapi_reply_config }, + { HTTPD_METHOD_GET, "^/api/settings$", jsonapi_reply_settings_get }, + { HTTPD_METHOD_GET, "^/api/settings/[A-Za-z0-9_]+$", jsonapi_reply_settings_category_get }, + { HTTPD_METHOD_GET, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_get }, + { HTTPD_METHOD_PUT, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_put }, + { HTTPD_METHOD_DELETE, "^/api/settings/[A-Za-z0-9_]+/[A-Za-z0-9_]+$", jsonapi_reply_settings_option_delete }, + { HTTPD_METHOD_GET, "^/api/library$", jsonapi_reply_library }, + { HTTPD_METHOD_GET | + HTTPD_METHOD_PUT, "^/api/update$", jsonapi_reply_update }, + { HTTPD_METHOD_PUT, "^/api/rescan$", jsonapi_reply_meta_rescan }, + { HTTPD_METHOD_GET, "^/api/spotify-logout$", jsonapi_reply_spotify_logout }, + { HTTPD_METHOD_GET, "^/api/spotify$", jsonapi_reply_spotify }, + { HTTPD_METHOD_GET, "^/api/pairing$", jsonapi_reply_pairing_get }, + { HTTPD_METHOD_POST, "^/api/pairing$", jsonapi_reply_pairing_pair }, + { HTTPD_METHOD_POST, "^/api/lastfm-login$", jsonapi_reply_lastfm_login }, + { HTTPD_METHOD_GET, "^/api/lastfm-logout$", jsonapi_reply_lastfm_logout }, + { HTTPD_METHOD_GET, "^/api/lastfm$", jsonapi_reply_lastfm }, + { HTTPD_METHOD_POST, "^/api/verification$", jsonapi_reply_verification }, - { EVHTTP_REQ_GET, "^/api/outputs$", jsonapi_reply_outputs }, - { EVHTTP_REQ_PUT, "^/api/outputs/set$", jsonapi_reply_outputs_set }, - { EVHTTP_REQ_POST, "^/api/select-outputs$", jsonapi_reply_outputs_set }, // deprecated: use "/api/outputs/set" - { EVHTTP_REQ_GET, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_get_byid }, - { EVHTTP_REQ_PUT, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_put_byid }, - { EVHTTP_REQ_PUT, "^/api/outputs/[[:digit:]]+/toggle$", jsonapi_reply_outputs_toggle_byid }, + { HTTPD_METHOD_GET, "^/api/outputs$", jsonapi_reply_outputs }, + { HTTPD_METHOD_PUT, "^/api/outputs/set$", jsonapi_reply_outputs_set }, + { HTTPD_METHOD_POST, "^/api/select-outputs$", jsonapi_reply_outputs_set }, // deprecated: use "/api/outputs/set" + { HTTPD_METHOD_GET, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_get_byid }, + { HTTPD_METHOD_PUT, "^/api/outputs/[[:digit:]]+$", jsonapi_reply_outputs_put_byid }, + { HTTPD_METHOD_PUT, "^/api/outputs/[[:digit:]]+/toggle$", jsonapi_reply_outputs_toggle_byid }, - { EVHTTP_REQ_GET, "^/api/player$", jsonapi_reply_player }, - { EVHTTP_REQ_PUT, "^/api/player/play$", jsonapi_reply_player_play }, - { EVHTTP_REQ_PUT, "^/api/player/pause$", jsonapi_reply_player_pause }, - { EVHTTP_REQ_PUT, "^/api/player/stop$", jsonapi_reply_player_stop }, - { EVHTTP_REQ_PUT, "^/api/player/toggle$", jsonapi_reply_player_toggle }, - { EVHTTP_REQ_PUT, "^/api/player/next$", jsonapi_reply_player_next }, - { EVHTTP_REQ_PUT, "^/api/player/previous$", jsonapi_reply_player_previous }, - { EVHTTP_REQ_PUT, "^/api/player/shuffle$", jsonapi_reply_player_shuffle }, - { EVHTTP_REQ_PUT, "^/api/player/repeat$", jsonapi_reply_player_repeat }, - { EVHTTP_REQ_PUT, "^/api/player/consume$", jsonapi_reply_player_consume }, - { EVHTTP_REQ_PUT, "^/api/player/volume$", jsonapi_reply_player_volume }, - { EVHTTP_REQ_PUT, "^/api/player/seek$", jsonapi_reply_player_seek }, + { HTTPD_METHOD_GET, "^/api/player$", jsonapi_reply_player }, + { HTTPD_METHOD_PUT, "^/api/player/play$", jsonapi_reply_player_play }, + { HTTPD_METHOD_PUT, "^/api/player/pause$", jsonapi_reply_player_pause }, + { HTTPD_METHOD_PUT, "^/api/player/stop$", jsonapi_reply_player_stop }, + { HTTPD_METHOD_PUT, "^/api/player/toggle$", jsonapi_reply_player_toggle }, + { HTTPD_METHOD_PUT, "^/api/player/next$", jsonapi_reply_player_next }, + { HTTPD_METHOD_PUT, "^/api/player/previous$", jsonapi_reply_player_previous }, + { HTTPD_METHOD_PUT, "^/api/player/shuffle$", jsonapi_reply_player_shuffle }, + { HTTPD_METHOD_PUT, "^/api/player/repeat$", jsonapi_reply_player_repeat }, + { HTTPD_METHOD_PUT, "^/api/player/consume$", jsonapi_reply_player_consume }, + { HTTPD_METHOD_PUT, "^/api/player/volume$", jsonapi_reply_player_volume }, + { HTTPD_METHOD_PUT, "^/api/player/seek$", jsonapi_reply_player_seek }, - { EVHTTP_REQ_GET, "^/api/queue$", jsonapi_reply_queue }, - { EVHTTP_REQ_PUT, "^/api/queue/clear$", jsonapi_reply_queue_clear }, - { EVHTTP_REQ_POST, "^/api/queue/items/add$", jsonapi_reply_queue_tracks_add }, - { EVHTTP_REQ_PUT, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_update }, - { EVHTTP_REQ_PUT, "^/api/queue/items/now_playing$", jsonapi_reply_queue_tracks_update }, - { EVHTTP_REQ_DELETE, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_delete }, - { EVHTTP_REQ_POST, "^/api/queue/save$", jsonapi_reply_queue_save}, + { HTTPD_METHOD_GET, "^/api/queue$", jsonapi_reply_queue }, + { HTTPD_METHOD_PUT, "^/api/queue/clear$", jsonapi_reply_queue_clear }, + { HTTPD_METHOD_POST, "^/api/queue/items/add$", jsonapi_reply_queue_tracks_add }, + { HTTPD_METHOD_PUT, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_update }, + { HTTPD_METHOD_PUT, "^/api/queue/items/now_playing$", jsonapi_reply_queue_tracks_update }, + { HTTPD_METHOD_DELETE, "^/api/queue/items/[[:digit:]]+$", jsonapi_reply_queue_tracks_delete }, + { HTTPD_METHOD_POST, "^/api/queue/save$", jsonapi_reply_queue_save}, - { EVHTTP_REQ_GET, "^/api/library/playlists$", jsonapi_reply_library_playlists }, - { EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_get }, - { EVHTTP_REQ_PUT, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_put }, - { EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks }, - { EVHTTP_REQ_PUT, "^/api/library/playlists/[[:digit:]]+/tracks", jsonapi_reply_library_playlist_tracks_put_byid}, -// { EVHTTP_REQ_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks }, - { EVHTTP_REQ_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_delete }, - { EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/playlists", jsonapi_reply_library_playlist_playlists }, - { EVHTTP_REQ_GET, "^/api/library/artists$", jsonapi_reply_library_artists }, - { EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist }, - { EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums }, - { EVHTTP_REQ_GET, "^/api/library/albums$", jsonapi_reply_library_albums }, - { EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+$", jsonapi_reply_library_album }, - { EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks }, - { EVHTTP_REQ_PUT, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks_put_byid }, - { EVHTTP_REQ_PUT, "^/api/library/tracks$", jsonapi_reply_library_tracks_put }, - { EVHTTP_REQ_GET, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_get_byid }, - { EVHTTP_REQ_PUT, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_put_byid }, - { EVHTTP_REQ_GET, "^/api/library/tracks/[[:digit:]]+/playlists$", jsonapi_reply_library_track_playlists }, - { EVHTTP_REQ_GET, "^/api/library/(genres|composers)$", jsonapi_reply_library_browse }, - { EVHTTP_REQ_GET, "^/api/library/(genres|composers)/.*$", jsonapi_reply_library_browseitem }, - { EVHTTP_REQ_GET, "^/api/library/count$", jsonapi_reply_library_count }, - { EVHTTP_REQ_GET, "^/api/library/files$", jsonapi_reply_library_files }, - { EVHTTP_REQ_POST, "^/api/library/add$", jsonapi_reply_library_add }, - { EVHTTP_REQ_PUT, "^/api/library/backup$", jsonapi_reply_library_backup }, + { HTTPD_METHOD_GET, "^/api/library/playlists$", jsonapi_reply_library_playlists }, + { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_get }, + { HTTPD_METHOD_PUT, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_put }, + { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks }, + { HTTPD_METHOD_PUT, "^/api/library/playlists/[[:digit:]]+/tracks", jsonapi_reply_library_playlist_tracks_put_byid}, +// { HTTPD_METHOD_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks }, + { HTTPD_METHOD_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_delete }, + { HTTPD_METHOD_GET, "^/api/library/playlists/[[:digit:]]+/playlists", jsonapi_reply_library_playlist_playlists }, + { HTTPD_METHOD_GET, "^/api/library/artists$", jsonapi_reply_library_artists }, + { HTTPD_METHOD_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist }, + { HTTPD_METHOD_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums }, + { HTTPD_METHOD_GET, "^/api/library/albums$", jsonapi_reply_library_albums }, + { HTTPD_METHOD_GET, "^/api/library/albums/[[:digit:]]+$", jsonapi_reply_library_album }, + { HTTPD_METHOD_GET, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks }, + { HTTPD_METHOD_PUT, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks_put_byid }, + { HTTPD_METHOD_PUT, "^/api/library/tracks$", jsonapi_reply_library_tracks_put }, + { HTTPD_METHOD_GET, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_get_byid }, + { HTTPD_METHOD_PUT, "^/api/library/tracks/[[:digit:]]+$", jsonapi_reply_library_tracks_put_byid }, + { HTTPD_METHOD_GET, "^/api/library/tracks/[[:digit:]]+/playlists$", jsonapi_reply_library_track_playlists }, + { HTTPD_METHOD_GET, "^/api/library/(genres|composers)$", jsonapi_reply_library_browse }, + { HTTPD_METHOD_GET, "^/api/library/(genres|composers)/.*$", jsonapi_reply_library_browseitem }, + { HTTPD_METHOD_GET, "^/api/library/count$", jsonapi_reply_library_count }, + { HTTPD_METHOD_GET, "^/api/library/files$", jsonapi_reply_library_files }, + { HTTPD_METHOD_POST, "^/api/library/add$", jsonapi_reply_library_add }, + { HTTPD_METHOD_PUT, "^/api/library/backup$", jsonapi_reply_library_backup }, - { EVHTTP_REQ_GET, "^/api/search$", jsonapi_reply_search }, + { HTTPD_METHOD_GET, "^/api/search$", jsonapi_reply_search }, { 0, NULL, NULL } }; @@ -4720,7 +4706,7 @@ static void jsonapi_request(struct httpd_request *hreq) { ; - struct evkeyvalq *headers; + httpd_headers *headers; int status_code; DPRINTF(E_DBG, L_WEB, "JSON api request: '%s'\n", hreq->uri); @@ -4747,8 +4733,8 @@ jsonapi_request(struct httpd_request *hreq) switch (status_code) { case HTTP_OK: /* 200 OK */ - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(headers, "Content-Type", "application/json"); + headers = httpd_request_output_headers_get(hreq); + httpd_header_add(headers, "Content-Type", "application/json"); httpd_send_reply(hreq, status_code, "OK", hreq->reply, HTTPD_SEND_NO_GZIP); break; case HTTP_NOCONTENT: /* 204 No Content */ diff --git a/src/httpd_libevhttp.c b/src/httpd_libevhttp.c new file mode 100644 index 00000000..44636551 --- /dev/null +++ b/src/httpd_libevhttp.c @@ -0,0 +1,163 @@ +#include + +#include +#include + +#include "httpd_internal.h" + +int +httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg) +{ + evhttp_connection_set_closecb(conn, cb, arg); + return 0; +} +/* +int +httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn) +{ + return evhttp_connection_get_peer(conn, addr, port); +} +*/ +void +httpd_connection_free(httpd_connection *conn) +{ + evhttp_connection_free(conn); +} + +httpd_connection * +httpd_request_connection_get(struct httpd_request *hreq) +{ + return evhttp_request_get_connection(hreq->req); +} +/* +const char * +httpd_request_uri_get(httpd_request *req) +{ + return evhttp_request_get_uri(req); +} + +int +httpd_request_peer_get(char **addr, uint16_t *port, httpd_request *req) +{ + httpd_connection *conn = httpd_request_connection_get(req); + if (!conn) + return -1; + + return httpd_connection_peer_get(addr, port, conn); +} +*/ +int +httpd_request_method_get(enum httpd_methods *method, struct httpd_request *hreq) +{ + enum evhttp_cmd_type cmd = evhttp_request_get_command(hreq->req); + + switch (cmd) + { + case EVHTTP_REQ_GET: *method = HTTPD_METHOD_GET; break; + case EVHTTP_REQ_POST: *method = HTTPD_METHOD_POST; break; + case EVHTTP_REQ_HEAD: *method = HTTPD_METHOD_HEAD; break; + case EVHTTP_REQ_PUT: *method = HTTPD_METHOD_PUT; break; + case EVHTTP_REQ_DELETE: *method = HTTPD_METHOD_DELETE; break; + case EVHTTP_REQ_OPTIONS: *method = HTTPD_METHOD_OPTIONS; break; + case EVHTTP_REQ_TRACE: *method = HTTPD_METHOD_TRACE; break; + case EVHTTP_REQ_CONNECT: *method = HTTPD_METHOD_CONNECT; break; + case EVHTTP_REQ_PATCH: *method = HTTPD_METHOD_PATCH; break; + default: *method = HTTPD_METHOD_GET; return -1; + } + + return 0; +} + +void +httpd_request_backend_free(struct httpd_request *hreq) +{ + evhttp_request_free(hreq->req); +} + +int +httpd_request_closecb_set(struct httpd_request *hreq, httpd_connection_closecb cb, void *arg) +{ + httpd_connection *conn = httpd_request_connection_get(hreq); + if (!conn) + return -1; + + return httpd_connection_closecb_set(conn, cb, arg); +} + +const char * +httpd_query_value_find(httpd_query *query, const char *key) +{ + return evhttp_find_header(query, key); +} + +void +httpd_query_iterate(httpd_query *query, httpd_query_iteratecb cb, void *arg) +{ + struct evkeyval *param; + + TAILQ_FOREACH(param, query, next) + { + cb(param->key, param->value, arg); + } +} + +const char * +httpd_header_find(httpd_headers *headers, const char *key) +{ + return evhttp_find_header(headers, key); +} + +void +httpd_header_remove(httpd_headers *headers, const char *key) +{ + evhttp_remove_header(headers, key); +} + +void +httpd_header_add(httpd_headers *headers, const char *key, const char *val) +{ + evhttp_add_header(headers, key, val); +} + +void +httpd_headers_clear(httpd_headers *headers) +{ + evhttp_clear_headers(headers); +} + +httpd_headers * +httpd_request_input_headers_get(struct httpd_request *hreq) +{ + return evhttp_request_get_input_headers(hreq->req); +} + +httpd_headers * +httpd_request_output_headers_get(struct httpd_request *hreq) +{ + return evhttp_request_get_output_headers(hreq->req); +} + +/* +void +httpd_reply_send(httpd_request *req, int code, const char *reason, struct evbuffer *evbuf) +{ + evhttp_send_reply(req, code, reason, evbuf); +} +*/ +void +httpd_reply_start_send(struct httpd_request *hreq, int code, const char *reason) +{ + evhttp_send_reply_start(hreq->req, code, reason); +} + +void +httpd_reply_chunk_send(struct httpd_request *hreq, struct evbuffer *evbuf, httpd_connection_chunkcb cb, void *arg) +{ + evhttp_send_reply_chunk_with_cb(hreq->req, evbuf, cb, arg); +} + +void +httpd_reply_end_send(struct httpd_request *hreq) +{ + evhttp_send_reply_end(hreq->req); +} diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index 70691c8c..09a7d25c 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -160,7 +160,7 @@ static void rsp_send_error(struct httpd_request *hreq, char *errmsg) { struct evbuffer *evbuf; - struct evkeyvalq *headers; + httpd_headers *headers; mxml_node_t *reply; mxml_node_t *status; mxml_node_t *node; @@ -196,9 +196,9 @@ rsp_send_error(struct httpd_request *hreq, char *errmsg) return; } - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); - evhttp_add_header(headers, "Connection", "close"); + headers = httpd_request_output_headers_get(hreq); + httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8"); + httpd_header_add(headers, "Connection", "close"); httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, HTTPD_SEND_NO_GZIP); @@ -214,7 +214,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq) int ret; qp->offset = 0; - param = evhttp_find_header(hreq->query, "offset"); + param = httpd_query_value_find(hreq->query, "offset"); if (param) { ret = safe_atoi32(param, &qp->offset); @@ -226,7 +226,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq) } qp->limit = 0; - param = evhttp_find_header(hreq->query, "limit"); + param = httpd_query_value_find(hreq->query, "limit"); if (param) { ret = safe_atoi32(param, &qp->limit); @@ -243,7 +243,7 @@ query_params_set(struct query_params *qp, struct httpd_request *hreq) qp->idx_type = I_NONE; qp->filter = NULL; - param = evhttp_find_header(hreq->query, "query"); + param = httpd_query_value_find(hreq->query, "query"); if (param) { ret = snprintf(query, sizeof(query), "%s", param); @@ -280,7 +280,7 @@ static void rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply) { struct evbuffer *evbuf; - struct evkeyvalq *headers; + httpd_headers *headers; evbuf = mxml_to_evbuf(reply); mxmlDelete(reply); @@ -292,9 +292,9 @@ rsp_send_reply(struct httpd_request *hreq, mxml_node_t *reply) return; } - headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(headers, "Content-Type", "text/xml; charset=utf-8"); - evhttp_add_header(headers, "Connection", "close"); + headers = httpd_request_output_headers_get(hreq); + httpd_header_add(headers, "Content-Type", "text/xml; charset=utf-8"); + httpd_header_add(headers, "Connection", "close"); httpd_send_reply(hreq, HTTP_OK, "OK", evbuf, 0); @@ -488,7 +488,6 @@ rsp_reply_playlist(struct httpd_request *hreq) { struct query_params qp; struct db_media_file_info dbmfi; - struct evkeyvalq *headers; const char *param; const char *ua; const char *client_codecs; @@ -522,7 +521,7 @@ rsp_reply_playlist(struct httpd_request *hreq) qp.sort = S_NAME; mode = F_FULL; - param = evhttp_find_header(hreq->query, "type"); + param = httpd_query_value_find(hreq->query, "type"); if (param) { if (strcasecmp(param, "full") == 0) @@ -586,10 +585,8 @@ rsp_reply_playlist(struct httpd_request *hreq) /* Items block (all items) */ while ((ret = db_query_fetch_file(&dbmfi, &qp)) == 0) { - headers = evhttp_request_get_input_headers(hreq->req); - - ua = evhttp_find_header(headers, "User-Agent"); - client_codecs = evhttp_find_header(headers, "Accept-Codecs"); + ua = httpd_header_find(hreq->in_headers, "User-Agent"); + client_codecs = httpd_header_find(hreq->in_headers, "Accept-Codecs"); transcode = transcode_needed(ua, client_codecs, dbmfi.codectype); diff --git a/src/httpd_streaming.c b/src/httpd_streaming.c index 4e8b9485..56ad2c6d 100644 --- a/src/httpd_streaming.c +++ b/src/httpd_streaming.c @@ -58,7 +58,7 @@ extern struct event_base *evbase_httpd; // Linked list of mp3 streaming requests struct streaming_session { - struct evhttp_request *req; + struct httpd_request *hreq; struct streaming_session *next; bool require_icy; // Client requested icy meta @@ -102,18 +102,15 @@ static char streaming_icy_title[STREAMING_ICY_METATITLELEN_MAX]; static void -streaming_close_cb(struct evhttp_connection *evcon, void *arg) +streaming_close_cb(httpd_connection *conn, void *arg) { struct streaming_session *this; struct streaming_session *session; struct streaming_session *prev; - char *address; - ev_uint16_t port; this = (struct streaming_session *)arg; - evhttp_connection_get_peer(evcon, &address, &port); - DPRINTF(E_INFO, L_STREAMING, "Stopping mp3 streaming to %s:%d\n", address, (int)port); + DPRINTF(E_INFO, L_STREAMING, "Stopping mp3 streaming to %s:%d\n", this->hreq->peer_address, (int)this->hreq->peer_port); pthread_mutex_lock(&streaming_sessions_lck); if (!streaming_sessions) @@ -127,7 +124,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg) prev = NULL; for (session = streaming_sessions; session; session = session->next) { - if (session->req == this->req) + if (session->hreq == this->hreq) break; prev = session; @@ -135,7 +132,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg) if (!session) { - DPRINTF(E_LOG, L_STREAMING, "Bug! Got a failure callback for an unknown stream (%s:%d)\n", address, (int)port); + DPRINTF(E_LOG, L_STREAMING, "Bug! Got a failure callback for an unknown stream (%s:%d)\n", this->hreq->peer_address, (int)this->hreq->peer_port); free(this); pthread_mutex_unlock(&streaming_sessions_lck); return; @@ -151,7 +148,7 @@ streaming_close_cb(struct evhttp_connection *evcon, void *arg) // Valgrind says libevent doesn't free the request on disconnect (even though it owns it - libevent bug?), // so we do it with a reply end - evhttp_send_reply_end(session->req); + httpd_reply_end_send(session->hreq); free(session); if (!streaming_sessions) @@ -168,21 +165,14 @@ static void streaming_end(void) { struct streaming_session *session; - struct evhttp_connection *evcon; - char *address; - ev_uint16_t port; pthread_mutex_lock(&streaming_sessions_lck); for (session = streaming_sessions; streaming_sessions; session = streaming_sessions) { - evcon = evhttp_request_get_connection(session->req); - if (evcon) - { - evhttp_connection_set_closecb(evcon, NULL, NULL); - evhttp_connection_get_peer(evcon, &address, &port); - DPRINTF(E_INFO, L_STREAMING, "Force close stream to %s:%d\n", address, (int)port); - } - evhttp_send_reply_end(session->req); + DPRINTF(E_INFO, L_STREAMING, "Force close stream to %s:%d\n", session->hreq->peer_address, (int)session->hreq->peer_port); + + httpd_request_closecb_set(session->hreq, NULL, NULL); + httpd_reply_end_send(session->hreq); streaming_sessions = session->next; free(session); @@ -450,7 +440,7 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg) free(splice_buf); splice_buf = NULL; - evhttp_send_reply_chunk(session->req, evbuf); + httpd_reply_chunk_send(session->hreq, evbuf, NULL, NULL); if (session->next == NULL) { @@ -465,11 +455,11 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg) { buf = evbuffer_pullup(streaming_encoded_data, -1); evbuffer_add(evbuf, buf, len); - evhttp_send_reply_chunk(session->req, evbuf); + httpd_reply_chunk_send(session->hreq, evbuf, NULL, NULL); } else { - evhttp_send_reply_chunk(session->req, streaming_encoded_data); + httpd_reply_chunk_send(session->hreq, streaming_encoded_data, NULL, NULL); } session->bytes_sent += len; } @@ -547,12 +537,9 @@ static void streaming_request(struct httpd_request *hreq) { struct streaming_session *session; - struct evhttp_connection *evcon; - struct evkeyvalq *output_headers; + httpd_headers *output_headers; cfg_t *lib; const char *name; - char *address; - ev_uint16_t port; const char *param; bool require_icy = false; char buf[9]; @@ -561,45 +548,43 @@ streaming_request(struct httpd_request *hreq) { DPRINTF(E_LOG, L_STREAMING, "Got MP3 streaming request, but cannot encode to MP3\n"); - evhttp_send_error(hreq->req, HTTP_NOTFOUND, "Not Found"); + httpd_send_error(hreq, HTTP_NOTFOUND, "Not Found"); return; } - evcon = evhttp_request_get_connection(hreq->req); - evhttp_connection_get_peer(evcon, &address, &port); - param = evhttp_find_header( evhttp_request_get_input_headers(hreq->req), "Icy-MetaData"); + param = httpd_header_find(hreq->in_headers, "Icy-MetaData"); if (param && strcmp(param, "1") == 0) require_icy = true; - DPRINTF(E_INFO, L_STREAMING, "Beginning mp3 streaming (with icy=%d, icy_metaint=%d) to %s:%d\n", require_icy, streaming_icy_metaint, address, (int)port); + DPRINTF(E_INFO, L_STREAMING, "Beginning mp3 streaming (with icy=%d, icy_metaint=%d) to %s:%d\n", require_icy, streaming_icy_metaint, hreq->peer_address, (int)hreq->peer_port); lib = cfg_getsec(cfg, "library"); name = cfg_getstr(lib, "name"); - output_headers = evhttp_request_get_output_headers(hreq->req); - evhttp_add_header(output_headers, "Content-Type", "audio/mpeg"); - evhttp_add_header(output_headers, "Server", PACKAGE_NAME "/" VERSION); - evhttp_add_header(output_headers, "Cache-Control", "no-cache"); - evhttp_add_header(output_headers, "Pragma", "no-cache"); - evhttp_add_header(output_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT"); + output_headers = httpd_request_output_headers_get(hreq); + httpd_header_add(output_headers, "Content-Type", "audio/mpeg"); + httpd_header_add(output_headers, "Server", PACKAGE_NAME "/" VERSION); + httpd_header_add(output_headers, "Cache-Control", "no-cache"); + httpd_header_add(output_headers, "Pragma", "no-cache"); + httpd_header_add(output_headers, "Expires", "Mon, 31 Aug 2015 06:00:00 GMT"); if (require_icy) { ++streaming_icy_clients; - evhttp_add_header(output_headers, "icy-name", name); + httpd_header_add(output_headers, "icy-name", name); snprintf(buf, sizeof(buf)-1, "%d", streaming_icy_metaint); - evhttp_add_header(output_headers, "icy-metaint", buf); + httpd_header_add(output_headers, "icy-metaint", buf); } - evhttp_add_header(output_headers, "Access-Control-Allow-Origin", "*"); - evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); + httpd_header_add(output_headers, "Access-Control-Allow-Origin", "*"); + httpd_header_add(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); - evhttp_send_reply_start(hreq->req, HTTP_OK, "OK"); + httpd_reply_start_send(hreq, HTTP_OK, "OK"); session = calloc(1, sizeof(struct streaming_session)); if (!session) { DPRINTF(E_LOG, L_STREAMING, "Out of memory for streaming request\n"); - evhttp_send_error(hreq->req, HTTP_SERVUNAVAIL, "Internal Server Error"); + httpd_send_error(hreq, HTTP_SERVUNAVAIL, "Internal Server Error"); return; } @@ -611,7 +596,7 @@ streaming_request(struct httpd_request *hreq) event_add(metaev, NULL); } - session->req = hreq->req; + session->hreq = hreq; session->next = streaming_sessions; session->require_icy = require_icy; session->bytes_sent = 0; @@ -619,7 +604,7 @@ streaming_request(struct httpd_request *hreq) pthread_mutex_unlock(&streaming_sessions_lck); - evhttp_connection_set_closecb(evcon, streaming_close_cb, session); + httpd_request_closecb_set(hreq, streaming_close_cb, session); } static int