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