summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--telepathy-glib/base-contact-list.c96
-rw-r--r--tests/dbus/contact-lists.c110
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 ();
}