/* * contacts-conn.c - connection with contact info * * Copyright (C) 2007-2008 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 "contacts-conn.h" #include #include #include #include "debug.h" #include "my-conn-proxy.h" static void init_aliasing (gpointer, gpointer); static void init_avatars (gpointer, gpointer); static void init_contact_info (gpointer, gpointer); static void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); G_DEFINE_TYPE_WITH_CODE (TpTestsContactsConnection, tp_tests_contacts_connection, TP_TESTS_TYPE_SIMPLE_CONNECTION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING1, init_aliasing); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS1, init_avatars); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE1, tp_presence_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION1, NULL) G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES1, NULL) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO1, init_contact_info) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST1, tp_base_contact_list_mixin_list_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS1, tp_base_contact_list_mixin_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CLIENT_TYPES1, NULL); /* We don't really implement this one, but the proxy-preparation test * wants to be able to add interfaces at runtime like Gabble does. * It can be any interface we don't really need (implementation detail: * it's PowerSaving1). */ G_IMPLEMENT_INTERFACE (TP_TESTS_TYPE_SVC_CONNECTION_INTERFACE_LATER, NULL); ); /* type definition stuff */ static const char *mime_types[] = { "image/png", NULL }; static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = { { "MinimumAvatarWidth", GUINT_TO_POINTER (1), NULL }, { "MinimumAvatarHeight", GUINT_TO_POINTER (2), NULL }, { "RecommendedAvatarWidth", GUINT_TO_POINTER (3), NULL }, { "RecommendedAvatarHeight", GUINT_TO_POINTER (4), NULL }, { "MaximumAvatarWidth", GUINT_TO_POINTER (5), NULL }, { "MaximumAvatarHeight", GUINT_TO_POINTER (6), NULL }, { "MaximumAvatarBytes", GUINT_TO_POINTER (7), NULL }, /* special-cased - it's the only one with a non-guint value */ { "SupportedAvatarMIMETypes", NULL, NULL }, { NULL } }; enum { N_SIGNALS }; struct _TpTestsContactsConnectionPrivate { /* TpHandle => gchar * */ GHashTable *aliases; /* TpHandle => AvatarData */ GHashTable *avatars; /* TpHandle => ContactsConnectionPresenceStatusIndex */ GHashTable *presence_statuses; /* TpHandle => gchar * */ GHashTable *presence_messages; /* TpHandle => GHashTable * */ GHashTable *locations; /* TpHandle => GPtrArray * */ GHashTable *capabilities; /* TpHandle => GPtrArray * */ GHashTable *contact_info; GPtrArray *default_contact_info; TpTestsContactListManager *list_manager; }; typedef struct { GArray *data; gchar *mime_type; gchar *token; } AvatarData; static AvatarData * avatar_data_new (GArray *data, const gchar *mime_type, const gchar *token) { AvatarData *a; a = g_slice_new (AvatarData); a->data = data ? g_array_ref (data) : NULL; a->mime_type = g_strdup (mime_type); a->token = g_strdup (token); return a; } static void avatar_data_free (gpointer data) { AvatarData *a = data; if (a != NULL) { if (a->data != NULL) g_array_unref (a->data); g_free (a->mime_type); g_free (a->token); g_slice_free (AvatarData, a); } } static void free_rcc_list (GPtrArray *rccs) { g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs); } static void tp_tests_contacts_connection_init (TpTestsContactsConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_CONTACTS_CONNECTION, TpTestsContactsConnectionPrivate); self->priv->aliases = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); self->priv->avatars = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, avatar_data_free); self->priv->presence_statuses = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); self->priv->presence_messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); self->priv->locations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_unref); self->priv->capabilities = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_rcc_list); self->priv->contact_info = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); } static void dispose (GObject *object) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); g_clear_object (&self->priv->list_manager); G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->dispose (object); } static void finalize (GObject *object) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); g_hash_table_unref (self->priv->aliases); g_hash_table_unref (self->priv->avatars); g_hash_table_unref (self->priv->presence_statuses); g_hash_table_unref (self->priv->presence_messages); g_hash_table_unref (self->priv->locations); g_hash_table_unref (self->priv->capabilities); g_hash_table_unref (self->priv->contact_info); if (self->priv->default_contact_info != NULL) g_ptr_array_unref (self->priv->default_contact_info); G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->finalize (object); } static GVariant * _tp_g_variant_new_boxed (GType gtype, gpointer boxed) { GValue value = G_VALUE_INIT; GVariant *variant; g_value_init (&value, gtype); g_value_set_boxed (&value, boxed); variant = dbus_g_value_build_g_variant (&value); g_value_unset (&value); return variant; } static void tp_tests_contacts_connection_fill_contact_attributes (TpBaseConnection *base, const gchar *dbus_interface, TpHandle contact, GVariantDict *attributes) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (base); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); if (!tp_strdiff (dbus_interface, TP_IFACE_CONNECTION_INTERFACE_ALIASING1)) { const gchar *alias = g_hash_table_lookup (self->priv->aliases, GUINT_TO_POINTER (contact)); if (alias == NULL) { alias = tp_handle_inspect (contact_repo, contact); } g_variant_dict_insert (attributes, TP_IFACE_CONNECTION_INTERFACE_ALIASING1 "/alias", "s", alias); return; } if (!tp_strdiff (dbus_interface, TP_IFACE_CONNECTION_INTERFACE_AVATARS1)) { AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (contact)); if (a != NULL && a->token != NULL) { g_variant_dict_insert (attributes, TP_IFACE_CONNECTION_INTERFACE_AVATARS1 "/token", "s", a->token); } return; } if (!tp_strdiff (dbus_interface, TP_IFACE_CONNECTION_INTERFACE_LOCATION1)) { GHashTable *location = g_hash_table_lookup (self->priv->locations, GUINT_TO_POINTER (contact)); if (location != NULL) { g_variant_dict_insert_value (attributes, TP_IFACE_CONNECTION_INTERFACE_LOCATION1 "/location", _tp_g_variant_new_boxed (TP_HASH_TYPE_LOCATION, location)); } return; } if (!tp_strdiff (dbus_interface, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES1)) { GPtrArray *caps = g_hash_table_lookup (self->priv->capabilities, GUINT_TO_POINTER (contact)); if (caps != NULL) { g_variant_dict_insert_value (attributes, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES1 "/capabilities", _tp_g_variant_new_boxed ( TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps)); } return; } if (!tp_strdiff (dbus_interface, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO1)) { GPtrArray *info = g_hash_table_lookup (self->priv->contact_info, GUINT_TO_POINTER (contact)); if (info != NULL) { g_variant_dict_insert_value (attributes, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO1 "/info", _tp_g_variant_new_boxed (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info)); } return; } if (tp_base_contact_list_fill_contact_attributes ( TP_BASE_CONTACT_LIST (self->priv->list_manager), dbus_interface, contact, attributes)) return; if (tp_presence_mixin_fill_contact_attributes (G_OBJECT (self), dbus_interface, contact, attributes)) return; ((TpBaseConnectionClass *) tp_tests_contacts_connection_parent_class)-> fill_contact_attributes (base, dbus_interface, contact, attributes); } static TpDBusPropertiesMixinPropImpl conn_contact_info_properties[] = { { "ContactInfoFlags", GUINT_TO_POINTER (TP_CONTACT_INFO_FLAG_PUSH | TP_CONTACT_INFO_FLAG_CAN_SET), NULL }, { "SupportedFields", NULL, NULL }, { NULL } }; static void conn_contact_info_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark q_supported_fields = g_quark_from_static_string ("SupportedFields"); static GPtrArray *supported_fields = NULL; if (name == q_supported_fields) { if (supported_fields == NULL) { supported_fields = g_ptr_array_new (); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "bday", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, 1, G_TYPE_INVALID)); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "email", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, G_MAXUINT32, G_TYPE_INVALID)); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "fn", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, 1, G_TYPE_INVALID)); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "tel", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, G_MAXUINT32, G_TYPE_INVALID)); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "url", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, G_MAXUINT32, G_TYPE_INVALID)); } g_value_set_boxed (value, supported_fields); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } static void constructed (GObject *object) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); GDBusObjectSkeleton *skel = G_DBUS_OBJECT_SKELETON (object); GDBusInterfaceSkeleton *iface; void (*parent_impl) (GObject *) = G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->constructed; if (parent_impl != NULL) parent_impl (object); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_CLIENT_TYPES1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); iface = tp_svc_interface_skeleton_new (skel, TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE1); g_dbus_object_skeleton_add_interface (skel, iface); g_object_unref (iface); self->priv->list_manager = g_object_new (TP_TESTS_TYPE_CONTACT_LIST_MANAGER, "connection", self, NULL); tp_presence_mixin_init (object, G_STRUCT_OFFSET (TpTestsContactsConnection, presence_mixin)); } /* Must match TpTestsContactsConnectionPresenceStatusIndex in the .h */ static const TpPresenceStatusSpec my_statuses[] = { { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, TRUE}, { "busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, TRUE }, { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, TRUE }, { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, FALSE }, { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, FALSE }, { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, FALSE }, { NULL } }; static gboolean my_status_available (GObject *object, guint index) { TpBaseConnection *base = TP_BASE_CONNECTION (object); return tp_base_connection_check_connected (base, NULL); } static TpPresenceStatus * my_get_contact_status (GObject *object, TpHandle contact) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); gpointer key = GUINT_TO_POINTER (contact); TpTestsContactsConnectionPresenceStatusIndex index; const gchar *presence_message; index = GPOINTER_TO_UINT (g_hash_table_lookup ( self->priv->presence_statuses, key)); presence_message = g_hash_table_lookup ( self->priv->presence_messages, key); return tp_presence_status_new (index, presence_message); } static gboolean my_set_own_status (GObject *object, const TpPresenceStatus *status, GError **error) { TpBaseConnection *base_conn = TP_BASE_CONNECTION (object); TpTestsContactsConnectionPresenceStatusIndex index = status->index; const gchar *message = status->message; TpHandle self_handle; self_handle = tp_base_connection_get_self_handle (base_conn); tp_tests_contacts_connection_change_presences (TP_TESTS_CONTACTS_CONNECTION (object), 1, &self_handle, &index, &message); return TRUE; } static guint my_get_maximum_status_message_length_cb (GObject *obj) { return 512; } static GPtrArray * create_channel_managers (TpBaseConnection *conn) { return g_ptr_array_new (); } enum { ALIASING_DP_ALIAS_FLAGS, }; static void aliasing_get_dbus_property (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer user_data) { switch (GPOINTER_TO_UINT (user_data)) { case ALIASING_DP_ALIAS_FLAGS: g_value_set_uint (value, TP_CONNECTION_ALIAS_FLAG_USER_SET); break; default: g_assert_not_reached (); } } static void tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass) { TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; TpPresenceMixinClass *mixin_class; static TpDBusPropertiesMixinPropImpl aliasing_props[] = { { "AliasFlags", GUINT_TO_POINTER (ALIASING_DP_ALIAS_FLAGS), NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CONNECTION_INTERFACE_AVATARS1, conn_avatars_properties_getter, NULL, conn_avatars_properties, }, { TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO1, conn_contact_info_properties_getter, NULL, conn_contact_info_properties, }, { TP_IFACE_CONNECTION_INTERFACE_ALIASING1, aliasing_get_dbus_property, NULL, aliasing_props, }, { NULL } }; object_class->constructed = constructed; object_class->dispose = dispose; object_class->finalize = finalize; g_type_class_add_private (klass, sizeof (TpTestsContactsConnectionPrivate)); base_class->create_channel_managers = create_channel_managers; base_class->fill_contact_attributes = tp_tests_contacts_connection_fill_contact_attributes; tp_presence_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsContactsConnectionClass, presence_mixin), my_status_available, my_get_contact_status, my_set_own_status, my_statuses); mixin_class = TP_PRESENCE_MIXIN_CLASS(klass); mixin_class->get_maximum_status_message_length = my_get_maximum_status_message_length_cb; tp_presence_mixin_init_dbus_properties (object_class); klass->properties_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsContactsConnectionClass, properties_class)); tp_base_contact_list_mixin_class_init (base_class); } TpTestsContactListManager * tp_tests_contacts_connection_get_contact_list_manager ( TpTestsContactsConnection *self) { return self->priv->list_manager; } /** * tp_tests_contacts_connection_change_aliases: * @self: a #TpTestsContactsConnection * @n: the number of handles * @handles: (array length=n): the handles * @aliases: (array length=n): aliases * */ void tp_tests_contacts_connection_change_aliases (TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *aliases) { GHashTable *changes = g_hash_table_new (NULL, NULL); guint i; for (i = 0; i < n; i++) { DEBUG ("contact#%u -> %s", handles[i], aliases[i]); g_hash_table_insert (self->priv->aliases, GUINT_TO_POINTER (handles[i]), g_strdup (aliases[i])); g_hash_table_insert (changes, GUINT_TO_POINTER (handles[i]), (gchar *) aliases[i]); } tp_svc_connection_interface_aliasing1_emit_aliases_changed (self, changes); g_hash_table_unref (changes); } void tp_tests_contacts_connection_change_presences ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, const TpTestsContactsConnectionPresenceStatusIndex *indexes, const gchar * const *messages) { GHashTable *presences = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_presence_status_free); guint i; for (i = 0; i < n; i++) { gpointer key = GUINT_TO_POINTER (handles[i]); DEBUG ("contact#%u -> %s \"%s\"", handles[i], my_statuses[indexes[i]].name, messages[i]); g_hash_table_insert (self->priv->presence_statuses, key, GUINT_TO_POINTER (indexes[i])); g_hash_table_insert (self->priv->presence_messages, key, g_strdup (messages[i])); g_hash_table_insert (presences, key, tp_presence_status_new (indexes[i], messages[i])); } tp_presence_mixin_emit_presence_update ((GObject *) self, presences); g_hash_table_unref (presences); } void tp_tests_contacts_connection_change_avatar_tokens (TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *tokens) { guint i; for (i = 0; i < n; i++) { DEBUG ("contact#%u -> %s", handles[i], tokens[i]); g_hash_table_insert (self->priv->avatars, GUINT_TO_POINTER (handles[i]), avatar_data_new (NULL, NULL, tokens[i])); tp_svc_connection_interface_avatars1_emit_avatar_updated (self, handles[i], tokens[i]); } } void tp_tests_contacts_connection_change_avatar_data ( TpTestsContactsConnection *self, TpHandle handle, GArray *data, const gchar *mime_type, const gchar *token) { g_hash_table_insert (self->priv->avatars, GUINT_TO_POINTER (handle), avatar_data_new (data, mime_type, token)); tp_svc_connection_interface_avatars1_emit_avatar_updated (self, handle, token); } void tp_tests_contacts_connection_change_locations (TpTestsContactsConnection *self, guint n, const TpHandle *handles, GHashTable **locations) { guint i; for (i = 0; i < n; i++) { DEBUG ("contact#%u ->", handles[i]); tp_asv_dump (locations[i]); g_hash_table_insert (self->priv->locations, GUINT_TO_POINTER (handles[i]), g_hash_table_ref (locations[i])); tp_svc_connection_interface_location1_emit_location_updated (self, handles[i], locations[i]); } } void tp_tests_contacts_connection_change_capabilities ( TpTestsContactsConnection *self, GHashTable *capabilities) { GHashTableIter iter; gpointer handle, caps; g_hash_table_iter_init (&iter, capabilities); while (g_hash_table_iter_next (&iter, &handle, &caps)) { g_hash_table_insert (self->priv->capabilities, handle, g_boxed_copy (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps)); } tp_svc_connection_interface_contact_capabilities1_emit_contact_capabilities_changed ( self, capabilities); } void tp_tests_contacts_connection_change_contact_info ( TpTestsContactsConnection *self, TpHandle handle, GPtrArray *info) { g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), g_ptr_array_ref (info)); tp_svc_connection_interface_contact_info1_emit_contact_info_changed (self, handle, info); } void tp_tests_contacts_connection_set_default_contact_info ( TpTestsContactsConnection *self, GPtrArray *info) { if (self->priv->default_contact_info != NULL) g_ptr_array_unref (self->priv->default_contact_info); self->priv->default_contact_info = g_ptr_array_ref (info); } static void my_request_aliases (TpSvcConnectionInterfaceAliasing1 *aliasing, const GArray *contacts, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); GPtrArray *result; gchar **strings; GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { g_dbus_method_invocation_return_gerror (context, error); g_error_free (error); return; } result = g_ptr_array_sized_new (contacts->len + 1); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); const gchar *alias = g_hash_table_lookup (self->priv->aliases, GUINT_TO_POINTER (handle)); if (alias == NULL) g_ptr_array_add (result, (gchar *) tp_handle_inspect (contact_repo, handle)); else g_ptr_array_add (result, (gchar *) alias); } g_ptr_array_add (result, NULL); strings = (gchar **) g_ptr_array_free (result, FALSE); tp_svc_connection_interface_aliasing1_return_from_request_aliases (context, (const gchar **) strings); g_free (strings); } static void my_set_aliases (TpSvcConnectionInterfaceAliasing1 *aliasing, GHashTable *table, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); guint n; GArray *handles; GPtrArray *aliases; GHashTableIter iter; gpointer key, value; GError *error = NULL; /* Convert the hash table to arrays of handles and aliases */ n = g_hash_table_size (table); handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), n); aliases = g_ptr_array_sized_new (n); g_hash_table_iter_init (&iter, table); while (g_hash_table_iter_next (&iter, &key, &value)) { TpHandle handle = GPOINTER_TO_UINT (key); g_array_append_val (handles, handle); g_ptr_array_add (aliases, value); } g_assert_cmpuint (handles->len, ==, n); g_assert_cmpuint (aliases->len, ==, n); /* Verify all handles are valid */ if (!tp_handles_are_valid (contact_repo, handles, FALSE, &error)) { g_dbus_method_invocation_return_gerror (context, error); g_clear_error (&error); goto out; } /* Change aliases */ tp_tests_contacts_connection_change_aliases (self, n, (const TpHandle *) handles->data, (const gchar * const *) aliases->pdata); tp_svc_connection_interface_aliasing1_return_from_set_aliases (context); out: g_array_unref (handles); g_ptr_array_unref (aliases); } static void init_aliasing (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAliasing1Class *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_aliasing1_implement_##x (\ klass, my_##x) IMPLEMENT(request_aliases); IMPLEMENT(set_aliases); #undef IMPLEMENT } static void my_request_avatars (TpSvcConnectionInterfaceAvatars1 *avatars, const GArray *contacts, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); TpBaseConnection *base = TP_BASE_CONNECTION (avatars); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { g_dbus_method_invocation_return_gerror (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (handle)); if (a != NULL) tp_svc_connection_interface_avatars1_emit_avatar_retrieved (self, handle, a->token, a->data, a->mime_type); } tp_svc_connection_interface_avatars1_return_from_request_avatars (context); } static void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark q_mime_types = g_quark_from_static_string ( "SupportedAvatarMIMETypes"); if (name == q_mime_types) { g_value_set_static_boxed (value, mime_types); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } static void init_avatars (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAvatars1Class *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_avatars1_implement_##x (\ klass, my_##x) /* IMPLEMENT(get_avatar_requirements); */ /* IMPLEMENT(request_avatar); */ IMPLEMENT(request_avatars); /* IMPLEMENT(set_avatar); */ /* IMPLEMENT(clear_avatar); */ #undef IMPLEMENT } static GPtrArray * lookup_contact_info (TpTestsContactsConnection *self, TpHandle handle) { GPtrArray *ret = g_hash_table_lookup (self->priv->contact_info, GUINT_TO_POINTER (handle)); if (ret == NULL && self->priv->default_contact_info != NULL) { ret = self->priv->default_contact_info; g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), g_ptr_array_ref (ret)); } if (ret == NULL) return g_ptr_array_new (); return g_ptr_array_ref (ret); } static void my_refresh_contact_info (TpSvcConnectionInterfaceContactInfo1 *obj, const GArray *contacts, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { g_dbus_method_invocation_return_gerror (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GPtrArray *arr = lookup_contact_info (self, handle); tp_svc_connection_interface_contact_info1_emit_contact_info_changed (self, handle, arr); g_ptr_array_unref (arr); } tp_svc_connection_interface_contact_info1_return_from_refresh_contact_info ( context); } static void my_request_contact_info (TpSvcConnectionInterfaceContactInfo1 *obj, guint handle, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_ENTITY_TYPE_CONTACT); GError *error = NULL; GPtrArray *ret; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handle_is_valid (contact_repo, handle, &error)) { g_dbus_method_invocation_return_gerror (context, error); g_error_free (error); return; } ret = lookup_contact_info (self, handle); tp_svc_connection_interface_contact_info1_return_from_request_contact_info ( context, ret); g_ptr_array_unref (ret); } static void my_set_contact_info (TpSvcConnectionInterfaceContactInfo1 *obj, const GPtrArray *info, GDBusMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); GPtrArray *copy; guint i; TpHandle self_handle; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); /* Deep copy info */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS copy = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free); for (i = 0; i < info->len; i++) g_ptr_array_add (copy, g_value_array_copy (g_ptr_array_index (info, i))); G_GNUC_END_IGNORE_DEPRECATIONS self_handle = tp_base_connection_get_self_handle (base); tp_tests_contacts_connection_change_contact_info (self, self_handle, copy); g_ptr_array_unref (copy); tp_svc_connection_interface_contact_info1_return_from_set_contact_info ( context); } static void init_contact_info (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceContactInfo1Class *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_contact_info1_implement_##x (\ klass, my_##x) IMPLEMENT (refresh_contact_info); IMPLEMENT (request_contact_info); IMPLEMENT (set_contact_info); #undef IMPLEMENT }