1/*** 2 This file is part of PulseAudio. 3 4 PulseAudio is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as published 6 by the Free Software Foundation; either version 2.1 of the License, 7 or (at your option) any later version. 8 9 PulseAudio is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public License 15 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 16***/ 17 18#ifdef HAVE_CONFIG_H 19#include <config.h> 20#endif 21 22#include <stdlib.h> 23#include <stdio.h> 24 25#include <pulse/xmalloc.h> 26 27#include <pulsecore/core.h> 28#include <pulsecore/core-util.h> 29#include <pulsecore/log.h> 30#include <pulsecore/macro.h> 31 32#include "message-handler.h" 33 34/* Check if a path string starts with a / and only contains valid characters. 35 * Also reject double slashes. */ 36static bool object_path_is_valid(const char *test_string) { 37 uint32_t i; 38 39 if (!test_string) 40 return false; 41 42 /* Make sure the string starts with a / */ 43 if (test_string[0] != '/') 44 return false; 45 46 for (i = 0; test_string[i]; i++) { 47 48 if ((test_string[i] >= 'a' && test_string[i] <= 'z') || 49 (test_string[i] >= 'A' && test_string[i] <= 'Z') || 50 (test_string[i] >= '0' && test_string[i] <= '9') || 51 test_string[i] == '.' || 52 test_string[i] == '_' || 53 test_string[i] == '-' || 54 (test_string[i] == '/' && test_string[i + 1] != '/')) 55 continue; 56 57 return false; 58 } 59 60 /* Make sure the string does not end with a / */ 61 if (test_string[i - 1] == '/') 62 return false; 63 64 return true; 65} 66 67/* Message handler functions */ 68 69/* Register message handler for the specified object. object_path must be a unique name starting with "/". */ 70void pa_message_handler_register(pa_core *c, const char *object_path, const char *description, pa_message_handler_cb_t cb, void *userdata) { 71 struct pa_message_handler *handler; 72 73 pa_assert(c); 74 pa_assert(object_path); 75 pa_assert(cb); 76 pa_assert(userdata); 77 78 /* Ensure that object path is valid */ 79 pa_assert(object_path_is_valid(object_path)); 80 81 handler = pa_xnew0(struct pa_message_handler, 1); 82 handler->userdata = userdata; 83 handler->callback = cb; 84 handler->object_path = pa_xstrdup(object_path); 85 handler->description = pa_xstrdup(description); 86 87 pa_assert_se(pa_hashmap_put(c->message_handlers, handler->object_path, handler) == 0); 88} 89 90/* Unregister a message handler */ 91void pa_message_handler_unregister(pa_core *c, const char *object_path) { 92 struct pa_message_handler *handler; 93 94 pa_assert(c); 95 pa_assert(object_path); 96 97 pa_assert_se(handler = pa_hashmap_remove(c->message_handlers, object_path)); 98 99 pa_xfree(handler->object_path); 100 pa_xfree(handler->description); 101 pa_xfree(handler); 102} 103 104/* Send a message to an object identified by object_path */ 105int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) { 106 struct pa_message_handler *handler; 107 int ret; 108 char *path_copy; 109 pa_json_object *parameters = NULL; 110 111 pa_assert(c); 112 pa_assert(object_path); 113 pa_assert(message); 114 pa_assert(response); 115 116 *response = NULL; 117 118 path_copy = pa_xstrdup(object_path); 119 120 /* Remove trailing / from path name if present */ 121 if (path_copy[strlen(path_copy) - 1] == '/') 122 path_copy[strlen(path_copy) - 1] = 0; 123 124 if (!(handler = pa_hashmap_get(c->message_handlers, path_copy))) { 125 pa_xfree(path_copy); 126 return -PA_ERR_NOENTITY; 127 } 128 129 pa_xfree(path_copy); 130 131 if (message_parameters) { 132 parameters = pa_json_parse(message_parameters); 133 134 if (!parameters) { 135 char *wrapped_message_parameters; 136 137 /* Message parameters is not a valid JSON 138 * 139 * Wrap message parameters into JSON string and try again. 140 * User might have missed double-quotes and passed ARGSTRING instead of proper JSON "ARGSTRING" 141 */ 142 pa_log_warn("Message parameters is not a valid JSON, wrapping into JSON string '\"%s\"'", message_parameters); 143 144 wrapped_message_parameters = pa_sprintf_malloc("\"%s\"", message_parameters); 145 parameters = pa_json_parse(wrapped_message_parameters); 146 pa_xfree(wrapped_message_parameters); 147 148 if (!parameters) { 149 pa_log_error("Message parameters is not a valid JSON object. Tried both '%s' and '\"%s\"'", 150 message_parameters, message_parameters); 151 return -PA_ERR_INVALID; 152 } 153 } 154 } 155 156 /* The handler is expected to return an error code and may also 157 return an error string in response */ 158 ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata); 159 160 if (parameters) 161 pa_json_object_free(parameters); 162 return ret; 163} 164 165/* Set handler description */ 166int pa_message_handler_set_description(pa_core *c, const char *object_path, const char *description) { 167 struct pa_message_handler *handler; 168 169 pa_assert(c); 170 pa_assert(object_path); 171 172 if (!(handler = pa_hashmap_get(c->message_handlers, object_path))) 173 return -PA_ERR_NOENTITY; 174 175 pa_xfree(handler->description); 176 handler->description = pa_xstrdup(description); 177 178 return PA_OK; 179} 180