X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-tp-contact-factory.c;h=db019c9569c2d599433685013cf0ea62c9131ce3;hp=7ec56a3af56ded1fce7b6d6a13817c80534f6a6e;hb=b409d1f2ca05bd2e7ebda4d42714b5511c227845;hpb=a76c204cb8b97ba81a8ce244e3eccd67c301a3f3 diff --git a/libempathy/empathy-tp-contact-factory.c b/libempathy/empathy-tp-contact-factory.c index 7ec56a3a..db019c95 100644 --- a/libempathy/empathy-tp-contact-factory.c +++ b/libempathy/empathy-tp-contact-factory.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Copyright (C) 2007-2008 Collabora Ltd. + * Copyright (C) 2007-2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,36 +24,47 @@ #include #include -#include -#include +#include + +#include #include "empathy-tp-contact-factory.h" #include "empathy-utils.h" -#include "empathy-debug.h" - -#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - EMPATHY_TYPE_TP_CONTACT_FACTORY, EmpathyTpContactFactoryPriv)) -#define DEBUG_DOMAIN "TpContactFactory" +#define DEBUG_FLAG EMPATHY_DEBUG_TP | EMPATHY_DEBUG_CONTACT +#include "empathy-debug.h" -struct _EmpathyTpContactFactoryPriv { - MissionControl *mc; - McAccount *account; +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpContactFactory) +typedef struct { TpConnection *connection; - gboolean ready; - GList *contacts; - guint self_handle; -}; -static void empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass); -static void empathy_tp_contact_factory_init (EmpathyTpContactFactory *factory); + gchar **avatar_mime_types; + guint avatar_min_width; + guint avatar_min_height; + guint avatar_max_width; + guint avatar_max_height; + guint avatar_max_size; + gboolean can_request_ft; +} EmpathyTpContactFactoryPriv; G_DEFINE_TYPE (EmpathyTpContactFactory, empathy_tp_contact_factory, G_TYPE_OBJECT); enum { PROP_0, - PROP_ACCOUNT, + PROP_CONNECTION, + + PROP_MIME_TYPES, + PROP_MIN_WIDTH, + PROP_MIN_HEIGHT, + PROP_MAX_WIDTH, + PROP_MAX_HEIGHT, + PROP_MAX_SIZE +}; + +static TpContactFeature contact_features[] = { + TP_CONTACT_FEATURE_ALIAS, + TP_CONTACT_FEATURE_PRESENCE, }; static EmpathyContact * @@ -73,14 +84,14 @@ tp_contact_factory_find_by_handle (EmpathyTpContactFactory *tp_factory, } static EmpathyContact * -tp_contact_factory_find_by_id (EmpathyTpContactFactory *tp_factory, - const gchar *id) +tp_contact_factory_find_by_tp_contact (EmpathyTpContactFactory *tp_factory, + TpContact *tp_contact) { EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); GList *l; for (l = priv->contacts; l; l = l->next) { - if (!tp_strdiff (empathy_contact_get_id (l->data), id)) { + if (empathy_contact_get_tp_contact (l->data) == tp_contact) { return l->data; } } @@ -94,86 +105,11 @@ tp_contact_factory_weak_notify (gpointer data, { EmpathyTpContactFactoryPriv *priv = GET_PRIV (data); - empathy_debug (DEBUG_DOMAIN, "Remove finalized contact %p", - where_the_object_was); + DEBUG ("Remove finalized contact %p", where_the_object_was); priv->contacts = g_list_remove (priv->contacts, where_the_object_was); } -static void -tp_contact_factory_presences_table_foreach (const gchar *state_str, - GHashTable *presences_table, - EmpathyContact *contact) -{ - const GValue *message; - - empathy_contact_set_presence (contact, - empathy_presence_from_str (state_str)); - - message = g_hash_table_lookup (presences_table, "message"); - if (message != NULL) { - empathy_contact_set_presence_message (contact, - g_value_get_string (message)); - } else { - empathy_contact_set_presence_message (contact, NULL); - } -} - -static void -tp_contact_factory_parse_presence_foreach (guint handle, - GValueArray *presence_struct, - EmpathyTpContactFactory *tp_factory) -{ - GHashTable *presences_table; - EmpathyContact *contact; - - contact = tp_contact_factory_find_by_handle (tp_factory, handle); - if (!contact) { - return; - } - - presences_table = g_value_get_boxed (g_value_array_get_nth (presence_struct, 1)); - - g_hash_table_foreach (presences_table, - (GHFunc) tp_contact_factory_presences_table_foreach, - contact); - - empathy_debug (DEBUG_DOMAIN, "Changing presence for contact %s (%d) to %s (%d)", - empathy_contact_get_id (contact), - handle, - empathy_contact_get_presence_message (contact), - empathy_contact_get_presence (contact)); -} - -static void -tp_contact_factory_get_presence_cb (TpConnection *connection, - GHashTable *handle_table, - const GError *error, - gpointer user_data, - GObject *tp_factory) -{ - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error getting presence: %s", - error->message); - return; - } - - g_hash_table_foreach (handle_table, - (GHFunc) tp_contact_factory_parse_presence_foreach, - EMPATHY_TP_CONTACT_FACTORY (tp_factory)); -} - -static void -tp_contact_factory_presence_update_cb (TpConnection *connection, - GHashTable *handle_table, - gpointer user_data, - GObject *tp_factory) -{ - g_hash_table_foreach (handle_table, - (GHFunc) tp_contact_factory_parse_presence_foreach, - EMPATHY_TP_CONTACT_FACTORY (tp_factory)); -} - static void tp_contact_factory_set_aliases_cb (TpConnection *connection, const GError *error, @@ -181,78 +117,7 @@ tp_contact_factory_set_aliases_cb (TpConnection *connection, GObject *tp_factory) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error setting alias: %s", - error->message); - } -} - -static void -tp_contact_factory_request_aliases_cb (TpConnection *connection, - const gchar **contact_names, - const GError *error, - gpointer user_data, - GObject *tp_factory) -{ - guint *handles = user_data; - guint i = 0; - const gchar **name; - - if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting aliases: %s", - error->message); - return; - } - - for (name = contact_names; *name; name++) { - EmpathyContact *contact; - - contact = tp_contact_factory_find_by_handle (EMPATHY_TP_CONTACT_FACTORY (tp_factory), - handles[i]); - if (!contact) { - continue; - } - - empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (request cb)", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - *name); - - empathy_contact_set_name (contact, *name); - - i++; - } -} - -static void -tp_contact_factory_aliases_changed_cb (TpConnection *connection, - const GPtrArray *renamed_handlers, - gpointer user_data, - GObject *weak_object) -{ - EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object); - guint i; - - for (i = 0; renamed_handlers->len > i; i++) { - guint handle; - const gchar *alias; - GValueArray *renamed_struct; - EmpathyContact *contact; - - renamed_struct = g_ptr_array_index (renamed_handlers, i); - handle = g_value_get_uint(g_value_array_get_nth (renamed_struct, 0)); - alias = g_value_get_string(g_value_array_get_nth (renamed_struct, 1)); - contact = tp_contact_factory_find_by_handle (tp_factory, handle); - - if (!contact) { - /* We don't know this contact, skip */ - continue; - } - - empathy_debug (DEBUG_DOMAIN, "Renaming contact %s (%d) to %s (changed cb)", - empathy_contact_get_id (contact), - handle, alias); - - empathy_contact_set_name (contact, alias); + DEBUG ("Error: %s", error->message); } } @@ -264,8 +129,7 @@ tp_contact_factory_set_avatar_cb (TpConnection *connection, GObject *tp_factory) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error setting avatar: %s", - error->message); + DEBUG ("Error: %s", error->message); } } @@ -276,8 +140,7 @@ tp_contact_factory_clear_avatar_cb (TpConnection *connection, GObject *tp_factory) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error clearing avatar: %s", - error->message); + DEBUG ("Error: %s", error->message); } } @@ -291,7 +154,6 @@ tp_contact_factory_avatar_retrieved_cb (TpConnection *connection, GObject *tp_factory) { EmpathyContact *contact; - EmpathyAvatar *avatar; contact = tp_contact_factory_find_by_handle (EMPATHY_TP_CONTACT_FACTORY (tp_factory), handle); @@ -299,17 +161,15 @@ tp_contact_factory_avatar_retrieved_cb (TpConnection *connection, return; } - empathy_debug (DEBUG_DOMAIN, "Avatar retrieved for contact %s (%d)", - empathy_contact_get_id (contact), - handle); + DEBUG ("Avatar retrieved for contact %s (%d)", + empathy_contact_get_id (contact), + handle); - avatar = empathy_avatar_new (avatar_data->data, - avatar_data->len, - mime_type, - token); - - empathy_contact_set_avatar (contact, avatar); - empathy_avatar_unref (avatar); + empathy_contact_load_avatar_data (contact, + avatar_data->data, + avatar_data->len, + mime_type, + token); } static void @@ -319,8 +179,7 @@ tp_contact_factory_request_avatars_cb (TpConnection *connection, GObject *tp_factory) { if (error) { - empathy_debug (DEBUG_DOMAIN, "Error requesting avatars: %s", - error->message); + DEBUG ("Error: %s", error->message); } } @@ -338,7 +197,7 @@ tp_contact_factory_avatar_maybe_update (EmpathyTpContactFactory *tp_factory, } /* Check if we have an avatar */ - if (G_STR_EMPTY (token)) { + if (EMP_STR_EMPTY (token)) { empathy_contact_set_avatar (contact, NULL); return TRUE; } @@ -350,11 +209,8 @@ tp_contact_factory_avatar_maybe_update (EmpathyTpContactFactory *tp_factory, } /* The avatar changed, search the new one in the cache */ - avatar = empathy_avatar_new_from_cache (token); - if (avatar) { + if (empathy_contact_load_avatar_cache (contact, token)) { /* Got from cache, use it */ - empathy_contact_set_avatar (contact, avatar); - empathy_avatar_unref (avatar); return TRUE; } @@ -383,42 +239,39 @@ tp_contact_factory_avatar_tokens_foreach (gpointer key, } static void -tp_contact_factory_get_known_avatar_tokens_cb (TpConnection *connection, - GHashTable *tokens, - const GError *error, - gpointer user_data, - GObject *tp_factory) +tp_contact_factory_got_known_avatar_tokens (EmpathyTpContactFactory *tp_factory, + GHashTable *tokens, + const GError *error) { + EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); TokensData data; if (error) { - empathy_debug (DEBUG_DOMAIN, - "Error getting known avatars tokens: %s", - error->message); + DEBUG ("Error: %s", error->message); return; } - data.tp_factory = EMPATHY_TP_CONTACT_FACTORY (tp_factory); + data.tp_factory = tp_factory; data.handles = g_array_new (FALSE, FALSE, sizeof (guint)); g_hash_table_foreach (tokens, tp_contact_factory_avatar_tokens_foreach, &data); - empathy_debug (DEBUG_DOMAIN, "Got %d tokens, need to request %d avatars", - g_hash_table_size (tokens), - data.handles->len); + DEBUG ("Got %d tokens, need to request %d avatars", + g_hash_table_size (tokens), data.handles->len); /* Request needed avatars */ if (data.handles->len > 0) { - tp_cli_connection_interface_avatars_call_request_avatars (connection, + tp_cli_connection_interface_avatars_call_request_avatars (priv->connection, -1, data.handles, tp_contact_factory_request_avatars_cb, NULL, NULL, - tp_factory); + G_OBJECT (tp_factory)); } g_array_free (data.handles, TRUE); + g_hash_table_destroy (tokens); } static void @@ -436,8 +289,7 @@ tp_contact_factory_avatar_updated_cb (TpConnection *connection, return; } - empathy_debug (DEBUG_DOMAIN, "Need to request avatar for token %s", - new_token); + DEBUG ("Need to request avatar for token %s", new_token); handles = g_array_new (FALSE, FALSE, sizeof (guint)); g_array_append_val (handles, handle); @@ -480,27 +332,23 @@ tp_contact_factory_update_capabilities (EmpathyTpContactFactory *tp_factory, } } - empathy_debug (DEBUG_DOMAIN, "Changing capabilities for contact %s (%d) to %d", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact), - capabilities); + DEBUG ("Changing capabilities for contact %s (%d) to %d", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + capabilities); empathy_contact_set_capabilities (contact, capabilities); } static void -tp_contact_factory_get_capabilities_cb (TpConnection *connection, - const GPtrArray *capabilities, - const GError *error, - gpointer user_data, - GObject *weak_object) +tp_contact_factory_got_capabilities (EmpathyTpContactFactory *tp_factory, + GPtrArray *capabilities, + const GError *error) { - EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object); - guint i; + guint i; if (error) { - empathy_debug (DEBUG_DOMAIN, "Error getting capabilities: %s", - error->message); + DEBUG ("Error: %s", error->message); /* FIXME Should set the capabilities of the contacts for which this request * originated to NONE */ return; @@ -524,7 +372,11 @@ tp_contact_factory_get_capabilities_cb (TpConnection *connection, channel_type, generic, specific); + + g_value_array_free (values); } + + g_ptr_array_free (capabilities, TRUE); } static void @@ -558,447 +410,294 @@ tp_contact_factory_capabilities_changed_cb (TpConnection *connection, } static void -tp_contact_factory_request_everything (EmpathyTpContactFactory *tp_factory, - const GArray *handles) -{ - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - guint *dup_handles; - - tp_cli_connection_interface_presence_call_get_presence (priv->connection, - -1, - handles, - tp_contact_factory_get_presence_cb, - NULL, NULL, - G_OBJECT (tp_factory)); - - dup_handles = g_memdup (handles->data, handles->len * sizeof (guint)); - tp_cli_connection_interface_aliasing_call_request_aliases (priv->connection, - -1, - handles, - tp_contact_factory_request_aliases_cb, - dup_handles, g_free, - G_OBJECT (tp_factory)); - - tp_cli_connection_interface_avatars_call_get_known_avatar_tokens (priv->connection, - -1, - handles, - tp_contact_factory_get_known_avatar_tokens_cb, - NULL, NULL, - G_OBJECT (tp_factory)); - - tp_cli_connection_interface_capabilities_call_get_capabilities (priv->connection, - -1, - handles, - tp_contact_factory_get_capabilities_cb, - NULL, NULL, - G_OBJECT (tp_factory)); -} - -static void -tp_contact_factory_list_free (gpointer data) -{ - GList *l = data; - - g_list_foreach (l, (GFunc) g_object_unref, NULL); - g_list_free (l); -} - -static void -tp_contact_factory_request_handles_cb (TpConnection *connection, - const GArray *handles, - const GError *error, - gpointer user_data, - GObject *tp_factory) +get_requestable_channel_classes_cb (TpProxy *connection, + const GValue *value, + const GError *error, + gpointer user_data, + GObject *weak_object) { - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - GList *contacts = user_data; - GList *l; - guint i = 0; + EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object); + EmpathyTpContactFactoryPriv *priv = GET_PRIV (self); + GPtrArray *classes; + guint i; - if (error) { - empathy_debug (DEBUG_DOMAIN, "Failed to request handles: %s", - error->message); + if (error != NULL) { + DEBUG ("Error: %s", error->message); return; } - for (l = contacts; l; l = l->next) { - guint handle; + classes = g_value_get_boxed (value); + for (i = 0; i < classes->len; i++) { + GValueArray *class_struct; + GHashTable *fixed_prop; + GValue *chan_type, *handle_type; + GList *l; + + class_struct = g_ptr_array_index (classes, i); + fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0)); + + chan_type = g_hash_table_lookup (fixed_prop, + TP_IFACE_CHANNEL ".ChannelType"); + if (chan_type == NULL || + tp_strdiff (g_value_get_string (chan_type), + TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) { + continue; + } - handle = g_array_index (handles, guint, i); - empathy_contact_set_handle (l->data, handle); - if (handle == priv->self_handle) { - empathy_contact_set_is_user (l->data, TRUE); + handle_type = g_hash_table_lookup (fixed_prop, + TP_IFACE_CHANNEL ".TargetHandleType"); + if (handle_type == NULL || + g_value_get_uint (handle_type) != TP_HANDLE_TYPE_CONTACT) { + continue; } - i++; - } + /* We can request file transfer channel to contacts. */ + priv->can_request_ft = TRUE; - tp_contact_factory_request_everything (EMPATHY_TP_CONTACT_FACTORY (tp_factory), - handles); -} + /* Update the capabilities of all contacts */ + for (l = priv->contacts; l != NULL; l = g_list_next (l)) { + EmpathyContact *contact = l->data; + EmpathyCapabilities caps; -static void -tp_contact_factory_disconnect_contact_foreach (gpointer data, - gpointer user_data) -{ - EmpathyContact *contact = data; - - empathy_contact_set_presence (contact, MC_PRESENCE_UNSET); - empathy_contact_set_handle (contact, 0); + caps = empathy_contact_get_capabilities (contact); + empathy_contact_set_capabilities (contact, caps | + EMPATHY_CAPABILITIES_FT); + } + break; + } } static void -tp_contact_factory_connection_invalidated_cb (EmpathyTpContactFactory *tp_factory) +tp_contact_factory_got_avatar_requirements_cb (TpConnection *proxy, + const gchar **mime_types, + guint min_width, + guint min_height, + guint max_width, + guint max_height, + guint max_size, + const GError *error, + gpointer user_data, + GObject *tp_factory) { EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - empathy_debug (DEBUG_DOMAIN, "Connection invalidated"); - - g_object_unref (priv->connection); - priv->connection = NULL; - priv->ready = FALSE; - - g_list_foreach (priv->contacts, - tp_contact_factory_disconnect_contact_foreach, - tp_factory); - + if (error) { + DEBUG ("Failed to get avatar requirements: %s", error->message); + /* We'll just leave avatar_mime_types as NULL; the + * avatar-setting code can use this as a signal that you can't + * set avatars. + */ + } else { + priv->avatar_mime_types = g_strdupv ((gchar **) mime_types); + priv->avatar_min_width = min_width; + priv->avatar_min_height = min_height; + priv->avatar_max_width = max_width; + priv->avatar_max_height = max_height; + priv->avatar_max_size = max_size; + } } - static void -tp_contact_factory_got_self_handle_cb (TpConnection *proxy, - guint handle, - const GError *error, - gpointer user_data, - GObject *tp_factory) +tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory, + EmpathyContact *contact) { EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - const gchar **contact_ids; - guint i; - GList *l; - - if (error) { - empathy_debug (DEBUG_DOMAIN, "Failed to get self handles: %s", - error->message); - return; - } + TpHandle handle; + GArray handles = {(gchar*) &handle, 1}; + GHashTable *tokens; + GPtrArray *capabilities; + GError *error = NULL; - priv->self_handle = handle; - priv->ready = TRUE; - - /* Connect signals */ - tp_cli_connection_interface_aliasing_connect_to_aliases_changed (priv->connection, - tp_contact_factory_aliases_changed_cb, - NULL, NULL, - G_OBJECT (tp_factory), - NULL); - tp_cli_connection_interface_avatars_connect_to_avatar_updated (priv->connection, - tp_contact_factory_avatar_updated_cb, - NULL, NULL, - G_OBJECT (tp_factory), - NULL); - tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection, - tp_contact_factory_avatar_retrieved_cb, - NULL, NULL, - G_OBJECT (tp_factory), - NULL); - tp_cli_connection_interface_presence_connect_to_presence_update (priv->connection, - tp_contact_factory_presence_update_cb, - NULL, NULL, - G_OBJECT (tp_factory), - NULL); - tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection, - tp_contact_factory_capabilities_changed_cb, - NULL, NULL, - G_OBJECT (tp_factory), - NULL); + /* Keep a weak ref to that contact */ + g_object_weak_ref (G_OBJECT (contact), + tp_contact_factory_weak_notify, + tp_factory); + priv->contacts = g_list_prepend (priv->contacts, contact); - /* Request new handles for all contacts */ - if (priv->contacts) { - GList *contacts; + /* The contact keeps a ref to its factory */ + g_object_set_data_full (G_OBJECT (contact), "empathy-factory", + g_object_ref (tp_factory), + g_object_unref); - contacts = g_list_copy (priv->contacts); - g_list_foreach (contacts, (GFunc) g_object_ref, NULL); + /* Set the FT capability */ + if (priv->can_request_ft) { + EmpathyCapabilities caps; - i = g_list_length (contacts); - contact_ids = g_new0 (const gchar*, i + 1); - i = 0; - for (l = contacts; l; l = l->next) { - contact_ids[i] = empathy_contact_get_id (l->data); - i++; - } + caps = empathy_contact_get_capabilities (contact); + caps |= EMPATHY_CAPABILITIES_FT; - tp_cli_connection_call_request_handles (priv->connection, - -1, - TP_HANDLE_TYPE_CONTACT, - contact_ids, - tp_contact_factory_request_handles_cb, - contacts, tp_contact_factory_list_free, - G_OBJECT (tp_factory)); - g_free (contact_ids); + empathy_contact_set_capabilities (contact, caps); } -} -static void -tp_contact_factory_connection_ready_cb (EmpathyTpContactFactory *tp_factory) -{ - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); + /* FIXME: This should be done by TpContact */ + handle = empathy_contact_get_handle (contact); + tp_cli_connection_interface_avatars_run_get_known_avatar_tokens (priv->connection, + -1, + &handles, + &tokens, + &error, + NULL); + tp_contact_factory_got_known_avatar_tokens (tp_factory, tokens, error); + g_clear_error (&error); - /* Get our own handle */ - tp_cli_connection_call_get_self_handle (priv->connection, - -1, - tp_contact_factory_got_self_handle_cb, - NULL, NULL, - G_OBJECT (tp_factory)); + tp_cli_connection_interface_capabilities_run_get_capabilities (priv->connection, + -1, + &handles, + &capabilities, + &error, + NULL); + tp_contact_factory_got_capabilities (tp_factory, capabilities, error); + g_clear_error (&error); + + DEBUG ("Contact added: %s (%d)", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact)); } +typedef struct { + EmpathyTpContactFactory *tp_factory; + EmpathyTpContactFactoryGotContactsCb callback; + gpointer user_data; + GDestroyNotify destroy; +} GetContactsData; + static void -tp_contact_factory_status_updated (EmpathyTpContactFactory *tp_factory) +get_contacts_data_free (gpointer user_data) { - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - TpConn *tp_conn; - - if (priv->connection) { - /* We already have our connection object */ - g_object_unref (tp_conn); - return; - } + GetContactsData *data = user_data; - tp_conn = mission_control_get_connection (priv->mc, priv->account, NULL); - if (!tp_conn) { - return; + if (data->destroy) { + data->destroy (data->user_data); } + g_object_unref (data->tp_factory); - /* We got a new connection, wait for it to be ready */ - priv->connection = tp_conn_dup_connection (tp_conn); - g_object_unref (tp_conn); - - g_signal_connect_swapped (priv->connection, "invalidated", - G_CALLBACK (tp_contact_factory_connection_invalidated_cb), - tp_factory); - g_signal_connect_swapped (priv->connection, "notify::connection-ready", - G_CALLBACK (tp_contact_factory_connection_ready_cb), - tp_factory); + g_slice_free (GetContactsData, data); } static void -tp_contact_factory_status_changed_cb (MissionControl *mc, - TpConnectionStatus status, - McPresence presence, - TpConnectionStatusReason reason, - const gchar *unique_name, - EmpathyTpContactFactory *tp_factory) +got_contacts (GetContactsData *data, + GObject *weak_object, + guint n_contacts, + TpContact * const *contacts, + const GError *error) { - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - McAccount *account; + GList *ret = NULL; + guint i; - account = mc_account_lookup (unique_name); - if (account && empathy_account_equal (account, priv->account)) { - tp_contact_factory_status_updated (tp_factory); + if (error) { + DEBUG ("Error: %s", error->message); } - g_object_unref (account); -} -static void -tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory, - EmpathyContact *contact) -{ - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); + for (i = 0; i < n_contacts; i++) { + EmpathyContact *contact; - g_object_weak_ref (G_OBJECT (contact), - tp_contact_factory_weak_notify, - tp_factory); - priv->contacts = g_list_prepend (priv->contacts, contact); + contact = tp_contact_factory_find_by_tp_contact (data->tp_factory, + contacts[i]); - if (!tp_proxy_has_interface_by_id (priv->connection, - TP_IFACE_QUARK_CONNECTION_INTERFACE_PRESENCE)) { - /* We have no presence iface, set default presence - * to available */ - empathy_contact_set_presence (contact, MC_PRESENCE_AVAILABLE); + if (contact != NULL) { + g_object_ref (contact); + } else { + contact = empathy_contact_new (contacts[i]); + tp_contact_factory_add_contact (data->tp_factory, contact); + } + ret = g_list_prepend (ret, contact); } - empathy_debug (DEBUG_DOMAIN, "Contact added: %s (%d)", - empathy_contact_get_id (contact), - empathy_contact_get_handle (contact)); + if (data->callback) + data->callback (data->tp_factory, ret, data->user_data, weak_object); + + g_list_foreach (ret, (GFunc) g_object_unref, NULL); + g_list_free (ret); } static void -tp_contact_factory_hold_handles_cb (TpConnection *connection, - const GError *error, - gpointer userdata, - GObject *tp_factory) +get_contacts_by_handle_cb (TpConnection *connection, + guint n_contacts, + TpContact * const *contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer user_data, + GObject *weak_object) { - if (error) { - empathy_debug (DEBUG_DOMAIN, "Failed to hold handles: %s", - error->message); - } + got_contacts (user_data, weak_object, + n_contacts, contacts, + error); } -EmpathyContact * -empathy_tp_contact_factory_get_user (EmpathyTpContactFactory *tp_factory) +static void +get_contacts_by_id_cb (TpConnection *connection, + guint n_contacts, + TpContact * const *contacts, + const gchar * const *requested_ids, + GHashTable *failed_id_errors, + const GError *error, + gpointer user_data, + GObject *weak_object) { - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL); - - return empathy_tp_contact_factory_get_from_handle (tp_factory, - priv->self_handle); + got_contacts (user_data, weak_object, + n_contacts, contacts, + error); } -EmpathyContact * -empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory, - const gchar *id) +void +empathy_tp_contact_factory_get_from_ids (EmpathyTpContactFactory *tp_factory, + guint n_ids, + const gchar * const *ids, + EmpathyTpContactFactoryGotContactsCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) { EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - EmpathyContact *contact; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL); - g_return_val_if_fail (id != NULL, NULL); - - /* Check if the contact already exists */ - contact = tp_contact_factory_find_by_id (tp_factory, id); - if (contact) { - return g_object_ref (contact); - } - - /* Create new contact */ - contact = g_object_new (EMPATHY_TYPE_CONTACT, - "account", priv->account, - "id", id, - NULL); - tp_contact_factory_add_contact (tp_factory, contact); - - /* If the account is connected, request contact's handle */ - if (priv->connection) { - const gchar *contact_ids[] = {id, NULL}; - GList *contacts; - - contacts = g_list_prepend (NULL, g_object_ref (contact)); - tp_cli_connection_call_request_handles (priv->connection, - -1, - TP_HANDLE_TYPE_CONTACT, - contact_ids, - tp_contact_factory_request_handles_cb, - contacts, tp_contact_factory_list_free, - G_OBJECT (tp_factory)); - } + GetContactsData *data; - return contact; -} - -EmpathyContact * -empathy_tp_contact_factory_get_from_handle (EmpathyTpContactFactory *tp_factory, - guint handle) -{ - EmpathyContact *contact; - GArray *handles; - GList *contacts; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL); - - handles = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (handles, handle); - - contacts = empathy_tp_contact_factory_get_from_handles (tp_factory, handles); - g_array_free (handles, TRUE); - - contact = contacts ? contacts->data : NULL; - g_list_free (contacts); - - return contact; + g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory)); + g_return_if_fail (ids != NULL); + + data = g_slice_new (GetContactsData); + data->callback = callback; + data->user_data = user_data; + data->destroy = destroy; + data->tp_factory = g_object_ref (tp_factory); + tp_connection_get_contacts_by_id (priv->connection, + n_ids, ids, + G_N_ELEMENTS (contact_features), + contact_features, + get_contacts_by_id_cb, + data, + (GDestroyNotify) get_contacts_data_free, + weak_object); } -GList * +void empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory, - const GArray *handles) + guint n_handles, + const TpHandle *handles, + EmpathyTpContactFactoryGotContactsCb callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) { EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); - GList *contacts = NULL; - GArray *new_handles; - gchar **handles_names; - guint i; - GError *error = NULL; - - g_return_val_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory), NULL); - g_return_val_if_fail (handles != NULL, NULL); - - /* Search all contacts we already have */ - new_handles = g_array_new (FALSE, FALSE, sizeof (guint)); - for (i = 0; i < handles->len; i++) { - EmpathyContact *contact; - guint handle; + GetContactsData *data; - handle = g_array_index (handles, guint, i); - if (handle == 0) { - continue; - } - - contact = tp_contact_factory_find_by_handle (tp_factory, handle); - if (contact) { - contacts = g_list_prepend (contacts, g_object_ref (contact)); - } else { - g_array_append_val (new_handles, handle); - } - } - - if (new_handles->len == 0) { - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Get the IDs of all new handles */ - if (!tp_cli_connection_block_on_inspect_handles (priv->connection, - -1, - TP_HANDLE_TYPE_CONTACT, - new_handles, - &handles_names, - &error)) { - empathy_debug (DEBUG_DOMAIN, - "Couldn't inspect contact: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - g_array_free (new_handles, TRUE); - return contacts; - } - - /* Create new contacts */ - for (i = 0; i < new_handles->len; i++) { - EmpathyContact *contact; - gchar *id; - guint handle; - gboolean is_user; - - id = handles_names[i]; - handle = g_array_index (new_handles, guint, i); - - is_user = (handle == priv->self_handle); - contact = g_object_new (EMPATHY_TYPE_CONTACT, - "account", priv->account, - "handle", handle, - "id", id, - "is-user", is_user, - NULL); - tp_contact_factory_add_contact (tp_factory, contact); - contacts = g_list_prepend (contacts, contact); - g_free (id); - } - g_free (handles_names); - - /* Hold all new handles. */ - /* FIXME: Should be unholded when removed from the factory */ - tp_cli_connection_call_hold_handles (priv->connection, - -1, - TP_HANDLE_TYPE_CONTACT, - new_handles, - tp_contact_factory_hold_handles_cb, - NULL, NULL, - G_OBJECT (tp_factory)); - - tp_contact_factory_request_everything (tp_factory, new_handles); - - g_array_free (new_handles, TRUE); - - return contacts; + g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory)); + g_return_if_fail (handles != NULL); + + data = g_slice_new (GetContactsData); + data->callback = callback; + data->user_data = user_data; + data->destroy = destroy; + data->tp_factory = g_object_ref (tp_factory); + tp_connection_get_contacts_by_handle (priv->connection, + n_handles, handles, + G_N_ELEMENTS (contact_features), + contact_features, + get_contacts_by_handle_cb, + data, + (GDestroyNotify) get_contacts_data_free, + weak_object); } void @@ -1012,14 +711,12 @@ empathy_tp_contact_factory_set_alias (EmpathyTpContactFactory *tp_factory, g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory)); g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - g_return_if_fail (empathy_account_equal (empathy_contact_get_account (contact), - priv->account)); handle = empathy_contact_get_handle (contact); - empathy_debug (DEBUG_DOMAIN, "Setting alias for contact %s (%d) to %s", - empathy_contact_get_id (contact), - handle, alias); + DEBUG ("Setting alias for contact %s (%d) to %s", + empathy_contact_get_id (contact), + handle, alias); new_alias = g_hash_table_new_full (g_direct_hash, g_direct_equal, @@ -1056,8 +753,8 @@ empathy_tp_contact_factory_set_avatar (EmpathyTpContactFactory *tp_factory, avatar.data = (gchar*) data; avatar.len = size; - empathy_debug (DEBUG_DOMAIN, "Setting avatar on account %s", - mc_account_get_unique_name (priv->account)); + DEBUG ("Setting avatar on connection %s", + tp_proxy_get_object_path (TP_PROXY (priv->connection))); tp_cli_connection_interface_avatars_call_set_avatar (priv->connection, -1, @@ -1067,8 +764,8 @@ empathy_tp_contact_factory_set_avatar (EmpathyTpContactFactory *tp_factory, NULL, NULL, G_OBJECT (tp_factory)); } else { - empathy_debug (DEBUG_DOMAIN, "Clearing avatar on account %s", - mc_account_get_unique_name (priv->account)); + DEBUG ("Clearing avatar on connection %s", + tp_proxy_get_object_path (TP_PROXY (priv->connection))); tp_cli_connection_interface_avatars_call_clear_avatar (priv->connection, -1, @@ -1087,8 +784,26 @@ tp_contact_factory_get_property (GObject *object, EmpathyTpContactFactoryPriv *priv = GET_PRIV (object); switch (param_id) { - case PROP_ACCOUNT: - g_value_set_object (value, priv->account); + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + case PROP_MIME_TYPES: + g_value_set_boxed (value, priv->avatar_mime_types); + break; + case PROP_MIN_WIDTH: + g_value_set_uint (value, priv->avatar_min_width); + break; + case PROP_MIN_HEIGHT: + g_value_set_uint (value, priv->avatar_min_height); + break; + case PROP_MAX_WIDTH: + g_value_set_uint (value, priv->avatar_max_width); + break; + case PROP_MAX_HEIGHT: + g_value_set_uint (value, priv->avatar_max_height); + break; + case PROP_MAX_SIZE: + g_value_set_uint (value, priv->avatar_max_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -1105,8 +820,8 @@ tp_contact_factory_set_property (GObject *object, EmpathyTpContactFactoryPriv *priv = GET_PRIV (object); switch (param_id) { - case PROP_ACCOUNT: - priv->account = g_object_ref (g_value_get_object (value)); + case PROP_CONNECTION: + priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -1120,14 +835,7 @@ tp_contact_factory_finalize (GObject *object) EmpathyTpContactFactoryPriv *priv = GET_PRIV (object); GList *l; - empathy_debug (DEBUG_DOMAIN, "Finalized: %p (%s)", - object, - mc_account_get_normalized_name (priv->account)); - - dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_factory_status_changed_cb), - object); + DEBUG ("Finalized: %p", object); for (l = priv->contacts; l; l = l->next) { g_object_weak_unref (G_OBJECT (l->data), @@ -1136,12 +844,10 @@ tp_contact_factory_finalize (GObject *object) } g_list_free (priv->contacts); - g_object_unref (priv->mc); - g_object_unref (priv->account); - if (priv->connection) { - g_object_unref (priv->connection); - } + g_object_unref (priv->connection); + + g_strfreev (priv->avatar_mime_types); G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object); } @@ -1157,13 +863,39 @@ tp_contact_factory_constructor (GType type, tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props); priv = GET_PRIV (tp_factory); - priv->ready = FALSE; - tp_contact_factory_status_updated (EMPATHY_TP_CONTACT_FACTORY (tp_factory)); + /* FIXME: This should be moved to TpContact */ + tp_cli_connection_interface_avatars_connect_to_avatar_updated (priv->connection, + tp_contact_factory_avatar_updated_cb, + NULL, NULL, + tp_factory, + NULL); + tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection, + tp_contact_factory_avatar_retrieved_cb, + NULL, NULL, + tp_factory, + NULL); + tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection, + tp_contact_factory_capabilities_changed_cb, + NULL, NULL, + tp_factory, + NULL); + + + /* FIXME: This should be moved to TpConnection */ + tp_cli_connection_interface_avatars_call_get_avatar_requirements (priv->connection, + -1, + tp_contact_factory_got_avatar_requirements_cb, + NULL, NULL, + tp_factory); + tp_cli_dbus_properties_call_get (priv->connection, -1, + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + "RequestableChannelClasses", + get_requestable_channel_classes_cb, NULL, NULL, + G_OBJECT (tp_factory)); return tp_factory; } - static void empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass) { @@ -1174,15 +906,84 @@ empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass) object_class->get_property = tp_contact_factory_get_property; object_class->set_property = tp_contact_factory_set_property; - /* Construct-only properties */ g_object_class_install_property (object_class, - PROP_ACCOUNT, - g_param_spec_object ("account", - "Factory's Account", - "The account associated with the factory", - MC_TYPE_ACCOUNT, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Factory's Connection", + "The connection associated with the factory", + TP_TYPE_CONNECTION, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MIME_TYPES, + g_param_spec_boxed ("avatar-mime-types", + "Supported MIME types for avatars", + "Types of images that may be set as " + "avatars on this connection. Only valid " + "once 'ready' becomes TRUE.", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MIN_WIDTH, + g_param_spec_uint ("avatar-min-width", + "Minimum width for avatars", + "Minimum width of avatar that may be set. " + "Only valid once 'ready' becomes TRUE.", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MIN_HEIGHT, + g_param_spec_uint ("avatar-min-height", + "Minimum height for avatars", + "Minimum height of avatar that may be set. " + "Only valid once 'ready' becomes TRUE.", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MAX_WIDTH, + g_param_spec_uint ("avatar-max-width", + "Maximum width for avatars", + "Maximum width of avatar that may be set " + "or 0 if there is no maximum. " + "Only valid once 'ready' becomes TRUE.", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MAX_HEIGHT, + g_param_spec_uint ("avatar-max-height", + "Maximum height for avatars", + "Maximum height of avatar that may be set " + "or 0 if there is no maximum. " + "Only valid once 'ready' becomes TRUE.", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_MAX_SIZE, + g_param_spec_uint ("avatar-max-size", + "Maximum size for avatars in bytes", + "Maximum file size of avatar that may be " + "set or 0 if there is no maximum. " + "Only valid once 'ready' becomes TRUE.", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + g_type_class_add_private (object_class, sizeof (EmpathyTpContactFactoryPriv)); } @@ -1190,20 +991,72 @@ empathy_tp_contact_factory_class_init (EmpathyTpContactFactoryClass *klass) static void empathy_tp_contact_factory_init (EmpathyTpContactFactory *tp_factory) { - EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory); + EmpathyTpContactFactoryPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (tp_factory, + EMPATHY_TYPE_TP_CONTACT_FACTORY, EmpathyTpContactFactoryPriv); + + tp_factory->priv = priv; + priv->can_request_ft = FALSE; +} + +static GHashTable *factories = NULL; + +static void +tp_contact_factory_connection_invalidated_cb (TpProxy *connection, + guint domain, + gint code, + gchar *message, + gpointer user_data) +{ + DEBUG ("Message: %s", message); + g_hash_table_remove (factories, connection); +} + +static void +tp_contact_factory_connection_weak_notify_cb (gpointer connection, + GObject *where_the_object_was) +{ + g_hash_table_remove (factories, connection); +} - priv->mc = empathy_mission_control_new (); - dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), - "AccountStatusChanged", - G_CALLBACK (tp_contact_factory_status_changed_cb), - tp_factory, NULL); +static void +tp_contact_factory_remove_connection (gpointer connection) +{ + g_signal_handlers_disconnect_by_func (connection, + tp_contact_factory_connection_invalidated_cb, NULL); + g_object_unref (connection); } EmpathyTpContactFactory * -empathy_tp_contact_factory_new (McAccount *account) +empathy_tp_contact_factory_dup_singleton (TpConnection *connection) { - return g_object_new (EMPATHY_TYPE_TP_CONTACT_FACTORY, - "account", account, - NULL); + EmpathyTpContactFactory *tp_factory; + + g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL); + + if (factories == NULL) { + factories = g_hash_table_new_full (empathy_proxy_hash, + empathy_proxy_equal, + tp_contact_factory_remove_connection, + NULL); + } + + tp_factory = g_hash_table_lookup (factories, connection); + if (tp_factory == NULL) { + tp_factory = g_object_new (EMPATHY_TYPE_TP_CONTACT_FACTORY, + "connection", connection, + NULL); + g_hash_table_insert (factories, g_object_ref (connection), + tp_factory); + g_object_weak_ref (G_OBJECT (tp_factory), + tp_contact_factory_connection_weak_notify_cb, + connection); + g_signal_connect (connection, "invalidated", + G_CALLBACK (tp_contact_factory_connection_invalidated_cb), + NULL); + } else { + g_object_ref (tp_factory); + } + + return tp_factory; }