diff --git a/src/httpd.c b/src/httpd.c index 4212e463..e8bd4250 100644 --- a/src/httpd.c +++ b/src/httpd.c @@ -40,9 +40,7 @@ # include #endif #include -#include -#include #include #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, "/"); diff --git a/src/httpd_artworkapi.c b/src/httpd_artworkapi.c index e321c002..9870ec95 100644 --- a/src/httpd_artworkapi.c +++ b/src/httpd_artworkapi.c @@ -20,7 +20,6 @@ # include #endif -#include #include #include #include @@ -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; diff --git a/src/httpd_daap.c b/src/httpd_daap.c index 43a41a5c..48c2bcd2 100644 --- a/src/httpd_daap.c +++ b/src/httpd_daap.c @@ -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; diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index b6cbcfda..4b6b02fc 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -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; diff --git a/src/httpd_internal.h b/src/httpd_internal.h index 0dd65a9a..b567e3cb 100644 --- a/src/httpd_internal.h +++ b/src/httpd_internal.h @@ -5,17 +5,25 @@ #include #include #include -#include -#include + +#ifdef HAVE_CONFIG_H +# include +#endif struct httpd_request; +#include +#include +#include // 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__ */ diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 9bc26ef1..51751449 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -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(¬found, 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(¬found, 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(); diff --git a/src/httpd_libevhttp.c b/src/httpd_libevhttp.c index 604f48b7..ad9972d0 100644 --- a/src/httpd_libevhttp.c +++ b/src/httpd_libevhttp.c @@ -1,3 +1,5 @@ +#include +#include #include #include @@ -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)); +} diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index ec87f0ce..4466b116 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -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");