153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2009 Tanu Kaskinen
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <dbus/dbus.h>
2553a5a1b3Sopenharmony_ci
2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
2753a5a1b3Sopenharmony_ci
2853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
2953a5a1b3Sopenharmony_ci#include <pulsecore/dbus-util.h>
3053a5a1b3Sopenharmony_ci#include <pulsecore/hashmap.h>
3153a5a1b3Sopenharmony_ci#include <pulsecore/idxset.h>
3253a5a1b3Sopenharmony_ci#include <pulsecore/shared.h>
3353a5a1b3Sopenharmony_ci#include <pulsecore/strbuf.h>
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci#include "protocol-dbus.h"
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_cistruct pa_dbus_protocol {
3853a5a1b3Sopenharmony_ci    PA_REFCNT_DECLARE;
3953a5a1b3Sopenharmony_ci
4053a5a1b3Sopenharmony_ci    pa_core *core;
4153a5a1b3Sopenharmony_ci    pa_hashmap *objects; /* Object path -> struct object_entry */
4253a5a1b3Sopenharmony_ci    pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
4353a5a1b3Sopenharmony_ci    pa_idxset *extensions; /* Strings */
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_ci    pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
4653a5a1b3Sopenharmony_ci};
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_cistruct object_entry {
4953a5a1b3Sopenharmony_ci    char *path;
5053a5a1b3Sopenharmony_ci    pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
5153a5a1b3Sopenharmony_ci    char *introspection;
5253a5a1b3Sopenharmony_ci};
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_cistruct connection_entry {
5553a5a1b3Sopenharmony_ci    DBusConnection *connection;
5653a5a1b3Sopenharmony_ci    pa_client *client;
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_ci    bool listening_for_all_signals;
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_ci    /* Contains object paths. If this is empty, then signals from all objects
6153a5a1b3Sopenharmony_ci     * are accepted. Only used when listening_for_all_signals == true. */
6253a5a1b3Sopenharmony_ci    pa_idxset *all_signals_objects;
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci    /* Signal name -> signal paths entry. The entries contain object paths. If
6553a5a1b3Sopenharmony_ci     * a path set is empty, then that signal is accepted from all objects. This
6653a5a1b3Sopenharmony_ci     * variable is only used when listening_for_all_signals == false. */
6753a5a1b3Sopenharmony_ci    pa_hashmap *listening_signals;
6853a5a1b3Sopenharmony_ci};
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci/* Only used in connection entries' listening_signals hashmap. */
7153a5a1b3Sopenharmony_cistruct signal_paths_entry {
7253a5a1b3Sopenharmony_ci    char *signal;
7353a5a1b3Sopenharmony_ci    pa_idxset *paths;
7453a5a1b3Sopenharmony_ci};
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_cistruct interface_entry {
7753a5a1b3Sopenharmony_ci    char *name;
7853a5a1b3Sopenharmony_ci    pa_hashmap *method_handlers;
7953a5a1b3Sopenharmony_ci    pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
8053a5a1b3Sopenharmony_ci    pa_hashmap *property_handlers;
8153a5a1b3Sopenharmony_ci    pa_dbus_receive_cb_t get_all_properties_cb;
8253a5a1b3Sopenharmony_ci    pa_dbus_signal_info *signals;
8353a5a1b3Sopenharmony_ci    unsigned n_signals;
8453a5a1b3Sopenharmony_ci    void *userdata;
8553a5a1b3Sopenharmony_ci};
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_cichar *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
8853a5a1b3Sopenharmony_ci    char *address = NULL;
8953a5a1b3Sopenharmony_ci    char *runtime_path = NULL;
9053a5a1b3Sopenharmony_ci    char *escaped_path = NULL;
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    switch (server_type) {
9353a5a1b3Sopenharmony_ci        case PA_SERVER_TYPE_USER:
9453a5a1b3Sopenharmony_ci            pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
9553a5a1b3Sopenharmony_ci            pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
9653a5a1b3Sopenharmony_ci            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
9753a5a1b3Sopenharmony_ci            break;
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci        case PA_SERVER_TYPE_SYSTEM:
10053a5a1b3Sopenharmony_ci            pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
10153a5a1b3Sopenharmony_ci            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
10253a5a1b3Sopenharmony_ci            break;
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_ci        case PA_SERVER_TYPE_NONE:
10553a5a1b3Sopenharmony_ci            address = pa_xnew0(char, 1);
10653a5a1b3Sopenharmony_ci            break;
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci        default:
10953a5a1b3Sopenharmony_ci            pa_assert_not_reached();
11053a5a1b3Sopenharmony_ci    }
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    pa_xfree(runtime_path);
11353a5a1b3Sopenharmony_ci    dbus_free(escaped_path);
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    return address;
11653a5a1b3Sopenharmony_ci}
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_cistatic pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
11953a5a1b3Sopenharmony_ci    pa_dbus_protocol *p;
12053a5a1b3Sopenharmony_ci    unsigned i;
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci    pa_assert(c);
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    p = pa_xnew(pa_dbus_protocol, 1);
12553a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(p);
12653a5a1b3Sopenharmony_ci    p->core = c;
12753a5a1b3Sopenharmony_ci    p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
12853a5a1b3Sopenharmony_ci    p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
12953a5a1b3Sopenharmony_ci    p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
13253a5a1b3Sopenharmony_ci        pa_hook_init(&p->hooks[i], p);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci    return p;
13753a5a1b3Sopenharmony_ci}
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_cipa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
14053a5a1b3Sopenharmony_ci    pa_dbus_protocol *p;
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci    if ((p = pa_shared_get(c, "dbus-protocol")))
14353a5a1b3Sopenharmony_ci        return pa_dbus_protocol_ref(p);
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci    return dbus_protocol_new(c);
14653a5a1b3Sopenharmony_ci}
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_cipa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
14953a5a1b3Sopenharmony_ci    pa_assert(p);
15053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci    PA_REFCNT_INC(p);
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci    return p;
15553a5a1b3Sopenharmony_ci}
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_civoid pa_dbus_protocol_unref(pa_dbus_protocol *p) {
15853a5a1b3Sopenharmony_ci    unsigned i;
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    pa_assert(p);
16153a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(p) > 0)
16453a5a1b3Sopenharmony_ci        return;
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    pa_assert(pa_hashmap_isempty(p->objects));
16753a5a1b3Sopenharmony_ci    pa_assert(pa_hashmap_isempty(p->connections));
16853a5a1b3Sopenharmony_ci    pa_assert(pa_idxset_isempty(p->extensions));
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci    pa_hashmap_free(p->objects);
17153a5a1b3Sopenharmony_ci    pa_hashmap_free(p->connections);
17253a5a1b3Sopenharmony_ci    pa_idxset_free(p->extensions, NULL);
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
17553a5a1b3Sopenharmony_ci        pa_hook_done(&p->hooks[i]);
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    pa_xfree(p);
18053a5a1b3Sopenharmony_ci}
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_cistatic void update_introspection(struct object_entry *oe) {
18353a5a1b3Sopenharmony_ci    pa_strbuf *buf;
18453a5a1b3Sopenharmony_ci    void *interfaces_state = NULL;
18553a5a1b3Sopenharmony_ci    struct interface_entry *iface_entry = NULL;
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    pa_assert(oe);
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci    buf = pa_strbuf_new();
19053a5a1b3Sopenharmony_ci    pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
19153a5a1b3Sopenharmony_ci    pa_strbuf_puts(buf, "<node>\n");
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
19453a5a1b3Sopenharmony_ci        pa_dbus_method_handler *method_handler;
19553a5a1b3Sopenharmony_ci        pa_dbus_property_handler *property_handler;
19653a5a1b3Sopenharmony_ci        void *handlers_state = NULL;
19753a5a1b3Sopenharmony_ci        unsigned i;
19853a5a1b3Sopenharmony_ci        unsigned j;
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci        pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
20353a5a1b3Sopenharmony_ci            pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_ci            for (i = 0; i < method_handler->n_arguments; ++i)
20653a5a1b3Sopenharmony_ci                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
20753a5a1b3Sopenharmony_ci                                 method_handler->arguments[i].name,
20853a5a1b3Sopenharmony_ci                                 method_handler->arguments[i].type,
20953a5a1b3Sopenharmony_ci                                 method_handler->arguments[i].direction);
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci            pa_strbuf_puts(buf, "  </method>\n");
21253a5a1b3Sopenharmony_ci        }
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci        handlers_state = NULL;
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
21753a5a1b3Sopenharmony_ci            pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
21853a5a1b3Sopenharmony_ci                             property_handler->property_name,
21953a5a1b3Sopenharmony_ci                             property_handler->type,
22053a5a1b3Sopenharmony_ci                             property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci        for (i = 0; i < iface_entry->n_signals; ++i) {
22353a5a1b3Sopenharmony_ci            pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci            for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
22653a5a1b3Sopenharmony_ci                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
22753a5a1b3Sopenharmony_ci                                                                             iface_entry->signals[i].arguments[j].type);
22853a5a1b3Sopenharmony_ci
22953a5a1b3Sopenharmony_ci            pa_strbuf_puts(buf, "  </signal>\n");
23053a5a1b3Sopenharmony_ci        }
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci        pa_strbuf_puts(buf, " </interface>\n");
23353a5a1b3Sopenharmony_ci    }
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_ci    pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
23653a5a1b3Sopenharmony_ci                        "  <method name=\"Introspect\">\n"
23753a5a1b3Sopenharmony_ci                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
23853a5a1b3Sopenharmony_ci                        "  </method>\n"
23953a5a1b3Sopenharmony_ci                        " </interface>\n"
24053a5a1b3Sopenharmony_ci                        " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
24153a5a1b3Sopenharmony_ci                        "  <method name=\"Get\">\n"
24253a5a1b3Sopenharmony_ci                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
24353a5a1b3Sopenharmony_ci                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
24453a5a1b3Sopenharmony_ci                        "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
24553a5a1b3Sopenharmony_ci                        "  </method>\n"
24653a5a1b3Sopenharmony_ci                        "  <method name=\"Set\">\n"
24753a5a1b3Sopenharmony_ci                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
24853a5a1b3Sopenharmony_ci                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
24953a5a1b3Sopenharmony_ci                        "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
25053a5a1b3Sopenharmony_ci                        "  </method>\n"
25153a5a1b3Sopenharmony_ci                        "  <method name=\"GetAll\">\n"
25253a5a1b3Sopenharmony_ci                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
25353a5a1b3Sopenharmony_ci                        "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
25453a5a1b3Sopenharmony_ci                        "  </method>\n"
25553a5a1b3Sopenharmony_ci                        " </interface>\n");
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci    pa_strbuf_puts(buf, "</node>\n");
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci    pa_xfree(oe->introspection);
26053a5a1b3Sopenharmony_ci    oe->introspection = pa_strbuf_to_string_free(buf);
26153a5a1b3Sopenharmony_ci}
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci/* Return value of find_handler() and its subfunctions. */
26453a5a1b3Sopenharmony_cienum find_result_t {
26553a5a1b3Sopenharmony_ci    /* The received message is a valid .Get call. */
26653a5a1b3Sopenharmony_ci    FOUND_GET_PROPERTY,
26753a5a1b3Sopenharmony_ci
26853a5a1b3Sopenharmony_ci    /* The received message is a valid .Set call. */
26953a5a1b3Sopenharmony_ci    FOUND_SET_PROPERTY,
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci    /* The received message is a valid .GetAll call. */
27253a5a1b3Sopenharmony_ci    FOUND_GET_ALL,
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    /* The received message is a valid method call. */
27553a5a1b3Sopenharmony_ci    FOUND_METHOD,
27653a5a1b3Sopenharmony_ci
27753a5a1b3Sopenharmony_ci    /* The interface of the received message hasn't been registered for the
27853a5a1b3Sopenharmony_ci     * destination object. */
27953a5a1b3Sopenharmony_ci    NO_SUCH_INTERFACE,
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    /* No property handler was found for the received .Get or .Set call. */
28253a5a1b3Sopenharmony_ci    NO_SUCH_PROPERTY,
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci    /* The interface argument of a property call didn't match any registered
28553a5a1b3Sopenharmony_ci     * interface. */
28653a5a1b3Sopenharmony_ci    NO_SUCH_PROPERTY_INTERFACE,
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    /* The received message called .Get or .Set for a property whose access
28953a5a1b3Sopenharmony_ci     * mode doesn't match the call. */
29053a5a1b3Sopenharmony_ci    PROPERTY_ACCESS_DENIED,
29153a5a1b3Sopenharmony_ci
29253a5a1b3Sopenharmony_ci    /* The new value signature of a .Set call didn't match the expected
29353a5a1b3Sopenharmony_ci     * signature. */
29453a5a1b3Sopenharmony_ci    INVALID_PROPERTY_SIG,
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ci    /* No method handler was found for the received message. */
29753a5a1b3Sopenharmony_ci    NO_SUCH_METHOD,
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci    /* The signature of the received message didn't match the expected
30053a5a1b3Sopenharmony_ci     * signature. Despite the name, this can also be returned for a property
30153a5a1b3Sopenharmony_ci     * call if its message signature is invalid. */
30253a5a1b3Sopenharmony_ci    INVALID_METHOD_SIG
30353a5a1b3Sopenharmony_ci};
30453a5a1b3Sopenharmony_ci
30553a5a1b3Sopenharmony_ci/* Data for resolving the correct reaction to a received message. */
30653a5a1b3Sopenharmony_cistruct call_info {
30753a5a1b3Sopenharmony_ci    DBusMessage *message; /* The received message. */
30853a5a1b3Sopenharmony_ci    struct object_entry *obj_entry;
30953a5a1b3Sopenharmony_ci    const char *interface; /* Destination interface name (extracted from the message). */
31053a5a1b3Sopenharmony_ci    struct interface_entry *iface_entry;
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    const char *property; /* Property name (extracted from the message). */
31353a5a1b3Sopenharmony_ci    const char *property_interface; /* The interface argument of a property call is stored here. */
31453a5a1b3Sopenharmony_ci    pa_dbus_property_handler *property_handler;
31553a5a1b3Sopenharmony_ci    const char *expected_property_sig; /* Property signature from the introspection data. */
31653a5a1b3Sopenharmony_ci    char *property_sig; /* The signature of the new value in the received .Set message. */
31753a5a1b3Sopenharmony_ci    DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_ci    const char *method; /* Method name (extracted from the message). */
32053a5a1b3Sopenharmony_ci    pa_dbus_method_handler *method_handler;
32153a5a1b3Sopenharmony_ci    const char *expected_method_sig; /* Method signature from the introspection data. */
32253a5a1b3Sopenharmony_ci    const char *method_sig; /* The signature of the received message. */
32353a5a1b3Sopenharmony_ci};
32453a5a1b3Sopenharmony_ci
32553a5a1b3Sopenharmony_ci/* Called when call_info->property has been set and the property interface has
32653a5a1b3Sopenharmony_ci * not been given. In case of a Set call, call_info->property_sig is also set,
32753a5a1b3Sopenharmony_ci * which is checked against the expected value in this function. */
32853a5a1b3Sopenharmony_cistatic enum find_result_t find_handler_by_property(struct call_info *call_info) {
32953a5a1b3Sopenharmony_ci    void *state = NULL;
33053a5a1b3Sopenharmony_ci
33153a5a1b3Sopenharmony_ci    pa_assert(call_info);
33253a5a1b3Sopenharmony_ci
33353a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
33453a5a1b3Sopenharmony_ci        if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
33553a5a1b3Sopenharmony_ci            if (pa_streq(call_info->method, "Get"))
33653a5a1b3Sopenharmony_ci                return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
33753a5a1b3Sopenharmony_ci
33853a5a1b3Sopenharmony_ci            else if (pa_streq(call_info->method, "Set")) {
33953a5a1b3Sopenharmony_ci                call_info->expected_property_sig = call_info->property_handler->type;
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
34253a5a1b3Sopenharmony_ci                    return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
34353a5a1b3Sopenharmony_ci                else
34453a5a1b3Sopenharmony_ci                    return INVALID_PROPERTY_SIG;
34553a5a1b3Sopenharmony_ci
34653a5a1b3Sopenharmony_ci            } else
34753a5a1b3Sopenharmony_ci                pa_assert_not_reached();
34853a5a1b3Sopenharmony_ci        }
34953a5a1b3Sopenharmony_ci    }
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci    return NO_SUCH_PROPERTY;
35253a5a1b3Sopenharmony_ci}
35353a5a1b3Sopenharmony_ci
35453a5a1b3Sopenharmony_cistatic enum find_result_t find_handler_by_method(struct call_info *call_info) {
35553a5a1b3Sopenharmony_ci    void *state = NULL;
35653a5a1b3Sopenharmony_ci
35753a5a1b3Sopenharmony_ci    pa_assert(call_info);
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
36053a5a1b3Sopenharmony_ci        if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
36153a5a1b3Sopenharmony_ci            pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci            if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
36453a5a1b3Sopenharmony_ci                return FOUND_METHOD;
36553a5a1b3Sopenharmony_ci            else
36653a5a1b3Sopenharmony_ci                return INVALID_METHOD_SIG;
36753a5a1b3Sopenharmony_ci        }
36853a5a1b3Sopenharmony_ci    }
36953a5a1b3Sopenharmony_ci
37053a5a1b3Sopenharmony_ci    return NO_SUCH_METHOD;
37153a5a1b3Sopenharmony_ci}
37253a5a1b3Sopenharmony_ci
37353a5a1b3Sopenharmony_cistatic enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
37453a5a1b3Sopenharmony_ci    pa_assert(call_info);
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_ci    if (pa_streq(call_info->method, "GetAll")) {
37753a5a1b3Sopenharmony_ci        call_info->expected_method_sig = "s";
37853a5a1b3Sopenharmony_ci        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
37953a5a1b3Sopenharmony_ci            return INVALID_METHOD_SIG;
38053a5a1b3Sopenharmony_ci
38153a5a1b3Sopenharmony_ci        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
38253a5a1b3Sopenharmony_ci                                           DBUS_TYPE_STRING, &call_info->property_interface,
38353a5a1b3Sopenharmony_ci                                           DBUS_TYPE_INVALID));
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci        if (*call_info->property_interface) {
38653a5a1b3Sopenharmony_ci            if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
38753a5a1b3Sopenharmony_ci                return FOUND_GET_ALL;
38853a5a1b3Sopenharmony_ci            else
38953a5a1b3Sopenharmony_ci                return NO_SUCH_PROPERTY_INTERFACE;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci        } else {
39253a5a1b3Sopenharmony_ci            pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
39353a5a1b3Sopenharmony_ci            return FOUND_GET_ALL;
39453a5a1b3Sopenharmony_ci        }
39553a5a1b3Sopenharmony_ci
39653a5a1b3Sopenharmony_ci    } else if (pa_streq(call_info->method, "Get")) {
39753a5a1b3Sopenharmony_ci        call_info->expected_method_sig = "ss";
39853a5a1b3Sopenharmony_ci        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
39953a5a1b3Sopenharmony_ci            return INVALID_METHOD_SIG;
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
40253a5a1b3Sopenharmony_ci                                           DBUS_TYPE_STRING, &call_info->property_interface,
40353a5a1b3Sopenharmony_ci                                           DBUS_TYPE_STRING, &call_info->property,
40453a5a1b3Sopenharmony_ci                                           DBUS_TYPE_INVALID));
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci        if (*call_info->property_interface) {
40753a5a1b3Sopenharmony_ci            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
40853a5a1b3Sopenharmony_ci                return NO_SUCH_PROPERTY_INTERFACE;
40953a5a1b3Sopenharmony_ci            else if ((call_info->property_handler =
41053a5a1b3Sopenharmony_ci                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
41153a5a1b3Sopenharmony_ci                return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
41253a5a1b3Sopenharmony_ci            else
41353a5a1b3Sopenharmony_ci                return NO_SUCH_PROPERTY;
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci        } else
41653a5a1b3Sopenharmony_ci            return find_handler_by_property(call_info);
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_ci    } else if (pa_streq(call_info->method, "Set")) {
41953a5a1b3Sopenharmony_ci        DBusMessageIter msg_iter;
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci        call_info->expected_method_sig = "ssv";
42253a5a1b3Sopenharmony_ci        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
42353a5a1b3Sopenharmony_ci            return INVALID_METHOD_SIG;
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci        pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
42653a5a1b3Sopenharmony_ci
42753a5a1b3Sopenharmony_ci        dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
42853a5a1b3Sopenharmony_ci        pa_assert_se(dbus_message_iter_next(&msg_iter));
42953a5a1b3Sopenharmony_ci        dbus_message_iter_get_basic(&msg_iter, &call_info->property);
43053a5a1b3Sopenharmony_ci        pa_assert_se(dbus_message_iter_next(&msg_iter));
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci        dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_ci        pa_assert_se(call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter));
43553a5a1b3Sopenharmony_ci
43653a5a1b3Sopenharmony_ci        if (*call_info->property_interface) {
43753a5a1b3Sopenharmony_ci            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
43853a5a1b3Sopenharmony_ci                return NO_SUCH_PROPERTY_INTERFACE;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci            else if ((call_info->property_handler =
44153a5a1b3Sopenharmony_ci                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
44253a5a1b3Sopenharmony_ci                call_info->expected_property_sig = call_info->property_handler->type;
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
44553a5a1b3Sopenharmony_ci                    return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
44653a5a1b3Sopenharmony_ci                else
44753a5a1b3Sopenharmony_ci                    return INVALID_PROPERTY_SIG;
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci            } else
45053a5a1b3Sopenharmony_ci                return NO_SUCH_PROPERTY;
45153a5a1b3Sopenharmony_ci
45253a5a1b3Sopenharmony_ci        } else
45353a5a1b3Sopenharmony_ci            return find_handler_by_property(call_info);
45453a5a1b3Sopenharmony_ci
45553a5a1b3Sopenharmony_ci    } else
45653a5a1b3Sopenharmony_ci        pa_assert_not_reached();
45753a5a1b3Sopenharmony_ci}
45853a5a1b3Sopenharmony_ci
45953a5a1b3Sopenharmony_cistatic enum find_result_t find_handler(struct call_info *call_info) {
46053a5a1b3Sopenharmony_ci    pa_assert(call_info);
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci    if (call_info->interface) {
46353a5a1b3Sopenharmony_ci        if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
46453a5a1b3Sopenharmony_ci            return find_handler_from_properties_call(call_info);
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci        else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
46753a5a1b3Sopenharmony_ci            return NO_SUCH_INTERFACE;
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci        else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
47053a5a1b3Sopenharmony_ci            pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_ci            if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
47353a5a1b3Sopenharmony_ci                return INVALID_METHOD_SIG;
47453a5a1b3Sopenharmony_ci
47553a5a1b3Sopenharmony_ci            return FOUND_METHOD;
47653a5a1b3Sopenharmony_ci
47753a5a1b3Sopenharmony_ci        } else
47853a5a1b3Sopenharmony_ci            return NO_SUCH_METHOD;
47953a5a1b3Sopenharmony_ci
48053a5a1b3Sopenharmony_ci    } else { /* The method call doesn't contain an interface. */
48153a5a1b3Sopenharmony_ci        if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
48253a5a1b3Sopenharmony_ci            if (find_handler_by_method(call_info) == FOUND_METHOD)
48353a5a1b3Sopenharmony_ci                /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
48453a5a1b3Sopenharmony_ci                return FOUND_METHOD;
48553a5a1b3Sopenharmony_ci            else
48653a5a1b3Sopenharmony_ci                /* Assume this is a .Properties call. */
48753a5a1b3Sopenharmony_ci                return find_handler_from_properties_call(call_info);
48853a5a1b3Sopenharmony_ci
48953a5a1b3Sopenharmony_ci        } else /* This is not a .Properties call. */
49053a5a1b3Sopenharmony_ci            return find_handler_by_method(call_info);
49153a5a1b3Sopenharmony_ci    }
49253a5a1b3Sopenharmony_ci}
49353a5a1b3Sopenharmony_ci
49453a5a1b3Sopenharmony_cistatic DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
49553a5a1b3Sopenharmony_ci    pa_dbus_protocol *p = user_data;
49653a5a1b3Sopenharmony_ci    struct call_info call_info;
49753a5a1b3Sopenharmony_ci    call_info.property_sig = NULL;
49853a5a1b3Sopenharmony_ci
49953a5a1b3Sopenharmony_ci    pa_assert(connection);
50053a5a1b3Sopenharmony_ci    pa_assert(message);
50153a5a1b3Sopenharmony_ci    pa_assert(p);
50253a5a1b3Sopenharmony_ci    pa_assert(p->objects);
50353a5a1b3Sopenharmony_ci
50453a5a1b3Sopenharmony_ci    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
50553a5a1b3Sopenharmony_ci        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
50653a5a1b3Sopenharmony_ci
50753a5a1b3Sopenharmony_ci    pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
50853a5a1b3Sopenharmony_ci                 dbus_message_get_path(message),
50953a5a1b3Sopenharmony_ci                 dbus_message_get_interface(message),
51053a5a1b3Sopenharmony_ci                 dbus_message_get_member(message));
51153a5a1b3Sopenharmony_ci
51253a5a1b3Sopenharmony_ci    call_info.message = message;
51353a5a1b3Sopenharmony_ci    pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
51453a5a1b3Sopenharmony_ci    call_info.interface = dbus_message_get_interface(message);
51553a5a1b3Sopenharmony_ci    pa_assert_se(call_info.method = dbus_message_get_member(message));
51653a5a1b3Sopenharmony_ci    pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
51753a5a1b3Sopenharmony_ci
51853a5a1b3Sopenharmony_ci    if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
51953a5a1b3Sopenharmony_ci        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
52053a5a1b3Sopenharmony_ci        pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
52153a5a1b3Sopenharmony_ci        goto finish;
52253a5a1b3Sopenharmony_ci    }
52353a5a1b3Sopenharmony_ci
52453a5a1b3Sopenharmony_ci    switch (find_handler(&call_info)) {
52553a5a1b3Sopenharmony_ci        case FOUND_GET_PROPERTY:
52653a5a1b3Sopenharmony_ci            call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
52753a5a1b3Sopenharmony_ci            break;
52853a5a1b3Sopenharmony_ci
52953a5a1b3Sopenharmony_ci        case FOUND_SET_PROPERTY:
53053a5a1b3Sopenharmony_ci            call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
53153a5a1b3Sopenharmony_ci            break;
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_ci        case FOUND_METHOD:
53453a5a1b3Sopenharmony_ci            call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
53553a5a1b3Sopenharmony_ci            break;
53653a5a1b3Sopenharmony_ci
53753a5a1b3Sopenharmony_ci        case FOUND_GET_ALL:
53853a5a1b3Sopenharmony_ci            if (call_info.iface_entry->get_all_properties_cb)
53953a5a1b3Sopenharmony_ci                call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
54053a5a1b3Sopenharmony_ci            else {
54153a5a1b3Sopenharmony_ci                DBusMessage *dummy_reply = NULL;
54253a5a1b3Sopenharmony_ci                DBusMessageIter msg_iter;
54353a5a1b3Sopenharmony_ci                DBusMessageIter dict_iter;
54453a5a1b3Sopenharmony_ci
54553a5a1b3Sopenharmony_ci                pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
54653a5a1b3Sopenharmony_ci                dbus_message_iter_init_append(dummy_reply, &msg_iter);
54753a5a1b3Sopenharmony_ci                pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
54853a5a1b3Sopenharmony_ci                pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
54953a5a1b3Sopenharmony_ci                pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
55053a5a1b3Sopenharmony_ci                dbus_message_unref(dummy_reply);
55153a5a1b3Sopenharmony_ci            }
55253a5a1b3Sopenharmony_ci            break;
55353a5a1b3Sopenharmony_ci
55453a5a1b3Sopenharmony_ci        case PROPERTY_ACCESS_DENIED:
55553a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
55653a5a1b3Sopenharmony_ci                               "%s access denied for property %s", call_info.method, call_info.property);
55753a5a1b3Sopenharmony_ci            break;
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_ci        case NO_SUCH_METHOD:
56053a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
56153a5a1b3Sopenharmony_ci            break;
56253a5a1b3Sopenharmony_ci
56353a5a1b3Sopenharmony_ci        case NO_SUCH_INTERFACE:
56453a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such interface: %s", call_info.interface);
56553a5a1b3Sopenharmony_ci            break;
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_ci        case NO_SUCH_PROPERTY:
56853a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
56953a5a1b3Sopenharmony_ci            break;
57053a5a1b3Sopenharmony_ci
57153a5a1b3Sopenharmony_ci        case NO_SUCH_PROPERTY_INTERFACE:
57253a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such property interface: %s", call_info.property_interface);
57353a5a1b3Sopenharmony_ci            break;
57453a5a1b3Sopenharmony_ci
57553a5a1b3Sopenharmony_ci        case INVALID_METHOD_SIG:
57653a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
57753a5a1b3Sopenharmony_ci                               "Invalid signature for method %s: '%s'. Expected '%s'.",
57853a5a1b3Sopenharmony_ci                               call_info.method, call_info.method_sig, call_info.expected_method_sig);
57953a5a1b3Sopenharmony_ci            break;
58053a5a1b3Sopenharmony_ci
58153a5a1b3Sopenharmony_ci        case INVALID_PROPERTY_SIG:
58253a5a1b3Sopenharmony_ci            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
58353a5a1b3Sopenharmony_ci                               "Invalid signature for property %s: '%s'. Expected '%s'.",
58453a5a1b3Sopenharmony_ci                               call_info.property, call_info.property_sig, call_info.expected_property_sig);
58553a5a1b3Sopenharmony_ci            break;
58653a5a1b3Sopenharmony_ci
58753a5a1b3Sopenharmony_ci        default:
58853a5a1b3Sopenharmony_ci            pa_assert_not_reached();
58953a5a1b3Sopenharmony_ci    }
59053a5a1b3Sopenharmony_ci
59153a5a1b3Sopenharmony_cifinish:
59253a5a1b3Sopenharmony_ci    if (call_info.property_sig)
59353a5a1b3Sopenharmony_ci        dbus_free(call_info.property_sig);
59453a5a1b3Sopenharmony_ci
59553a5a1b3Sopenharmony_ci    return DBUS_HANDLER_RESULT_HANDLED;
59653a5a1b3Sopenharmony_ci}
59753a5a1b3Sopenharmony_ci
59853a5a1b3Sopenharmony_cistatic DBusObjectPathVTable vtable = {
59953a5a1b3Sopenharmony_ci    .unregister_function = NULL,
60053a5a1b3Sopenharmony_ci    .message_function = handle_message_cb,
60153a5a1b3Sopenharmony_ci    .dbus_internal_pad1 = NULL,
60253a5a1b3Sopenharmony_ci    .dbus_internal_pad2 = NULL,
60353a5a1b3Sopenharmony_ci    .dbus_internal_pad3 = NULL,
60453a5a1b3Sopenharmony_ci    .dbus_internal_pad4 = NULL
60553a5a1b3Sopenharmony_ci};
60653a5a1b3Sopenharmony_ci
60753a5a1b3Sopenharmony_cistatic void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
60853a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry;
60953a5a1b3Sopenharmony_ci    void *state = NULL;
61053a5a1b3Sopenharmony_ci
61153a5a1b3Sopenharmony_ci    pa_assert(p);
61253a5a1b3Sopenharmony_ci    pa_assert(obj_entry);
61353a5a1b3Sopenharmony_ci
61453a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
61553a5a1b3Sopenharmony_ci        pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
61653a5a1b3Sopenharmony_ci}
61753a5a1b3Sopenharmony_ci
61853a5a1b3Sopenharmony_cistatic pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
61953a5a1b3Sopenharmony_ci    pa_dbus_arg_info *dst;
62053a5a1b3Sopenharmony_ci    unsigned i;
62153a5a1b3Sopenharmony_ci
62253a5a1b3Sopenharmony_ci    if (n == 0)
62353a5a1b3Sopenharmony_ci        return NULL;
62453a5a1b3Sopenharmony_ci
62553a5a1b3Sopenharmony_ci    pa_assert(src);
62653a5a1b3Sopenharmony_ci
62753a5a1b3Sopenharmony_ci    dst = pa_xnew0(pa_dbus_arg_info, n);
62853a5a1b3Sopenharmony_ci
62953a5a1b3Sopenharmony_ci    for (i = 0; i < n; ++i) {
63053a5a1b3Sopenharmony_ci        dst[i].name = pa_xstrdup(src[i].name);
63153a5a1b3Sopenharmony_ci        dst[i].type = pa_xstrdup(src[i].type);
63253a5a1b3Sopenharmony_ci        dst[i].direction = pa_xstrdup(src[i].direction);
63353a5a1b3Sopenharmony_ci    }
63453a5a1b3Sopenharmony_ci
63553a5a1b3Sopenharmony_ci    return dst;
63653a5a1b3Sopenharmony_ci}
63753a5a1b3Sopenharmony_ci
63853a5a1b3Sopenharmony_cistatic void method_handler_free(pa_dbus_method_handler *h) {
63953a5a1b3Sopenharmony_ci    unsigned i;
64053a5a1b3Sopenharmony_ci
64153a5a1b3Sopenharmony_ci    pa_assert(h);
64253a5a1b3Sopenharmony_ci
64353a5a1b3Sopenharmony_ci    pa_xfree((char *) h->method_name);
64453a5a1b3Sopenharmony_ci
64553a5a1b3Sopenharmony_ci    for (i = 0; i < h->n_arguments; ++i) {
64653a5a1b3Sopenharmony_ci        pa_xfree((char *) h->arguments[i].name);
64753a5a1b3Sopenharmony_ci        pa_xfree((char *) h->arguments[i].type);
64853a5a1b3Sopenharmony_ci        pa_xfree((char *) h->arguments[i].direction);
64953a5a1b3Sopenharmony_ci    }
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_ci    pa_xfree((pa_dbus_arg_info *) h->arguments);
65253a5a1b3Sopenharmony_ci    pa_xfree(h);
65353a5a1b3Sopenharmony_ci}
65453a5a1b3Sopenharmony_ci
65553a5a1b3Sopenharmony_cistatic pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
65653a5a1b3Sopenharmony_ci    pa_hashmap *handlers;
65753a5a1b3Sopenharmony_ci    unsigned i;
65853a5a1b3Sopenharmony_ci
65953a5a1b3Sopenharmony_ci    pa_assert(info);
66053a5a1b3Sopenharmony_ci    pa_assert(info->method_handlers || info->n_method_handlers == 0);
66153a5a1b3Sopenharmony_ci
66253a5a1b3Sopenharmony_ci    handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) method_handler_free);
66353a5a1b3Sopenharmony_ci
66453a5a1b3Sopenharmony_ci    for (i = 0; i < info->n_method_handlers; ++i) {
66553a5a1b3Sopenharmony_ci        pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
66653a5a1b3Sopenharmony_ci        h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
66753a5a1b3Sopenharmony_ci        h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
66853a5a1b3Sopenharmony_ci        h->n_arguments = info->method_handlers[i].n_arguments;
66953a5a1b3Sopenharmony_ci        h->receive_cb = info->method_handlers[i].receive_cb;
67053a5a1b3Sopenharmony_ci
67153a5a1b3Sopenharmony_ci        pa_hashmap_put(handlers, (char *) h->method_name, h);
67253a5a1b3Sopenharmony_ci    }
67353a5a1b3Sopenharmony_ci
67453a5a1b3Sopenharmony_ci    return handlers;
67553a5a1b3Sopenharmony_ci}
67653a5a1b3Sopenharmony_ci
67753a5a1b3Sopenharmony_cistatic pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
67853a5a1b3Sopenharmony_ci    pa_hashmap *signatures = NULL;
67953a5a1b3Sopenharmony_ci    pa_dbus_method_handler *handler = NULL;
68053a5a1b3Sopenharmony_ci    void *state = NULL;
68153a5a1b3Sopenharmony_ci    pa_strbuf *sig_buf = NULL;
68253a5a1b3Sopenharmony_ci    unsigned i = 0;
68353a5a1b3Sopenharmony_ci
68453a5a1b3Sopenharmony_ci    pa_assert(method_handlers);
68553a5a1b3Sopenharmony_ci
68653a5a1b3Sopenharmony_ci    signatures = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
68753a5a1b3Sopenharmony_ci
68853a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(handler, method_handlers, state) {
68953a5a1b3Sopenharmony_ci        sig_buf = pa_strbuf_new();
69053a5a1b3Sopenharmony_ci
69153a5a1b3Sopenharmony_ci        for (i = 0; i < handler->n_arguments; ++i) {
69253a5a1b3Sopenharmony_ci            if (pa_streq(handler->arguments[i].direction, "in"))
69353a5a1b3Sopenharmony_ci                pa_strbuf_puts(sig_buf, handler->arguments[i].type);
69453a5a1b3Sopenharmony_ci        }
69553a5a1b3Sopenharmony_ci
69653a5a1b3Sopenharmony_ci        pa_hashmap_put(signatures, (char *) handler->method_name, pa_strbuf_to_string_free(sig_buf));
69753a5a1b3Sopenharmony_ci    }
69853a5a1b3Sopenharmony_ci
69953a5a1b3Sopenharmony_ci    return signatures;
70053a5a1b3Sopenharmony_ci}
70153a5a1b3Sopenharmony_ci
70253a5a1b3Sopenharmony_cistatic void property_handler_free(pa_dbus_property_handler *h) {
70353a5a1b3Sopenharmony_ci    pa_assert(h);
70453a5a1b3Sopenharmony_ci
70553a5a1b3Sopenharmony_ci    pa_xfree((char *) h->property_name);
70653a5a1b3Sopenharmony_ci    pa_xfree((char *) h->type);
70753a5a1b3Sopenharmony_ci
70853a5a1b3Sopenharmony_ci    pa_xfree(h);
70953a5a1b3Sopenharmony_ci}
71053a5a1b3Sopenharmony_ci
71153a5a1b3Sopenharmony_cistatic pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
71253a5a1b3Sopenharmony_ci    pa_hashmap *handlers;
71353a5a1b3Sopenharmony_ci    unsigned i = 0;
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_ci    pa_assert(info);
71653a5a1b3Sopenharmony_ci    pa_assert(info->property_handlers || info->n_property_handlers == 0);
71753a5a1b3Sopenharmony_ci
71853a5a1b3Sopenharmony_ci    handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) property_handler_free);
71953a5a1b3Sopenharmony_ci
72053a5a1b3Sopenharmony_ci    for (i = 0; i < info->n_property_handlers; ++i) {
72153a5a1b3Sopenharmony_ci        pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
72253a5a1b3Sopenharmony_ci        h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
72353a5a1b3Sopenharmony_ci        h->type = pa_xstrdup(info->property_handlers[i].type);
72453a5a1b3Sopenharmony_ci        h->get_cb = info->property_handlers[i].get_cb;
72553a5a1b3Sopenharmony_ci        h->set_cb = info->property_handlers[i].set_cb;
72653a5a1b3Sopenharmony_ci
72753a5a1b3Sopenharmony_ci        pa_hashmap_put(handlers, (char *) h->property_name, h);
72853a5a1b3Sopenharmony_ci    }
72953a5a1b3Sopenharmony_ci
73053a5a1b3Sopenharmony_ci    return handlers;
73153a5a1b3Sopenharmony_ci}
73253a5a1b3Sopenharmony_ci
73353a5a1b3Sopenharmony_cistatic pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
73453a5a1b3Sopenharmony_ci    pa_dbus_signal_info *dst;
73553a5a1b3Sopenharmony_ci    unsigned i;
73653a5a1b3Sopenharmony_ci
73753a5a1b3Sopenharmony_ci    pa_assert(info);
73853a5a1b3Sopenharmony_ci
73953a5a1b3Sopenharmony_ci    if (info->n_signals == 0)
74053a5a1b3Sopenharmony_ci        return NULL;
74153a5a1b3Sopenharmony_ci
74253a5a1b3Sopenharmony_ci    pa_assert(info->signals);
74353a5a1b3Sopenharmony_ci
74453a5a1b3Sopenharmony_ci    dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
74553a5a1b3Sopenharmony_ci
74653a5a1b3Sopenharmony_ci    for (i = 0; i < info->n_signals; ++i) {
74753a5a1b3Sopenharmony_ci        dst[i].name = pa_xstrdup(info->signals[i].name);
74853a5a1b3Sopenharmony_ci        dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
74953a5a1b3Sopenharmony_ci        dst[i].n_arguments = info->signals[i].n_arguments;
75053a5a1b3Sopenharmony_ci    }
75153a5a1b3Sopenharmony_ci
75253a5a1b3Sopenharmony_ci    return dst;
75353a5a1b3Sopenharmony_ci}
75453a5a1b3Sopenharmony_ci
75553a5a1b3Sopenharmony_ciint pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
75653a5a1b3Sopenharmony_ci                                   const char *path,
75753a5a1b3Sopenharmony_ci                                   const pa_dbus_interface_info *info,
75853a5a1b3Sopenharmony_ci                                   void *userdata) {
75953a5a1b3Sopenharmony_ci    struct object_entry *obj_entry;
76053a5a1b3Sopenharmony_ci    struct interface_entry *iface_entry;
76153a5a1b3Sopenharmony_ci    bool obj_entry_created = false;
76253a5a1b3Sopenharmony_ci
76353a5a1b3Sopenharmony_ci    pa_assert(p);
76453a5a1b3Sopenharmony_ci    pa_assert(path);
76553a5a1b3Sopenharmony_ci    pa_assert(info);
76653a5a1b3Sopenharmony_ci    pa_assert(info->name);
76753a5a1b3Sopenharmony_ci    pa_assert(info->method_handlers || info->n_method_handlers == 0);
76853a5a1b3Sopenharmony_ci    pa_assert(info->property_handlers || info->n_property_handlers == 0);
76953a5a1b3Sopenharmony_ci    pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
77053a5a1b3Sopenharmony_ci    pa_assert(info->signals || info->n_signals == 0);
77153a5a1b3Sopenharmony_ci
77253a5a1b3Sopenharmony_ci    if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
77353a5a1b3Sopenharmony_ci        obj_entry = pa_xnew(struct object_entry, 1);
77453a5a1b3Sopenharmony_ci        obj_entry->path = pa_xstrdup(path);
77553a5a1b3Sopenharmony_ci        obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
77653a5a1b3Sopenharmony_ci        obj_entry->introspection = NULL;
77753a5a1b3Sopenharmony_ci
77853a5a1b3Sopenharmony_ci        pa_hashmap_put(p->objects, obj_entry->path, obj_entry);
77953a5a1b3Sopenharmony_ci        obj_entry_created = true;
78053a5a1b3Sopenharmony_ci    }
78153a5a1b3Sopenharmony_ci
78253a5a1b3Sopenharmony_ci    if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
78353a5a1b3Sopenharmony_ci        goto fail; /* The interface was already registered. */
78453a5a1b3Sopenharmony_ci
78553a5a1b3Sopenharmony_ci    iface_entry = pa_xnew(struct interface_entry, 1);
78653a5a1b3Sopenharmony_ci    iface_entry->name = pa_xstrdup(info->name);
78753a5a1b3Sopenharmony_ci    iface_entry->method_handlers = create_method_handlers(info);
78853a5a1b3Sopenharmony_ci    iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
78953a5a1b3Sopenharmony_ci    iface_entry->property_handlers = create_property_handlers(info);
79053a5a1b3Sopenharmony_ci    iface_entry->get_all_properties_cb = info->get_all_properties_cb;
79153a5a1b3Sopenharmony_ci    iface_entry->signals = copy_signals(info);
79253a5a1b3Sopenharmony_ci    iface_entry->n_signals = info->n_signals;
79353a5a1b3Sopenharmony_ci    iface_entry->userdata = userdata;
79453a5a1b3Sopenharmony_ci    pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
79553a5a1b3Sopenharmony_ci
79653a5a1b3Sopenharmony_ci    update_introspection(obj_entry);
79753a5a1b3Sopenharmony_ci
79853a5a1b3Sopenharmony_ci    if (obj_entry_created)
79953a5a1b3Sopenharmony_ci        register_object(p, obj_entry);
80053a5a1b3Sopenharmony_ci
80153a5a1b3Sopenharmony_ci    pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
80253a5a1b3Sopenharmony_ci
80353a5a1b3Sopenharmony_ci    return 0;
80453a5a1b3Sopenharmony_ci
80553a5a1b3Sopenharmony_cifail:
80653a5a1b3Sopenharmony_ci    return -1;
80753a5a1b3Sopenharmony_ci}
80853a5a1b3Sopenharmony_ci
80953a5a1b3Sopenharmony_cistatic void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
81053a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry;
81153a5a1b3Sopenharmony_ci    void *state = NULL;
81253a5a1b3Sopenharmony_ci
81353a5a1b3Sopenharmony_ci    pa_assert(p);
81453a5a1b3Sopenharmony_ci    pa_assert(obj_entry);
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
81753a5a1b3Sopenharmony_ci        pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
81853a5a1b3Sopenharmony_ci}
81953a5a1b3Sopenharmony_ci
82053a5a1b3Sopenharmony_ciint pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
82153a5a1b3Sopenharmony_ci    struct object_entry *obj_entry;
82253a5a1b3Sopenharmony_ci    struct interface_entry *iface_entry;
82353a5a1b3Sopenharmony_ci    unsigned i;
82453a5a1b3Sopenharmony_ci
82553a5a1b3Sopenharmony_ci    pa_assert(p);
82653a5a1b3Sopenharmony_ci    pa_assert(path);
82753a5a1b3Sopenharmony_ci    pa_assert(interface);
82853a5a1b3Sopenharmony_ci
82953a5a1b3Sopenharmony_ci    if (!(obj_entry = pa_hashmap_get(p->objects, path)))
83053a5a1b3Sopenharmony_ci        return -1;
83153a5a1b3Sopenharmony_ci
83253a5a1b3Sopenharmony_ci    if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
83353a5a1b3Sopenharmony_ci        return -1;
83453a5a1b3Sopenharmony_ci
83553a5a1b3Sopenharmony_ci    update_introspection(obj_entry);
83653a5a1b3Sopenharmony_ci
83753a5a1b3Sopenharmony_ci    pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
83853a5a1b3Sopenharmony_ci
83953a5a1b3Sopenharmony_ci    pa_xfree(iface_entry->name);
84053a5a1b3Sopenharmony_ci    pa_hashmap_free(iface_entry->method_signatures);
84153a5a1b3Sopenharmony_ci    pa_hashmap_free(iface_entry->method_handlers);
84253a5a1b3Sopenharmony_ci    pa_hashmap_free(iface_entry->property_handlers);
84353a5a1b3Sopenharmony_ci
84453a5a1b3Sopenharmony_ci    for (i = 0; i < iface_entry->n_signals; ++i) {
84553a5a1b3Sopenharmony_ci        unsigned j;
84653a5a1b3Sopenharmony_ci
84753a5a1b3Sopenharmony_ci        pa_xfree((char *) iface_entry->signals[i].name);
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci        for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
85053a5a1b3Sopenharmony_ci            pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
85153a5a1b3Sopenharmony_ci            pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
85253a5a1b3Sopenharmony_ci            pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
85353a5a1b3Sopenharmony_ci        }
85453a5a1b3Sopenharmony_ci
85553a5a1b3Sopenharmony_ci        pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
85653a5a1b3Sopenharmony_ci    }
85753a5a1b3Sopenharmony_ci
85853a5a1b3Sopenharmony_ci    pa_xfree(iface_entry->signals);
85953a5a1b3Sopenharmony_ci    pa_xfree(iface_entry);
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    if (pa_hashmap_isempty(obj_entry->interfaces)) {
86253a5a1b3Sopenharmony_ci        unregister_object(p, obj_entry);
86353a5a1b3Sopenharmony_ci
86453a5a1b3Sopenharmony_ci        pa_hashmap_remove(p->objects, path);
86553a5a1b3Sopenharmony_ci        pa_xfree(obj_entry->path);
86653a5a1b3Sopenharmony_ci        pa_hashmap_free(obj_entry->interfaces);
86753a5a1b3Sopenharmony_ci        pa_xfree(obj_entry->introspection);
86853a5a1b3Sopenharmony_ci        pa_xfree(obj_entry);
86953a5a1b3Sopenharmony_ci    }
87053a5a1b3Sopenharmony_ci
87153a5a1b3Sopenharmony_ci    return 0;
87253a5a1b3Sopenharmony_ci}
87353a5a1b3Sopenharmony_ci
87453a5a1b3Sopenharmony_cistatic void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
87553a5a1b3Sopenharmony_ci    struct object_entry *obj_entry;
87653a5a1b3Sopenharmony_ci    void *state = NULL;
87753a5a1b3Sopenharmony_ci
87853a5a1b3Sopenharmony_ci    pa_assert(p);
87953a5a1b3Sopenharmony_ci    pa_assert(conn);
88053a5a1b3Sopenharmony_ci
88153a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
88253a5a1b3Sopenharmony_ci        pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
88353a5a1b3Sopenharmony_ci}
88453a5a1b3Sopenharmony_ci
88553a5a1b3Sopenharmony_cistatic void signal_paths_entry_free(struct signal_paths_entry *e) {
88653a5a1b3Sopenharmony_ci    pa_assert(e);
88753a5a1b3Sopenharmony_ci
88853a5a1b3Sopenharmony_ci    pa_xfree(e->signal);
88953a5a1b3Sopenharmony_ci    pa_idxset_free(e->paths, pa_xfree);
89053a5a1b3Sopenharmony_ci    pa_xfree(e);
89153a5a1b3Sopenharmony_ci}
89253a5a1b3Sopenharmony_ci
89353a5a1b3Sopenharmony_ciint pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
89453a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry;
89553a5a1b3Sopenharmony_ci
89653a5a1b3Sopenharmony_ci    pa_assert(p);
89753a5a1b3Sopenharmony_ci    pa_assert(conn);
89853a5a1b3Sopenharmony_ci    pa_assert(client);
89953a5a1b3Sopenharmony_ci
90053a5a1b3Sopenharmony_ci    if (pa_hashmap_get(p->connections, conn))
90153a5a1b3Sopenharmony_ci        return -1; /* The connection was already registered. */
90253a5a1b3Sopenharmony_ci
90353a5a1b3Sopenharmony_ci    register_all_objects(p, conn);
90453a5a1b3Sopenharmony_ci
90553a5a1b3Sopenharmony_ci    conn_entry = pa_xnew(struct connection_entry, 1);
90653a5a1b3Sopenharmony_ci    conn_entry->connection = dbus_connection_ref(conn);
90753a5a1b3Sopenharmony_ci    conn_entry->client = client;
90853a5a1b3Sopenharmony_ci    conn_entry->listening_for_all_signals = false;
90953a5a1b3Sopenharmony_ci    conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
91053a5a1b3Sopenharmony_ci    conn_entry->listening_signals = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
91153a5a1b3Sopenharmony_ci                                                        (pa_free_cb_t) signal_paths_entry_free);
91253a5a1b3Sopenharmony_ci
91353a5a1b3Sopenharmony_ci    pa_hashmap_put(p->connections, conn, conn_entry);
91453a5a1b3Sopenharmony_ci
91553a5a1b3Sopenharmony_ci    return 0;
91653a5a1b3Sopenharmony_ci}
91753a5a1b3Sopenharmony_ci
91853a5a1b3Sopenharmony_cistatic void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
91953a5a1b3Sopenharmony_ci    struct object_entry *obj_entry;
92053a5a1b3Sopenharmony_ci    void *state = NULL;
92153a5a1b3Sopenharmony_ci
92253a5a1b3Sopenharmony_ci    pa_assert(p);
92353a5a1b3Sopenharmony_ci    pa_assert(conn);
92453a5a1b3Sopenharmony_ci
92553a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
92653a5a1b3Sopenharmony_ci        pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
92753a5a1b3Sopenharmony_ci}
92853a5a1b3Sopenharmony_ci
92953a5a1b3Sopenharmony_cistatic struct signal_paths_entry *signal_paths_entry_new(const char *signal_name) {
93053a5a1b3Sopenharmony_ci    struct signal_paths_entry *e = NULL;
93153a5a1b3Sopenharmony_ci
93253a5a1b3Sopenharmony_ci    pa_assert(signal_name);
93353a5a1b3Sopenharmony_ci
93453a5a1b3Sopenharmony_ci    e = pa_xnew0(struct signal_paths_entry, 1);
93553a5a1b3Sopenharmony_ci    e->signal = pa_xstrdup(signal_name);
93653a5a1b3Sopenharmony_ci    e->paths = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
93753a5a1b3Sopenharmony_ci
93853a5a1b3Sopenharmony_ci    return e;
93953a5a1b3Sopenharmony_ci}
94053a5a1b3Sopenharmony_ci
94153a5a1b3Sopenharmony_ciint pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
94253a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry = NULL;
94353a5a1b3Sopenharmony_ci
94453a5a1b3Sopenharmony_ci    pa_assert(p);
94553a5a1b3Sopenharmony_ci    pa_assert(conn);
94653a5a1b3Sopenharmony_ci
94753a5a1b3Sopenharmony_ci    if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
94853a5a1b3Sopenharmony_ci        return -1;
94953a5a1b3Sopenharmony_ci
95053a5a1b3Sopenharmony_ci    unregister_all_objects(p, conn);
95153a5a1b3Sopenharmony_ci
95253a5a1b3Sopenharmony_ci    dbus_connection_unref(conn_entry->connection);
95353a5a1b3Sopenharmony_ci    pa_idxset_free(conn_entry->all_signals_objects, pa_xfree);
95453a5a1b3Sopenharmony_ci    pa_hashmap_free(conn_entry->listening_signals);
95553a5a1b3Sopenharmony_ci    pa_xfree(conn_entry);
95653a5a1b3Sopenharmony_ci
95753a5a1b3Sopenharmony_ci    return 0;
95853a5a1b3Sopenharmony_ci}
95953a5a1b3Sopenharmony_ci
96053a5a1b3Sopenharmony_cipa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
96153a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry;
96253a5a1b3Sopenharmony_ci
96353a5a1b3Sopenharmony_ci    pa_assert(p);
96453a5a1b3Sopenharmony_ci    pa_assert(conn);
96553a5a1b3Sopenharmony_ci
96653a5a1b3Sopenharmony_ci    if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
96753a5a1b3Sopenharmony_ci        return NULL;
96853a5a1b3Sopenharmony_ci
96953a5a1b3Sopenharmony_ci    return conn_entry->client;
97053a5a1b3Sopenharmony_ci}
97153a5a1b3Sopenharmony_ci
97253a5a1b3Sopenharmony_civoid pa_dbus_protocol_add_signal_listener(
97353a5a1b3Sopenharmony_ci        pa_dbus_protocol *p,
97453a5a1b3Sopenharmony_ci        DBusConnection *conn,
97553a5a1b3Sopenharmony_ci        const char *signal_name,
97653a5a1b3Sopenharmony_ci        char **objects,
97753a5a1b3Sopenharmony_ci        unsigned n_objects) {
97853a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry = NULL;
97953a5a1b3Sopenharmony_ci    struct signal_paths_entry *signal_paths_entry = NULL;
98053a5a1b3Sopenharmony_ci    unsigned i = 0;
98153a5a1b3Sopenharmony_ci
98253a5a1b3Sopenharmony_ci    pa_assert(p);
98353a5a1b3Sopenharmony_ci    pa_assert(conn);
98453a5a1b3Sopenharmony_ci    pa_assert(objects || n_objects == 0);
98553a5a1b3Sopenharmony_ci
98653a5a1b3Sopenharmony_ci    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
98753a5a1b3Sopenharmony_ci
98853a5a1b3Sopenharmony_ci    /* all_signals_objects will either be emptied or replaced with new objects,
98953a5a1b3Sopenharmony_ci     * so we empty it here unconditionally. If listening_for_all_signals is
99053a5a1b3Sopenharmony_ci     * currently false, the idxset is empty already so this does nothing. */
99153a5a1b3Sopenharmony_ci    pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
99253a5a1b3Sopenharmony_ci
99353a5a1b3Sopenharmony_ci    if (signal_name) {
99453a5a1b3Sopenharmony_ci        conn_entry->listening_for_all_signals = false;
99553a5a1b3Sopenharmony_ci
99653a5a1b3Sopenharmony_ci        /* Replace the old signal paths entry for this signal with a new
99753a5a1b3Sopenharmony_ci         * one. */
99853a5a1b3Sopenharmony_ci        pa_hashmap_remove_and_free(conn_entry->listening_signals, signal_name);
99953a5a1b3Sopenharmony_ci        signal_paths_entry = signal_paths_entry_new(signal_name);
100053a5a1b3Sopenharmony_ci
100153a5a1b3Sopenharmony_ci        for (i = 0; i < n_objects; ++i)
100253a5a1b3Sopenharmony_ci            pa_idxset_put(signal_paths_entry->paths, pa_xstrdup(objects[i]), NULL);
100353a5a1b3Sopenharmony_ci
100453a5a1b3Sopenharmony_ci        pa_hashmap_put(conn_entry->listening_signals, signal_paths_entry->signal, signal_paths_entry);
100553a5a1b3Sopenharmony_ci
100653a5a1b3Sopenharmony_ci    } else {
100753a5a1b3Sopenharmony_ci        conn_entry->listening_for_all_signals = true;
100853a5a1b3Sopenharmony_ci
100953a5a1b3Sopenharmony_ci        /* We're not interested in individual signals anymore, so let's empty
101053a5a1b3Sopenharmony_ci         * listening_signals. */
101153a5a1b3Sopenharmony_ci        pa_hashmap_remove_all(conn_entry->listening_signals);
101253a5a1b3Sopenharmony_ci
101353a5a1b3Sopenharmony_ci        for (i = 0; i < n_objects; ++i)
101453a5a1b3Sopenharmony_ci            pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
101553a5a1b3Sopenharmony_ci    }
101653a5a1b3Sopenharmony_ci}
101753a5a1b3Sopenharmony_ci
101853a5a1b3Sopenharmony_civoid pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal_name) {
101953a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry = NULL;
102053a5a1b3Sopenharmony_ci    struct signal_paths_entry *signal_paths_entry = NULL;
102153a5a1b3Sopenharmony_ci
102253a5a1b3Sopenharmony_ci    pa_assert(p);
102353a5a1b3Sopenharmony_ci    pa_assert(conn);
102453a5a1b3Sopenharmony_ci
102553a5a1b3Sopenharmony_ci    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
102653a5a1b3Sopenharmony_ci
102753a5a1b3Sopenharmony_ci    if (signal_name) {
102853a5a1b3Sopenharmony_ci        if ((signal_paths_entry = pa_hashmap_remove(conn_entry->listening_signals, signal_name)))
102953a5a1b3Sopenharmony_ci            signal_paths_entry_free(signal_paths_entry);
103053a5a1b3Sopenharmony_ci
103153a5a1b3Sopenharmony_ci    } else {
103253a5a1b3Sopenharmony_ci        conn_entry->listening_for_all_signals = false;
103353a5a1b3Sopenharmony_ci        pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
103453a5a1b3Sopenharmony_ci        pa_hashmap_remove_all(conn_entry->listening_signals);
103553a5a1b3Sopenharmony_ci    }
103653a5a1b3Sopenharmony_ci}
103753a5a1b3Sopenharmony_ci
103853a5a1b3Sopenharmony_civoid pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal_msg) {
103953a5a1b3Sopenharmony_ci    struct connection_entry *conn_entry;
104053a5a1b3Sopenharmony_ci    struct signal_paths_entry *signal_paths_entry;
104153a5a1b3Sopenharmony_ci    void *state = NULL;
104253a5a1b3Sopenharmony_ci    DBusMessage *signal_copy;
104353a5a1b3Sopenharmony_ci    char *signal_string;
104453a5a1b3Sopenharmony_ci
104553a5a1b3Sopenharmony_ci    pa_assert(p);
104653a5a1b3Sopenharmony_ci    pa_assert(signal_msg);
104753a5a1b3Sopenharmony_ci    pa_assert(dbus_message_get_type(signal_msg) == DBUS_MESSAGE_TYPE_SIGNAL);
104853a5a1b3Sopenharmony_ci    pa_assert(dbus_message_get_path(signal_msg));
104953a5a1b3Sopenharmony_ci    pa_assert(dbus_message_get_interface(signal_msg));
105053a5a1b3Sopenharmony_ci    pa_assert(dbus_message_get_member(signal_msg));
105153a5a1b3Sopenharmony_ci
105253a5a1b3Sopenharmony_ci    signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal_msg), dbus_message_get_member(signal_msg));
105353a5a1b3Sopenharmony_ci
105453a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
105553a5a1b3Sopenharmony_ci        if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
105653a5a1b3Sopenharmony_ci             && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal_msg), NULL)
105753a5a1b3Sopenharmony_ci                 || pa_idxset_isempty(conn_entry->all_signals_objects)))
105853a5a1b3Sopenharmony_ci
105953a5a1b3Sopenharmony_ci            || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
106053a5a1b3Sopenharmony_ci                && (signal_paths_entry = pa_hashmap_get(conn_entry->listening_signals, signal_string))
106153a5a1b3Sopenharmony_ci                && (pa_idxset_get_by_data(signal_paths_entry->paths, dbus_message_get_path(signal_msg), NULL)
106253a5a1b3Sopenharmony_ci                    || pa_idxset_isempty(signal_paths_entry->paths)))) {
106353a5a1b3Sopenharmony_ci
106453a5a1b3Sopenharmony_ci            pa_assert_se(signal_copy = dbus_message_copy(signal_msg));
106553a5a1b3Sopenharmony_ci            pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
106653a5a1b3Sopenharmony_ci            dbus_message_unref(signal_copy);
106753a5a1b3Sopenharmony_ci        }
106853a5a1b3Sopenharmony_ci    }
106953a5a1b3Sopenharmony_ci
107053a5a1b3Sopenharmony_ci    pa_xfree(signal_string);
107153a5a1b3Sopenharmony_ci}
107253a5a1b3Sopenharmony_ci
107353a5a1b3Sopenharmony_ciconst char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
107453a5a1b3Sopenharmony_ci    const char **extensions;
107553a5a1b3Sopenharmony_ci    const char *ext_name;
107653a5a1b3Sopenharmony_ci    void *state = NULL;
107753a5a1b3Sopenharmony_ci    unsigned i = 0;
107853a5a1b3Sopenharmony_ci
107953a5a1b3Sopenharmony_ci    pa_assert(p);
108053a5a1b3Sopenharmony_ci    pa_assert(n);
108153a5a1b3Sopenharmony_ci
108253a5a1b3Sopenharmony_ci    *n = pa_idxset_size(p->extensions);
108353a5a1b3Sopenharmony_ci
108453a5a1b3Sopenharmony_ci    if (*n <= 0)
108553a5a1b3Sopenharmony_ci        return NULL;
108653a5a1b3Sopenharmony_ci
108753a5a1b3Sopenharmony_ci    extensions = pa_xnew(const char *, *n);
108853a5a1b3Sopenharmony_ci
108953a5a1b3Sopenharmony_ci    while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
109053a5a1b3Sopenharmony_ci        extensions[i++] = ext_name;
109153a5a1b3Sopenharmony_ci
109253a5a1b3Sopenharmony_ci    return extensions;
109353a5a1b3Sopenharmony_ci}
109453a5a1b3Sopenharmony_ci
109553a5a1b3Sopenharmony_ciint pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
109653a5a1b3Sopenharmony_ci    char *internal_name;
109753a5a1b3Sopenharmony_ci
109853a5a1b3Sopenharmony_ci    pa_assert(p);
109953a5a1b3Sopenharmony_ci    pa_assert(name);
110053a5a1b3Sopenharmony_ci
110153a5a1b3Sopenharmony_ci    internal_name = pa_xstrdup(name);
110253a5a1b3Sopenharmony_ci
110353a5a1b3Sopenharmony_ci    if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
110453a5a1b3Sopenharmony_ci        pa_xfree(internal_name);
110553a5a1b3Sopenharmony_ci        return -1;
110653a5a1b3Sopenharmony_ci    }
110753a5a1b3Sopenharmony_ci
110853a5a1b3Sopenharmony_ci    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
110953a5a1b3Sopenharmony_ci
111053a5a1b3Sopenharmony_ci    return 0;
111153a5a1b3Sopenharmony_ci}
111253a5a1b3Sopenharmony_ci
111353a5a1b3Sopenharmony_ciint pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
111453a5a1b3Sopenharmony_ci    char *internal_name;
111553a5a1b3Sopenharmony_ci
111653a5a1b3Sopenharmony_ci    pa_assert(p);
111753a5a1b3Sopenharmony_ci    pa_assert(name);
111853a5a1b3Sopenharmony_ci
111953a5a1b3Sopenharmony_ci    if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
112053a5a1b3Sopenharmony_ci        return -1;
112153a5a1b3Sopenharmony_ci
112253a5a1b3Sopenharmony_ci    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
112353a5a1b3Sopenharmony_ci
112453a5a1b3Sopenharmony_ci    pa_xfree(internal_name);
112553a5a1b3Sopenharmony_ci
112653a5a1b3Sopenharmony_ci    return 0;
112753a5a1b3Sopenharmony_ci}
112853a5a1b3Sopenharmony_ci
112953a5a1b3Sopenharmony_cipa_hook_slot *pa_dbus_protocol_hook_connect(
113053a5a1b3Sopenharmony_ci        pa_dbus_protocol *p,
113153a5a1b3Sopenharmony_ci        pa_dbus_protocol_hook_t hook,
113253a5a1b3Sopenharmony_ci        pa_hook_priority_t prio,
113353a5a1b3Sopenharmony_ci        pa_hook_cb_t cb,
113453a5a1b3Sopenharmony_ci        void *data) {
113553a5a1b3Sopenharmony_ci    pa_assert(p);
113653a5a1b3Sopenharmony_ci    pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
113753a5a1b3Sopenharmony_ci    pa_assert(cb);
113853a5a1b3Sopenharmony_ci
113953a5a1b3Sopenharmony_ci    return pa_hook_connect(&p->hooks[hook], prio, cb, data);
114053a5a1b3Sopenharmony_ci}
1141