153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 553a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 653a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 753a5a1b3Sopenharmony_ci or (at your option) any later version. 853a5a1b3Sopenharmony_ci 953a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1053a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1153a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1253a5a1b3Sopenharmony_ci General Public License for more details. 1353a5a1b3Sopenharmony_ci 1453a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1553a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1653a5a1b3Sopenharmony_ci***/ 1753a5a1b3Sopenharmony_ci 1853a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 1953a5a1b3Sopenharmony_ci#include <config.h> 2053a5a1b3Sopenharmony_ci#endif 2153a5a1b3Sopenharmony_ci 2253a5a1b3Sopenharmony_ci#include <stdlib.h> 2353a5a1b3Sopenharmony_ci#include <stdio.h> 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulsecore/core.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include "message-handler.h" 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci/* Check if a path string starts with a / and only contains valid characters. 3553a5a1b3Sopenharmony_ci * Also reject double slashes. */ 3653a5a1b3Sopenharmony_cistatic bool object_path_is_valid(const char *test_string) { 3753a5a1b3Sopenharmony_ci uint32_t i; 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ci if (!test_string) 4053a5a1b3Sopenharmony_ci return false; 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_ci /* Make sure the string starts with a / */ 4353a5a1b3Sopenharmony_ci if (test_string[0] != '/') 4453a5a1b3Sopenharmony_ci return false; 4553a5a1b3Sopenharmony_ci 4653a5a1b3Sopenharmony_ci for (i = 0; test_string[i]; i++) { 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_ci if ((test_string[i] >= 'a' && test_string[i] <= 'z') || 4953a5a1b3Sopenharmony_ci (test_string[i] >= 'A' && test_string[i] <= 'Z') || 5053a5a1b3Sopenharmony_ci (test_string[i] >= '0' && test_string[i] <= '9') || 5153a5a1b3Sopenharmony_ci test_string[i] == '.' || 5253a5a1b3Sopenharmony_ci test_string[i] == '_' || 5353a5a1b3Sopenharmony_ci test_string[i] == '-' || 5453a5a1b3Sopenharmony_ci (test_string[i] == '/' && test_string[i + 1] != '/')) 5553a5a1b3Sopenharmony_ci continue; 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_ci return false; 5853a5a1b3Sopenharmony_ci } 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_ci /* Make sure the string does not end with a / */ 6153a5a1b3Sopenharmony_ci if (test_string[i - 1] == '/') 6253a5a1b3Sopenharmony_ci return false; 6353a5a1b3Sopenharmony_ci 6453a5a1b3Sopenharmony_ci return true; 6553a5a1b3Sopenharmony_ci} 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci/* Message handler functions */ 6853a5a1b3Sopenharmony_ci 6953a5a1b3Sopenharmony_ci/* Register message handler for the specified object. object_path must be a unique name starting with "/". */ 7053a5a1b3Sopenharmony_civoid pa_message_handler_register(pa_core *c, const char *object_path, const char *description, pa_message_handler_cb_t cb, void *userdata) { 7153a5a1b3Sopenharmony_ci struct pa_message_handler *handler; 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_ci pa_assert(c); 7453a5a1b3Sopenharmony_ci pa_assert(object_path); 7553a5a1b3Sopenharmony_ci pa_assert(cb); 7653a5a1b3Sopenharmony_ci pa_assert(userdata); 7753a5a1b3Sopenharmony_ci 7853a5a1b3Sopenharmony_ci /* Ensure that object path is valid */ 7953a5a1b3Sopenharmony_ci pa_assert(object_path_is_valid(object_path)); 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci handler = pa_xnew0(struct pa_message_handler, 1); 8253a5a1b3Sopenharmony_ci handler->userdata = userdata; 8353a5a1b3Sopenharmony_ci handler->callback = cb; 8453a5a1b3Sopenharmony_ci handler->object_path = pa_xstrdup(object_path); 8553a5a1b3Sopenharmony_ci handler->description = pa_xstrdup(description); 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_put(c->message_handlers, handler->object_path, handler) == 0); 8853a5a1b3Sopenharmony_ci} 8953a5a1b3Sopenharmony_ci 9053a5a1b3Sopenharmony_ci/* Unregister a message handler */ 9153a5a1b3Sopenharmony_civoid pa_message_handler_unregister(pa_core *c, const char *object_path) { 9253a5a1b3Sopenharmony_ci struct pa_message_handler *handler; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci pa_assert(c); 9553a5a1b3Sopenharmony_ci pa_assert(object_path); 9653a5a1b3Sopenharmony_ci 9753a5a1b3Sopenharmony_ci pa_assert_se(handler = pa_hashmap_remove(c->message_handlers, object_path)); 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci pa_xfree(handler->object_path); 10053a5a1b3Sopenharmony_ci pa_xfree(handler->description); 10153a5a1b3Sopenharmony_ci pa_xfree(handler); 10253a5a1b3Sopenharmony_ci} 10353a5a1b3Sopenharmony_ci 10453a5a1b3Sopenharmony_ci/* Send a message to an object identified by object_path */ 10553a5a1b3Sopenharmony_ciint pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) { 10653a5a1b3Sopenharmony_ci struct pa_message_handler *handler; 10753a5a1b3Sopenharmony_ci int ret; 10853a5a1b3Sopenharmony_ci char *path_copy; 10953a5a1b3Sopenharmony_ci pa_json_object *parameters = NULL; 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci pa_assert(c); 11253a5a1b3Sopenharmony_ci pa_assert(object_path); 11353a5a1b3Sopenharmony_ci pa_assert(message); 11453a5a1b3Sopenharmony_ci pa_assert(response); 11553a5a1b3Sopenharmony_ci 11653a5a1b3Sopenharmony_ci *response = NULL; 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci path_copy = pa_xstrdup(object_path); 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci /* Remove trailing / from path name if present */ 12153a5a1b3Sopenharmony_ci if (path_copy[strlen(path_copy) - 1] == '/') 12253a5a1b3Sopenharmony_ci path_copy[strlen(path_copy) - 1] = 0; 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci if (!(handler = pa_hashmap_get(c->message_handlers, path_copy))) { 12553a5a1b3Sopenharmony_ci pa_xfree(path_copy); 12653a5a1b3Sopenharmony_ci return -PA_ERR_NOENTITY; 12753a5a1b3Sopenharmony_ci } 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci pa_xfree(path_copy); 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci if (message_parameters) { 13253a5a1b3Sopenharmony_ci parameters = pa_json_parse(message_parameters); 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci if (!parameters) { 13553a5a1b3Sopenharmony_ci char *wrapped_message_parameters; 13653a5a1b3Sopenharmony_ci 13753a5a1b3Sopenharmony_ci /* Message parameters is not a valid JSON 13853a5a1b3Sopenharmony_ci * 13953a5a1b3Sopenharmony_ci * Wrap message parameters into JSON string and try again. 14053a5a1b3Sopenharmony_ci * User might have missed double-quotes and passed ARGSTRING instead of proper JSON "ARGSTRING" 14153a5a1b3Sopenharmony_ci */ 14253a5a1b3Sopenharmony_ci pa_log_warn("Message parameters is not a valid JSON, wrapping into JSON string '\"%s\"'", message_parameters); 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci wrapped_message_parameters = pa_sprintf_malloc("\"%s\"", message_parameters); 14553a5a1b3Sopenharmony_ci parameters = pa_json_parse(wrapped_message_parameters); 14653a5a1b3Sopenharmony_ci pa_xfree(wrapped_message_parameters); 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci if (!parameters) { 14953a5a1b3Sopenharmony_ci pa_log_error("Message parameters is not a valid JSON object. Tried both '%s' and '\"%s\"'", 15053a5a1b3Sopenharmony_ci message_parameters, message_parameters); 15153a5a1b3Sopenharmony_ci return -PA_ERR_INVALID; 15253a5a1b3Sopenharmony_ci } 15353a5a1b3Sopenharmony_ci } 15453a5a1b3Sopenharmony_ci } 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci /* The handler is expected to return an error code and may also 15753a5a1b3Sopenharmony_ci return an error string in response */ 15853a5a1b3Sopenharmony_ci ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata); 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci if (parameters) 16153a5a1b3Sopenharmony_ci pa_json_object_free(parameters); 16253a5a1b3Sopenharmony_ci return ret; 16353a5a1b3Sopenharmony_ci} 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci/* Set handler description */ 16653a5a1b3Sopenharmony_ciint pa_message_handler_set_description(pa_core *c, const char *object_path, const char *description) { 16753a5a1b3Sopenharmony_ci struct pa_message_handler *handler; 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci pa_assert(c); 17053a5a1b3Sopenharmony_ci pa_assert(object_path); 17153a5a1b3Sopenharmony_ci 17253a5a1b3Sopenharmony_ci if (!(handler = pa_hashmap_get(c->message_handlers, object_path))) 17353a5a1b3Sopenharmony_ci return -PA_ERR_NOENTITY; 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci pa_xfree(handler->description); 17653a5a1b3Sopenharmony_ci handler->description = pa_xstrdup(description); 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_ci return PA_OK; 17953a5a1b3Sopenharmony_ci} 180