/* * simple-conn.c - a simple connection * * Copyright (C) 2007-2010 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "config.h" #include "simple-conn.h" #include #include #include #include #include #include #include #include #include "textchan-null.h" #include "util.h" static void conn_iface_init (TpSvcConnectionClass *); G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleConnection, tp_tests_simple_connection, TP_TYPE_BASE_CONNECTION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, conn_iface_init)) /* type definition stuff */ enum { PROP_ACCOUNT = 1, N_PROPS }; enum { SIGNAL_GOT_SELF_HANDLE, N_SIGNALS }; static guint signals[N_SIGNALS] = {0}; struct _TpTestsSimpleConnectionPrivate { gchar *account; guint connect_source; guint disconnect_source; /* TpHandle => reffed TpTestsTextChannelNull */ GHashTable *channels; GError *get_self_handle_error /* initially NULL */ ; }; static void tp_tests_simple_connection_init (TpTestsSimpleConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_CONNECTION, TpTestsSimpleConnectionPrivate); self->priv->channels = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); } static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *spec) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); switch (property_id) { case PROP_ACCOUNT: g_value_set_string (value, self->priv->account); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); } } static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *spec) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); switch (property_id) { case PROP_ACCOUNT: g_free (self->priv->account); self->priv->account = g_utf8_strdown (g_value_get_string (value), -1); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); } } static void dispose (GObject *object) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); g_hash_table_unref (self->priv->channels); G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->dispose (object); } static void finalize (GObject *object) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); if (self->priv->connect_source != 0) { g_source_remove (self->priv->connect_source); } if (self->priv->disconnect_source != 0) { g_source_remove (self->priv->disconnect_source); } g_clear_error (&self->priv->get_self_handle_error); g_free (self->priv->account); G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->finalize (object); } static gchar * get_unique_connection_name (TpBaseConnection *conn) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); return g_strdup (self->priv->account); } static gchar * tp_tests_simple_normalize_contact (TpHandleRepoIface *repo, const gchar *id, gpointer context, GError **error) { if (id[0] == '\0') { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE, "ID must not be empty"); return NULL; } if (strchr (id, ' ') != NULL) { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE, "ID must not contain spaces"); return NULL; } return g_utf8_strdown (id, -1); } static void create_handle_repos (TpBaseConnection *conn, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT, tp_tests_simple_normalize_contact, NULL); repos[TP_HANDLE_TYPE_ROOM] = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_ROOM, NULL, NULL); } static GPtrArray * create_channel_factories (TpBaseConnection *conn) { return g_ptr_array_sized_new (0); } void tp_tests_simple_connection_inject_disconnect (TpTestsSimpleConnection *self) { tp_base_connection_change_status ((TpBaseConnection *) self, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } static gboolean pretend_connected (gpointer data) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); TpBaseConnection *conn = (TpBaseConnection *) self; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); conn->self_handle = tp_handle_ensure (contact_repo, self->priv->account, NULL, NULL); if (conn->status == TP_CONNECTION_STATUS_CONNECTING) { tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } self->priv->connect_source = 0; return FALSE; } static gboolean start_connecting (TpBaseConnection *conn, GError **error) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTING, TP_CONNECTION_STATUS_REASON_REQUESTED); /* In a real connection manager we'd ask the underlying implementation to * start connecting, then go to state CONNECTED when finished. Here there * isn't actually a connection, so we'll fake a connection process that * takes time. */ self->priv->connect_source = g_timeout_add (0, pretend_connected, self); return TRUE; } static gboolean pretend_disconnected (gpointer data) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); /* We are disconnected, all our channels are invalidated */ g_hash_table_remove_all (self->priv->channels); tp_base_connection_finish_shutdown (TP_BASE_CONNECTION (data)); self->priv->disconnect_source = 0; return FALSE; } static void shut_down (TpBaseConnection *conn) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); /* In a real connection manager we'd ask the underlying implementation to * start shutting down, then call this function when finished. Here there * isn't actually a connection, so we'll fake a disconnection process that * takes time. */ self->priv->disconnect_source = g_timeout_add (0, pretend_disconnected, conn); } static void tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass) { TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; static const gchar *interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL }; object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; object_class->finalize = finalize; g_type_class_add_private (klass, sizeof (TpTestsSimpleConnectionPrivate)); base_class->create_handle_repos = create_handle_repos; base_class->get_unique_connection_name = get_unique_connection_name; base_class->create_channel_factories = create_channel_factories; base_class->start_connecting = start_connecting; base_class->shut_down = shut_down; base_class->interfaces_always_present = interfaces_always_present; param_spec = g_param_spec_string ("account", "Account name", "The username of this user", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec); signals[SIGNAL_GOT_SELF_HANDLE] = g_signal_new ("got-self-handle", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } void tp_tests_simple_connection_set_identifier (TpTestsSimpleConnection *self, const gchar *identifier) { TpBaseConnection *conn = (TpBaseConnection *) self; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); TpHandle handle = tp_handle_ensure (contact_repo, identifier, NULL, NULL); /* if this fails then the identifier was bad - caller error */ g_return_if_fail (handle != 0); tp_base_connection_set_self_handle (conn, handle); tp_handle_unref (contact_repo, handle); } TpTestsSimpleConnection * tp_tests_simple_connection_new (const gchar *account, const gchar *protocol) { return TP_TESTS_SIMPLE_CONNECTION (g_object_new ( TP_TESTS_TYPE_SIMPLE_CONNECTION, "account", account, "protocol", protocol, NULL)); } gchar * tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self, const gchar *target_id, GHashTable **props) { TpTestsTextChannelNull *chan; gchar *chan_path; TpHandleRepoIface *contact_repo; TpHandle handle; static guint count = 0; TpBaseConnection *base_conn = (TpBaseConnection *) self; /* Get contact handle */ contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); g_assert (contact_repo != NULL); handle = tp_handle_ensure (contact_repo, target_id, NULL, NULL); chan = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle)); if (chan != NULL) { /* Channel already exist, reuse it */ g_object_get (chan, "object-path", &chan_path, NULL); } else { chan_path = g_strdup_printf ("%s/Channel%u", base_conn->object_path, count++); chan = TP_TESTS_TEXT_CHANNEL_NULL ( tp_tests_object_new_static_class ( TP_TESTS_TYPE_TEXT_CHANNEL_NULL, "connection", self, "object-path", chan_path, "handle", handle, NULL)); g_hash_table_insert (self->priv->channels, GUINT_TO_POINTER (handle), chan); } tp_handle_unref (contact_repo, handle); if (props != NULL) *props = tp_tests_text_channel_get_props (chan); return chan_path; } void tp_tests_simple_connection_set_get_self_handle_error ( TpTestsSimpleConnection *self, GQuark domain, gint code, const gchar *message) { self->priv->get_self_handle_error = g_error_new_literal (domain, code, message); } static void get_self_handle (TpSvcConnection *iface, DBusGMethodInvocation *context) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (iface); TpBaseConnection *base = TP_BASE_CONNECTION (iface); g_assert (TP_IS_BASE_CONNECTION (base)); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (self->priv->get_self_handle_error != NULL) { dbus_g_method_return_error (context, self->priv->get_self_handle_error); return; } tp_svc_connection_return_from_get_self_handle (context, base->self_handle); g_signal_emit (self, signals[SIGNAL_GOT_SELF_HANDLE], 0); } static void conn_iface_init (TpSvcConnectionClass *iface) { #define IMPLEMENT(prefix,x) \ tp_svc_connection_implement_##x (iface, prefix##x) IMPLEMENT(,get_self_handle); #undef IMPLEMENT }