]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-contact.c
Merge commit 'jtellier/video-call-button-sensitivity'
[empathy.git] / libempathy / empathy-contact.c
index 59d55fd4d162a912961d34c18b20ce0a8a341925..c82ecd46714a2580715430107dfeea1348b331b2 100644 (file)
@@ -1,27 +1,22 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /*
- * Copyright (C) 2004 Imendio AB
- * Copyright (C) 2007-2008 Collabora Ltd.
+ * Copyright (C) 2007-2009 Collabora Ltd.
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
- * Authors: Mikael Hallendal <micke@imendio.com>
- *          Martyn Russell <martyn@imendio.com>
- *          Xavier Claessens <xclaesse@gmail.com>
- *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
  */
 
 #include "config.h"
 #include <glib/gi18n-lib.h>
 
 #include <telepathy-glib/util.h>
-#include <libmissioncontrol/mc-enum-types.h>
 
 #include "empathy-contact.h"
-#include "empathy-contact-factory.h"
+#include "empathy-account-manager.h"
 #include "empathy-utils.h"
 #include "empathy-enum-types.h"
 #include "empathy-marshal.h"
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
 typedef struct {
-  EmpathyContactFactory *factory;
+  TpContact *tp_contact;
+  EmpathyAccount *account;
   gchar *id;
   gchar *name;
   EmpathyAvatar *avatar;
-  McAccount *account;
-  McPresence presence;
+  TpConnectionPresenceType presence;
   gchar *presence_message;
   guint handle;
   EmpathyCapabilities capabilities;
   gboolean is_user;
   guint hash;
-  EmpathyContactReady ready;
-  GList *ready_callbacks;
+  /* Location is composed of string keys and GValues.
+   * Example: a "city" key would have "Helsinki" as string GValue,
+   *          a "latitude" would have 65.0 as double GValue.
+   */
+  GHashTable *location;
 } EmpathyContactPriv;
 
-typedef struct {
-    EmpathyContactReady ready;
-    EmpathyContactReadyCb *callback;
-    gpointer user_data;
-    GDestroyNotify destroy;
-    GObject *weak_object;
-} ReadyCbData;
-
 static void contact_finalize (GObject *object);
 static void contact_get_property (GObject *object, guint param_id,
     GValue *value, GParamSpec *pspec);
@@ -78,16 +67,17 @@ G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
 enum
 {
   PROP_0,
+  PROP_TP_CONTACT,
+  PROP_ACCOUNT,
   PROP_ID,
   PROP_NAME,
   PROP_AVATAR,
-  PROP_ACCOUNT,
   PROP_PRESENCE,
   PROP_PRESENCE_MESSAGE,
   PROP_HANDLE,
   PROP_CAPABILITIES,
   PROP_IS_USER,
-  PROP_READY
+  PROP_LOCATION
 };
 
 enum {
@@ -97,19 +87,50 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
+static void
+tp_contact_notify_cb (TpContact *tp_contact,
+                      GParamSpec *param,
+                      GObject *contact)
+{
+  EmpathyContactPriv *priv = GET_PRIV (contact);
+
+  /* Forward property notifications */
+  if (!tp_strdiff (param->name, "alias"))
+    g_object_notify (contact, "name");
+  else if (!tp_strdiff (param->name, "presence-type")) {
+    TpConnectionPresenceType presence;
+
+    presence = empathy_contact_get_presence (EMPATHY_CONTACT (contact));
+    g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence,
+      priv->presence);
+    priv->presence = presence;
+    g_object_notify (contact, "presence");
+  }
+  else if (!tp_strdiff (param->name, "presence-message"))
+    g_object_notify (contact, "presence-message");
+  else if (!tp_strdiff (param->name, "identifier"))
+    g_object_notify (contact, "id");
+  else if (!tp_strdiff (param->name, "handle"))
+    g_object_notify (contact, "handle");
+}
+
 static void
 contact_dispose (GObject *object)
 {
   EmpathyContactPriv *priv = GET_PRIV (object);
 
+  if (priv->tp_contact)
+    {
+      g_signal_handlers_disconnect_by_func (priv->tp_contact,
+          tp_contact_notify_cb, object);
+      g_object_unref (priv->tp_contact);
+    }
+  priv->tp_contact = NULL;
+
   if (priv->account)
     g_object_unref (priv->account);
   priv->account = NULL;
 
-  if (priv->factory)
-    g_object_unref (priv->factory);
-  priv->factory = NULL;
-
   G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
 }
 
@@ -125,13 +146,29 @@ empathy_contact_class_init (EmpathyContactClass *class)
   object_class->get_property = contact_get_property;
   object_class->set_property = contact_set_property;
 
+  g_object_class_install_property (object_class,
+      PROP_TP_CONTACT,
+      g_param_spec_object ("tp-contact",
+        "TpContact",
+        "The TpContact associated with the contact",
+        TP_TYPE_CONTACT,
+        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      PROP_ACCOUNT,
+      g_param_spec_object ("account",
+        "The account",
+        "The account associated with the contact",
+        EMPATHY_TYPE_ACCOUNT,
+        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (object_class,
       PROP_ID,
       g_param_spec_string ("id",
         "Contact id",
         "String identifying contact",
         NULL,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_NAME,
@@ -139,7 +176,7 @@ empathy_contact_class_init (EmpathyContactClass *class)
         "Contact Name",
         "The name of the contact",
         NULL,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_AVATAR,
@@ -147,25 +184,17 @@ empathy_contact_class_init (EmpathyContactClass *class)
         "Avatar image",
         "The avatar image",
         EMPATHY_TYPE_AVATAR,
-        G_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class,
-      PROP_ACCOUNT,
-      g_param_spec_object ("account",
-        "Contact Account",
-        "The account associated with the contact",
-        MC_TYPE_ACCOUNT,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_PRESENCE,
       g_param_spec_uint ("presence",
         "Contact presence",
         "Presence of contact",
-        MC_PRESENCE_UNSET,
-        LAST_MC_PRESENCE,
-        MC_PRESENCE_UNSET,
-        G_PARAM_READWRITE));
+        TP_CONNECTION_PRESENCE_TYPE_UNSET,
+        NUM_TP_CONNECTION_PRESENCE_TYPES,
+        TP_CONNECTION_PRESENCE_TYPE_UNSET,
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_PRESENCE_MESSAGE,
@@ -173,7 +202,7 @@ empathy_contact_class_init (EmpathyContactClass *class)
         "Contact presence message",
         "Presence message of contact",
         NULL,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_HANDLE,
@@ -183,7 +212,7 @@ empathy_contact_class_init (EmpathyContactClass *class)
         0,
         G_MAXUINT,
         0,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_CAPABILITIES,
@@ -192,7 +221,7 @@ empathy_contact_class_init (EmpathyContactClass *class)
         "Capabilities of the contact",
         EMPATHY_TYPE_CAPABILITIES,
         EMPATHY_CAPABILITIES_UNKNOWN,
-        G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
+        G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (object_class,
       PROP_IS_USER,
@@ -200,16 +229,16 @@ empathy_contact_class_init (EmpathyContactClass *class)
         "Contact is-user",
         "Is contact the user",
         FALSE,
-        G_PARAM_READWRITE));
+        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
 
   g_object_class_install_property (object_class,
-      PROP_READY,
-      g_param_spec_flags ("ready",
-        "Contact ready flags",
-        "Flags for ready properties",
-        EMPATHY_TYPE_CONTACT_READY,
-        EMPATHY_CONTACT_READY_NONE,
-        G_PARAM_READABLE));
+      PROP_LOCATION,
+      g_param_spec_boxed ("location",
+        "Contact location",
+        "Physical location of the contact",
+        G_TYPE_HASH_TABLE,
+        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   signals[PRESENCE_CHANGED] =
     g_signal_new ("presence-changed",
@@ -217,10 +246,10 @@ empathy_contact_class_init (EmpathyContactClass *class)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL,
-                  _empathy_marshal_VOID__ENUM_ENUM,
+                  _empathy_marshal_VOID__UINT_UINT,
                   G_TYPE_NONE,
-                  2, MC_TYPE_PRESENCE,
-                  MC_TYPE_PRESENCE);
+                  2, G_TYPE_UINT,
+                  G_TYPE_UINT);
 
   g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
 }
@@ -233,16 +262,13 @@ empathy_contact_init (EmpathyContact *contact)
 
   contact->priv = priv;
 
-  /* Keep a ref to the factory to be sure it is not finalized while there is
-   * still contacts alive. */
-  priv->factory = empathy_contact_factory_dup_singleton ();
+  priv->location = NULL;
 }
 
 static void
 contact_finalize (GObject *object)
 {
   EmpathyContactPriv *priv;
-  GList *l;
 
   priv = GET_PRIV (object);
 
@@ -252,66 +278,71 @@ contact_finalize (GObject *object)
   g_free (priv->id);
   g_free (priv->presence_message);
 
-  for (l = priv->ready_callbacks; l != NULL; l = g_list_next (l))
-    {
-      ReadyCbData *d = (ReadyCbData *)l->data;
-
-      if (d->destroy != NULL)
-        d->destroy (d->user_data);
-      g_slice_free (ReadyCbData, d);
-    }
-
-  g_list_free (priv->ready_callbacks);
-  priv->ready_callbacks = NULL;
-
   if (priv->avatar)
       empathy_avatar_unref (priv->avatar);
 
+  if (priv->location != NULL)
+      g_hash_table_unref (priv->location);
+
   G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
 }
 
+static void
+set_tp_contact (EmpathyContact *contact,
+                TpContact *tp_contact)
+{
+  EmpathyContactPriv *priv = GET_PRIV (contact);
+
+  if (tp_contact == NULL)
+    return;
+
+  g_assert (priv->tp_contact == NULL);
+  priv->tp_contact = g_object_ref (tp_contact);
+  priv->presence = empathy_contact_get_presence (contact);
+
+  g_signal_connect (priv->tp_contact, "notify",
+    G_CALLBACK (tp_contact_notify_cb), contact);
+}
+
 static void
 contact_get_property (GObject *object,
                       guint param_id,
                       GValue *value,
                       GParamSpec *pspec)
 {
-  EmpathyContactPriv *priv;
-
-  priv = GET_PRIV (object);
+  EmpathyContact *contact = EMPATHY_CONTACT (object);
 
   switch (param_id)
     {
+      case PROP_TP_CONTACT:
+        g_value_set_object (value, empathy_contact_get_tp_contact (contact));
+        break;
+      case PROP_ACCOUNT:
+        g_value_set_object (value, empathy_contact_get_account (contact));
+        break;
       case PROP_ID:
-        g_value_set_string (value, priv->id);
+        g_value_set_string (value, empathy_contact_get_id (contact));
         break;
       case PROP_NAME:
-        g_value_set_string (value,
-            empathy_contact_get_name (EMPATHY_CONTACT (object)));
+        g_value_set_string (value, empathy_contact_get_name (contact));
         break;
       case PROP_AVATAR:
-        g_value_set_boxed (value, priv->avatar);
-        break;
-      case PROP_ACCOUNT:
-        g_value_set_object (value, priv->account);
+        g_value_set_boxed (value, empathy_contact_get_avatar (contact));
         break;
       case PROP_PRESENCE:
-        g_value_set_uint (value, priv->presence);
+        g_value_set_uint (value, empathy_contact_get_presence (contact));
         break;
       case PROP_PRESENCE_MESSAGE:
-        g_value_set_string (value, priv->presence_message);
+        g_value_set_string (value, empathy_contact_get_presence_message (contact));
         break;
       case PROP_HANDLE:
-        g_value_set_uint (value, priv->handle);
+        g_value_set_uint (value, empathy_contact_get_handle (contact));
         break;
       case PROP_CAPABILITIES:
-        g_value_set_flags (value, priv->capabilities);
+        g_value_set_flags (value, empathy_contact_get_capabilities (contact));
         break;
       case PROP_IS_USER:
-        g_value_set_boolean (value, priv->is_user);
-        break;
-      case PROP_READY:
-        g_value_set_flags (value, priv->ready);
+        g_value_set_boolean (value, empathy_contact_is_user (contact));
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -325,47 +356,41 @@ contact_set_property (GObject *object,
                       const GValue *value,
                       GParamSpec *pspec)
 {
-  EmpathyContactPriv *priv;
-
-  priv = GET_PRIV (object);
+  EmpathyContact *contact = EMPATHY_CONTACT (object);
+  EmpathyContactPriv *priv = GET_PRIV (object);
 
   switch (param_id)
     {
+      case PROP_TP_CONTACT:
+        set_tp_contact (contact, g_value_get_object (value));
+        break;
+      case PROP_ACCOUNT:
+        g_assert (priv->account == NULL);
+        priv->account = g_value_dup_object (value);
+        break;
       case PROP_ID:
-        empathy_contact_set_id (EMPATHY_CONTACT (object),
-        g_value_get_string (value));
+        empathy_contact_set_id (contact, g_value_get_string (value));
         break;
       case PROP_NAME:
-        empathy_contact_set_name (EMPATHY_CONTACT (object),
-        g_value_get_string (value));
+        empathy_contact_set_name (contact, g_value_get_string (value));
         break;
       case PROP_AVATAR:
-        empathy_contact_set_avatar (EMPATHY_CONTACT (object),
-        g_value_get_boxed (value));
-        break;
-      case PROP_ACCOUNT:
-        empathy_contact_set_account (EMPATHY_CONTACT (object),
-        MC_ACCOUNT (g_value_get_object (value)));
+        empathy_contact_set_avatar (contact, g_value_get_boxed (value));
         break;
       case PROP_PRESENCE:
-        empathy_contact_set_presence (EMPATHY_CONTACT (object),
-        g_value_get_uint (value));
+        empathy_contact_set_presence (contact, g_value_get_uint (value));
         break;
       case PROP_PRESENCE_MESSAGE:
-        empathy_contact_set_presence_message (EMPATHY_CONTACT (object),
-        g_value_get_string (value));
+        empathy_contact_set_presence_message (contact, g_value_get_string (value));
         break;
       case PROP_HANDLE:
-        empathy_contact_set_handle (EMPATHY_CONTACT (object),
-        g_value_get_uint (value));
+        empathy_contact_set_handle (contact, g_value_get_uint (value));
         break;
       case PROP_CAPABILITIES:
-        empathy_contact_set_capabilities (EMPATHY_CONTACT (object),
-        g_value_get_flags (value));
+        empathy_contact_set_capabilities (contact, g_value_get_flags (value));
         break;
       case PROP_IS_USER:
-        empathy_contact_set_is_user (EMPATHY_CONTACT (object),
-        g_value_get_boolean (value));
+        empathy_contact_set_is_user (contact, g_value_get_boolean (value));
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -373,107 +398,42 @@ contact_set_property (GObject *object,
     };
 }
 
-static gboolean
-contact_is_ready (EmpathyContact *contact, EmpathyContactReady ready)
-{
-  EmpathyContactPriv *priv = GET_PRIV (contact);
-
-  /* When the name is NULL, empathy_contact_get_name() fallback to the id.
-   * When the caller want to wait the name to be ready, it also want to wait
-   * the id to be ready in case of fallback. */
-  if ((ready & EMPATHY_CONTACT_READY_NAME) && EMP_STR_EMPTY (priv->name))
-      ready |= EMPATHY_CONTACT_READY_ID;
-
-  return (priv->ready & ready) == ready;
-}
-
-static void
-contact_weak_object_notify (gpointer data, GObject *old_object)
-{
-  EmpathyContact *contact = EMPATHY_CONTACT (data);
-  EmpathyContactPriv *priv = GET_PRIV (contact);
-
-  GList *l, *ln;
-
-  for (l = priv->ready_callbacks ; l != NULL ; l = ln )
-    {
-      ReadyCbData *d = (ReadyCbData *)l->data;
-      ln = g_list_next (l);
-
-      if (d->weak_object == old_object)
-        {
-          if (d->destroy != NULL)
-            d->destroy (d->user_data);
-
-          priv->ready_callbacks = g_list_delete_link (priv->ready_callbacks,
-            l);
-
-          g_slice_free (ReadyCbData, d);
-        }
-    }
-}
-
-static void
-contact_call_ready_callback (EmpathyContact *contact, const GError *error,
-  ReadyCbData *data)
-{
-  data->callback (contact, error, data->user_data, data->weak_object);
-  if (data->destroy != NULL)
-    data->destroy (data->user_data);
-
-  if (data->weak_object)
-    g_object_weak_unref (data->weak_object,
-      contact_weak_object_notify, contact);
-}
-
-
-static void
-contact_set_ready_flag (EmpathyContact *contact,
-                        EmpathyContactReady flag)
+EmpathyContact *
+empathy_contact_new (TpContact *tp_contact)
 {
-  EmpathyContactPriv *priv = GET_PRIV (contact);
-
-  if (!(priv->ready & flag))
-    {
-      GList *l, *ln;
+  g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
 
-      priv->ready |= flag;
-      g_object_notify (G_OBJECT (contact), "ready");
-
-      for (l = priv->ready_callbacks ; l != NULL ; l = ln )
-        {
-          ReadyCbData *d = (ReadyCbData *)l->data;
-          ln = g_list_next (l);
-
-          if (contact_is_ready (contact, d->ready))
-            {
-              contact_call_ready_callback (contact, NULL, d);
-              priv->ready_callbacks = g_list_delete_link
-                (priv->ready_callbacks, l);
-              g_slice_free (ReadyCbData, d);
-            }
-        }
-    }
+  return g_object_new (EMPATHY_TYPE_CONTACT,
+      "tp-contact", tp_contact,
+      NULL);
 }
 
 EmpathyContact *
-empathy_contact_new (McAccount *account)
+empathy_contact_new_for_log (EmpathyAccount *account,
+                             const gchar *id,
+                             const gchar *name,
+                             gboolean is_user)
 {
+  g_return_val_if_fail (id != NULL, NULL);
+
   return g_object_new (EMPATHY_TYPE_CONTACT,
       "account", account,
+      "id", id,
+      "name", name,
+      "is-user", is_user,
       NULL);
 }
 
-EmpathyContact *
-empathy_contact_new_full (McAccount  *account,
-                          const gchar *id,
-                          const gchar *name)
+TpContact *
+empathy_contact_get_tp_contact (EmpathyContact *contact)
 {
-  return g_object_new (EMPATHY_TYPE_CONTACT,
-      "account", account,
-       "name", name,
-       "id", id,
-       NULL);
+  EmpathyContactPriv *priv;
+
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+  priv = GET_PRIV (contact);
+
+  return priv->tp_contact;
 }
 
 const gchar *
@@ -485,6 +445,9 @@ empathy_contact_get_id (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_identifier (priv->tp_contact);
+
   return priv->id;
 }
 
@@ -511,7 +474,6 @@ empathy_contact_set_id (EmpathyContact *contact,
       if (EMP_STR_EMPTY (priv->name))
           g_object_notify (G_OBJECT (contact), "name");
     }
-  contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_ID);
 
   g_object_unref (contact);
 }
@@ -525,6 +487,9 @@ empathy_contact_get_name (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_alias (priv->tp_contact);
+
   if (EMP_STR_EMPTY (priv->name))
       return empathy_contact_get_id (contact);
 
@@ -548,7 +513,6 @@ empathy_contact_set_name (EmpathyContact *contact,
       priv->name = g_strdup (name);
       g_object_notify (G_OBJECT (contact), "name");
     }
-  contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_NAME);
   g_object_unref (contact);
 }
 
@@ -589,7 +553,7 @@ empathy_contact_set_avatar (EmpathyContact *contact,
   g_object_notify (G_OBJECT (contact), "avatar");
 }
 
-McAccount *
+EmpathyAccount *
 empathy_contact_get_account (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
@@ -598,48 +562,59 @@ empathy_contact_get_account (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
+  if (priv->account == NULL && priv->tp_contact != NULL)
+    {
+      EmpathyAccountManager *manager;
+      TpConnection *connection;
+
+      /* FIXME: This assume the account manager already exists */
+      manager = empathy_account_manager_dup_singleton ();
+      connection = tp_contact_get_connection (priv->tp_contact);
+      priv->account = empathy_account_manager_get_account (manager, connection);
+      g_object_ref (priv->account);
+      g_object_unref (manager);
+    }
+
   return priv->account;
 }
 
-void
-empathy_contact_set_account (EmpathyContact *contact,
-                             McAccount *account)
+TpConnection *
+empathy_contact_get_connection (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
 
-  g_return_if_fail (EMPATHY_IS_CONTACT (contact));
-  g_return_if_fail (MC_IS_ACCOUNT (account));
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
 
   priv = GET_PRIV (contact);
 
-  if (account == priv->account)
-    return;
-
-  if (priv->account)
-      g_object_unref (priv->account);
-  priv->account = g_object_ref (account);
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_connection (priv->tp_contact);
 
-  g_object_notify (G_OBJECT (contact), "account");
+  return NULL;
 }
 
-McPresence
+TpConnectionPresenceType
 empathy_contact_get_presence (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
 
-  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), MC_PRESENCE_UNSET);
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
+    TP_CONNECTION_PRESENCE_TYPE_UNSET);
 
   priv = GET_PRIV (contact);
 
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_presence_type (priv->tp_contact);
+
   return priv->presence;
 }
 
 void
 empathy_contact_set_presence (EmpathyContact *contact,
-                              McPresence presence)
+                              TpConnectionPresenceType presence)
 {
   EmpathyContactPriv *priv;
-  McPresence old_presence;
+  TpConnectionPresenceType old_presence;
 
   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
 
@@ -665,6 +640,9 @@ empathy_contact_get_presence_message (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_presence_message (priv->tp_contact);
+
   return priv->presence_message;
 }
 
@@ -694,6 +672,9 @@ empathy_contact_get_handle (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
+  if (priv->tp_contact != NULL)
+    return tp_contact_get_handle (priv->tp_contact);
+
   return priv->handle;
 }
 
@@ -713,7 +694,6 @@ empathy_contact_set_handle (EmpathyContact *contact,
       priv->handle = handle;
       g_object_notify (G_OBJECT (contact), "handle");
     }
-  contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
   g_object_unref (contact);
 }
 
@@ -780,28 +760,32 @@ empathy_contact_set_is_user (EmpathyContact *contact,
 gboolean
 empathy_contact_is_online (EmpathyContact *contact)
 {
-  EmpathyContactPriv *priv;
-
   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
 
-  priv = GET_PRIV (contact);
-
-  return (priv->presence > MC_PRESENCE_OFFLINE);
+  switch (empathy_contact_get_presence (contact))
+    {
+      case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
+      case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
+      case TP_CONNECTION_PRESENCE_TYPE_ERROR:
+        return FALSE;
+      default:
+        return TRUE;
+    }
 }
 
 const gchar *
 empathy_contact_get_status (EmpathyContact *contact)
 {
-  EmpathyContactPriv *priv;
+  const gchar *message;
 
   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
 
-  priv = GET_PRIV (contact);
-
-  if (priv->presence_message)
-    return priv->presence_message;
+  message = empathy_contact_get_presence_message (contact);
+  if (!EMP_STR_EMPTY (message))
+    return message;
 
-  return empathy_presence_get_default_message (priv->presence);
+  return empathy_presence_get_default_message (
+      empathy_contact_get_presence (contact));
 }
 
 gboolean
@@ -818,7 +802,7 @@ empathy_contact_can_voip (EmpathyContact *contact)
 }
 
 gboolean
-empathy_contact_can_send_files (EmpathyContact *contact)
+empathy_contact_can_voip_audio (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
 
@@ -826,11 +810,11 @@ empathy_contact_can_send_files (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
-  return priv->capabilities & EMPATHY_CAPABILITIES_FT;
+  return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
 }
 
-EmpathyContactReady
-empathy_contact_get_ready (EmpathyContact *contact)
+gboolean
+empathy_contact_can_voip_video (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
 
@@ -838,118 +822,55 @@ empathy_contact_get_ready (EmpathyContact *contact)
 
   priv = GET_PRIV (contact);
 
-  return priv->ready;
+  return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
 }
 
 gboolean
-empathy_contact_equal (gconstpointer v1,
-                       gconstpointer v2)
-{
-  McAccount *account_a;
-  McAccount *account_b;
-  const gchar *id_a;
-  const gchar *id_b;
-
-  g_return_val_if_fail (EMPATHY_IS_CONTACT (v1), FALSE);
-  g_return_val_if_fail (EMPATHY_IS_CONTACT (v2), FALSE);
-
-  account_a = empathy_contact_get_account (EMPATHY_CONTACT (v1));
-  account_b = empathy_contact_get_account (EMPATHY_CONTACT (v2));
-
-  id_a = empathy_contact_get_id (EMPATHY_CONTACT (v1));
-  id_b = empathy_contact_get_id (EMPATHY_CONTACT (v2));
-
-  return empathy_account_equal (account_a, account_b) &&
-      !tp_strdiff (id_a, id_b);
-}
-
-guint
-empathy_contact_hash (gconstpointer key)
+empathy_contact_can_send_files (EmpathyContact *contact)
 {
   EmpathyContactPriv *priv;
 
-  g_return_val_if_fail (EMPATHY_IS_CONTACT (key), +1);
-
-  priv = GET_PRIV (EMPATHY_CONTACT (key));
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
 
-  if (priv->hash == 0)
-    {
-      priv->hash = empathy_account_hash (priv->account) ^
-          g_str_hash (priv->id);
-    }
+  priv = GET_PRIV (contact);
 
-  return priv->hash;
+  return priv->capabilities & EMPATHY_CAPABILITIES_FT;
 }
 
-void empathy_contact_call_when_ready (EmpathyContact *contact,
-  EmpathyContactReady ready, EmpathyContactReadyCb *callback,
-  gpointer user_data, GDestroyNotify destroy, GObject *weak_object)
+gboolean
+empathy_contact_can_use_stream_tube (EmpathyContact *contact)
 {
-  EmpathyContactPriv *priv = GET_PRIV (contact);
-
-  g_return_if_fail (contact != NULL);
-  g_return_if_fail (callback != NULL);
-
-  if (contact_is_ready (contact, ready))
-    {
-      callback (contact, NULL, user_data, weak_object);
-      if (destroy != NULL)
-        destroy (user_data);
-    }
-  else
-    {
-      ReadyCbData *d = g_slice_new0 (ReadyCbData);
-      d->ready = ready;
-      d->callback = callback;
-      d->user_data = user_data;
-      d->destroy = destroy;
-      d->weak_object = weak_object;
-
-      if (weak_object != NULL)
-        g_object_weak_ref (weak_object, contact_weak_object_notify, contact);
+  EmpathyContactPriv *priv;
 
-      priv->ready_callbacks = g_list_prepend (priv->ready_callbacks, d);
-    }
-}
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
 
-static gboolean
-contact_is_ready_func (GObject *contact,
-                       gpointer user_data)
-{
-  return contact_is_ready (EMPATHY_CONTACT (contact),
-    GPOINTER_TO_UINT (user_data));
-}
+  priv = GET_PRIV (contact);
 
-void
-empathy_contact_run_until_ready (EmpathyContact *contact,
-                                 EmpathyContactReady ready,
-                                 GMainLoop **loop)
-{
-  empathy_run_until_ready_full (contact, "notify::ready",
-      contact_is_ready_func, GUINT_TO_POINTER (ready),
-      loop);
+  return priv->capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE;
 }
 
 static gchar *
 contact_get_avatar_filename (EmpathyContact *contact,
                              const gchar *token)
 {
-  EmpathyContactPriv *priv = GET_PRIV (contact);
+  EmpathyAccount *account;
   gchar *avatar_path;
   gchar *avatar_file;
   gchar *token_escaped;
   gchar *contact_escaped;
 
-  if (EMP_STR_EMPTY (priv->id))
+  if (EMP_STR_EMPTY (empathy_contact_get_id (contact)))
     return NULL;
 
-  contact_escaped = tp_escape_as_identifier (priv->id);
+  contact_escaped = tp_escape_as_identifier (empathy_contact_get_id (contact));
   token_escaped = tp_escape_as_identifier (token);
+  account = empathy_contact_get_account (contact);
 
+  /* FIXME: Do not use the account, but proto/cm instead */
   avatar_path = g_build_filename (g_get_user_cache_dir (),
       PACKAGE_NAME,
       "avatars",
-      mc_account_get_unique_name (priv->account),
+      empathy_account_get_unique_name (account),
       contact_escaped,
       NULL);
   g_mkdir_with_parents (avatar_path, 0700);
@@ -965,7 +886,7 @@ contact_get_avatar_filename (EmpathyContact *contact,
 
 void
 empathy_contact_load_avatar_data (EmpathyContact *contact,
-                                  const guchar  *data,
+                                  const guchar *data,
                                   const gsize len,
                                   const gchar *format,
                                   const gchar *token)
@@ -981,13 +902,13 @@ empathy_contact_load_avatar_data (EmpathyContact *contact,
   g_return_if_fail (!EMP_STR_EMPTY (token));
 
   /* Load and set the avatar */
+  filename = contact_get_avatar_filename (contact, token);
   avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
-      g_strdup (token));
+      g_strdup (token), filename);
   empathy_contact_set_avatar (contact, avatar);
   empathy_avatar_unref (avatar);
 
   /* Save to cache if not yet in it */
-  filename = contact_get_avatar_filename (contact, token);
   if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
     {
       if (!empathy_avatar_save_to_file (avatar, filename, &error))
@@ -999,7 +920,6 @@ empathy_contact_load_avatar_data (EmpathyContact *contact,
       else
           DEBUG ("Avatar saved to %s", filename);
     }
-  g_free (filename);
 }
 
 gboolean
@@ -1030,13 +950,11 @@ empathy_contact_load_avatar_cache (EmpathyContact *contact,
   if (data)
     {
       DEBUG ("Avatar loaded from %s", filename);
-      avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
+      avatar = empathy_avatar_new (data, len, NULL, g_strdup (token), filename);
       empathy_contact_set_avatar (contact, avatar);
       empathy_avatar_unref (avatar);
     }
 
-  g_free (filename);
-
   return data != NULL;
 }
 
@@ -1055,11 +973,25 @@ empathy_avatar_get_type (void)
   return type_id;
 }
 
+/**
+ * empathy_avatar_new:
+ * @data: the avatar data
+ * @len: the size of avatar data
+ * @format: the mime type of the avatar image
+ * @token: the token of the avatar
+ * @filename: the filename where the avatar is stored in cache
+ *
+ * Create a #EmpathyAvatar from the provided data. This function takes the
+ * ownership of @data, @format, @token and @filename.
+ *
+ * Returns: a new #EmpathyAvatar
+ */
 EmpathyAvatar *
 empathy_avatar_new (guchar *data,
                     gsize len,
                     gchar *format,
-                    gchar *token)
+                    gchar *token,
+                    gchar *filename)
 {
   EmpathyAvatar *avatar;
 
@@ -1068,6 +1000,7 @@ empathy_avatar_new (guchar *data,
   avatar->len = len;
   avatar->format = format;
   avatar->token = token;
+  avatar->filename = filename;
   avatar->refcount = 1;
 
   return avatar;
@@ -1106,7 +1039,7 @@ empathy_avatar_ref (EmpathyAvatar *avatar)
  *
  * Save the avatar to a file named filename
  *
- * Returns: %TRUE on success, %FALSE if an error occurred 
+ * Returns: %TRUE on success, %FALSE if an error occurred
  */
 gboolean
 empathy_avatar_save_to_file (EmpathyAvatar *self,
@@ -1116,3 +1049,96 @@ empathy_avatar_save_to_file (EmpathyAvatar *self,
   return g_file_set_contents (filename, self->data, self->len, error);
 }
 
+/**
+ * empathy_contact_get_location:
+ * @contact: an #EmpathyContact
+ *
+ * Returns the user's location if available.  The keys are defined in
+ * empathy-location.h. If the contact doesn't have location
+ * information, the GHashTable will be empthy. Use #g_hash_table_unref when
+ * you are done with the #GHashTable.
+ *
+ * It is composed of string keys and GValues.  Keys are
+ * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
+ * Example: a "city" key would have "Helsinki" as string GValue,
+ *          a "latitude" would have 65.0 as double GValue.
+ *
+ * Returns: a #GHashTable of location values
+ */
+GHashTable *
+empathy_contact_get_location (EmpathyContact *contact)
+{
+  EmpathyContactPriv *priv;
+
+  g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
+
+  priv = GET_PRIV (contact);
+
+  return priv->location;
+}
+
+/**
+ * empathy_contact_set_location:
+ * @contact: an #EmpathyContact
+ * @location: a #GHashTable of the location
+ *
+ * Sets the user's location based on the location #GHashTable passed.
+ * It is composed of string keys and GValues.  Keys are
+ * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
+ * Example: a "city" key would have "Helsinki" as string GValue,
+ *          a "latitude" would have 65.0 as double GValue.
+ */
+void
+empathy_contact_set_location (EmpathyContact *contact,
+                              GHashTable *location)
+{
+  EmpathyContactPriv *priv;
+
+  g_return_if_fail (EMPATHY_CONTACT (contact));
+  g_return_if_fail (location != NULL);
+
+  priv = GET_PRIV (contact);
+
+  if (priv->location != NULL)
+    g_hash_table_unref (priv->location);
+
+  priv->location = g_hash_table_ref (location);
+  g_object_notify (G_OBJECT (contact), "location");
+}
+
+/**
+ * empathy_contact_equal:
+ * @contact1: an #EmpathyContact
+ * @contact2: an #EmpathyContact
+ *
+ * Returns FALSE if one of the contacts is NULL but the other is not.
+ * Otherwise returns TRUE if both pointer are equal or if they bith
+ * refer to the same id.
+ * It's only necessary to call this function if your contact objects
+ * come from logs where contacts are created dynamically and comparing
+ * pointers is not enough.
+ */
+gboolean
+empathy_contact_equal (gconstpointer contact1,
+                       gconstpointer contact2)
+{
+  EmpathyContact *c1;
+  EmpathyContact *c2;
+  const gchar *id1;
+  const gchar *id2;
+
+  if ((contact1 == NULL) != (contact2 == NULL)) {
+    return FALSE;
+  }
+  if (contact1 == contact2) {
+    return TRUE;
+  }
+  c1 = EMPATHY_CONTACT (contact1);
+  c2 = EMPATHY_CONTACT (contact2);
+  id1 = empathy_contact_get_id (c1);
+  id2 = empathy_contact_get_id (c2);
+  if (!tp_strdiff (id1, id2)) {
+    return TRUE;
+  }
+  return FALSE;
+}