From cd9def0eae42cf4253ae82e86c66e5c4b773f71e Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 24 Jun 2011 12:51:37 +0200 Subject: [PATCH] TpBaseClient: add org.freedesktop.Telepathy.ChannelRequest.DelegateToPreferredHandler support (#38249) --- docs/reference/telepathy-glib-sections.txt | 2 + telepathy-glib/base-client.c | 168 ++++++++++++++++++++++++++ telepathy-glib/base-client.h | 10 ++ tests/dbus/base-client.c | 176 ++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+), 0 deletions(-) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index a04315c..fb42746 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -4830,6 +4830,8 @@ tp_base_client_get_handled_channels tp_base_client_is_handling_channel tp_base_client_delegate_channels_async tp_base_client_delegate_channels_finish +TpBaseClientDelegatedChannelsCb +tp_base_client_set_delegated_channels_callback tp_channel_dispatcher_present_channel_async tp_channel_dispatcher_present_channel_finish tp_base_client_get_pending_requests diff --git a/telepathy-glib/base-client.c b/telepathy-glib/base-client.c index 76bdea6..45791e1 100644 --- a/telepathy-glib/base-client.c +++ b/telepathy-glib/base-client.c @@ -157,6 +157,19 @@ * Since: 0.11.6 */ +/** + * TpBaseClientDelegatedChannelsCb + * @client: a #TpBaseClient instance + * @channels: (element-type TelepathyGLib.Channel): a #GPtrArray of #TpChannel + * @user_data: arbitrary user-supplied data passed to + * tp_base_client_set_delegated_channels_callback() + * + * Called when a client asked us to delegate @channels to another Handler. + * When this function is called @client is not longer handling @channels. + * + * Since: 0.15.UNRELEASED + */ + #include "telepathy-glib/base-client.h" #include "telepathy-glib/base-client-internal.h" @@ -264,6 +277,10 @@ struct _TpBaseClientPrivate GArray *account_features; GArray *connection_features; GArray *channel_features; + + TpBaseClientDelegatedChannelsCb delegated_channels_cb; + gpointer delegated_channels_data; + GDestroyNotify delegated_channels_destroy; }; /* @@ -975,6 +992,13 @@ tp_base_client_dispose (GObject *object) tp_clear_pointer (&self->priv->my_chans, g_hash_table_unref); + if (self->priv->delegated_channels_destroy != NULL) + { + self->priv->delegated_channels_destroy ( + self->priv->delegated_channels_data); + self->priv->delegated_channels_destroy = NULL; + } + if (dispose != NULL) dispose (object); } @@ -1954,6 +1978,111 @@ ctx_done_cb (TpHandleChannelsContext *context, } static void +delegate_to_preferred_handler_delegate_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpBaseClient *self = (TpBaseClient *) source; + GError *error = NULL; + GPtrArray *delegated; + + if (!tp_base_client_delegate_channels_finish (self, result, &delegated, NULL, + &error)) + { + DEBUG ("DelegateChannels failed; we are still handling channels: %s", + error->message); + + g_error_free (error); + return; + } + + self->priv->delegated_channels_cb (self, delegated, + self->priv->delegated_channels_data); + + g_ptr_array_unref (delegated); +} + +static gboolean +delegate_channels_if_needed (TpBaseClient *self, + TpHandleChannelsContext *ctx) +{ + GList *requests, *l; + const gchar *handler_to_delegate = NULL; + gint64 user_action_time; + guint i; + GList *chans = NULL; + gboolean delegate = FALSE; + + /* User has to explicitely enable this feature */ + if (self->priv->delegated_channels_cb == NULL) + return FALSE; + + requests = tp_handle_channels_context_get_requests (ctx); + for (l = requests; l != NULL; l = g_list_next (l)) + { + TpChannelRequest *cr = l->data; + const GHashTable *hints; + gboolean should_delegate; + + hints = tp_channel_request_get_hints (cr); + if (hints == NULL) + continue; + + should_delegate = tp_asv_get_boolean (hints, + "org.freedesktop.Telepathy.ChannelRequest.DelegateToPreferredHandler", + NULL); + + if (!should_delegate) + continue; + + /* As stated in the spec, we should use the first CR having the + * DelegateToPreferredHandler hint */ + handler_to_delegate = tp_channel_request_get_preferred_handler (cr); + user_action_time = tp_channel_request_get_user_action_time (cr); + } + + if (handler_to_delegate == NULL) + /* No need to delegate */ + goto out; + + if (!tp_strdiff (handler_to_delegate, self->priv->name)) + /* We are already the one handling the channels */ + goto out; + + /* We are supposed to delegate the channels; check if we are handling + * them */ + for (i = 0; i < ctx->channels->len; i++) + { + TpChannel *channel = g_ptr_array_index (ctx->channels, i); + + if (!tp_base_client_is_handling_channel (self, channel)) + { + /* Don't delegate as there is at least one channel we are not + * handling */ + DEBUG ("We have been asked to delegate channels but we are " + "not handling %s", tp_proxy_get_object_path (channel)); + goto out; + } + + chans = g_list_prepend (chans, channel); + } + + DEBUG ("Delegate channels as requested"); + delegate = TRUE; + + tp_base_client_delegate_channels_async (self, chans, user_action_time, + handler_to_delegate, delegate_to_preferred_handler_delegate_cb, self); + + g_list_free (chans); + + tp_handle_channels_context_accept (ctx); + +out: + g_list_free_full (requests, g_object_unref); + return delegate; +} + +static void handle_channels_context_prepare_cb (GObject *source, GAsyncResult *result, gpointer user_data) @@ -1972,6 +2101,9 @@ handle_channels_context_prepare_cb (GObject *source, return; } + if (delegate_channels_if_needed (self, ctx)) + return; + channels_list = ptr_array_to_list (ctx->channels); requests_list = ptr_array_to_list (ctx->requests_satisfied); @@ -3049,3 +3181,39 @@ tp_base_client_delegate_channels_finish (TpBaseClient *self, return TRUE; } + +/** + * tp_base_client_set_delegated_channels_callback: + * @self: a #TpBaseClient implementing Handler + * @callback: function called when channels currently handled by @self are + * delegated, may not be %NULL + * @user_data: arbitrary user-supplied data passed to @callback + * @destroy: called with the @user_data as argument, when @self is destroyed + * + * Turn on support for + * the org.freedesktop.Telepathy.ChannelRequest.DelegateToPreferredHandler + * hint. + * + * When receiving a request containing this hint, @self will automatically + * delegate the channels to the preferred handler of the request and then call + * @callback to inform the client that it is no longer handling those + * channels. + * + * Since: 0.15.UNRELEASED + */ +void +tp_base_client_set_delegated_channels_callback (TpBaseClient *self, + TpBaseClientDelegatedChannelsCb callback, + gpointer user_data, + GDestroyNotify destroy) +{ + g_return_if_fail (TP_IS_BASE_CLIENT (self)); + g_return_if_fail (self->priv->flags & CLIENT_IS_HANDLER); + g_return_if_fail (!self->priv->registered); + + g_return_if_fail (self->priv->delegated_channels_cb == NULL); + + self->priv->delegated_channels_cb = callback; + self->priv->delegated_channels_data = user_data; + self->priv->delegated_channels_destroy = destroy; +} diff --git a/telepathy-glib/base-client.h b/telepathy-glib/base-client.h index 1a1eb05..d7429dc 100644 --- a/telepathy-glib/base-client.h +++ b/telepathy-glib/base-client.h @@ -160,6 +160,16 @@ void tp_base_client_set_channel_factory (TpBaseClient *self, TpClientChannelFactory *tp_base_client_get_channel_factory ( TpBaseClient *self); +typedef void (*TpBaseClientDelegatedChannelsCb) ( + TpBaseClient *client, + GPtrArray *channels, + gpointer user_data); + +void tp_base_client_set_delegated_channels_callback (TpBaseClient *self, + TpBaseClientDelegatedChannelsCb callback, + gpointer user_data, + GDestroyNotify destroy); + /* future, potentially (currently in spec as a draft): void tp_base_client_set_handler_related_conferences_bypass_approval ( TpBaseClient *self, gboolean bypass_approval); diff --git a/tests/dbus/base-client.c b/tests/dbus/base-client.c index 8263582..256a661 100644 --- a/tests/dbus/base-client.c +++ b/tests/dbus/base-client.c @@ -23,6 +23,7 @@ #include "tests/lib/simple-account.h" #include "tests/lib/simple-channel-dispatch-operation.h" #include "tests/lib/simple-channel-dispatcher.h" +#include "tests/lib/simple-channel-request.h" #include "tests/lib/simple-client.h" #include "tests/lib/simple-conn.h" #include "tests/lib/textchan-null.h" @@ -1403,6 +1404,177 @@ test_present_channel (Test *test, g_object_unref (cd); } +#define PREFERRED_HANDLER_NAME TP_CLIENT_BUS_NAME_BASE ".Badger" + +static gboolean +channel_in_array (GPtrArray *array, + TpChannel *channel) +{ + guint i; + + for (i = 0; i < array->len; i++) + { + TpChannel *c = g_ptr_array_index (array, i); + + if (!tp_strdiff (tp_proxy_get_object_path (channel), + tp_proxy_get_object_path (c))) + return TRUE; + } + + return FALSE; +} + +static void +delegated_channels_cb (TpBaseClient *client, + GPtrArray *channels, + gpointer user_data) +{ + Test *test = user_data; + + g_assert_cmpuint (channels->len, ==, 2); + + g_assert (channel_in_array (channels, test->text_chan)); + g_assert (channel_in_array (channels, test->text_chan_2)); + + test->wait--; + if (test->wait == 0) + g_main_loop_quit (test->mainloop); +} + +static void +delegate_to_preferred_handler (Test *test, + gboolean supported) +{ + GPtrArray *channels; + GPtrArray *requests_satisified; + GPtrArray *requests; + GHashTable *request_props; + GHashTable *info; + TpTestsSimpleChannelRequest *cr; + GHashTable *hints; + + tp_base_client_be_a_handler (test->base_client); + + if (supported) + { + tp_base_client_set_delegated_channels_callback (test->base_client, + delegated_channels_cb, test, NULL); + } + + tp_base_client_register (test->base_client, &test->error); + g_assert_no_error (test->error); + + /* Call HandleChannels */ + channels = g_ptr_array_sized_new (2); + add_channel_to_ptr_array (channels, test->text_chan); + add_channel_to_ptr_array (channels, test->text_chan_2); + + requests_satisified = g_ptr_array_sized_new (0); + info = g_hash_table_new (NULL, NULL); + + tp_proxy_add_interface_by_id (TP_PROXY (test->client), + TP_IFACE_QUARK_CLIENT_HANDLER); + + tp_cli_client_handler_call_handle_channels (test->client, -1, + tp_proxy_get_object_path (test->account), + tp_proxy_get_object_path (test->connection), + channels, requests_satisified, 0, info, + no_return_cb, test, NULL, NULL); + + test->wait++; + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + /* The client is handling the 2 channels */ + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan)); + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan_2)); + + /* Another client asks to dispatch the channel to it */ + requests = g_ptr_array_new (); + + hints = tp_asv_new ( + "org.freedesktop.Telepathy.ChannelRequest.DelegateToPreferredHandler", + G_TYPE_BOOLEAN, TRUE, + NULL); + + cr = tp_tests_simple_channel_request_new ("/CR", + TP_TESTS_SIMPLE_CONNECTION (test->base_connection), ACCOUNT_PATH, + TP_USER_ACTION_TIME_CURRENT_TIME, PREFERRED_HANDLER_NAME, + requests, hints); + + g_ptr_array_add (requests_satisified, "/CR"); + + request_props = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) g_hash_table_unref); + + g_hash_table_insert (request_props, "/CR", + tp_tests_simple_channel_request_dup_immutable_props (cr)); + + tp_asv_set_boxed (info, + "request-properties", TP_HASH_TYPE_OBJECT_IMMUTABLE_PROPERTIES_MAP, + request_props); + + tp_cli_client_handler_call_handle_channels (test->client, -1, + tp_proxy_get_object_path (test->account), + tp_proxy_get_object_path (test->connection), + channels, requests_satisified, 0, info, + no_return_cb, test, NULL, NULL); + + test->wait = 1; + + /* If we support the DelegateToPreferredHandler hint, we wait for + * delegated_channels_cb to be called */ + if (supported) + test->wait++; + + g_main_loop_run (test->mainloop); + g_assert_no_error (test->error); + + if (supported) + { + /* We are not handling the channels any more */ + g_assert (!tp_base_client_is_handling_channel (test->base_client, + test->text_chan)); + g_assert (!tp_base_client_is_handling_channel (test->base_client, + test->text_chan_2)); + } + else + { + /* We are still handling the channels */ + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan)); + g_assert (tp_base_client_is_handling_channel (test->base_client, + test->text_chan_2)); + } + + tp_base_client_unregister (test->base_client); + + g_object_unref (cr); + g_ptr_array_foreach (channels, free_channel_details, NULL); + g_ptr_array_free (channels, TRUE); + g_ptr_array_free (requests_satisified, TRUE); + g_ptr_array_free (requests, TRUE); + g_hash_table_unref (info); + g_hash_table_unref (hints); + g_hash_table_unref (request_props); +} + +static void +test_delegate_to_preferred_handler_not_supported (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + delegate_to_preferred_handler (test, FALSE); +} + +static void +test_delegate_to_preferred_handler_supported (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + delegate_to_preferred_handler (test, TRUE); +} + int main (int argc, char **argv) @@ -1428,6 +1600,10 @@ main (int argc, test_delegate_channels, teardown); g_test_add ("/cd/present-channel", Test, NULL, setup, test_present_channel, teardown); + g_test_add ("/cd/delegate-to-preferred-handler/not-supported", Test, NULL, + setup, test_delegate_to_preferred_handler_not_supported, teardown); + g_test_add ("/cd/delegate-to-preferred-handler/supported", Test, NULL, + setup, test_delegate_to_preferred_handler_supported, teardown); return g_test_run (); } -- 1.7.4.1