diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-10-30 15:12:46 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-10-30 15:12:46 +0000 |
commit | c91f7e307c10a9c5851cff468065d75251ffd7dc (patch) | |
tree | 4644cde796566e13d06affa9c9f4c50fb2f18a78 | |
parent | 1d0df665f77ed683f8b1fe1e769801716227f054 (diff) |
-rw-r--r-- | docs/reference/telepathy-glib-sections.txt | 2 | ||||
-rw-r--r-- | telepathy-glib/base-connection.c | 9 | ||||
-rw-r--r-- | telepathy-glib/base-connection.h | 70 | ||||
-rw-r--r-- | telepathy-glib/connection-manager.c | 126 | ||||
-rw-r--r-- | telepathy-glib/connection-manager.h | 15 | ||||
-rw-r--r-- | tests/dbus/cm.c | 48 | ||||
-rw-r--r-- | tests/python/cm.py | 60 |
7 files changed, 328 insertions, 2 deletions
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index fb518c587..908ec02d9 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -4842,6 +4842,8 @@ tp_connection_manager_dup_protocol_names tp_connection_manager_has_protocol tp_connection_manager_get_protocol tp_connection_manager_get_protocol_object +tp_connection_manager_request_connection_async +tp_connection_manager_request_connection_finish TpConnectionManagerProtocol tp_connection_manager_protocol_can_register tp_connection_manager_protocol_dup_param_names diff --git a/telepathy-glib/base-connection.c b/telepathy-glib/base-connection.c index c44027951..5b418295c 100644 --- a/telepathy-glib/base-connection.c +++ b/telepathy-glib/base-connection.c @@ -122,6 +122,15 @@ */ /** + * TpBaseConnectionClass::get_unique_connection_name: + * @self: The implementation, a subclass of TpBaseConnection + * + * Returns: (transfer full): a name for this connection which will be unique + * within this connection manager process, as a string which the caller must + * free with #g_free. + */ + +/** * TpBaseConnectionGetInterfacesImpl: * @self: a #TpBaseConnection * diff --git a/telepathy-glib/base-connection.h b/telepathy-glib/base-connection.h index 264f64213..1e35553aa 100644 --- a/telepathy-glib/base-connection.h +++ b/telepathy-glib/base-connection.h @@ -241,6 +241,76 @@ void tp_base_connection_add_client_interest (TpBaseConnection *self, void tp_base_connection_add_possible_client_interest (TpBaseConnection *self, GQuark token); +/* ---- Implemented by subclasses to work around gobject-introspection ---- */ + +#define TP_TYPE_INTROSPECTABLE_BASE_CONNECTION \ + (tp_introspectable_base_connection_get_type ()) + +#define TP_INTROSPECTABLE_BASE_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TP_TYPE_INTROSPECTABLE_BASE_CONNECTION, TpIntrospectableBaseConnection)) + +#define TP_IS_INTROSPECTABLE_BASE_CONNECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TP_TYPE_INTROSPECTABLE_BASE_CONNECTION)) + +#define TP_INTROSPECTABLE_BASE_CONNECTION_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ + TP_TYPE_INTROSPECTABLE_BASE_CONNECTION, \ + TpIntrospectableBaseConnectionInterface)) + +_TP_AVAILABLE_IN_UNRELEASED +GType tp_introspectable_base_connection_get_type (void); + +typedef struct _TpIntrospectableBaseConnection TpIntrospectableBaseConnection; +typedef struct _TpIntrospectableBaseConnectionInterface + TpIntrospectableBaseConnectionInterface; + +struct _TpIntrospectableBaseConnectionInterface +{ + GTypeInterface parent; + + gchar *(*introspectable_normalize_contact) ( + TpIntrospectableBaseConnection *self, + const gchar *id, + gpointer context, + guint *domain, + gint *code, + gchar **message); + void (*introspectable_normalize_contact_async) ( + TpIntrospectableBaseConnection *self, + const gchar *id, + gpointer context, + GAsyncReadyCallback callback, + gpointer user_data); + gchar *(*introspectable_normalize_contact_finish) ( + TpIntrospectableBaseConnection *self, + GAsyncResult *result, + guint *domain, + gint *code, + gchar **message); + + gchar *(*introspectable_normalize_room) ( + TpIntrospectableBaseConnection *self, + const gchar *id, + gpointer context, + guint *domain, + gint *code, + gchar **message); + void (*introspectable_normalize_room_async) ( + TpIntrospectableBaseConnection *self, + const gchar *id, + gpointer context, + GAsyncReadyCallback callback, + gpointer user_data); + gchar *(*introspectable_normalize_room_finish) ( + TpIntrospectableBaseConnection *self, + GAsyncResult *result, + guint *domain, + gint *code, + gchar **message); +}; + G_END_DECLS #endif /* #ifndef __TP_BASE_CONNECTION_H__*/ diff --git a/telepathy-glib/connection-manager.c b/telepathy-glib/connection-manager.c index 2dd1c4ec8..1e49da581 100644 --- a/telepathy-glib/connection-manager.c +++ b/telepathy-glib/connection-manager.c @@ -32,12 +32,14 @@ #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-internal.h> #include <telepathy-glib/proxy-subclass.h> +#include "telepathy-glib/simple-client-factory.h" #include "telepathy-glib/util.h" #define DEBUG_FLAG TP_DEBUG_MANAGER #include "telepathy-glib/debug-internal.h" #include "telepathy-glib/protocol-internal.h" #include "telepathy-glib/util-internal.h" +#include "telepathy-glib/variant-util-internal.h" #include "telepathy-glib/_gen/tp-cli-connection-manager-body.h" @@ -2692,3 +2694,127 @@ tp_connection_manager_param_dup_default_variant ( return g_variant_ref_sink (dbus_g_value_build_g_variant ( ¶m->default_value)); } + +static void +tp_connection_manager_request_connection_cb (TpConnectionManager *self, + const gchar *bus_name, + const gchar *object_path, + const GError *error, + gpointer user_data, + GObject *weak_object G_GNUC_UNUSED) +{ + if (error == NULL) + { + TpSimpleClientFactory *factory; + TpConnection *conn; + GError *error2 = NULL; + + factory = g_task_get_task_data (user_data); + conn = tp_simple_client_factory_ensure_connection (factory, + object_path, NULL, &error2); + + if (G_UNLIKELY (conn == NULL)) + { + g_task_return_error (user_data, error2); + return; + } + + if (g_task_return_error_if_cancelled (user_data)) + { + tp_connection_disconnect_async (conn, NULL, NULL); + g_object_unref (conn); + } + else + { + g_task_return_pointer (user_data, conn, g_object_unref); + } + } + else + { + g_task_return_error (user_data, g_error_copy (error)); + } +} + +/** + * tp_connection_manager_request_connection_async: + * @self: a connection manager + * @client_factory: a client factory to manufacture the #TpConnection + * @protocol_name: the short name of a protocol + * @vardict: the account parameters as a #GVariant of + * type %G_VARIANT_TYPE_VARDICT. If it is floating, ownership will + * be taken, as if via g_variant_ref_sink(). + * @cancellable: (allow-none): may be used to cancel the async request + * @callback: (scope async): a callback to call when + * the request is satisfied + * @user_data: (closure) (allow-none): data to pass to @callback + * + * Return a new, unconnected connection to @protocol. + * tp_connection_connect_async() can be used connect it. + * + * This is a low-level function. Applications other than the + * Account Manager should normally operate by finding or creating a + * #TpAccount on the #TpAccountManager, connecting it + * using tp_account_request_presence_async() or + * using tp_account_manager_set_all_requested_presences(), + * and obtaining the #TpConnection (if required) with + * tp_account_get_connection(). + * + * Since: 0.UNRELEASED + */ +void +tp_connection_manager_request_connection_async (TpConnectionManager *self, + TpSimpleClientFactory *client_factory, + const gchar *protocol_name, + GVariant *vardict, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GHashTable *asv; + + g_return_if_fail (TP_IS_CONNECTION_MANAGER (self)); + g_return_if_fail (TP_IS_SIMPLE_CLIENT_FACTORY (client_factory)); + g_return_if_fail (tp_connection_manager_check_valid_protocol_name ( + protocol_name, NULL)); + g_return_if_fail (vardict != NULL); + g_return_if_fail (g_variant_is_of_type (vardict, G_VARIANT_TYPE_VARDICT)); + /* this makes no sense to call for its side-effects: you'll need to + * do something with the connection */ + g_return_if_fail (callback != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, tp_connection_manager_request_connection_async); + g_task_set_task_data (task, g_object_ref (client_factory), g_object_unref); + + g_variant_ref_sink (vardict); + asv = _tp_asv_from_vardict (vardict); + tp_cli_connection_manager_call_request_connection (self, -1, + protocol_name, asv, + tp_connection_manager_request_connection_cb, task, g_object_unref, NULL); + g_hash_table_unref (asv); + g_variant_unref (vardict); +} + +/** + * tp_connection_manager_request_connection_finish: + * @self: a connection manager + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Interpret the result of tp_connection_manager_request_connection_async(). + * + * Returns: (transfer full): the #TpConnection, or %NULL on error + * Since: 0.UNRELEASED + */ +TpConnection * +tp_connection_manager_request_connection_finish (TpConnectionManager *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + tp_connection_manager_request_connection_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/telepathy-glib/connection-manager.h b/telepathy-glib/connection-manager.h index ac89341c3..cba2aab67 100644 --- a/telepathy-glib/connection-manager.h +++ b/telepathy-glib/connection-manager.h @@ -224,6 +224,21 @@ _TP_DEPRECATED_IN_0_20 void tp_connection_manager_protocol_free (TpConnectionManagerProtocol *proto); #endif +_TP_AVAILABLE_IN_UNRELEASED +void tp_connection_manager_request_connection_async (TpConnectionManager *self, + TpSimpleClientFactory *client_factory, + const gchar *protocol_name, + GVariant *vardict, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +_TP_AVAILABLE_IN_UNRELEASED G_GNUC_WARN_UNUSED_RESULT +TpConnection *tp_connection_manager_request_connection_finish ( + TpConnectionManager *self, + GAsyncResult *result, + GError **error); + G_END_DECLS #include <telepathy-glib/_gen/tp-cli-connection-manager.h> diff --git a/tests/dbus/cm.c b/tests/dbus/cm.c index 3888277d2..fff255d62 100644 --- a/tests/dbus/cm.c +++ b/tests/dbus/cm.c @@ -1121,6 +1121,51 @@ test_list (Test *test, g_assert (tp_connection_manager_has_protocol (test->spurious, "normal")); } +static void +test_request_connection (Test *test, + gconstpointer data) +{ + TpSimpleClientFactory *factory; + GAsyncResult *res = NULL; + TpConnection *conn; + + factory = tp_simple_client_factory_new (test->dbus); + g_assert (TP_IS_SIMPLE_CLIENT_FACTORY (factory)); + + test->error = NULL; + test->cm = tp_connection_manager_new (test->dbus, + TP_BASE_CONNECTION_MANAGER_GET_CLASS (test->service_cm)->cm_dbus_name, + NULL, &test->error); + g_assert (TP_IS_CONNECTION_MANAGER (test->cm)); + g_assert_no_error (test->error); + + tp_connection_manager_request_connection_async (test->cm, + factory, "foo", g_variant_new_parsed ("@a{sv} {}"), + NULL, tp_tests_result_ready_cb, &res); + tp_tests_run_until_result (&res); + conn = tp_connection_manager_request_connection_finish (test->cm, + res, &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED); + g_assert (conn == NULL); + g_clear_error (&test->error); + g_clear_object (&res); + + tp_connection_manager_request_connection_async (test->cm, + factory, "example", g_variant_new_parsed ("{'account': <'herobrine'>}"), + NULL, tp_tests_result_ready_cb, &res); + tp_tests_run_until_result (&res); + conn = tp_connection_manager_request_connection_finish (test->cm, + res, &test->error); + g_assert_no_error (test->error); + g_assert (conn != NULL); + g_clear_object (&res); + + tp_tests_connection_assert_disconnect_succeeds (conn); + g_clear_object (&conn); + + g_object_unref (factory); +} + int main (int argc, char **argv) @@ -1191,5 +1236,8 @@ main (int argc, g_test_add ("/cm/list", Test, GINT_TO_POINTER (USE_OLD_LIST), setup, test_list, teardown); + g_test_add ("/cm/request-connection", Test, GINT_TO_POINTER (0), setup, + test_request_connection, teardown); + return tp_tests_run_with_bus (); } diff --git a/tests/python/cm.py b/tests/python/cm.py index f9e09dfae..6a33e83f0 100644 --- a/tests/python/cm.py +++ b/tests/python/cm.py @@ -23,6 +23,44 @@ import unittest from gi.repository import GLib from gi.repository import TelepathyGLib as Tp +class ExampleConnection(Tp.BaseConnection): + def do_create_channel_managers(self): + return [] + + def do_get_interfaces_always_present(self): + return Tp.BaseConnection.do_get_interfaces_always_present(self) + + def do_get_unique_connection_name(self): + return Tp.BaseConnection.do_get_unique_connection_name(self) + + def do_connecting(self): + print("connecting") + + def do_connected(self): + print("connected") + + def do_disconnected(self): + print("disconnected") + + def _idle_shut_down_cb(self): + self.finish_shutdown() + return False + + def do_shut_down(self): + GLib.idle_add(self._idle_shut_down_cb) + + def _idle_fail_to_connect_cb(self): + self.disconnect_with_dbus_error_vardict( + "im.telepathy.JustAnExample.NotActuallyImplemented", + None, Tp.ConnectionStatusReason.NETWORK_ERROR) + return False + + def do_start_connecting(self): + # Implementations of this method in Python must return True + # due to GNOME bug #710671 + GLib.idle_add(self._idle_fail_to_connect_cb) + return True + class ExampleProtocol(Tp.BaseProtocol, Tp.IntrospectableBaseProtocol, Tp.ProtocolAddressing): @@ -195,6 +233,7 @@ class TestIntrospectedCM(unittest.TestCase): self.cm = None self.dbus_daemon = Tp.DBusDaemon.dup() + self.factory = Tp.SimpleClientFactory.new(self.dbus_daemon) cm_client = None @@ -419,8 +458,25 @@ class TestIntrospectedCM(unittest.TestCase): protocol.identify_account_finish(waiter.result) self.assertEqual(trap.exception.code, Tp.Error.INVALID_ARGUMENT) - # FIXME: test CM.NewConnection (there's no medium-level API, - # and the high-level API needs an AccountManager) + waiter = Waiter() + cm_client.request_connection_async(self.factory, 'foo', + GLib.Variant('a{sv}', { + }), None, waiter.callback, waiter) + waiter.wait() + with self.assertRaises(GLib.GError) as trap: + cm_client.request_connection_finish(waiter.result) + self.assertEqual(trap.exception.code, Tp.Error.NOT_IMPLEMENTED) + + waiter = Waiter() + cm_client.request_connection_async(self.factory, 'example', + GLib.Variant('a{sv}', { + 'account': GLib.Variant('s', ''), + 'foo': GLib.Variant('u', 42), + }), None, waiter.callback, waiter) + waiter.wait() + with self.assertRaises(GLib.GError) as trap: + cm_client.request_connection_finish(waiter.result) + self.assertEqual(trap.exception.code, Tp.Error.INVALID_ARGUMENT) def tearDown(self): if self.cm is not None: |