diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2009-08-17 15:30:16 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2009-08-17 16:20:52 +0100 |
commit | 48b48dca4a73d3bee4d1311163001e1cac26adb8 (patch) | |
tree | a00266e9760c84730fd530e8c0a30954c9a8eded /telepathy-glib/dbus-daemon.c | |
parent | f6d4e669ec944cb9da603f4ded8732f65ddd26a4 (diff) | |
download | telepathy-glib-48b48dca4a73d3bee4d1311163001e1cac26adb8.tar.gz |
Add tp_dbus_daemon_list_names, tp_dbus_daemon_list_activatable_names
These use libdbus directly, to avoid DBusGProxy's undesirable
signal-binding (which causes us to wake up on every NameOwnerChanged).
Diffstat (limited to 'telepathy-glib/dbus-daemon.c')
-rw-r--r-- | telepathy-glib/dbus-daemon.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/telepathy-glib/dbus-daemon.c b/telepathy-glib/dbus-daemon.c index 1a34af1ac..928a3a281 100644 --- a/telepathy-glib/dbus-daemon.c +++ b/telepathy-glib/dbus-daemon.c @@ -931,6 +931,278 @@ tp_dbus_daemon_get_unique_name (TpDBusDaemon *self) return dbus_bus_get_unique_name (self->priv->libdbus); } +typedef struct { + TpDBusDaemon *self; + DBusMessage *reply; + TpDBusDaemonListNamesCb callback; + gpointer user_data; + GDestroyNotify destroy; + gpointer weak_object; + gsize refs; +} ListNamesContext; + +static ListNamesContext * +list_names_context_new (TpDBusDaemon *self, + TpDBusDaemonListNamesCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + ListNamesContext *context = g_slice_new (ListNamesContext); + + context->self = g_object_ref (self); + context->reply = NULL; + context->callback = callback; + context->user_data = user_data; + context->destroy = destroy; + context->weak_object = weak_object; + + if (context->weak_object != NULL) + g_object_add_weak_pointer (weak_object, &context->weak_object); + + context->refs = 1; + return context; +} + +static void +list_names_context_unref (gpointer data) +{ + ListNamesContext *context = data; + + if (--context->refs == 0) + { + g_object_unref (context->self); + + if (context->reply != NULL) + dbus_message_unref (context->reply); + + if (context->destroy != NULL) + context->destroy (context->user_data); + + context->destroy = NULL; + + if (context->weak_object != NULL) + g_object_remove_weak_pointer (context->weak_object, + &context->weak_object); + + g_slice_free (ListNamesContext, context); + } +} + +static gboolean +_tp_dbus_daemon_list_names_idle (gpointer data) +{ + ListNamesContext *context = data; + char **array = NULL; + const gchar * const *result = NULL; + GError *error = NULL; + + if (context->callback == NULL) + { + DEBUG ("Caller no longer cares (weak object vanished), ignoring"); + return FALSE; + } + + if (context->reply == NULL) + { + g_set_error_literal (&error, DBUS_GERROR, DBUS_GERROR_DISCONNECTED, + "DBusConnection disconnected"); + } + else if (dbus_message_get_type (context->reply) == + DBUS_MESSAGE_TYPE_METHOD_RETURN) + { + int n_elements; + + if (dbus_message_get_args (context->reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &array, &n_elements, + DBUS_TYPE_INVALID)) + { + result = (const gchar * const *) array; + g_assert (result[n_elements] == NULL); + } + else + { + g_set_error_literal (&error, DBUS_GERROR, DBUS_GERROR_INVALID_ARGS, + "Malformed reply from List*Names()"); + } + } + else + { + DBusError dbus_error = DBUS_ERROR_INIT; + + if (dbus_set_error_from_message (&dbus_error, context->reply)) + { + /* FIXME: ideally we'd use dbus-glib's error mapping here, but we + * don't have access to it */ + g_set_error (&error, DBUS_GERROR, DBUS_GERROR_FAILED, + "List*Names() raised %s: %s", dbus_error.name, + dbus_error.message); + dbus_error_free (&dbus_error); + } + else + { + g_set_error_literal (&error, DBUS_GERROR, DBUS_GERROR_INVALID_ARGS, + "Unexpected message type from List*Names()"); + } + } + + if (error != NULL) + DEBUG ("%s", error->message); + + context->callback (context->self, result, error, context->user_data, + context->weak_object); + dbus_free_string_array (array); /* NULL-safe */ + return FALSE; +} + +static void +_tp_dbus_daemon_list_names_notify (DBusPendingCall *pc, + gpointer data) +{ + ListNamesContext *context = data; + + /* we recycle this function for the case where the connection is already + * disconnected: in that case we use pc = NULL */ + if (pc != NULL) + context->reply = dbus_pending_call_steal_reply (pc); + + /* We have to do the real work in an idle, so we don't break re-entrant + * calls (the dbus-glib event source isn't re-entrant) */ + context->refs++; + g_idle_add_full (G_PRIORITY_HIGH, _tp_dbus_daemon_list_names_idle, + context, list_names_context_unref); + + if (pc != NULL) + dbus_pending_call_unref (pc); +} + +/** + * TpDBusDaemonListNamesCb: + * @bus_daemon: object representing a connection to a bus + * @names: constant %NULL-terminated array of constant strings representing + * bus names, or %NULL on error + * @error: the error that occurred, or %NULL on success + * @user_data: the same user data that was passed to + * tp_dbus_daemon_list_names or tp_dbus_daemon_list_activatable_names + * @weak_object: the same object that was passed to + * tp_dbus_daemon_list_names or tp_dbus_daemon_list_activatable_names + * + * Signature of a callback for functions that list bus names. + * + * Since: 0.7.UNRELEASED + */ + +static void +_tp_dbus_daemon_list_names_common (TpDBusDaemon *self, + const gchar *method, + gint timeout_ms, + TpDBusDaemonListNamesCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + DBusMessage *message; + DBusPendingCall *pc = NULL; + ListNamesContext *context; + + g_return_if_fail (TP_IS_DBUS_DAEMON (self)); + g_return_if_fail (callback != NULL); + g_return_if_fail (weak_object == NULL || G_IS_OBJECT (weak_object)); + + message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, method); + + if (message == NULL) + g_error ("Out of memory"); + + if (!dbus_connection_send_with_reply (self->priv->libdbus, + message, &pc, timeout_ms)) + g_error ("Out of memory"); + /* pc is unreffed by _tp_dbus_daemon_list_names_notify */ + + context = list_names_context_new (self, callback, user_data, destroy, + weak_object); + + if (pc == NULL || dbus_pending_call_get_completed (pc)) + { + /* pc can be NULL when the connection is already disconnected */ + _tp_dbus_daemon_list_names_notify (pc, context); + list_names_context_unref (context); + } + else if (!dbus_pending_call_set_notify (pc, + _tp_dbus_daemon_list_names_notify, context, + list_names_context_unref)) + { + g_error ("Out of memory"); + } +} + +/** + * tp_dbus_daemon_list_names: + * @self: object representing a connection to a bus + * @timeout_ms: timeout for the call + * @callback: callback to be called on success or failure; must not be %NULL + * @user_data: opaque user-supplied data to pass to the callback + * @destroy: if not %NULL, called with @user_data as argument after the call + * has succeeded or failed, or after @weak_object has been destroyed + * @weak_object: if not %NULL, a GObject which will be weakly referenced; if + * it is destroyed, @callback will not be called at all + * + * Call the ListNames method on the bus daemon, asynchronously. The @callback + * will be called from the main loop with a list of all the names (either + * unique or well-known) that exist on the bus. + * + * In versions of telepathy-glib that have it, this should be preferred + * instead of calling tp_cli_dbus_daemon_call_list_names(), since that + * function will result in wakeups for every NameOwnerChanged signal. + * + * Since: 0.7.UNRELEASED + */ +void +tp_dbus_daemon_list_names (TpDBusDaemon *self, + gint timeout_ms, + TpDBusDaemonListNamesCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + _tp_dbus_daemon_list_names_common (self, "ListNames", timeout_ms, + callback, user_data, destroy, weak_object); +} + +/** + * tp_dbus_daemon_list_activatable_names: + * @self: object representing a connection to a bus daemon + * @timeout_ms: timeout for the call + * @callback: callback to be called on success or failure; must not be %NULL + * @user_data: opaque user-supplied data to pass to the callback + * @destroy: if not %NULL, called with @user_data as argument after the call + * has succeeded or failed, or after @weak_object has been destroyed + * @weak_object: if not %NULL, a GObject which will be weakly referenced; if + * it is destroyed, @callback will not be called at all + * + * Call the ListActivatableNames method on the bus daemon, asynchronously. + * The @callback will be called from the main loop with a list of all the + * well-known names that are available for service-activation on the bus. + * + * In versions of telepathy-glib that have it, this should be preferred + * instead of calling tp_cli_dbus_daemon_call_list_activatable_names(), since + * that function will result in wakeups for every NameOwnerChanged signal. + * + * Since: 0.7.UNRELEASED + */ +void +tp_dbus_daemon_list_activatable_names (TpDBusDaemon *self, + gint timeout_ms, + TpDBusDaemonListNamesCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) +{ + _tp_dbus_daemon_list_names_common (self, "ListActivatableNames", timeout_ms, + callback, user_data, destroy, weak_object); +} + static void free_daemon_list (gpointer p) { |