]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tp-contact-factory.c
Updated Polish translation
[empathy.git] / libempathy / empathy-tp-contact-factory.c
index 27160a4454abf1665e2253a013d175996a7966f0..4d5a7295e665a40bfc5a62e2d666728e89ef43e3 100644 (file)
@@ -26,6 +26,8 @@
 #include <telepathy-glib/util.h>
 #include <telepathy-glib/gtypes.h>
 #include <telepathy-glib/dbus.h>
+#include <telepathy-glib/interfaces.h>
+
 #if HAVE_GEOCLUE
 #include <geoclue/geoclue-geocode.h>
 #endif
@@ -50,8 +52,13 @@ typedef struct {
        guint           avatar_max_width;
        guint           avatar_max_height;
        guint           avatar_max_size;
+       /* can_request_ft and can_request_st are meaningful only if the connection
+        * doesn't implement ContactCapabilities. If it's implemented, we use it to
+        * check if contacts support file transfer and stream tubes. */
        gboolean        can_request_ft;
        gboolean        can_request_st;
+       /* TRUE if ContactCapabilities is implemented by the Connection */
+       gboolean        contact_caps_supported;
 } EmpathyTpContactFactoryPriv;
 
 G_DEFINE_TYPE (EmpathyTpContactFactory, empathy_tp_contact_factory, G_TYPE_OBJECT);
@@ -128,7 +135,7 @@ tp_contact_factory_set_aliases_cb (TpConnection *connection,
 }
 
 static void
-tp_contact_factory_set_location_cb (TpProxy *proxy,
+tp_contact_factory_set_location_cb (TpConnection *tp_conn,
                                    const GError *error,
                                    gpointer user_data,
                                    GObject *weak_object)
@@ -183,7 +190,7 @@ tp_contact_factory_avatar_retrieved_cb (TpConnection *connection,
                handle);
 
        empathy_contact_load_avatar_data (contact,
-                                         avatar_data->data,
+                                         (guchar *) avatar_data->data,
                                          avatar_data->len,
                                          mime_type,
                                          token);
@@ -235,60 +242,51 @@ tp_contact_factory_avatar_maybe_update (EmpathyTpContactFactory *tp_factory,
        return FALSE;
 }
 
-typedef struct {
-       EmpathyTpContactFactory *tp_factory;
-       GArray                  *handles;
-} TokensData;
-
-static void
-tp_contact_factory_avatar_tokens_foreach (gpointer key,
-                                         gpointer value,
-                                         gpointer user_data)
-{
-       TokensData  *data = user_data;
-       const gchar *token = value;
-       guint        handle = GPOINTER_TO_UINT (key);
-
-       if (!tp_contact_factory_avatar_maybe_update (data->tp_factory,
-                                                    handle, token)) {
-               g_array_append_val (data->handles, handle);
-       }
-}
-
 static void
-tp_contact_factory_got_known_avatar_tokens (EmpathyTpContactFactory *tp_factory,
-                                           GHashTable              *tokens,
-                                           const GError            *error)
+tp_contact_factory_got_known_avatar_tokens (TpConnection *connection,
+                                           GHashTable   *tokens,
+                                           const GError *error,
+                                           gpointer      user_data,
+                                           GObject      *weak_object)
 {
+       EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
        EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
-       TokensData data;
+       GArray *handles;
+       GHashTableIter iter;
+       gpointer key, value;
 
        if (error) {
                DEBUG ("Error: %s", error->message);
                return;
        }
 
-       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);
+       handles = g_array_new (FALSE, FALSE, sizeof (guint));
+
+       g_hash_table_iter_init (&iter, tokens);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               guint handle = GPOINTER_TO_UINT (key);
+               const gchar *token = value;
+
+               if (!tp_contact_factory_avatar_maybe_update (tp_factory,
+                                                            handle, token)) {
+                       g_array_append_val (handles, handle);
+               }
+       }
 
        DEBUG ("Got %d tokens, need to request %d avatars",
-               g_hash_table_size (tokens), data.handles->len);
+               g_hash_table_size (tokens), handles->len);
 
        /* Request needed avatars */
-       if (data.handles->len > 0) {
+       if (handles->len > 0) {
                tp_cli_connection_interface_avatars_call_request_avatars (priv->connection,
                                                                          -1,
-                                                                         data.handles,
+                                                                         handles,
                                                                          tp_contact_factory_request_avatars_cb,
                                                                          NULL, NULL,
                                                                          G_OBJECT (tp_factory));
        }
 
-       g_array_free (data.handles, TRUE);
-       g_hash_table_destroy (tokens);
+       g_array_free (handles, TRUE);
 }
 
 static void
@@ -358,12 +356,17 @@ tp_contact_factory_update_capabilities (EmpathyTpContactFactory *tp_factory,
 }
 
 static void
-tp_contact_factory_got_capabilities (EmpathyTpContactFactory *tp_factory,
-                                    GPtrArray *capabilities,
-                                    const GError    *error)
+tp_contact_factory_got_capabilities (TpConnection    *connection,
+                                    const GPtrArray *capabilities,
+                                    const GError    *error,
+                                    gpointer         user_data,
+                                    GObject         *weak_object)
 {
+       EmpathyTpContactFactory *tp_factory;
        guint i;
 
+       tp_factory = EMPATHY_TP_CONTACT_FACTORY (weak_object);
+
        if (error) {
                DEBUG ("Error: %s", error->message);
                /* FIXME Should set the capabilities of the contacts for which this request
@@ -389,11 +392,7 @@ tp_contact_factory_got_capabilities (EmpathyTpContactFactory *tp_factory,
                                                        channel_type,
                                                        generic,
                                                        specific);
-
-               g_value_array_free (values);
        }
-
-       g_ptr_array_free (capabilities, TRUE);
 }
 
 #if HAVE_GEOCLUE
@@ -568,7 +567,7 @@ tp_contact_factory_update_location (EmpathyTpContactFactory *tp_factory,
 }
 
 static void
-tp_contact_factory_got_locations (TpProxy                 *tp_proxy,
+tp_contact_factory_got_locations (TpConnection                 *tp_conn,
                                  GHashTable              *locations,
                                  const GError            *error,
                                  gpointer                 user_data,
@@ -624,7 +623,7 @@ tp_contact_factory_capabilities_changed_cb (TpConnection    *connection,
 }
 
 static void
-tp_contact_factory_location_updated_cb (TpProxy      *proxy,
+tp_contact_factory_location_updated_cb (TpConnection      *tp_conn,
                                        guint         handle,
                                        GHashTable   *location,
                                        gpointer      user_data,
@@ -634,6 +633,57 @@ tp_contact_factory_location_updated_cb (TpProxy      *proxy,
        tp_contact_factory_update_location (tp_factory, handle, location);
 }
 
+static EmpathyCapabilities
+channel_classes_to_capabilities (GPtrArray *classes,
+                                gboolean audio_video)
+{
+       EmpathyCapabilities capabilities = 0;
+       guint i;
+
+       for (i = 0; i < classes->len; i++) {
+               GValueArray *class_struct;
+               GHashTable *fixed_prop;
+               GStrv allowed_prop;
+               TpHandleType handle_type;
+               const gchar *chan_type;
+
+               class_struct = g_ptr_array_index (classes, i);
+               fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0));
+               allowed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 1));
+
+               handle_type = tp_asv_get_uint32 (fixed_prop,
+                       TP_IFACE_CHANNEL ".TargetHandleType", NULL);
+               if (handle_type != TP_HANDLE_TYPE_CONTACT)
+                       continue;
+
+               chan_type = tp_asv_get_string (fixed_prop,
+                       TP_IFACE_CHANNEL ".ChannelType");
+
+               if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) {
+                       capabilities |= EMPATHY_CAPABILITIES_FT;
+               }
+
+               else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) {
+                       capabilities |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+               }
+               else if (audio_video && !tp_strdiff (chan_type,
+                       TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) {
+                       guint j;
+
+                       for (j = 0; allowed_prop[j] != NULL; j++) {
+                               if (!tp_strdiff (allowed_prop[j],
+                                               TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio"))
+                                       capabilities |= EMPATHY_CAPABILITIES_AUDIO;
+                               else if (!tp_strdiff (allowed_prop[j],
+                                               TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo"))
+                                       capabilities |= EMPATHY_CAPABILITIES_VIDEO;
+                       }
+               }
+       }
+
+       return capabilities;
+}
+
 static void
 get_requestable_channel_classes_cb (TpProxy *connection,
                                    const GValue *value,
@@ -644,8 +694,8 @@ get_requestable_channel_classes_cb (TpProxy *connection,
        EmpathyTpContactFactory     *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
        EmpathyTpContactFactoryPriv *priv = GET_PRIV (self);
        GPtrArray                   *classes;
-       guint                        i;
        GList                       *l;
+       EmpathyCapabilities         capabilities;
 
        if (error != NULL) {
                DEBUG ("Error: %s", error->message);
@@ -653,31 +703,17 @@ get_requestable_channel_classes_cb (TpProxy *connection,
        }
 
        classes = g_value_get_boxed (value);
-       for (i = 0; i < classes->len; i++) {
-               GValueArray *class_struct;
-               GHashTable *fixed_prop;
-               GValue *chan_type, *handle_type;
-
-               class_struct = g_ptr_array_index (classes, i);
-               fixed_prop = g_value_get_boxed (g_value_array_get_nth (class_struct, 0));
 
-               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;
-
-               chan_type = g_hash_table_lookup (fixed_prop,
-                       TP_IFACE_CHANNEL ".ChannelType");
-               if (chan_type == NULL)
-                       continue;
+       DEBUG ("ContactCapabilities is not implemented; use RCC");
+       capabilities = channel_classes_to_capabilities (classes, FALSE);
+       if ((capabilities & EMPATHY_CAPABILITIES_FT) != 0) {
+               DEBUG ("Assume all contacts support FT as CM implements it");
+               priv->can_request_ft = TRUE;
+       }
 
-               if (!tp_strdiff (g_value_get_string (chan_type),
-                   TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
-                       priv->can_request_ft = TRUE;
-               else if (!tp_strdiff (g_value_get_string (chan_type),
-                        TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
-                       priv->can_request_st = TRUE;
+       if ((capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE) != 0) {
+               DEBUG ("Assume all contacts support stream tubes as CM implements them");
+               priv->can_request_st = TRUE;
        }
 
        if (!priv->can_request_ft && !priv->can_request_st)
@@ -690,6 +726,8 @@ get_requestable_channel_classes_cb (TpProxy *connection,
 
                caps = empathy_contact_get_capabilities (contact);
 
+               /* ContactCapabilities is not supported; assume all contacts can do file
+                * transfer if it's implemented in the CM */
                if (priv->can_request_ft)
                        caps |= EMPATHY_CAPABILITIES_FT;
 
@@ -730,6 +768,55 @@ tp_contact_factory_got_avatar_requirements_cb (TpConnection *proxy,
        }
 }
 
+static void
+update_contact_capabilities (EmpathyTpContactFactory *self,
+                            GHashTable *caps)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_hash_table_iter_init (&iter, caps);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               TpHandle handle = GPOINTER_TO_UINT (key);
+               GPtrArray *classes = value;
+               EmpathyContact *contact;
+               EmpathyCapabilities  capabilities;
+
+               contact = tp_contact_factory_find_by_handle (self, handle);
+               if (contact == NULL)
+                       continue;
+
+               capabilities = empathy_contact_get_capabilities (contact);
+               capabilities &= ~EMPATHY_CAPABILITIES_UNKNOWN;
+
+               capabilities |= channel_classes_to_capabilities (classes, TRUE);
+
+               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_got_contact_capabilities (TpConnection *connection,
+                                            GHashTable *caps,
+                                            const GError *error,
+                                            gpointer user_data,
+                                            GObject *weak_object)
+{
+       EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
+
+       if (error != NULL) {
+               DEBUG ("Error: %s", error->message);
+               return;
+       }
+
+       update_contact_capabilities (self, caps);
+}
+
 static void
 tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
                                EmpathyContact          *contact)
@@ -738,9 +825,6 @@ tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
        TpHandle self_handle;
        TpHandle handle;
        GArray handles = {(gchar *) &handle, 1};
-       GHashTable *tokens;
-       GPtrArray *capabilities;
-       GError *error = NULL;
        EmpathyCapabilities caps;
 
        /* Keep a weak ref to that contact */
@@ -757,13 +841,17 @@ tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
        caps = empathy_contact_get_capabilities (contact);
 
        /* Set the FT capability */
-       if (priv->can_request_ft) {
-               caps |= EMPATHY_CAPABILITIES_FT;
-       }
+       if (!priv->contact_caps_supported) {
+               /* ContactCapabilities is not supported; assume all contacts can do file
+                * transfer if it's implemented in the CM */
+               if (priv->can_request_ft) {
+                       caps |= EMPATHY_CAPABILITIES_FT;
+               }
 
-       /* Set the Stream Tube capability */
-       if (priv->can_request_st) {
-               caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+               /* Set the Stream Tube capability */
+               if (priv->can_request_st) {
+                       caps |= EMPATHY_CAPABILITIES_STREAM_TUBE;
+               }
        }
 
        empathy_contact_set_capabilities (contact, caps);
@@ -776,27 +864,31 @@ tp_contact_factory_add_contact (EmpathyTpContactFactory *tp_factory,
        empathy_contact_set_is_user (contact, self_handle == handle);
 
        /* FIXME: This should be done by TpContact */
-       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);
-
-       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);
+       if (tp_proxy_has_interface_by_id (priv->connection,
+                       TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS)) {
+               tp_cli_connection_interface_avatars_call_get_known_avatar_tokens (
+                       priv->connection, -1, &handles,
+                       tp_contact_factory_got_known_avatar_tokens, NULL, NULL,
+                       G_OBJECT (tp_factory));
+       }
+
+       if (priv->contact_caps_supported) {
+               tp_cli_connection_interface_contact_capabilities_call_get_contact_capabilities (
+                       priv->connection, -1, &handles,
+                       tp_contact_factory_got_contact_capabilities, NULL, NULL,
+                       G_OBJECT (tp_factory));
+       }
+       else if (tp_proxy_has_interface_by_id (priv->connection,
+                       TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES)) {
+               tp_cli_connection_interface_capabilities_call_get_capabilities (
+                       priv->connection, -1, &handles,
+                       tp_contact_factory_got_capabilities, NULL, NULL,
+                       G_OBJECT (tp_factory));
+       }
 
        if (tp_proxy_has_interface_by_id (TP_PROXY (priv->connection),
-               EMP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION)) {
-               emp_cli_connection_interface_location_call_get_locations (TP_PROXY (priv->connection),
+               TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION)) {
+               tp_cli_connection_interface_location_call_get_locations (priv->connection,
                                                                         -1,
                                                                         &handles,
                                                                         tp_contact_factory_got_locations,
@@ -910,6 +1002,7 @@ get_contacts_by_id_cb (TpConnection *connection,
        contacts_array_free (n_contacts, empathy_contacts);
 }
 
+/* The callback is NOT given a reference to the EmpathyContact objects */
 void
 empathy_tp_contact_factory_get_from_ids (EmpathyTpContactFactory *tp_factory,
                                         guint                    n_ids,
@@ -976,8 +1069,12 @@ get_contact_by_id_cb (TpConnection *connection,
                                           error,
                                           data->user_data, weak_object);
        }
+
+       if (contact != NULL)
+               g_object_unref (contact);
 }
 
+/* The callback is NOT given a reference to the EmpathyContact objects */
 void
 empathy_tp_contact_factory_get_from_id (EmpathyTpContactFactory *tp_factory,
                                        const gchar             *id,
@@ -1033,6 +1130,7 @@ get_contacts_by_handle_cb (TpConnection *connection,
        contacts_array_free (n_contacts, empathy_contacts);
 }
 
+/* The callback is NOT given a reference to the EmpathyContact objects */
 void
 empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory,
                                             guint n_handles,
@@ -1045,6 +1143,11 @@ empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory
        EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
        GetContactsData *data;
 
+       if (n_handles == 0) {
+               callback (tp_factory, 0, NULL, 0, NULL, NULL, user_data, weak_object);
+               return;
+       }
+
        g_return_if_fail (EMPATHY_IS_TP_CONTACT_FACTORY (tp_factory));
        g_return_if_fail (handles != NULL);
 
@@ -1063,6 +1166,7 @@ empathy_tp_contact_factory_get_from_handles (EmpathyTpContactFactory *tp_factory
                                              weak_object);
 }
 
+/* The callback is NOT given a reference to the EmpathyContact objects */
 static void
 get_contact_by_handle_cb (TpConnection *connection,
                          guint n_contacts,
@@ -1075,18 +1179,37 @@ get_contact_by_handle_cb (TpConnection *connection,
 {
        GetContactsData *data = user_data;
        EmpathyContact  *contact = NULL;
+       GError *err = NULL;
 
        if (n_contacts == 1) {
                contact = dup_contact_for_tp_contact (data->tp_factory,
                                                      contacts[0]);
        }
+       else {
+               if (error == NULL) {
+                       /* tp-glib will provide an error only if the whole operation failed,
+                        * but not if, for example, the handle was invalid. We create an error
+                        * so the caller of empathy_tp_contact_factory_get_from_handle can
+                        * rely on the error to check if the operation succeeded or not. */
+
+                       err = g_error_new_literal (TP_ERRORS, TP_ERROR_INVALID_HANDLE,
+                                                     "handle is invalid");
+               }
+               else {
+                       err = g_error_copy (error);
+               }
+       }
 
        if (data->callback.contact_cb) {
                data->callback.contact_cb (data->tp_factory,
                                           contact,
-                                          error,
+                                          err,
                                           data->user_data, weak_object);
        }
+
+       g_clear_error (&err);
+       if (contact != NULL)
+               g_object_unref (contact);
 }
 
 void
@@ -1202,7 +1325,7 @@ empathy_tp_contact_factory_set_location (EmpathyTpContactFactory *tp_factory,
 
        DEBUG ("Setting location");
 
-       emp_cli_connection_interface_location_call_set_location (TP_PROXY (priv->connection),
+       tp_cli_connection_interface_location_call_set_location (priv->connection,
                                                                 -1,
                                                                 location,
                                                                 tp_contact_factory_set_location_cb,
@@ -1287,52 +1410,96 @@ tp_contact_factory_finalize (GObject *object)
        G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->finalize (object);
 }
 
-static GObject *
-tp_contact_factory_constructor (GType                  type,
-                               guint                  n_props,
-                               GObjectConstructParam *props)
+static void
+tp_contact_factory_contact_capabilities_changed_cb (TpConnection *connection,
+                                                   GHashTable *caps,
+                                                   gpointer user_data,
+                                                   GObject *weak_object)
 {
-       GObject *tp_factory;
-       EmpathyTpContactFactoryPriv *priv;
+       EmpathyTpContactFactory *self = EMPATHY_TP_CONTACT_FACTORY (weak_object);
 
-       tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
-       priv = GET_PRIV (tp_factory);
+       update_contact_capabilities (self, caps);
+}
+
+static void
+connection_ready_cb (TpConnection *connection,
+                               const GError *error,
+                               gpointer user_data)
+{
+       EmpathyTpContactFactory *tp_factory = EMPATHY_TP_CONTACT_FACTORY (user_data);
+       EmpathyTpContactFactoryPriv *priv = GET_PRIV (tp_factory);
+
+       if (error != NULL)
+               goto out;
 
        /* 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,
+                                                                      G_OBJECT (tp_factory),
                                                                       NULL);
        tp_cli_connection_interface_avatars_connect_to_avatar_retrieved (priv->connection,
                                                                         tp_contact_factory_avatar_retrieved_cb,
                                                                         NULL, NULL,
-                                                                        tp_factory,
+                                                                        G_OBJECT (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);
 
+       if (tp_proxy_has_interface_by_id (connection,
+                               TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES)) {
+               priv->contact_caps_supported = TRUE;
+
+               tp_cli_connection_interface_contact_capabilities_connect_to_contact_capabilities_changed (
+                       priv->connection, tp_contact_factory_contact_capabilities_changed_cb,
+                       NULL, NULL, G_OBJECT (tp_factory), NULL);
+       }
+       else {
+               tp_cli_connection_interface_capabilities_connect_to_capabilities_changed (priv->connection,
+                                                                                         tp_contact_factory_capabilities_changed_cb,
+                                                                                         NULL, NULL,
+                                                                                         G_OBJECT (tp_factory),
+                                                                                         NULL);
+       }
 
-       emp_cli_connection_interface_location_connect_to_location_updated (TP_PROXY (priv->connection),
+       tp_cli_connection_interface_location_connect_to_location_updated (priv->connection,
                                                                           tp_contact_factory_location_updated_cb,
                                                                           NULL, NULL,
                                                                           G_OBJECT (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));
+                                                                         G_OBJECT (tp_factory));
+
+       if (!priv->contact_caps_supported) {
+               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));
+       }
+
+out:
+       g_object_unref (tp_factory);
+}
+
+static GObject *
+tp_contact_factory_constructor (GType                  type,
+                               guint                  n_props,
+                               GObjectConstructParam *props)
+{
+       GObject *tp_factory;
+       EmpathyTpContactFactoryPriv *priv;
+
+       tp_factory = G_OBJECT_CLASS (empathy_tp_contact_factory_parent_class)->constructor (type, n_props, props);
+       priv = GET_PRIV (tp_factory);
+
+       /* Ensure to keep the self object alive while the call_when_ready is
+        * running */
+       g_object_ref (tp_factory);
+       tp_connection_call_when_ready (priv->connection, connection_ready_cb,
+               tp_factory);
 
        return tp_factory;
 }
@@ -1432,6 +1599,7 @@ empathy_tp_contact_factory_init (EmpathyTpContactFactory *tp_factory)
        tp_factory->priv = priv;
        priv->can_request_ft = FALSE;
        priv->can_request_st = FALSE;
+       priv->contact_caps_supported = FALSE;
 }
 
 static GHashTable *factories = NULL;