[httpd] Abstract parsing of URIs

This commit is contained in:
ejurgensen 2022-12-25 21:20:27 +01:00
parent 816f2c09b5
commit 96ce7c52c8
8 changed files with 226 additions and 225 deletions

View File

@ -40,9 +40,7 @@
# include <sys/eventfd.h>
#endif
#include <event2/event.h>
#include <event2/http.h>
#include <regex.h>
#include <zlib.h>
#include "logger.h"
@ -290,120 +288,18 @@ modules_search(const char *path)
/* --------------------------- REQUEST HELPERS ------------------------------ */
static void
uri_parsed_free(struct httpd_uri_parsed *parsed)
{
int i;
if (!parsed)
return;
free(parsed->uri_decoded);
free(parsed->path);
for (i = 0; i < ARRAY_SIZE(parsed->path_parts); i++)
{
free(parsed->path_parts[i]);
}
httpd_query_clear(&(parsed->query));
if (parsed->ev_uri)
evhttp_uri_free(parsed->ev_uri);
free(parsed);
}
static struct httpd_uri_parsed *
uri_parsed_create(const char *uri)
{
struct httpd_uri_parsed *parsed;
char *path = NULL;
const char *query;
char *path_part;
char *ptr;
int i;
int ret;
CHECK_NULL(L_HTTPD, parsed = calloc(1, sizeof(struct httpd_uri_parsed)));
parsed->uri = uri;
parsed->ev_uri = evhttp_uri_parse_with_flags(parsed->uri, EVHTTP_URI_NONCONFORMANT);
if (!parsed->ev_uri)
{
DPRINTF(E_LOG, L_HTTPD, "Could not parse request: '%s'\n", parsed->uri);
goto error;
}
parsed->uri_decoded = evhttp_uridecode(parsed->uri, 0, NULL);
if (!parsed->uri_decoded)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode request: '%s'\n", parsed->uri);
goto error;
}
query = evhttp_uri_get_query(parsed->ev_uri);
if (query && strchr(query, '='))
{
ret = evhttp_parse_query_str(query, &(parsed->query));
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Invalid query '%s' in request: '%s'\n", query, parsed->uri);
goto error;
}
}
path = strdup(evhttp_uri_get_path(parsed->ev_uri));
if (!path)
{
DPRINTF(E_WARN, L_HTTPD, "No path in request: '%s'\n", parsed->uri);
return parsed;
}
parsed->path = evhttp_uridecode(path, 0, NULL);
if (!parsed->path)
{
DPRINTF(E_LOG, L_HTTPD, "Could not URI decode path: '%s'\n", path);
goto error;
}
path_part = strtok_r(path, "/", &ptr);
for (i = 0; (i < ARRAY_SIZE(parsed->path_parts) && path_part); i++)
{
parsed->path_parts[i] = evhttp_uridecode(path_part, 0, NULL);
path_part = strtok_r(NULL, "/", &ptr);
}
if (path_part)
{
// If "path_part" is not NULL, we have path tokens that could not be parsed into the "parsed->path_parts" array
DPRINTF(E_LOG, L_HTTPD, "URI path has too many components (%d): '%s'\n", i, parsed->path);
goto error;
}
free(path);
return parsed;
error:
free(path);
uri_parsed_free(parsed);
return NULL;
}
static void
request_unset(struct httpd_request *hreq)
{
if (hreq->out_body)
evbuffer_free(hreq->out_body);
uri_parsed_free(hreq->uri_parsed);
httpd_uri_parsed_free(hreq->uri_parsed);
}
static void
request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri, const char *user_agent)
{
struct httpd_uri_parsed *uri_parsed;
struct httpd_uri_map *map;
int ret;
@ -432,17 +328,19 @@ request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri,
// buffer even if there is no backend.
CHECK_NULL(L_HTTPD, hreq->out_body = evbuffer_new());
uri_parsed = uri_parsed_create(hreq->uri);
if (!uri_parsed)
hreq->uri_parsed = httpd_uri_parsed_create(hreq->uri);
if (!hreq->uri_parsed)
{
DPRINTF(E_LOG, L_HTTPD, "Unable to parse URI '%s' in request from '%s'\n", hreq->uri, hreq->peer_address);
return;
}
hreq->uri_parsed = uri_parsed;
hreq->query = &(hreq->uri_parsed->query);
hreq->path = httpd_uri_path_get(hreq->uri_parsed);
hreq->query = httpd_uri_query_get(hreq->uri_parsed);
httpd_uri_path_parts_get(&hreq->path_parts, hreq->uri_parsed);
// Path with e.g. /api -> JSON module
hreq->module = modules_search(uri_parsed->path);
hreq->module = modules_search(hreq->path);
if (!hreq->module)
{
return;
@ -454,7 +352,7 @@ request_set(struct httpd_request *hreq, httpd_backend *backend, const char *uri,
if (map->method && hreq->method && !(map->method & hreq->method))
continue;
ret = regexec(map->preg, uri_parsed->path, 0, NULL, 0);
ret = regexec(map->preg, hreq->path, 0, NULL, 0);
if (ret != 0)
continue;
@ -564,7 +462,7 @@ serve_file(struct httpd_request *hreq)
if (!httpd_admin_check_auth(hreq))
return;
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, hreq->uri_parsed->path);
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, hreq->path);
if ((ret < 0) || (ret >= sizeof(path)))
{
DPRINTF(E_LOG, L_HTTPD, "Request exceeds PATH_MAX: %s\n", hreq->uri);
@ -968,7 +866,7 @@ httpd_gen_cb(httpd_backend *backend, void *arg)
httpd_redirect_to(&hreq, "/");
goto out;
}
else if (!(&hreq)->uri_parsed->path)
else if (!(&hreq)->path)
{
DPRINTF(E_WARN, L_HTTPD, "Invalid path in request: '%s'\n", (&hreq)->uri);
httpd_redirect_to(&hreq, "/");

View File

@ -20,7 +20,6 @@
# include <config.h>
#endif
#include <regex.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
@ -105,7 +104,7 @@ artworkapi_reply_item(struct httpd_request *hreq)
if (ret != 0)
return ret;
ret = safe_atou32(hreq->uri_parsed->path_parts[2], &id);
ret = safe_atou32(hreq->path_parts[2], &id);
if (ret != 0)
return HTTP_BADREQUEST;
@ -126,7 +125,7 @@ artworkapi_reply_group(struct httpd_request *hreq)
if (ret != 0)
return ret;
ret = safe_atou32(hreq->uri_parsed->path_parts[2], &id);
ret = safe_atou32(hreq->path_parts[2], &id);
if (ret != 0)
return HTTP_BADREQUEST;

View File

@ -713,7 +713,7 @@ daap_request_authorize(struct httpd_request *hreq)
// with httpd_basic_auth() if a library password is set. Remote clients will
// also call /login, but they should not get a httpd_basic_auth(), instead
// daap_reply_login() will take care of auth.
if (session->is_remote && (strcmp(hreq->uri_parsed->path, "/login") == 0))
if (session->is_remote && (strcmp(hreq->path, "/login") == 0))
return 0;
param = httpd_query_value_find(hreq->query, "session-id");
@ -721,7 +721,7 @@ daap_request_authorize(struct httpd_request *hreq)
{
if (session->id == 0)
{
DPRINTF(E_LOG, L_DAAP, "Unauthorized request from '%s', DAAP session not found: '%s'\n", hreq->peer_address, hreq->uri_parsed->uri);
DPRINTF(E_LOG, L_DAAP, "Unauthorized request from '%s', DAAP session not found: '%s'\n", hreq->peer_address, hreq->uri);
httpd_send_error(hreq, 401, "Unauthorized");
return -1;
@ -736,10 +736,10 @@ daap_request_authorize(struct httpd_request *hreq)
return 0;
// If no valid session then we may need to authenticate
if ((strcmp(hreq->uri_parsed->path, "/server-info") == 0)
|| (strcmp(hreq->uri_parsed->path, "/logout") == 0)
|| (strcmp(hreq->uri_parsed->path, "/content-codes") == 0)
|| (strncmp(hreq->uri_parsed->path, "/databases/1/items/", strlen("/databases/1/items/")) == 0))
if ((strcmp(hreq->path, "/server-info") == 0)
|| (strcmp(hreq->path, "/logout") == 0)
|| (strcmp(hreq->path, "/content-codes") == 0)
|| (strncmp(hreq->path, "/databases/1/items/", strlen("/databases/1/items/")) == 0))
return 0; // No authentication
DPRINTF(E_DBG, L_DAAP, "Checking authentication for library\n");
@ -1354,7 +1354,7 @@ daap_reply_plsonglist(struct httpd_request *hreq)
int playlist;
int ret;
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist);
ret = safe_atoi32(hreq->path_parts[3], &playlist);
if (ret < 0)
{
dmap_error_make(hreq->out_body, "apso", "Invalid playlist ID");
@ -1400,7 +1400,7 @@ daap_reply_playlists(struct httpd_request *hreq)
cfg_radiopl = cfg_getbool(cfg_getsec(cfg, "library"), "radio_playlists");
ret = safe_atoi32(hreq->uri_parsed->path_parts[1], &database);
ret = safe_atoi32(hreq->path_parts[1], &database);
if (ret < 0)
{
dmap_error_make(hreq->out_body, "aply", "Invalid database ID");
@ -1824,29 +1824,29 @@ daap_reply_browse(struct httpd_request *hreq)
int nitems;
int ret;
if (strcmp(hreq->uri_parsed->path_parts[3], "artists") == 0)
if (strcmp(hreq->path_parts[3], "artists") == 0)
{
tag = "abar";
query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_ARTISTS);
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "albums") == 0)
else if (strcmp(hreq->path_parts[3], "albums") == 0)
{
tag = "abal";
query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_ALBUMS);
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "genres") == 0)
else if (strcmp(hreq->path_parts[3], "genres") == 0)
{
tag = "abgn";
query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_GENRES);
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "composers") == 0)
else if (strcmp(hreq->path_parts[3], "composers") == 0)
{
tag = "abcp";
query_params_set(&qp, &sort_headers, hreq, Q_BROWSE_COMPOSERS);
}
else
{
DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", hreq->uri_parsed->path_parts[3]);
DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", hreq->path_parts[3]);
dmap_error_make(hreq->out_body, "abro", "Invalid browse type");
return DAAP_REPLY_ERROR;
}
@ -1950,10 +1950,10 @@ daap_reply_extra_data(struct httpd_request *hreq)
return DAAP_REPLY_NO_CONNECTION;
}
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &id);
ret = safe_atoi32(hreq->path_parts[3], &id);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not convert id parameter to integer: '%s'\n", hreq->uri_parsed->path_parts[3]);
DPRINTF(E_LOG, L_DAAP, "Could not convert id parameter to integer: '%s'\n", hreq->path_parts[3]);
return DAAP_REPLY_BAD_REQUEST;
}
@ -1983,9 +1983,9 @@ daap_reply_extra_data(struct httpd_request *hreq)
max_h = 0;
}
if (strcmp(hreq->uri_parsed->path_parts[2], "groups") == 0)
if (strcmp(hreq->path_parts[2], "groups") == 0)
ret = artwork_get_group(hreq->out_body, id, max_w, max_h, 0);
else if (strcmp(hreq->uri_parsed->path_parts[2], "items") == 0)
else if (strcmp(hreq->path_parts[2], "items") == 0)
ret = artwork_get_item(hreq->out_body, id, max_w, max_h, 0);
len = evbuffer_get_length(hreq->out_body);
@ -2030,7 +2030,7 @@ daap_stream(struct httpd_request *hreq)
return DAAP_REPLY_NO_CONNECTION;
}
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &id);
ret = safe_atoi32(hreq->path_parts[3], &id);
if (ret < 0)
return DAAP_REPLY_BAD_REQUEST;

View File

@ -653,7 +653,7 @@ dacp_request_authorize(struct httpd_request *hreq)
return 0;
invalid:
DPRINTF(E_LOG, L_DACP, "Unauthorized request '%s' from '%s' (is peer trusted in your config?)\n", hreq->uri_parsed->uri, hreq->peer_address);
DPRINTF(E_LOG, L_DACP, "Unauthorized request '%s' from '%s' (is peer trusted in your config?)\n", hreq->uri, hreq->peer_address);
httpd_send_error(hreq, 403, "Forbidden");
return -1;

View File

@ -5,17 +5,25 @@
#include <stdbool.h>
#include <time.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
struct httpd_request;
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#include <regex.h> // evhtp conflicts with regex since it brings it own
typedef struct evhttp httpd_server;
typedef struct evhttp_connection httpd_connection;
typedef struct evhttp_request httpd_backend;
typedef struct evkeyvalq httpd_headers;
typedef struct evkeyvalq httpd_query;
typedef struct httpd_uri_parsed httpd_uri_parsed;
typedef char *httpd_uri_path_parts[31];
typedef void (*httpd_general_cb)(httpd_backend *backend, void *arg);
typedef void (*httpd_connection_closecb)(httpd_connection *conn, void *arg);
typedef void (*httpd_connection_chunkcb)(httpd_connection *conn, void *arg);
@ -86,29 +94,6 @@ struct httpd_uri_map
/*------------------------------- HTTPD STRUCTS ------------------------------*/
/*
* Contains a parsed version of the URI httpd got. The URI may have been
* complete:
* scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
* or relative:
* [/path][?query][#fragment]
*
* We are interested in the path and the query, so they are disassembled to
* path_parts and ev_query. If the request is http://x:3689/foo/bar?key1=val1,
* then part_parts[0] is "foo", [1] is "bar" and the rest is null.
*
* Each path_part is an allocated URI decoded string.
*/
struct httpd_uri_parsed
{
const char *uri;
struct evhttp_uri *ev_uri;
httpd_query query;
char *uri_decoded;
char *path;
char *path_parts[31];
};
/*
* A collection of pointers to request data that the reply handlers may need.
* Also has the function pointer to the reply handler and a pointer to a reply
@ -117,22 +102,32 @@ struct httpd_uri_parsed
struct httpd_request {
// Request method
enum httpd_methods method;
// The request URI
const char *uri;
// User-agent (if available)
const char *user_agent;
// The parsed request URI given to us by httpd_uri_parse
struct httpd_uri_parsed *uri_parsed;
// Shortcut to &uri_parsed->query
httpd_query *query;
// Backend private request object
httpd_backend *backend;
// User-agent (if available)
const char *user_agent;
// Source IP address (ipv4 or ipv6) and port of the request (if available)
char *peer_address;
const char *peer_address;
unsigned short peer_port;
// A pointer to extra data that the module handling the request might need
void *extra_data;
// The original, request URI. The URI may have been complete:
// scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
// or relative:
// [/path][?query][#fragment]
const char *uri;
// URI decoded path from the request URI
const char *path;
// If the request is http://x:3689/foo/bar?key1=val1, then part_parts[0] is
// "foo", [1] is "bar" and the rest is null. Each path_part is an allocated
// URI decoded string.
httpd_uri_path_parts path_parts;
// Struct with the query, used with httpd_query_ functions
httpd_query *query;
// Backend private parser URI object
httpd_uri_parsed *uri_parsed;
// Request headers
httpd_headers *in_headers;
// Request body
@ -142,7 +137,7 @@ struct httpd_request {
// Response body
struct evbuffer *out_body;
// The module that will process this request
// Our httpd module that will process this request
struct httpd_module *module;
// A pointer to the handler that will process the request
int (*handler)(struct httpd_request *hreq);
@ -248,7 +243,7 @@ int
httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb, void *arg);
int
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn);
httpd_connection_peer_get(const char **addr, uint16_t *port, httpd_connection *conn);
void
httpd_connection_free(httpd_connection *conn);
@ -305,7 +300,7 @@ struct evbuffer *
httpd_backend_input_buffer_get(httpd_backend *backend);
int
httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend);
httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend);
int
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend);
@ -313,4 +308,19 @@ httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend);
void
httpd_backend_preprocess(httpd_backend *backend);
httpd_uri_parsed *
httpd_uri_parsed_create(const char *uri);
void
httpd_uri_parsed_free(httpd_uri_parsed *uri_parsed);
httpd_query *
httpd_uri_query_get(httpd_uri_parsed *parsed);
const char *
httpd_uri_path_get(httpd_uri_parsed *parsed);
void
httpd_uri_path_parts_get(httpd_uri_path_parts *part_parts, httpd_uri_parsed *parsed);
#endif /* !__HTTPD_INTERNAL_H__ */

View File

@ -34,7 +34,6 @@
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <regex.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -989,7 +988,7 @@ jsonapi_reply_settings_category_get(struct httpd_request *hreq)
json_object *jreply;
categoryname = hreq->uri_parsed->path_parts[2];
categoryname = hreq->path_parts[2];
category = settings_category_get(categoryname);
if (!category)
@ -1023,8 +1022,8 @@ jsonapi_reply_settings_option_get(struct httpd_request *hreq)
json_object *jreply;
categoryname = hreq->uri_parsed->path_parts[2];
optionname = hreq->uri_parsed->path_parts[3];
categoryname = hreq->path_parts[2];
optionname = hreq->path_parts[3];
category = settings_category_get(categoryname);
if (!category)
@ -1069,8 +1068,8 @@ jsonapi_reply_settings_option_put(struct httpd_request *hreq)
int ret;
categoryname = hreq->uri_parsed->path_parts[2];
optionname = hreq->uri_parsed->path_parts[3];
categoryname = hreq->path_parts[2];
optionname = hreq->path_parts[3];
category = settings_category_get(categoryname);
if (!category)
@ -1135,8 +1134,8 @@ jsonapi_reply_settings_option_delete(struct httpd_request *hreq)
int ret;
categoryname = hreq->uri_parsed->path_parts[2];
optionname = hreq->uri_parsed->path_parts[3];
categoryname = hreq->path_parts[2];
optionname = hreq->path_parts[3];
category = settings_category_get(categoryname);
if (!category)
@ -1585,10 +1584,10 @@ jsonapi_reply_outputs_get_byid(struct httpd_request *hreq)
json_object *jreply;
int ret;
ret = safe_atou64(hreq->uri_parsed->path_parts[2], &output_id);
ret = safe_atou64(hreq->path_parts[2], &output_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -1597,7 +1596,7 @@ jsonapi_reply_outputs_get_byid(struct httpd_request *hreq)
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No output found for '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No output found for '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -1623,10 +1622,10 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
const char *pin;
int ret;
ret = safe_atou64(hreq->uri_parsed->path_parts[2], &output_id);
ret = safe_atou64(hreq->path_parts[2], &output_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -1681,10 +1680,10 @@ jsonapi_reply_outputs_toggle_byid(struct httpd_request *hreq)
struct player_speaker_info spk;
int ret;
ret = safe_atou64(hreq->uri_parsed->path_parts[2], &output_id);
ret = safe_atou64(hreq->path_parts[2], &output_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid output id given to outputs endpoint '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -1692,7 +1691,7 @@ jsonapi_reply_outputs_toggle_byid(struct httpd_request *hreq)
ret = player_speaker_get_byid(&spk, output_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No output found for the given output id, toggle failed for '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No output found for the given output id, toggle failed for '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -2585,12 +2584,12 @@ jsonapi_reply_queue_tracks_update(struct httpd_request *hreq)
player_get_status(&status);
if (strcmp(hreq->uri_parsed->path_parts[3], "now_playing") != 0)
if (strcmp(hreq->path_parts[3], "now_playing") != 0)
{
ret = safe_atou32(hreq->uri_parsed->path_parts[3], &item_id);
ret = safe_atou32(hreq->path_parts[3], &item_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid item id given: '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid item id given: '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
}
@ -2600,7 +2599,7 @@ jsonapi_reply_queue_tracks_update(struct httpd_request *hreq)
queue_item = db_queue_fetch_byitemid(item_id);
if (!queue_item)
{
DPRINTF(E_LOG, L_WEB, "No valid item id given, or now_playing given but not playing: '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid item id given, or now_playing given but not playing: '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -2638,10 +2637,10 @@ jsonapi_reply_queue_tracks_delete(struct httpd_request *hreq)
uint32_t item_id;
int ret;
ret = safe_atou32(hreq->uri_parsed->path_parts[3], &item_id);
ret = safe_atou32(hreq->path_parts[3], &item_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid item id given '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid item id given '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -3005,7 +3004,7 @@ jsonapi_reply_library_artist(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
artist_id = hreq->uri_parsed->path_parts[3];
artist_id = hreq->path_parts[3];
reply = fetch_artist(&notfound, artist_id);
if (!reply)
@ -3040,7 +3039,7 @@ jsonapi_reply_library_artist_albums(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
artist_id = hreq->uri_parsed->path_parts[3];
artist_id = hreq->path_parts[3];
reply = json_object_new_object();
items = json_object_new_array();
@ -3154,7 +3153,7 @@ jsonapi_reply_library_album(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
album_id = hreq->uri_parsed->path_parts[3];
album_id = hreq->path_parts[3];
reply = fetch_album(&notfound, album_id);
if (!reply)
@ -3189,7 +3188,7 @@ jsonapi_reply_library_album_tracks(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_MODIFIED))
return HTTP_NOTMODIFIED;
album_id = hreq->uri_parsed->path_parts[3];
album_id = hreq->path_parts[3];
reply = json_object_new_object();
items = json_object_new_array();
@ -3235,7 +3234,7 @@ jsonapi_reply_library_album_tracks_put_byid(struct httpd_request *hreq)
int64_t album_id;;
int ret;
ret = safe_atoi64(hreq->uri_parsed->path_parts[3], &album_id);
ret = safe_atoi64(hreq->path_parts[3], &album_id);
if (ret < 0)
return HTTP_INTERNAL;
@ -3273,7 +3272,7 @@ jsonapi_reply_library_tracks_get_byid(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_MODIFIED))
return HTTP_NOTMODIFIED;
track_id = hreq->uri_parsed->path_parts[3];
track_id = hreq->path_parts[3];
memset(&query_params, 0, sizeof(struct query_params));
@ -3399,7 +3398,7 @@ jsonapi_reply_library_tracks_put_byid(struct httpd_request *hreq)
uint32_t val;
int ret;
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &track_id);
ret = safe_atoi32(hreq->path_parts[3], &track_id);
if (ret < 0)
return HTTP_INTERNAL;
@ -3470,7 +3469,7 @@ jsonapi_reply_library_track_playlists(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_MODIFIED))
return HTTP_NOTMODIFIED;
track_id = hreq->uri_parsed->path_parts[3];
track_id = hreq->path_parts[3];
if (safe_atoi32(track_id, &id) < 0)
{
DPRINTF(E_LOG, L_WEB, "Error converting track id '%s' to int.\n", track_id);
@ -3580,7 +3579,7 @@ jsonapi_reply_library_playlist_get(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
ret = safe_atou32(hreq->uri_parsed->path_parts[3], &playlist_id);
ret = safe_atou32(hreq->path_parts[3], &playlist_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "Could not parse playlist id to integer\n");
@ -3651,7 +3650,7 @@ jsonapi_reply_library_playlist_put(struct httpd_request *hreq)
const char *param;
int ret;
ret = safe_atou32(hreq->uri_parsed->path_parts[3], &playlist_id);
ret = safe_atou32(hreq->path_parts[3], &playlist_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "Could not parse playlist id to integer\n");
@ -3682,10 +3681,10 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
// Due to smart playlists possibly changing their tracks between rescans, disable caching in clients
httpd_response_not_cachable(hreq);
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
ret = safe_atoi32(hreq->path_parts[3], &playlist_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -3731,10 +3730,10 @@ jsonapi_reply_library_playlist_delete(struct httpd_request *hreq)
uint32_t pl_id;
int ret;
ret = safe_atou32(hreq->uri_parsed->path_parts[3], &pl_id);
ret = safe_atou32(hreq->path_parts[3], &pl_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -3758,10 +3757,10 @@ jsonapi_reply_library_playlist_playlists(struct httpd_request *hreq)
return HTTP_NOTMODIFIED;
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
ret = safe_atoi32(hreq->path_parts[3], &playlist_id);
if (ret < 0)
{
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->uri_parsed->path);
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->path);
return HTTP_BADREQUEST;
}
@ -3810,7 +3809,7 @@ jsonapi_reply_library_playlist_tracks_put_byid(struct httpd_request *hreq)
int playlist_id;
int ret;
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
ret = safe_atoi32(hreq->path_parts[3], &playlist_id);
if (ret < 0)
return HTTP_INTERNAL;
@ -3896,7 +3895,7 @@ jsonapi_reply_library_browse(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
browse_type = hreq->uri_parsed->path_parts[2];
browse_type = hreq->path_parts[2];
DPRINTF(E_DBG, L_WEB, "Browse query with type '%s'\n", browse_type);
media_kind = 0;
@ -3977,8 +3976,8 @@ jsonapi_reply_library_browseitem(struct httpd_request *hreq)
if (!is_modified(hreq, DB_ADMIN_DB_UPDATE))
return HTTP_NOTMODIFIED;
browse_type = hreq->uri_parsed->path_parts[2];
item_name = hreq->uri_parsed->path_parts[3];
browse_type = hreq->path_parts[2];
item_name = hreq->path_parts[3];
DPRINTF(E_DBG, L_WEB, "Browse item query with type '%s'\n", browse_type);
reply = json_object_new_object();

View File

@ -1,3 +1,5 @@
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <event2/http.h>
@ -7,6 +9,15 @@
#include "misc.h" // For net_evhttp_bind
#include "httpd_internal.h"
struct httpd_uri_parsed
{
struct evhttp_uri *ev_uri;
struct evkeyvalq query;
char *path;
httpd_uri_path_parts path_parts;
};
const char *
httpd_query_value_find(httpd_query *query, const char *key)
{
@ -62,9 +73,9 @@ httpd_connection_closecb_set(httpd_connection *conn, httpd_connection_closecb cb
}
int
httpd_connection_peer_get(char **addr, uint16_t *port, httpd_connection *conn)
httpd_connection_peer_get(const char **addr, uint16_t *port, httpd_connection *conn)
{
evhttp_connection_get_peer(conn, addr, port);
evhttp_connection_get_peer(conn, (char **)addr, port);
return 0;
}
@ -194,7 +205,7 @@ httpd_backend_output_buffer_get(httpd_backend *backend)
}
int
httpd_backend_peer_get(char **addr, uint16_t *port, httpd_backend *backend)
httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend)
{
httpd_connection *conn = httpd_backend_connection_get(backend);
if (!conn)
@ -232,3 +243,87 @@ httpd_backend_preprocess(httpd_backend *backend)
// It has side-effects on Connection: keep-alive
backend->flags &= ~EVHTTP_PROXY_REQUEST;
}
httpd_uri_parsed *
httpd_uri_parsed_create(const char *uri)
{
struct httpd_uri_parsed *parsed;
const char *query;
char *path = NULL;
char *path_part;
char *ptr;
int i;
parsed = calloc(1, sizeof(struct httpd_uri_parsed));
if (!parsed)
goto error;
parsed->ev_uri = evhttp_uri_parse_with_flags(uri, EVHTTP_URI_NONCONFORMANT);
if (!parsed->ev_uri)
goto error;
query = evhttp_uri_get_query(parsed->ev_uri);
if (query && strchr(query, '=') && evhttp_parse_query_str(query, &(parsed->query)) < 0)
goto error;
path = strdup(evhttp_uri_get_path(parsed->ev_uri));
if (!path || !(parsed->path = evhttp_uridecode(path, 0, NULL)))
goto error;
path_part = strtok_r(path, "/", &ptr);
for (i = 0; (i < ARRAY_SIZE(parsed->path_parts) && path_part); i++)
{
parsed->path_parts[i] = evhttp_uridecode(path_part, 0, NULL);
path_part = strtok_r(NULL, "/", &ptr);
}
// If "path_part" is not NULL, we have path tokens that could not be parsed into the "parsed->path_parts" array
if (path_part)
goto error;
free(path);
return parsed;
error:
free(path);
httpd_uri_parsed_free(parsed);
return NULL;
}
void
httpd_uri_parsed_free(httpd_uri_parsed *parsed)
{
int i;
if (!parsed)
return;
free(parsed->path);
for (i = 0; i < ARRAY_SIZE(parsed->path_parts); i++)
free(parsed->path_parts[i]);
httpd_query_clear(&(parsed->query));
if (parsed->ev_uri)
evhttp_uri_free(parsed->ev_uri);
free(parsed);
}
httpd_query *
httpd_uri_query_get(httpd_uri_parsed *parsed)
{
return &parsed->query;
}
const char *
httpd_uri_path_get(httpd_uri_parsed *parsed)
{
return parsed->path;
}
void
httpd_uri_path_parts_get(httpd_uri_path_parts *path_parts, httpd_uri_parsed *parsed)
{
memcpy(path_parts, parsed->path_parts, sizeof(httpd_uri_path_parts));
}

View File

@ -502,7 +502,7 @@ rsp_reply_playlist(struct httpd_request *hreq)
memset(&qp, 0, sizeof(struct query_params));
ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &qp.id);
ret = safe_atoi32(hreq->path_parts[2], &qp.id);
if (ret < 0)
{
rsp_send_error(hreq, "Invalid playlist ID");
@ -683,31 +683,31 @@ rsp_reply_browse(struct httpd_request *hreq)
memset(&qp, 0, sizeof(struct query_params));
if (strcmp(hreq->uri_parsed->path_parts[3], "artist") == 0)
if (strcmp(hreq->path_parts[3], "artist") == 0)
{
qp.type = Q_BROWSE_ARTISTS;
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "genre") == 0)
else if (strcmp(hreq->path_parts[3], "genre") == 0)
{
qp.type = Q_BROWSE_GENRES;
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "album") == 0)
else if (strcmp(hreq->path_parts[3], "album") == 0)
{
qp.type = Q_BROWSE_ALBUMS;
}
else if (strcmp(hreq->uri_parsed->path_parts[3], "composer") == 0)
else if (strcmp(hreq->path_parts[3], "composer") == 0)
{
qp.type = Q_BROWSE_COMPOSERS;
}
else
{
DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", hreq->uri_parsed->path_parts[3]);
DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", hreq->path_parts[3]);
rsp_send_error(hreq, "Unsupported browse type");
return -1;
}
ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &qp.id);
ret = safe_atoi32(hreq->path_parts[2], &qp.id);
if (ret < 0)
{
rsp_send_error(hreq, "Invalid playlist ID");
@ -801,7 +801,7 @@ rsp_stream(struct httpd_request *hreq)
int id;
int ret;
ret = safe_atoi32(hreq->uri_parsed->path_parts[2], &id);
ret = safe_atoi32(hreq->path_parts[2], &id);
if (ret < 0)
{
httpd_send_error(hreq, HTTP_BADREQUEST, "Bad Request");