ratelimit: fix log levels of log suppression messages
[platform/upstream/pulseaudio.git] / src / pulsecore / protocol-dbus.c
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, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <dbus/dbus.h>
27
28 #include <pulse/xmalloc.h>
29
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/dbus-util.h>
32 #include <pulsecore/hashmap.h>
33 #include <pulsecore/idxset.h>
34 #include <pulsecore/shared.h>
35 #include <pulsecore/strbuf.h>
36
37 #include "protocol-dbus.h"
38
39 struct pa_dbus_protocol {
40     PA_REFCNT_DECLARE;
41
42     pa_core *core;
43     pa_hashmap *objects; /* Object path -> struct object_entry */
44     pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
45     pa_idxset *extensions; /* Strings */
46
47     pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
48 };
49
50 struct object_entry {
51     char *path;
52     pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
53     char *introspection;
54 };
55
56 struct connection_entry {
57     DBusConnection *connection;
58     pa_client *client;
59
60     pa_bool_t listening_for_all_signals;
61
62     /* Contains object paths. If this is empty, then signals from all objects
63      * are accepted. Only used when listening_for_all_signals == TRUE. */
64     pa_idxset *all_signals_objects;
65
66     /* Signal name -> signal paths entry. The entries contain object paths. If
67      * a path set is empty, then that signal is accepted from all objects. This
68      * variable is only used when listening_for_all_signals == FALSE. */
69     pa_hashmap *listening_signals;
70 };
71
72 /* Only used in connection entries' listening_signals hashmap. */
73 struct signal_paths_entry {
74     char *signal;
75     pa_idxset *paths;
76 };
77
78 struct interface_entry {
79     char *name;
80     pa_hashmap *method_handlers;
81     pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
82     pa_hashmap *property_handlers;
83     pa_dbus_receive_cb_t get_all_properties_cb;
84     pa_dbus_signal_info *signals;
85     unsigned n_signals;
86     void *userdata;
87 };
88
89 char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
90     char *address = NULL;
91     char *runtime_path = NULL;
92     char *escaped_path = NULL;
93
94     switch (server_type) {
95         case PA_SERVER_TYPE_USER:
96             pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
97             pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
98             address = pa_sprintf_malloc("unix:path=%s", escaped_path);
99             break;
100
101         case PA_SERVER_TYPE_SYSTEM:
102             pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
103             address = pa_sprintf_malloc("unix:path=%s", escaped_path);
104             break;
105
106         case PA_SERVER_TYPE_NONE:
107             address = pa_xnew0(char, 1);
108             break;
109
110         default:
111             pa_assert_not_reached();
112     }
113
114     pa_xfree(runtime_path);
115     pa_xfree(escaped_path);
116
117     return address;
118 }
119
120 static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
121     pa_dbus_protocol *p;
122     unsigned i;
123
124     pa_assert(c);
125
126     p = pa_xnew(pa_dbus_protocol, 1);
127     PA_REFCNT_INIT(p);
128     p->core = pa_core_ref(c);
129     p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
130     p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
131     p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
132
133     for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
134         pa_hook_init(&p->hooks[i], p);
135
136     pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
137
138     return p;
139 }
140
141 pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
142     pa_dbus_protocol *p;
143
144     if ((p = pa_shared_get(c, "dbus-protocol")))
145         return pa_dbus_protocol_ref(p);
146
147     return dbus_protocol_new(c);
148 }
149
150 pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
151     pa_assert(p);
152     pa_assert(PA_REFCNT_VALUE(p) >= 1);
153
154     PA_REFCNT_INC(p);
155
156     return p;
157 }
158
159 void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
160     unsigned i;
161
162     pa_assert(p);
163     pa_assert(PA_REFCNT_VALUE(p) >= 1);
164
165     if (PA_REFCNT_DEC(p) > 0)
166         return;
167
168     pa_assert(pa_hashmap_isempty(p->objects));
169     pa_assert(pa_hashmap_isempty(p->connections));
170     pa_assert(pa_idxset_isempty(p->extensions));
171
172     pa_hashmap_free(p->objects, NULL, NULL);
173     pa_hashmap_free(p->connections, NULL, NULL);
174     pa_idxset_free(p->extensions, NULL, NULL);
175
176     for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
177         pa_hook_done(&p->hooks[i]);
178
179     pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
180
181     pa_core_unref(p->core);
182
183     pa_xfree(p);
184 }
185
186 static void update_introspection(struct object_entry *oe) {
187     pa_strbuf *buf;
188     void *interfaces_state = NULL;
189     struct interface_entry *iface_entry = NULL;
190
191     pa_assert(oe);
192
193     buf = pa_strbuf_new();
194     pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
195     pa_strbuf_puts(buf, "<node>\n");
196
197     PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
198         pa_dbus_method_handler *method_handler;
199         pa_dbus_property_handler *property_handler;
200         void *handlers_state = NULL;
201         unsigned i;
202         unsigned j;
203
204         pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
205
206         PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
207             pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
208
209             for (i = 0; i < method_handler->n_arguments; ++i)
210                 pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
211                                  method_handler->arguments[i].name,
212                                  method_handler->arguments[i].type,
213                                  method_handler->arguments[i].direction);
214
215             pa_strbuf_puts(buf, "  </method>\n");
216         }
217
218         handlers_state = NULL;
219
220         PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
221             pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
222                              property_handler->property_name,
223                              property_handler->type,
224                              property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
225
226         for (i = 0; i < iface_entry->n_signals; ++i) {
227             pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);
228
229             for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
230                 pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
231                                                                              iface_entry->signals[i].arguments[j].type);
232
233             pa_strbuf_puts(buf, "  </signal>\n");
234         }
235
236         pa_strbuf_puts(buf, " </interface>\n");
237     }
238
239     pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
240                         "  <method name=\"Introspect\">\n"
241                         "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
242                         "  </method>\n"
243                         " </interface>\n"
244                         " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
245                         "  <method name=\"Get\">\n"
246                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
247                         "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
248                         "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
249                         "  </method>\n"
250                         "  <method name=\"Set\">\n"
251                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
252                         "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
253                         "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
254                         "  </method>\n"
255                         "  <method name=\"GetAll\">\n"
256                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
257                         "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
258                         "  </method>\n"
259                         " </interface>\n");
260
261     pa_strbuf_puts(buf, "</node>\n");
262
263     pa_xfree(oe->introspection);
264     oe->introspection = pa_strbuf_tostring_free(buf);
265 }
266
267 /* Return value of find_handler() and its subfunctions. */
268 enum find_result_t {
269     /* The received message is a valid .Get call. */
270     FOUND_GET_PROPERTY,
271
272     /* The received message is a valid .Set call. */
273     FOUND_SET_PROPERTY,
274
275     /* The received message is a valid .GetAll call. */
276     FOUND_GET_ALL,
277
278     /* The received message is a valid method call. */
279     FOUND_METHOD,
280
281     /* The interface of the received message hasn't been registered for the
282      * destination object. */
283     NO_SUCH_INTERFACE,
284
285     /* No property handler was found for the received .Get or .Set call. */
286     NO_SUCH_PROPERTY,
287
288     /* The interface argument of a property call didn't match any registered
289      * interface. */
290     NO_SUCH_PROPERTY_INTERFACE,
291
292     /* The received message called .Get or .Set for a property whose access
293      * mode doesn't match the call. */
294     PROPERTY_ACCESS_DENIED,
295
296     /* The new value signature of a .Set call didn't match the expexted
297      * signature. */
298     INVALID_PROPERTY_SIG,
299
300     /* No method handler was found for the received message. */
301     NO_SUCH_METHOD,
302
303     /* The signature of the received message didn't match the expected
304      * signature. Despite the name, this can also be returned for a property
305      * call if its message signature is invalid. */
306     INVALID_METHOD_SIG
307 };
308
309 /* Data for resolving the correct reaction to a received message. */
310 struct call_info {
311     DBusMessage *message; /* The received message. */
312     struct object_entry *obj_entry;
313     const char *interface; /* Destination interface name (extracted from the message). */
314     struct interface_entry *iface_entry;
315
316     const char *property; /* Property name (extracted from the message). */
317     const char *property_interface; /* The interface argument of a property call is stored here. */
318     pa_dbus_property_handler *property_handler;
319     const char *expected_property_sig; /* Property signature from the introspection data. */
320     const char *property_sig; /* The signature of the new value in the received .Set message. */
321     DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
322
323     const char *method; /* Method name (extracted from the message). */
324     pa_dbus_method_handler *method_handler;
325     const char *expected_method_sig; /* Method signature from the introspection data. */
326     const char *method_sig; /* The signature of the received message. */
327 };
328
329 /* Called when call_info->property has been set and the property interface has
330  * not been given. In case of a Set call, call_info->property_sig is also set,
331  * which is checked against the expected value in this function. */
332 static enum find_result_t find_handler_by_property(struct call_info *call_info) {
333     void *state = NULL;
334
335     pa_assert(call_info);
336
337     PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
338         if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
339             if (pa_streq(call_info->method, "Get"))
340                 return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
341
342             else if (pa_streq(call_info->method, "Set")) {
343                 call_info->expected_property_sig = call_info->property_handler->type;
344
345                 if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
346                     return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
347                 else
348                     return INVALID_PROPERTY_SIG;
349
350             } else
351                 pa_assert_not_reached();
352         }
353     }
354
355     return NO_SUCH_PROPERTY;
356 }
357
358 static enum find_result_t find_handler_by_method(struct call_info *call_info) {
359     void *state = NULL;
360
361     pa_assert(call_info);
362
363     PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
364         if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
365             call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method);
366
367             if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
368                 return FOUND_METHOD;
369             else
370                 return INVALID_METHOD_SIG;
371         }
372     }
373
374     return NO_SUCH_METHOD;
375 }
376
377 static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
378     pa_assert(call_info);
379
380     if (pa_streq(call_info->method, "GetAll")) {
381         call_info->expected_method_sig = "s";
382         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
383             return INVALID_METHOD_SIG;
384
385         pa_assert_se(dbus_message_get_args(call_info->message, NULL,
386                                            DBUS_TYPE_STRING, &call_info->property_interface,
387                                            DBUS_TYPE_INVALID));
388
389         if (*call_info->property_interface) {
390             if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
391                 return FOUND_GET_ALL;
392             else
393                 return NO_SUCH_PROPERTY_INTERFACE;
394
395         } else {
396             pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
397             return FOUND_GET_ALL;
398         }
399
400     } else if (pa_streq(call_info->method, "Get")) {
401         call_info->expected_method_sig = "ss";
402         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
403             return INVALID_METHOD_SIG;
404
405         pa_assert_se(dbus_message_get_args(call_info->message, NULL,
406                                            DBUS_TYPE_STRING, &call_info->property_interface,
407                                            DBUS_TYPE_STRING, &call_info->property,
408                                            DBUS_TYPE_INVALID));
409
410         if (*call_info->property_interface) {
411             if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
412                 return NO_SUCH_PROPERTY_INTERFACE;
413             else if ((call_info->property_handler =
414                         pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
415                 return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
416             else
417                 return NO_SUCH_PROPERTY;
418
419         } else
420             return find_handler_by_property(call_info);
421
422     } else if (pa_streq(call_info->method, "Set")) {
423         DBusMessageIter msg_iter;
424
425         call_info->expected_method_sig = "ssv";
426         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
427             return INVALID_METHOD_SIG;
428
429         pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
430
431         dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
432         pa_assert_se(dbus_message_iter_next(&msg_iter));
433         dbus_message_iter_get_basic(&msg_iter, &call_info->property);
434         pa_assert_se(dbus_message_iter_next(&msg_iter));
435
436         dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
437
438         call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter);
439
440         if (*call_info->property_interface) {
441             if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
442                 return NO_SUCH_PROPERTY_INTERFACE;
443
444             else if ((call_info->property_handler =
445                         pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
446                 call_info->expected_property_sig = call_info->property_handler->type;
447
448                 if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
449                     return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
450                 else
451                     return INVALID_PROPERTY_SIG;
452
453             } else
454                 return NO_SUCH_PROPERTY;
455
456         } else
457             return find_handler_by_property(call_info);
458
459     } else
460         pa_assert_not_reached();
461 }
462
463 static enum find_result_t find_handler(struct call_info *call_info) {
464     pa_assert(call_info);
465
466     if (call_info->interface) {
467         if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
468             return find_handler_from_properties_call(call_info);
469
470         else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
471             return NO_SUCH_INTERFACE;
472
473         else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method)))
474             return FOUND_METHOD;
475
476         else
477             return NO_SUCH_METHOD;
478
479     } else { /* The method call doesn't contain an interface. */
480         if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
481             if (find_handler_by_method(call_info) == FOUND_METHOD)
482                 /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
483                 return FOUND_METHOD;
484             else
485                 /* Assume this is a .Properties call. */
486                 return find_handler_from_properties_call(call_info);
487
488         } else /* This is not a .Properties call. */
489             return find_handler_by_method(call_info);
490     }
491 }
492
493 static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
494     pa_dbus_protocol *p = user_data;
495     struct call_info call_info;
496
497     pa_assert(connection);
498     pa_assert(message);
499     pa_assert(p);
500     pa_assert(p->objects);
501
502     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
503         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
504
505     pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
506                  dbus_message_get_path(message),
507                  dbus_message_get_interface(message),
508                  dbus_message_get_member(message));
509
510     call_info.message = message;
511     pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
512     call_info.interface = dbus_message_get_interface(message);
513     pa_assert_se(call_info.method = dbus_message_get_member(message));
514     pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
515
516     if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
517         (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
518         pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
519         goto finish;
520     }
521
522     switch (find_handler(&call_info)) {
523         case FOUND_GET_PROPERTY:
524             call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
525             break;
526
527         case FOUND_SET_PROPERTY:
528             call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
529             break;
530
531         case FOUND_METHOD:
532             call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
533             break;
534
535         case FOUND_GET_ALL:
536             if (call_info.iface_entry->get_all_properties_cb)
537                 call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
538             else {
539                 DBusMessage *dummy_reply = NULL;
540                 DBusMessageIter msg_iter;
541                 DBusMessageIter dict_iter;
542
543                 pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
544                 dbus_message_iter_init_append(dummy_reply, &msg_iter);
545                 pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
546                 pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
547                 pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
548                 dbus_message_unref(dummy_reply);
549             }
550             break;
551
552         case PROPERTY_ACCESS_DENIED:
553             pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
554                                "%s access denied for property %s", call_info.method, call_info.property);
555             break;
556
557         case NO_SUCH_METHOD:
558             pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
559             break;
560
561         case NO_SUCH_INTERFACE:
562             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such interface: %s", call_info.interface);
563             break;
564
565         case NO_SUCH_PROPERTY:
566             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
567             break;
568
569         case NO_SUCH_PROPERTY_INTERFACE:
570             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such property interface: %s", call_info.property_interface);
571             break;
572
573         case INVALID_METHOD_SIG:
574             pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
575                                "Invalid signature for method %s: '%s'. Expected '%s'.",
576                                call_info.method, call_info.method_sig, call_info.expected_method_sig);
577             break;
578
579         case INVALID_PROPERTY_SIG:
580             pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
581                                "Invalid signature for property %s: '%s'. Expected '%s'.",
582                                call_info.property, call_info.property_sig, call_info.expected_property_sig);
583             break;
584
585         default:
586             pa_assert_not_reached();
587     }
588
589 finish:
590     return DBUS_HANDLER_RESULT_HANDLED;
591 }
592
593 static DBusObjectPathVTable vtable = {
594     .unregister_function = NULL,
595     .message_function = handle_message_cb,
596     .dbus_internal_pad1 = NULL,
597     .dbus_internal_pad2 = NULL,
598     .dbus_internal_pad3 = NULL,
599     .dbus_internal_pad4 = NULL
600 };
601
602 static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
603     struct connection_entry *conn_entry;
604     void *state = NULL;
605
606     pa_assert(p);
607     pa_assert(obj_entry);
608
609     PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
610         pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
611 }
612
613 static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
614     pa_dbus_arg_info *dst;
615     unsigned i;
616
617     if (n == 0)
618         return NULL;
619
620     pa_assert(src);
621
622     dst = pa_xnew0(pa_dbus_arg_info, n);
623
624     for (i = 0; i < n; ++i) {
625         dst[i].name = pa_xstrdup(src[i].name);
626         dst[i].type = pa_xstrdup(src[i].type);
627         dst[i].direction = pa_xstrdup(src[i].direction);
628     }
629
630     return dst;
631 }
632
633 static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
634     pa_hashmap *handlers;
635     unsigned i;
636
637     pa_assert(info);
638     pa_assert(info->method_handlers || info->n_method_handlers == 0);
639
640     handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
641
642     for (i = 0; i < info->n_method_handlers; ++i) {
643         pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
644         h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
645         h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
646         h->n_arguments = info->method_handlers[i].n_arguments;
647         h->receive_cb = info->method_handlers[i].receive_cb;
648
649         pa_hashmap_put(handlers, h->method_name, h);
650     }
651
652     return handlers;
653 }
654
655 static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
656     pa_hashmap *signatures = NULL;
657     pa_dbus_method_handler *handler = NULL;
658     void *state = NULL;
659     pa_strbuf *sig_buf = NULL;
660     unsigned i = 0;
661
662     pa_assert(method_handlers);
663
664     signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
665
666     PA_HASHMAP_FOREACH(handler, method_handlers, state) {
667         sig_buf = pa_strbuf_new();
668
669         for (i = 0; i < handler->n_arguments; ++i) {
670             if (pa_streq(handler->arguments[i].direction, "in"))
671                 pa_strbuf_puts(sig_buf, handler->arguments[i].type);
672         }
673
674         pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf));
675     }
676
677     return signatures;
678 }
679
680 static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
681     pa_hashmap *handlers;
682     unsigned i = 0;
683
684     pa_assert(info);
685     pa_assert(info->property_handlers || info->n_property_handlers == 0);
686
687     handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
688
689     for (i = 0; i < info->n_property_handlers; ++i) {
690         pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
691         h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
692         h->type = pa_xstrdup(info->property_handlers[i].type);
693         h->get_cb = info->property_handlers[i].get_cb;
694         h->set_cb = info->property_handlers[i].set_cb;
695
696         pa_hashmap_put(handlers, h->property_name, h);
697     }
698
699     return handlers;
700 }
701
702 static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
703     pa_dbus_signal_info *dst;
704     unsigned i;
705
706     pa_assert(info);
707
708     if (info->n_signals == 0)
709         return NULL;
710
711     pa_assert(info->signals);
712
713     dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
714
715     for (i = 0; i < info->n_signals; ++i) {
716         dst[i].name = pa_xstrdup(info->signals[i].name);
717         dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
718         dst[i].n_arguments = info->signals[i].n_arguments;
719     }
720
721     return dst;
722 }
723
724 int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
725                                    const char *path,
726                                    const pa_dbus_interface_info *info,
727                                    void *userdata) {
728     struct object_entry *obj_entry;
729     struct interface_entry *iface_entry;
730     pa_bool_t obj_entry_created = FALSE;
731
732     pa_assert(p);
733     pa_assert(path);
734     pa_assert(info);
735     pa_assert(info->name);
736     pa_assert(info->method_handlers || info->n_method_handlers == 0);
737     pa_assert(info->property_handlers || info->n_property_handlers == 0);
738     pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
739     pa_assert(info->signals || info->n_signals == 0);
740
741     if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
742         obj_entry = pa_xnew(struct object_entry, 1);
743         obj_entry->path = pa_xstrdup(path);
744         obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
745         obj_entry->introspection = NULL;
746
747         pa_hashmap_put(p->objects, path, obj_entry);
748         obj_entry_created = TRUE;
749     }
750
751     if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
752         goto fail; /* The interface was already registered. */
753
754     iface_entry = pa_xnew(struct interface_entry, 1);
755     iface_entry->name = pa_xstrdup(info->name);
756     iface_entry->method_handlers = create_method_handlers(info);
757     iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
758     iface_entry->property_handlers = create_property_handlers(info);
759     iface_entry->get_all_properties_cb = info->get_all_properties_cb;
760     iface_entry->signals = copy_signals(info);
761     iface_entry->n_signals = info->n_signals;
762     iface_entry->userdata = userdata;
763     pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
764
765     update_introspection(obj_entry);
766
767     if (obj_entry_created)
768         register_object(p, obj_entry);
769
770     pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
771
772     return 0;
773
774 fail:
775     if (obj_entry_created) {
776         pa_hashmap_remove(p->objects, path);
777         pa_xfree(obj_entry);
778     }
779
780     return -1;
781 }
782
783 static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
784     struct connection_entry *conn_entry;
785     void *state = NULL;
786
787     pa_assert(p);
788     pa_assert(obj_entry);
789
790     PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
791         pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
792 }
793
794 static void method_handler_free_cb(void *p, void *userdata) {
795     pa_dbus_method_handler *h = p;
796     unsigned i;
797
798     pa_assert(h);
799
800     pa_xfree((char *) h->method_name);
801
802     for (i = 0; i < h->n_arguments; ++i) {
803         pa_xfree((char *) h->arguments[i].name);
804         pa_xfree((char *) h->arguments[i].type);
805         pa_xfree((char *) h->arguments[i].direction);
806     }
807
808     pa_xfree((pa_dbus_arg_info *) h->arguments);
809 }
810
811 static void method_signature_free_cb(void *p, void *userdata) {
812     pa_assert(p);
813
814     pa_xfree(p);
815 }
816
817 static void property_handler_free_cb(void *p, void *userdata) {
818     pa_dbus_property_handler *h = p;
819
820     pa_assert(h);
821
822     pa_xfree((char *) h->property_name);
823     pa_xfree((char *) h->type);
824
825     pa_xfree(h);
826 }
827
828 int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
829     struct object_entry *obj_entry;
830     struct interface_entry *iface_entry;
831     unsigned i;
832
833     pa_assert(p);
834     pa_assert(path);
835     pa_assert(interface);
836
837     if (!(obj_entry = pa_hashmap_get(p->objects, path)))
838         return -1;
839
840     if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
841         return -1;
842
843     update_introspection(obj_entry);
844
845     pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
846
847     pa_xfree(iface_entry->name);
848     pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL);
849     pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL);
850     pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL);
851
852     for (i = 0; i < iface_entry->n_signals; ++i) {
853         unsigned j;
854
855         pa_xfree((char *) iface_entry->signals[i].name);
856
857         for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
858             pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
859             pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
860             pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
861         }
862
863         pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
864     }
865
866     pa_xfree(iface_entry->signals);
867     pa_xfree(iface_entry);
868
869     if (pa_hashmap_isempty(obj_entry->interfaces)) {
870         unregister_object(p, obj_entry);
871
872         pa_hashmap_remove(p->objects, path);
873         pa_xfree(obj_entry->path);
874         pa_hashmap_free(obj_entry->interfaces, NULL, NULL);
875         pa_xfree(obj_entry->introspection);
876         pa_xfree(obj_entry);
877     }
878
879     return 0;
880 }
881
882 static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
883     struct object_entry *obj_entry;
884     void *state = NULL;
885
886     pa_assert(p);
887     pa_assert(conn);
888
889     PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
890         pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
891 }
892
893 int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
894     struct connection_entry *conn_entry;
895
896     pa_assert(p);
897     pa_assert(conn);
898     pa_assert(client);
899
900     if (pa_hashmap_get(p->connections, conn))
901         return -1; /* The connection was already registered. */
902
903     register_all_objects(p, conn);
904
905     conn_entry = pa_xnew(struct connection_entry, 1);
906     conn_entry->connection = dbus_connection_ref(conn);
907     conn_entry->client = client;
908     conn_entry->listening_for_all_signals = FALSE;
909     conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
910     conn_entry->listening_signals = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
911
912     pa_hashmap_put(p->connections, conn, conn_entry);
913
914     return 0;
915 }
916
917 static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
918     struct object_entry *obj_entry;
919     void *state = NULL;
920
921     pa_assert(p);
922     pa_assert(conn);
923
924     PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
925         pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
926 }
927
928 static struct signal_paths_entry *signal_paths_entry_new(const char *signal_name) {
929     struct signal_paths_entry *e = NULL;
930
931     pa_assert(signal_name);
932
933     e = pa_xnew0(struct signal_paths_entry, 1);
934     e->signal = pa_xstrdup(signal_name);
935     e->paths = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
936
937     return e;
938 }
939
940 static void signal_paths_entry_free(struct signal_paths_entry *e) {
941     char *path = NULL;
942
943     pa_assert(e);
944
945     pa_xfree(e->signal);
946
947     while ((path = pa_idxset_steal_first(e->paths, NULL)))
948         pa_xfree(path);
949
950     pa_xfree(e);
951 }
952
953 int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
954     struct connection_entry *conn_entry = NULL;
955     struct signal_paths_entry *signal_paths_entry = NULL;
956     char *object_path = NULL;
957
958     pa_assert(p);
959     pa_assert(conn);
960
961     if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
962         return -1;
963
964     unregister_all_objects(p, conn);
965
966     dbus_connection_unref(conn_entry->connection);
967
968     while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
969         pa_xfree(object_path);
970
971     while ((signal_paths_entry = pa_hashmap_steal_first(conn_entry->listening_signals)))
972         signal_paths_entry_free(signal_paths_entry);
973
974     pa_xfree(conn_entry);
975
976     return 0;
977 }
978
979 pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
980     struct connection_entry *conn_entry;
981
982     pa_assert(p);
983     pa_assert(conn);
984
985     if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
986         return NULL;
987
988     return conn_entry->client;
989 }
990
991 void pa_dbus_protocol_add_signal_listener(
992         pa_dbus_protocol *p,
993         DBusConnection *conn,
994         const char *signal_name,
995         char **objects,
996         unsigned n_objects) {
997     struct connection_entry *conn_entry = NULL;
998     struct signal_paths_entry *signal_paths_entry = NULL;
999     char *object_path = NULL;
1000     unsigned i = 0;
1001
1002     pa_assert(p);
1003     pa_assert(conn);
1004     pa_assert(objects || n_objects == 0);
1005
1006     pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
1007
1008     /* all_signals_objects will either be emptied or replaced with new objects,
1009      * so we empty it here unconditionally. If listening_for_all_signals is
1010      * currently FALSE, the idxset is empty already so this does nothing. */
1011     while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
1012         pa_xfree(object_path);
1013
1014     if (signal_name) {
1015         conn_entry->listening_for_all_signals = FALSE;
1016
1017         /* Replace the old signal paths entry for this signal with a new
1018          * one. */
1019         if ((signal_paths_entry = pa_hashmap_remove(conn_entry->listening_signals, signal_name)))
1020             signal_paths_entry_free(signal_paths_entry);
1021         signal_paths_entry = signal_paths_entry_new(signal_name);
1022
1023         for (i = 0; i < n_objects; ++i)
1024             pa_idxset_put(signal_paths_entry->paths, pa_xstrdup(objects[i]), NULL);
1025
1026         pa_hashmap_put(conn_entry->listening_signals, signal_paths_entry->signal, signal_paths_entry);
1027
1028     } else {
1029         conn_entry->listening_for_all_signals = TRUE;
1030
1031         /* We're not interested in individual signals anymore, so let's empty
1032          * listening_signals. */
1033         while ((signal_paths_entry = pa_hashmap_steal_first(conn_entry->listening_signals)))
1034             signal_paths_entry_free(signal_paths_entry);
1035
1036         for (i = 0; i < n_objects; ++i)
1037             pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
1038     }
1039 }
1040
1041 void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal_name) {
1042     struct connection_entry *conn_entry = NULL;
1043     struct signal_paths_entry *signal_paths_entry = NULL;
1044
1045     pa_assert(p);
1046     pa_assert(conn);
1047
1048     pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
1049
1050     if (signal_name) {
1051         if ((signal_paths_entry = pa_hashmap_get(conn_entry->listening_signals, signal_name)))
1052             signal_paths_entry_free(signal_paths_entry);
1053
1054     } else {
1055         char *object_path;
1056
1057         conn_entry->listening_for_all_signals = FALSE;
1058
1059         while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
1060             pa_xfree(object_path);
1061
1062         while ((signal_paths_entry = pa_hashmap_steal_first(conn_entry->listening_signals)))
1063             signal_paths_entry_free(signal_paths_entry);
1064     }
1065 }
1066
1067 void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal_msg) {
1068     struct connection_entry *conn_entry;
1069     struct signal_paths_entry *signal_paths_entry;
1070     void *state = NULL;
1071     DBusMessage *signal_copy;
1072     char *signal_string;
1073
1074     pa_assert(p);
1075     pa_assert(signal_msg);
1076     pa_assert(dbus_message_get_type(signal_msg) == DBUS_MESSAGE_TYPE_SIGNAL);
1077     pa_assert(dbus_message_get_path(signal_msg));
1078     pa_assert(dbus_message_get_interface(signal_msg));
1079     pa_assert(dbus_message_get_member(signal_msg));
1080
1081     signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal_msg), dbus_message_get_member(signal_msg));
1082
1083     PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
1084         if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
1085              && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal_msg), NULL)
1086                  || pa_idxset_isempty(conn_entry->all_signals_objects)))
1087
1088             || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
1089                 && (signal_paths_entry = pa_hashmap_get(conn_entry->listening_signals, signal_string))
1090                 && (pa_idxset_get_by_data(signal_paths_entry->paths, dbus_message_get_path(signal_msg), NULL)
1091                     || pa_idxset_isempty(signal_paths_entry->paths)))) {
1092
1093             pa_assert_se(signal_copy = dbus_message_copy(signal_msg));
1094             pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
1095             dbus_message_unref(signal_copy);
1096         }
1097     }
1098
1099     pa_xfree(signal_string);
1100 }
1101
1102 const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
1103     const char **extensions;
1104     const char *ext_name;
1105     void *state = NULL;
1106     unsigned i = 0;
1107
1108     pa_assert(p);
1109     pa_assert(n);
1110
1111     *n = pa_idxset_size(p->extensions);
1112
1113     if (*n <= 0)
1114         return NULL;
1115
1116     extensions = pa_xnew(const char *, *n);
1117
1118     while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
1119         extensions[i++] = ext_name;
1120
1121     return extensions;
1122 }
1123
1124 int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
1125     char *internal_name;
1126
1127     pa_assert(p);
1128     pa_assert(name);
1129
1130     internal_name = pa_xstrdup(name);
1131
1132     if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
1133         pa_xfree(internal_name);
1134         return -1;
1135     }
1136
1137     pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
1138
1139     return 0;
1140 }
1141
1142 int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
1143     char *internal_name;
1144
1145     pa_assert(p);
1146     pa_assert(name);
1147
1148     if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
1149         return -1;
1150
1151     pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
1152
1153     pa_xfree(internal_name);
1154
1155     return 0;
1156 }
1157
1158 pa_hook_slot *pa_dbus_protocol_hook_connect(
1159         pa_dbus_protocol *p,
1160         pa_dbus_protocol_hook_t hook,
1161         pa_hook_priority_t prio,
1162         pa_hook_cb_t cb,
1163         void *data) {
1164     pa_assert(p);
1165     pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
1166     pa_assert(cb);
1167
1168     return pa_hook_connect(&p->hooks[hook], prio, cb, data);
1169 }