diff options
-rw-r--r-- | telepathy-glib/base-contact-list.c | 96 | ||||
-rw-r--r-- | tests/dbus/contact-lists.c | 110 |
2 files changed, 204 insertions, 2 deletions
diff --git a/telepathy-glib/base-contact-list.c b/telepathy-glib/base-contact-list.c index 84ca416fe..e9d9f966e 100644 --- a/telepathy-glib/base-contact-list.c +++ b/telepathy-glib/base-contact-list.c @@ -235,6 +235,11 @@ struct _TpBaseContactListPrivate * announced via NewChannels yet. */ GHashTable *channel_requests; + /* DBusGMethodInvocation *s for calls to RequestBlockedContacts which are + * waiting for the contact list to (fail to) be downloaded. + */ + GQueue blocked_contact_requests; + gulong status_changed_id; /* TRUE if @conn implements TpSvcConnectionInterface$FOO - used to @@ -462,6 +467,7 @@ tp_base_contact_list_init (TpBaseContactList *self) self->priv->groups = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); self->priv->channel_requests = g_hash_table_new (NULL, NULL); + g_queue_init (&self->priv->blocked_contact_requests); } static void @@ -501,13 +507,28 @@ tp_base_contact_list_fail_channel_requests (TpBaseContactList *self, } static void +tp_base_contact_list_fail_blocked_contact_requests ( + TpBaseContactList *self, + const GError *error) +{ + DBusGMethodInvocation *context; + + while ((context = g_queue_pop_head (&self->priv->blocked_contact_requests)) + != NULL) + dbus_g_method_return_error (context, error); +} + +static void tp_base_contact_list_free_contents (TpBaseContactList *self) { + GError error = { TP_ERRORS, TP_ERROR_DISCONNECTED, + "Disconnected before blocked contacts were retrieved" }; guint i; tp_base_contact_list_fail_channel_requests (self, TP_ERRORS, TP_ERROR_DISCONNECTED, "Unable to complete channel request due to disconnection"); + tp_base_contact_list_fail_blocked_contact_requests (self, &error); for (i = 0; i < NUM_TP_LIST_HANDLES; i++) tp_clear_object (self->priv->lists + i); @@ -1746,6 +1767,8 @@ tp_base_contact_list_set_list_failed (TpBaseContactList *self, self->priv->conn, self->priv->state); tp_base_contact_list_fail_channel_requests (self, domain, code, message); + tp_base_contact_list_fail_blocked_contact_requests (self, + self->priv->failure); } /** @@ -1845,6 +1868,23 @@ tp_base_contact_list_set_list_received (TpBaseContactList *self) } tp_base_contact_list_contact_blocking_changed (self, blocked); + + if (self->priv->svc_contact_blocking && + self->priv->blocked_contact_requests.length > 0) + { + GHashTable *map = tp_handle_set_to_identifier_map (blocked); + DBusGMethodInvocation *context; + + while ((context = g_queue_pop_head ( + &self->priv->blocked_contact_requests)) != NULL) + /* Sometimes I think we should either make our method names less + * verbose, or relax our 80-column limit. + */ + tp_svc_connection_interface_contact_blocking_return_from_request_blocked_contacts (context, map); + + g_hash_table_unref (map); + } + tp_handle_set_destroy (blocked); } @@ -5605,6 +5645,60 @@ tp_base_contact_list_mixin_groups_iface_init ( #undef IMPLEMENT } +#define ERROR_IF_BLOCKING_NOT_SUPPORTED(self, context) \ + if (!self->priv->svc_contact_blocking) \ + { \ + GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, \ + "ContactBlocking is not supported on this connection" }; \ + dbus_g_method_return_error (context, &e); \ + return; \ + } + +static void +tp_base_contact_list_mixin_request_blocked_contacts ( + TpSvcConnectionInterfaceContactBlocking *svc, + DBusGMethodInvocation *context) +{ + TpBaseContactList *self = _tp_base_connection_find_channel_manager ( + (TpBaseConnection *) svc, TP_TYPE_BASE_CONTACT_LIST); + + ERROR_IF_BLOCKING_NOT_SUPPORTED (self, context); + + switch (self->priv->state) + { + case TP_CONTACT_LIST_STATE_NONE: + case TP_CONTACT_LIST_STATE_WAITING: + g_queue_push_tail (&self->priv->blocked_contact_requests, context); + break; + + case TP_CONTACT_LIST_STATE_FAILURE: + g_warn_if_fail (self->priv->failure != NULL); + dbus_g_method_return_error (context, self->priv->failure); + break; + + case TP_CONTACT_LIST_STATE_SUCCESS: + { + TpHandleSet *blocked = tp_base_contact_list_dup_blocked_contacts (self); + GHashTable *map = tp_handle_set_to_identifier_map (blocked); + + tp_svc_connection_interface_contact_blocking_return_from_request_blocked_contacts (context, map); + + g_hash_table_unref (map); + tp_handle_set_destroy (blocked); + break; + } + + default: + { + GError broken = { TP_ERRORS, TP_ERROR_CONFUSED, + "My internal list of blocked contacts is inconsistent! " + "I apologise for any inconvenience caused." }; + dbus_g_method_return_error (context, &broken); + g_return_if_reached (); + } + } +} + /** * tp_base_contact_list_mixin_blocking_iface_init: * @klass: the service-side D-Bus interface @@ -5626,8 +5720,8 @@ tp_base_contact_list_mixin_blocking_iface_init ( /* TODO IMPLEMENT (block_contacts); IMPLEMENT (unblock_contacts); - IMPLEMENT (request_blocked_contacts); */ + IMPLEMENT (request_blocked_contacts); #undef IMPLEMENT } diff --git a/tests/dbus/contact-lists.c b/tests/dbus/contact-lists.c index cabfbbf48..852a14fb6 100644 --- a/tests/dbus/contact-lists.c +++ b/tests/dbus/contact-lists.c @@ -320,15 +320,24 @@ setup_pre_connect ( gconstpointer data) { GError *error = NULL; + const gchar *account; g_type_init (); tp_debug_set_flags ("all"); test->dbus = tp_tests_dbus_daemon_dup_or_die (); test->main_loop = g_main_loop_new (NULL, FALSE); + /* Some tests want 'account' to be an invalid identifier, so that Connect() + * will fail (and the status will change to Disconnected). + */ + if (!tp_strdiff (data, "break-account-parameter")) + account = ""; + else + account = "me@example.com"; + test->service_conn = tp_tests_object_new_static_class ( EXAMPLE_TYPE_CONTACT_LIST_CONNECTION, - "account", "me@example.com", + "account", account, "simulation-delay", 0, "protocol", "example-contact-list", NULL); @@ -2421,6 +2430,95 @@ test_remove_from_deny_no_op (Test *test, g_assert_cmpuint (test->log->len, ==, 0); } +static void +test_request_blocked_contacts (Test *test, + gconstpointer nil G_GNUC_UNUSED) +{ + GHashTable *blocked_contacts; + GError *error = NULL; + + tp_cli_connection_interface_contact_blocking_run_request_blocked_contacts ( + test->conn, -1, &blocked_contacts, &error, NULL); + g_assert_no_error (error); + g_assert (blocked_contacts != NULL); + + /* Both Bill and the shadowy Steve are blocked; Steve does not appear in this + * test, as he is in poor health. + */ + g_assert_cmpuint (g_hash_table_size (blocked_contacts), ==, 2); + g_assert_cmpstr (tp_handle_inspect (test->contact_repo, test->bill), ==, + g_hash_table_lookup (blocked_contacts, GUINT_TO_POINTER (test->bill))); + g_hash_table_unref (blocked_contacts); +} + +static void +request_blocked_contacts_succeeded_cb ( + TpConnection *conn, + GHashTable *blocked_contacts, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + g_assert_no_error (error); + + /* As above. */ + g_assert_cmpuint (g_hash_table_size (blocked_contacts), ==, 2); +} + +static void +test_request_blocked_contacts_pre_connect (Test *test, + gconstpointer nil G_GNUC_UNUSED) +{ + gboolean ok; + + /* This verifies that calling RequestBlockedContacts() + * before Connect(), when Connect() ultimately succeeds, returns correctly. + */ + tp_cli_connection_interface_contact_blocking_call_request_blocked_contacts ( + test->conn, -1, request_blocked_contacts_succeeded_cb, + test, test_quit_loop, NULL); + tp_cli_connection_call_connect (test->conn, -1, NULL, NULL, NULL, NULL); + g_main_loop_run (test->main_loop); + + ok = tp_cli_connection_run_disconnect (test->conn, -1, NULL, NULL); + g_assert (ok); +} + +static void +request_blocked_contacts_failed_cb ( + TpConnection *conn, + GHashTable *blocked_contacts, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + g_assert_error (error, TP_ERRORS, TP_ERROR_DISCONNECTED); +} + +static void +test_request_blocked_contacts_connect_failed (Test *test, + gconstpointer nil G_GNUC_UNUSED) +{ + /* This verifies that calling RequestBlockedContacts() (twice, no less) + * before Connect(), when Connect() ultimately fails, returns an appropriate + * error. + */ + tp_cli_connection_interface_contact_blocking_call_request_blocked_contacts ( + test->conn, -1, request_blocked_contacts_failed_cb, + test, test_quit_loop, NULL); + tp_cli_connection_interface_contact_blocking_call_request_blocked_contacts ( + test->conn, -1, request_blocked_contacts_failed_cb, + test, test_quit_loop, NULL); + + /* We expect calling Connect() to fail because the handle was invalid, but + * don't wait around for it. + */ + tp_cli_connection_call_connect (test->conn, -1, NULL, NULL, NULL, NULL); + /* Spin the mainloop twice, once for each outstanding call. */ + g_main_loop_run (test->main_loop); + g_main_loop_run (test->main_loop); +} + int main (int argc, char **argv) @@ -2565,5 +2663,15 @@ main (int argc, g_test_add ("/contact-lists/remove-from-deny/no-op", Test, NULL, setup, test_remove_from_deny_no_op, teardown); + g_test_add ("/contact-lists/request-blocked-contacts", + Test, NULL, setup, test_request_blocked_contacts, teardown); + g_test_add ("/contact-lists/request-blocked-contacts-before-connect", + Test, NULL, setup_pre_connect, + test_request_blocked_contacts_pre_connect, teardown_pre_connect); + g_test_add ("/contact-lists/request-blocked-contacts-connect-failed", + Test, "break-account-parameter", setup_pre_connect, + test_request_blocked_contacts_connect_failed, + teardown_pre_connect); + return g_test_run (); } |