diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 2d418b63..17cf4e78 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -64,13 +64,13 @@ * } */ static int -pairing_kickoff(struct evhttp_request *req) +jsonapi_reply_pairing_kickoff(struct httpd_request *hreq) { struct evbuffer *evbuf; json_object* request; const char* message; - evbuf = evhttp_request_get_input_buffer(req); + evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(evbuf); if (!request) { @@ -102,7 +102,7 @@ pairing_kickoff(struct evhttp_request *req) * } */ static int -pairing_get(struct evbuffer *evbuf) +jsonapi_reply_pairing_get(struct httpd_request *hreq) { char *remote_name; json_object *jreply; @@ -121,7 +121,7 @@ pairing_get(struct evbuffer *evbuf) json_object_object_add(jreply, "active", json_object_new_boolean(false)); } - CHECK_ERRNO(L_WEB, evbuffer_add_printf(evbuf, "%s", json_object_to_json_string(jreply))); + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply))); jparse_free(jreply); free(remote_name); @@ -371,23 +371,6 @@ jsonapi_reply_spotify_login(struct httpd_request *hreq) return HTTP_OK; } -/* - * Endpoint to pair daap/dacp client - * - * If request is a GET request, returns information about active pairing remote. - * If request is a POST request, tries to pair the active remote with the given pin. - */ -static int -jsonapi_reply_pairing(struct httpd_request *hreq) -{ - if (evhttp_request_get_command(hreq->req) == EVHTTP_REQ_POST) - { - return pairing_kickoff(hreq->req); - } - - return pairing_get(hreq->reply); -} - static int jsonapi_reply_lastfm(struct httpd_request *hreq) { @@ -494,14 +477,18 @@ jsonapi_reply_lastfm_logout(struct httpd_request *hreq) return HTTP_NOCONTENT; } -static void -speaker_enum_cb(struct spk_info *spk, void *arg) +struct outputs_param +{ + json_object *output; + uint64_t output_id; +}; + +static json_object * +speaker_to_json(struct spk_info *spk) { - json_object *outputs; json_object *output; char output_id[21]; - outputs = arg; output = json_object_new_object(); snprintf(output_id, sizeof(output_id), "%" PRIu64, spk->id); @@ -514,14 +501,132 @@ speaker_enum_cb(struct spk_info *spk, void *arg) json_object_object_add(output, "needs_auth_key", json_object_new_boolean(spk->needs_auth_key)); json_object_object_add(output, "volume", json_object_new_int(spk->absvol)); + return output; +} + +static void +speaker_enum_cb(struct spk_info *spk, void *arg) +{ + json_object *outputs; + json_object *output; + + outputs = arg; + + output = speaker_to_json(spk); json_object_array_add(outputs, output); } +static void +speaker_get_cb(struct spk_info *spk, void *arg) +{ + struct outputs_param *outputs_param = arg; + + if (outputs_param->output_id == spk->id) + { + outputs_param->output = speaker_to_json(spk); + } +} + +/* + * GET /api/outputs/[output_id] + */ +static int +jsonapi_reply_outputs_get_byid(struct httpd_request *hreq) +{ + struct outputs_param outputs_param; + uint64_t output_id; + int ret; + + ret = safe_atou64(hreq->uri_parsed->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); + + return HTTP_BADREQUEST; + } + + outputs_param.output_id = output_id; + outputs_param.output = NULL; + + player_speaker_enumerate(speaker_get_cb, &outputs_param); + + if (!outputs_param.output) + { + DPRINTF(E_LOG, L_WEB, "No output found for '%s'\n", hreq->uri_parsed->path); + + return HTTP_BADREQUEST; + } + + CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(outputs_param.output))); + + jparse_free(outputs_param.output); + + return HTTP_OK; +} + +/* + * PUT /api/outputs/[output_id] + */ +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; + int ret; + + ret = safe_atou64(hreq->uri_parsed->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); + + return HTTP_BADREQUEST; + } + + in_evbuf = evhttp_request_get_input_buffer(hreq->req); + request = jparse_obj_from_evbuffer(in_evbuf); + if (!request) + { + DPRINTF(E_LOG, L_WEB, "Failed to parse incoming request\n"); + + return HTTP_BADREQUEST; + } + + ret = 0; + + if (jparse_contains_key(request, "selected", json_type_boolean)) + { + selected = jparse_bool_from_obj(request, "selected"); + if (selected) + ret = player_speaker_enable(output_id); + else + ret = player_speaker_disable(output_id); + } + + if (ret == 0 && jparse_contains_key(request, "volume", json_type_int)) + { + volume = jparse_int_from_obj(request, "volume"); + ret = player_volume_setabs_speaker(output_id, volume); + } + + jparse_free(request); + + if (ret < 0) + return HTTP_INTERNAL; + + return HTTP_NOCONTENT; +} + +/* + * Endpoint "/api/outputs" + */ static int jsonapi_reply_outputs(struct httpd_request *hreq) { - json_object *jreply; json_object *outputs; + json_object *jreply; outputs = json_object_new_array(); @@ -544,12 +649,6 @@ jsonapi_reply_verification(struct httpd_request *hreq) json_object* request; const char* message; - if (evhttp_request_get_command(hreq->req) != EVHTTP_REQ_POST) - { - DPRINTF(E_LOG, L_WEB, "Verification: request is not a POST request\n"); - return HTTP_BADREQUEST; - } - in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(in_evbuf); if (!request) @@ -572,7 +671,7 @@ jsonapi_reply_verification(struct httpd_request *hreq) } static int -jsonapi_reply_select_outputs(struct httpd_request *hreq) +jsonapi_reply_outputs_set(struct httpd_request *hreq) { struct evbuffer *in_evbuf; json_object *request; @@ -581,12 +680,6 @@ jsonapi_reply_select_outputs(struct httpd_request *hreq) int nspk, i, ret; uint64_t *ids; - if (evhttp_request_get_command(hreq->req) != EVHTTP_REQ_POST) - { - DPRINTF(E_LOG, L_WEB, "Select outputs: request is not a POST request\n"); - return HTTP_BADREQUEST; - } - in_evbuf = evhttp_request_get_input_buffer(hreq->req); request = jparse_obj_from_evbuffer(in_evbuf); if (!request) @@ -986,30 +1079,38 @@ jsonapi_reply_player_volume(struct httpd_request *hreq) static struct httpd_uri_map adm_handlers[] = { - { .regexp = "^/api/config", .handler = jsonapi_reply_config }, - { .regexp = "^/api/library", .handler = jsonapi_reply_library }, - { .regexp = "^/api/update", .handler = jsonapi_reply_update }, - { .regexp = "^/api/spotify-login", .handler = jsonapi_reply_spotify_login }, - { .regexp = "^/api/spotify", .handler = jsonapi_reply_spotify }, - { .regexp = "^/api/pairing", .handler = jsonapi_reply_pairing }, - { .regexp = "^/api/lastfm-login", .handler = jsonapi_reply_lastfm_login }, - { .regexp = "^/api/lastfm-logout", .handler = jsonapi_reply_lastfm_logout }, - { .regexp = "^/api/lastfm", .handler = jsonapi_reply_lastfm }, - { .regexp = "^/api/outputs", .handler = jsonapi_reply_outputs }, - { .regexp = "^/api/select-outputs", .handler = jsonapi_reply_select_outputs }, - { .regexp = "^/api/verification", .handler = jsonapi_reply_verification }, - { .regexp = "^/api/player/play", .handler = jsonapi_reply_player_play }, - { .regexp = "^/api/player/pause", .handler = jsonapi_reply_player_pause }, - { .regexp = "^/api/player/stop", .handler = jsonapi_reply_player_stop }, - { .regexp = "^/api/player/next", .handler = jsonapi_reply_player_next }, - { .regexp = "^/api/player/previous", .handler = jsonapi_reply_player_previous }, - { .regexp = "^/api/player/shuffle", .handler = jsonapi_reply_player_shuffle }, - { .regexp = "^/api/player/repeat", .handler = jsonapi_reply_player_repeat }, - { .regexp = "^/api/player/consume", .handler = jsonapi_reply_player_consume }, - { .regexp = "^/api/player/volume", .handler = jsonapi_reply_player_volume }, - { .regexp = "^/api/player", .handler = jsonapi_reply_player }, - { .regexp = "^/api/queue", .handler = jsonapi_reply_queue }, - { .regexp = NULL, .handler = NULL } + { EVHTTP_REQ_GET, "^/api/config$", jsonapi_reply_config }, + { EVHTTP_REQ_GET, "^/api/library$", jsonapi_reply_library }, + { EVHTTP_REQ_GET, "^/api/update$", jsonapi_reply_update }, + { EVHTTP_REQ_POST, "^/api/spotify-login$", jsonapi_reply_spotify_login }, + { 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_kickoff }, + { 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 }, + + { 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/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/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_GET, "^/api/player$", jsonapi_reply_player }, + + { EVHTTP_REQ_GET, "^/api/queue$", jsonapi_reply_queue }, + + { 0, NULL, NULL } };