[httpd] Remove all traces of evhttp from httpd modules

This commit is contained in:
ejurgensen 2022-12-21 22:56:34 +01:00
parent 2b4ff98652
commit db25a714bd
10 changed files with 563 additions and 363 deletions

View File

@ -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 \

View File

@ -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)

View File

@ -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 }
};

View File

@ -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/<type> 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);

View File

@ -26,7 +26,6 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <stdint.h>
#include <inttypes.h>
@ -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);

View File

@ -7,6 +7,26 @@
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
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__ */

View File

@ -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 */

163
src/httpd_libevhttp.c Normal file
View File

@ -0,0 +1,163 @@
#include <sys/queue.h>
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#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);
}

View File

@ -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);

View File

@ -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