1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Tanu Kaskinen
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <dbus/dbus.h>
25
26 #include <pulse/client-conf.h>
27 #include <pulse/xmalloc.h>
28
29 #include <pulsecore/core.h>
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/dbus-shared.h>
32 #include <pulsecore/macro.h>
33 #include <pulsecore/protocol-dbus.h>
34
35 #include "server-lookup.h"
36
37 #define OBJECT_PATH "/org/pulseaudio/server_lookup1"
38 #define INTERFACE "org.PulseAudio.ServerLookup1"
39
40 struct pa_dbusobj_server_lookup {
41 pa_core *core;
42 pa_dbus_connection *conn;
43 bool path_registered;
44 };
45
46 static const char introspection[] =
47 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
48 "<node>"
49 " <!-- If you are looking for documentation make sure to check out\n"
50 " http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ -->\n"
51 " <interface name=\"" INTERFACE "\">\n"
52 " <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
53 " </interface>\n"
54 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
55 " <method name=\"Introspect\">\n"
56 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
57 " </method>\n"
58 " </interface>\n"
59 " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
60 " <method name=\"Get\">\n"
61 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
62 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
63 " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
64 " </method>\n"
65 " <method name=\"Set\">\n"
66 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
67 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
68 " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
69 " </method>\n"
70 " <method name=\"GetAll\">\n"
71 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
72 " <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
73 " </method>\n"
74 " </interface>\n"
75 "</node>\n";
76
unregister_cb(DBusConnection * conn,void * user_data)77 static void unregister_cb(DBusConnection *conn, void *user_data) {
78 pa_dbusobj_server_lookup *sl = user_data;
79
80 pa_assert(sl);
81 pa_assert(sl->path_registered);
82
83 sl->path_registered = false;
84 }
85
handle_introspect(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)86 static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
87 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
88 const char *i = introspection;
89 DBusMessage *reply = NULL;
90
91 pa_assert(conn);
92 pa_assert(msg);
93
94 if (!(reply = dbus_message_new_method_return(msg))) {
95 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
96 goto finish;
97 }
98 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) {
99 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
100 goto finish;
101 }
102 if (!dbus_connection_send(conn, reply, NULL)) {
103 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
104 goto finish;
105 }
106
107 finish:
108 if (reply)
109 dbus_message_unref(reply);
110
111 return r;
112 }
113
114 enum get_address_result_t {
115 SUCCESS,
116 SERVER_FROM_TYPE_FAILED
117 };
118
119 /* Caller frees the returned address. */
get_address(pa_server_type_t server_type,char ** address)120 static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) {
121 enum get_address_result_t r = SUCCESS;
122 pa_client_conf *conf = pa_client_conf_new();
123
124 *address = NULL;
125
126 pa_client_conf_load(conf, false, false);
127
128 if (conf->default_dbus_server)
129 *address = pa_xstrdup(conf->default_dbus_server);
130 else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) {
131 r = SERVER_FROM_TYPE_FAILED;
132 goto finish;
133 }
134
135 finish:
136 pa_client_conf_free(conf);
137 return r;
138 }
139
handle_get_address(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)140 static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
141 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
142 DBusMessage *reply = NULL;
143 char *address = NULL;
144 DBusMessageIter msg_iter;
145 DBusMessageIter variant_iter;
146
147 pa_assert(conn);
148 pa_assert(msg);
149 pa_assert(sl);
150
151 switch (get_address(sl->core->server_type, &address)) {
152 case SUCCESS:
153 if (!(reply = dbus_message_new_method_return(msg))) {
154 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
155 goto finish;
156 }
157 dbus_message_iter_init_append(reply, &msg_iter);
158 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
159 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
160 goto finish;
161 }
162 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
163 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
164 goto finish;
165 }
166 if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
167 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
168 goto finish;
169 }
170 if (!dbus_connection_send(conn, reply, NULL)) {
171 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
172 goto finish;
173 }
174 r = DBUS_HANDLER_RESULT_HANDLED;
175 goto finish;
176
177 case SERVER_FROM_TYPE_FAILED:
178 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
179 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
180 goto finish;
181 }
182 if (!dbus_connection_send(conn, reply, NULL)) {
183 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
184 goto finish;
185 }
186 r = DBUS_HANDLER_RESULT_HANDLED;
187 goto finish;
188
189 default:
190 pa_assert_not_reached();
191 }
192
193 finish:
194 pa_xfree(address);
195 if (reply)
196 dbus_message_unref(reply);
197
198 return r;
199 }
200
handle_get(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)201 static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
202 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
203 const char* interface;
204 const char* property;
205 DBusMessage *reply = NULL;
206
207 pa_assert(conn);
208 pa_assert(msg);
209 pa_assert(sl);
210
211 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
212 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
213 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
214 goto finish;
215 }
216 if (!dbus_connection_send(conn, reply, NULL)) {
217 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
218 goto finish;
219 }
220 r = DBUS_HANDLER_RESULT_HANDLED;
221 goto finish;
222 }
223
224 if (*interface && !pa_streq(interface, INTERFACE)) {
225 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
226 goto finish;
227 }
228
229 if (!pa_streq(property, "Address")) {
230 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
231 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
232 goto finish;
233 }
234 if (!dbus_connection_send(conn, reply, NULL)) {
235 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
236 goto finish;
237 }
238 r = DBUS_HANDLER_RESULT_HANDLED;
239 goto finish;
240 }
241
242 r = handle_get_address(conn, msg, sl);
243
244 finish:
245 if (reply)
246 dbus_message_unref(reply);
247
248 return r;
249 }
250
handle_set(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)251 static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
252 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
253 const char* interface;
254 const char* property;
255 DBusMessage *reply = NULL;
256
257 pa_assert(conn);
258 pa_assert(msg);
259 pa_assert(sl);
260
261 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
262 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
263 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
264 goto finish;
265 }
266 if (!dbus_connection_send(conn, reply, NULL)) {
267 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
268 goto finish;
269 }
270 r = DBUS_HANDLER_RESULT_HANDLED;
271 goto finish;
272 }
273
274 if (*interface && !pa_streq(interface, INTERFACE)) {
275 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
276 goto finish;
277 }
278
279 if (!pa_streq(property, "Address")) {
280 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
281 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
282 goto finish;
283 }
284 if (!dbus_connection_send(conn, reply, NULL)) {
285 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
286 goto finish;
287 }
288 r = DBUS_HANDLER_RESULT_HANDLED;
289 goto finish;
290 }
291
292 if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
293 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
294 goto finish;
295 }
296 if (!dbus_connection_send(conn, reply, NULL)) {
297 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
298 goto finish;
299 }
300 r = DBUS_HANDLER_RESULT_HANDLED;
301
302 finish:
303 if (reply)
304 dbus_message_unref(reply);
305
306 return r;
307 }
308
handle_get_all(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)309 static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
310 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
311 DBusMessage *reply = NULL;
312 const char *property = "Address";
313 char *interface = NULL;
314 char *address = NULL;
315 DBusMessageIter msg_iter;
316 DBusMessageIter dict_iter;
317 DBusMessageIter dict_entry_iter;
318 DBusMessageIter variant_iter;
319
320 pa_assert(conn);
321 pa_assert(msg);
322 pa_assert(sl);
323
324 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
325 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
326 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
327 goto finish;
328 }
329 if (!dbus_connection_send(conn, reply, NULL)) {
330 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
331 goto finish;
332 }
333 r = DBUS_HANDLER_RESULT_HANDLED;
334 goto finish;
335 }
336
337 switch (get_address(sl->core->server_type, &address)) {
338 case SUCCESS:
339 if (!(reply = dbus_message_new_method_return(msg))) {
340 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
341 goto finish;
342 }
343 dbus_message_iter_init_append(reply, &msg_iter);
344 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
345 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
346 goto finish;
347 }
348 if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
349 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
350 goto finish;
351 }
352 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) {
353 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
354 goto finish;
355 }
356 if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
357 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
358 goto finish;
359 }
360 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
361 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
362 goto finish;
363 }
364 if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
365 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
366 goto finish;
367 }
368 if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
369 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
370 goto finish;
371 }
372 if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
373 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
374 goto finish;
375 }
376 if (!dbus_connection_send(conn, reply, NULL)) {
377 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
378 goto finish;
379 }
380 r = DBUS_HANDLER_RESULT_HANDLED;
381 goto finish;
382
383 case SERVER_FROM_TYPE_FAILED:
384 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
385 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
386 goto finish;
387 }
388 if (!dbus_connection_send(conn, reply, NULL)) {
389 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
390 goto finish;
391 }
392 r = DBUS_HANDLER_RESULT_HANDLED;
393 goto finish;
394
395 default:
396 pa_assert_not_reached();
397 }
398
399 finish:
400 pa_xfree(address);
401 if (reply)
402 dbus_message_unref(reply);
403
404 return r;
405 }
406
message_cb(DBusConnection * conn,DBusMessage * msg,void * user_data)407 static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
408 pa_dbusobj_server_lookup *sl = user_data;
409
410 pa_assert(conn);
411 pa_assert(msg);
412 pa_assert(sl);
413
414 /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
415
416 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL)
417 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
418
419 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
420 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect")))
421 return handle_introspect(conn, msg, sl);
422
423 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") ||
424 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get")))
425 return handle_get(conn, msg, sl);
426
427 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") ||
428 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set")))
429 return handle_set(conn, msg, sl);
430
431 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
432 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll")))
433 return handle_get_all(conn, msg, sl);
434
435 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
436 }
437
438 static DBusObjectPathVTable vtable = {
439 .unregister_function = unregister_cb,
440 .message_function = message_cb,
441 .dbus_internal_pad1 = NULL,
442 .dbus_internal_pad2 = NULL,
443 .dbus_internal_pad3 = NULL,
444 .dbus_internal_pad4 = NULL
445 };
446
pa_dbusobj_server_lookup_new(pa_core * c)447 pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
448 pa_dbusobj_server_lookup *sl;
449 DBusError error;
450
451 dbus_error_init(&error);
452
453 sl = pa_xnew(pa_dbusobj_server_lookup, 1);
454 sl->core = c;
455 sl->path_registered = false;
456
457 if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
458 pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
459 goto fail;
460 }
461
462 if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) {
463 pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH ".");
464 goto fail;
465 }
466
467 sl->path_registered = true;
468
469 return sl;
470
471 fail:
472 dbus_error_free(&error);
473
474 pa_dbusobj_server_lookup_free(sl);
475
476 return NULL;
477 }
478
pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup * sl)479 void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
480 pa_assert(sl);
481
482 if (sl->path_registered) {
483 pa_assert(sl->conn);
484 if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH))
485 pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH ".");
486 }
487
488 if (sl->conn)
489 pa_dbus_connection_unref(sl->conn);
490
491 pa_xfree(sl);
492 }
493