From 219f7508f6420f94ad8c426c6aa3dc79df246f36 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:20:26 +0300 Subject: dbus: Finish the Client D-Bus interface. --- src/modules/dbus/iface-client.c | 163 ++++++++++++++++++++++++++++---- src/modules/dbus/module-dbus-protocol.c | 31 +++++- 2 files changed, 176 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 587d5c42..a68259a9 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -39,8 +39,10 @@ struct pa_dbusiface_client { pa_client *client; char *path; + pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -52,9 +54,9 @@ static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); enum property_handler_index { PROPERTY_HANDLER_INDEX, @@ -75,7 +77,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } }; -/*enum method_handler_index { +enum method_handler_index { METHOD_HANDLER_KILL, METHOD_HANDLER_UPDATE_PROPERTIES, METHOD_HANDLER_REMOVE_PROPERTIES, @@ -83,7 +85,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { }; static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; -static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } }; +static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { [METHOD_HANDLER_KILL] = { @@ -93,14 +95,14 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .receive_cb = handle_kill }, [METHOD_HANDLER_UPDATE_PROPERTIES] = { .method_name = "UpdateProperties", - .arguments = update_propertes_args, + .arguments = update_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_update_properties }, [METHOD_HANDLER_REMOVE_PROPERTIES] = { .method_name = "RemoveProperties", - .arguments = remove_propertes_args, + .arguments = remove_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), - .receive_cb = handle_update_properties } + .receive_cb = handle_remove_properties } }; enum signal_index { @@ -109,23 +111,25 @@ enum signal_index { SIGNAL_MAX }; -static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; -static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info client_event_args[] = { { "name", "s", NULL }, + { "property_list", "a{say}", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { - [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, - [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } -};*/ + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, + /* ClientEvent is sent from module-dbus-protocol.c. */ + [SIGNAL_CLIENT_EVENT] = { .name = "ClientEvent", .arguments = client_event_args, .n_arguments = 1 } +}; static pa_dbus_interface_info client_interface_info = { .name = PA_DBUSIFACE_CLIENT_INTERFACE, - .method_handlers = /*method_handlers*/ NULL, - .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, .n_property_handlers = PROPERTY_HANDLER_MAX, .get_all_properties_cb = handle_get_all, - .signals = /*signals*/ NULL, - .n_signals = /*SIGNAL_MAX*/ 0 + .signals = signals, + .n_signals = SIGNAL_MAX }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { @@ -304,6 +308,131 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_xfree(record_streams); } +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_connection_ref(conn); + + pa_client_kill(c->client); + + pa_dbus_send_empty_reply(conn, msg); + + dbus_connection_unref(conn); +} + +static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessageIter msg_iter; + pa_proplist *property_list = NULL; + dbus_uint32_t update_mode = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0) + goto finish; + + if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); + goto finish; + } + + pa_client_update_proplist(c->client, update_mode, property_list); + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + char **keys = NULL; + int n_keys = 0; + DBusError error; + pa_bool_t changed = FALSE; + int i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + for (i = 0; i < n_keys; ++i) + changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; + + pa_dbus_send_empty_reply(conn, msg); + + if (changed) + pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + + dbus_free_string_array(keys); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessage *signal = NULL; + + pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CLIENT); + pa_assert(c); + + /* We can't use idx != c->client->index, because the c->client pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->clients, idx) != c->client) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; + + if (!pa_proplist_equal(c->proplist, c->client->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist); + + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CLIENT_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { pa_dbusiface_client *c = NULL; @@ -314,7 +443,9 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client c->core = core; c->client = client; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); + c->proplist = pa_proplist_copy(client->proplist); c->dbus_protocol = pa_dbus_protocol_get(client->core); + c->subscription = pa_subscription_new(client->core, PA_SUBSCRIPTION_MASK_CLIENT, subscription_cb, c); pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c index 807d32da..11064c33 100644 --- a/src/modules/dbus/module-dbus-protocol.c +++ b/src/modules/dbus/module-dbus-protocol.c @@ -40,6 +40,7 @@ #include #include +#include "iface-client.h" #include "iface-core.h" #include "module-dbus-protocol-symdef.h" @@ -117,10 +118,36 @@ static void client_kill_cb(pa_client *c) { conn = c->userdata; connection_free(conn); + c->userdata = NULL; pa_log_info("Connection killed."); } +/* Called from pa_client_send_event(). */ +static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) { + struct connection *conn = NULL; + DBusMessage *signal = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(name); + pa_assert(data); + pa_assert(c->userdata); + + conn = c->userdata; + + pa_assert_se(signal = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c), + PA_DBUSIFACE_CLIENT_INTERFACE, + "ClientEvent")); + dbus_message_iter_init_append(signal, &msg_iter); + pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_proplist(&msg_iter, data); + + pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal, NULL)); + dbus_message_unref(signal); +} + +/* Called by D-Bus at the authentication phase. */ static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { pa_log_debug("Allowing connection by user %lu.", uid); @@ -140,7 +167,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne pa_client_new_data_init(&new_data); new_data.module = s->userdata->module; new_data.driver = __FILE__; - pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); @@ -162,7 +189,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne c->client = client; c->client->kill = client_kill_cb; - c->client->send_event = NULL; /* TODO: Implement this. */ + c->client->send_event = client_send_event_cb; c->client->userdata = c; pa_idxset_put(s->userdata->connections, c, NULL); -- cgit