]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-contact.c
Merge remote-tracking branch 'glassrose/add-All-service-selection-in-debug-window'
[empathy.git] / libempathy / empathy-contact.c
index 8150e9e671db0b35574183236679030dff6156dc..c947fad6295d10e85006c93ef0c64363317bdfe0 100644 (file)
 #endif
 
 #include "empathy-contact.h"
+#include "empathy-camera-monitor.h"
 #include "empathy-individual-manager.h"
 #include "empathy-utils.h"
 #include "empathy-enum-types.h"
-#include "empathy-marshal.h"
 #include "empathy-location.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
@@ -56,6 +56,7 @@ typedef struct {
   FolksPersona *persona;
   gchar *id;
   gchar *alias;
+  gchar *logged_alias;
   EmpathyAvatar *avatar;
   TpConnectionPresenceType presence;
   guint handle;
@@ -72,8 +73,6 @@ typedef struct {
   GHashTable *location;
   GeeHashSet *groups;
   gchar **client_types;
-
-  gboolean keep_tpcontact;
 } EmpathyContactPriv;
 
 static void contact_finalize (GObject *object);
@@ -111,6 +110,7 @@ enum
   PROP_PERSONA,
   PROP_ID,
   PROP_ALIAS,
+  PROP_LOGGED_ALIAS,
   PROP_AVATAR,
   PROP_PRESENCE,
   PROP_PRESENCE_MESSAGE,
@@ -194,9 +194,6 @@ contact_dispose (GObject *object)
 
   if (priv->tp_contact != NULL)
     {
-      if (!priv->keep_tpcontact)
-        g_hash_table_remove (contacts_table, priv->tp_contact);
-
       g_signal_handlers_disconnect_by_func (priv->tp_contact,
           tp_contact_notify_cb, object);
     }
@@ -322,6 +319,15 @@ empathy_contact_class_init (EmpathyContactClass *class)
         NULL,
         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (object_class,
+      PROP_LOGGED_ALIAS,
+      g_param_spec_string ("logged-alias",
+        "Logged alias",
+        "The alias the user had when a message was logged, "
+        "only set when using empathy_contact_from_tpl_contact()",
+        NULL,
+        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_property (object_class,
       PROP_AVATAR,
       g_param_spec_boxed ("avatar",
@@ -398,7 +404,7 @@ empathy_contact_class_init (EmpathyContactClass *class)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL,
-                  _empathy_marshal_VOID__UINT_UINT,
+                  g_cclosure_marshal_generic,
                   G_TYPE_NONE,
                   2, G_TYPE_UINT,
                   G_TYPE_UINT);
@@ -562,6 +568,9 @@ contact_get_property (GObject *object,
       case PROP_ALIAS:
         g_value_set_string (value, empathy_contact_get_alias (contact));
         break;
+      case PROP_LOGGED_ALIAS:
+        g_value_set_string (value, empathy_contact_get_logged_alias (contact));
+        break;
       case PROP_AVATAR:
         g_value_set_boxed (value, empathy_contact_get_avatar (contact));
         break;
@@ -613,6 +622,10 @@ contact_set_property (GObject *object,
       case PROP_ALIAS:
         empathy_contact_set_alias (contact, g_value_get_string (value));
         break;
+      case PROP_LOGGED_ALIAS:
+        g_assert (priv->logged_alias == NULL);
+        priv->logged_alias = g_value_dup_string (value);
+        break;
       case PROP_PRESENCE:
         empathy_contact_set_presence (contact, g_value_get_uint (value));
         break;
@@ -634,26 +647,79 @@ contact_set_property (GObject *object,
     };
 }
 
+static void
+remove_tp_contact (gpointer data,
+    GObject *object)
+{
+  g_hash_table_remove (contacts_table, data);
+}
+
 static EmpathyContact *
 empathy_contact_new (TpContact *tp_contact)
 {
+  EmpathyContact *retval;
+
   g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
 
-  return g_object_new (EMPATHY_TYPE_CONTACT,
+  retval = g_object_new (EMPATHY_TYPE_CONTACT,
       "tp-contact", tp_contact,
       NULL);
+
+  g_object_weak_ref (G_OBJECT (retval), remove_tp_contact, tp_contact);
+
+  return retval;
 }
 
+typedef struct
+{
+  TplEntity *entity;
+  TpAccount *account;
+} FindContactData;
+
 static gboolean
 contact_is_tpl_entity (gpointer key,
     gpointer value,
     gpointer user_data)
 {
-  TpContact *contact = key;
-  TplEntity *entity = user_data;
+  EmpathyContact *contact = value;
+  FindContactData *data = user_data;
+  TpAccount *account = empathy_contact_get_account (contact);
+  const gchar *path = NULL;
 
-  return !tp_strdiff (tp_contact_get_identifier (contact),
-      tpl_entity_get_identifier (entity));
+  if (account != NULL)
+    path = tp_proxy_get_object_path (account);
+
+  return !tp_strdiff (empathy_contact_get_id (contact),
+              tpl_entity_get_identifier (data->entity)) &&
+         !tp_strdiff (tp_proxy_get_object_path (data->account), path);
+}
+
+static void
+get_contacts_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)
+{
+  EmpathyContact *self = (EmpathyContact *) weak_object;
+  EmpathyContactPriv *priv = GET_PRIV (self);
+  TpContact *tp_contact;
+
+  if (n_contacts != 1)
+    return;
+
+  tp_contact = contacts[0];
+
+  g_return_if_fail (priv->tp_contact == NULL);
+  priv->tp_contact = g_object_ref (tp_contact);
+  g_object_notify (G_OBJECT (self), "tp-contact");
+
+  /* Update capabilities now that we have a TpContact */
+  set_capabilities_from_tp_caps (self,
+      tp_contact_get_capabilities (tp_contact));
 }
 
 EmpathyContact *
@@ -667,31 +733,52 @@ empathy_contact_from_tpl_contact (TpAccount *account,
   g_return_val_if_fail (TPL_IS_ENTITY (tpl_entity), NULL);
 
   if (contacts_table != NULL)
-    existing_contact = g_hash_table_find (contacts_table,
-        contact_is_tpl_entity, tpl_entity);
+    {
+      FindContactData data;
+
+      data.entity = tpl_entity;
+      data.account = account;
+
+      existing_contact = g_hash_table_find (contacts_table,
+        contact_is_tpl_entity, &data);
+    }
 
   if (existing_contact != NULL)
     {
-      EmpathyContactPriv *priv;
-
       retval = g_object_new (EMPATHY_TYPE_CONTACT,
           "tp-contact", empathy_contact_get_tp_contact (existing_contact),
-          "alias", tpl_entity_get_alias (tpl_entity),
+          "logged-alias", tpl_entity_get_alias (tpl_entity),
           NULL);
-
-      priv = GET_PRIV (retval);
-      priv->keep_tpcontact = TRUE;
     }
   else
     {
+      TpConnection *conn;
+      const gchar *id;
+
       is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity));
 
+      id = tpl_entity_get_identifier (tpl_entity);
+
       retval = g_object_new (EMPATHY_TYPE_CONTACT,
-          "id", tpl_entity_get_identifier (tpl_entity),
+          "id", id,
           "alias", tpl_entity_get_alias (tpl_entity),
           "account", account,
           "is-user", is_user,
           NULL);
+
+      /* Try to get a TpContact associated to have at least contact
+       * capabilities if possible. This is useful for CM supporting calling
+       * offline contacts for example. */
+      conn = tp_account_get_connection (account);
+      if (conn != NULL)
+        {
+          TpContactFeature features[] = { TP_CONTACT_FEATURE_CAPABILITIES };
+          conn = tp_account_get_connection (account);
+
+          tp_connection_get_contacts_by_id (conn, 1, &id,
+              G_N_ELEMENTS (features), features, get_contacts_cb,
+              NULL, NULL, G_OBJECT (retval));
+        }
     }
 
   if (!EMP_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity)))
@@ -749,6 +836,21 @@ empathy_contact_get_alias (EmpathyContact *contact)
     return empathy_contact_get_id (contact);
 }
 
+const gchar *
+empathy_contact_get_logged_alias (EmpathyContact *contact)
+{
+  EmpathyContactPriv *priv;
+
+  g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+  priv = GET_PRIV (contact);
+
+  if (priv->logged_alias != NULL)
+    return priv->logged_alias;
+  else
+    return empathy_contact_get_alias (contact);
+}
+
 void
 empathy_contact_set_alias (EmpathyContact *contact,
                           const gchar *alias)
@@ -884,7 +986,7 @@ empathy_contact_get_account (EmpathyContact *contact)
       /* FIXME: This assume the account manager already exists */
       connection = tp_contact_get_connection (priv->tp_contact);
       priv->account =
-        g_object_ref (empathy_get_account_for_connection (connection));
+        g_object_ref (tp_connection_get_account (connection));
     }
 
   return priv->account;
@@ -901,46 +1003,14 @@ empathy_contact_get_persona (EmpathyContact *contact)
 
   if (priv->persona == NULL && priv->tp_contact != NULL)
     {
-      /* FIXME: This is disgustingly slow */
-      /* Query for the persona */
-      EmpathyIndividualManager *manager;
-      GList *individuals, *l;
-
-      manager = empathy_individual_manager_dup_singleton ();
-      individuals = empathy_individual_manager_get_members (manager);
+      TpfPersona *persona;
 
-      for (l = individuals; l != NULL; l = l->next)
+      persona = tpf_persona_dup_for_contact (priv->tp_contact);
+      if (persona != NULL)
         {
-          FolksIndividual *individual = FOLKS_INDIVIDUAL (l->data);
-          GeeSet *personas;
-          GeeIterator *iter;
-          gboolean persona_found = FALSE;
-
-          personas = folks_individual_get_personas (individual);
-          iter = gee_iterable_iterator (GEE_ITERABLE (personas));
-          while (!persona_found && gee_iterator_next (iter))
-            {
-              TpfPersona *persona = gee_iterator_get (iter);
-
-              if (empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona)))
-                {
-                  TpContact *tp_contact = tpf_persona_get_contact (persona);
-
-                  if (tp_contact == priv->tp_contact)
-                    {
-                      /* Found the right persona */
-                      empathy_contact_set_persona (contact,
-                          (FolksPersona *) persona);
-                      persona_found = TRUE;
-                    }
-                  g_clear_object (&persona);
-                }
-            }
-          g_clear_object (&iter);
+          empathy_contact_set_persona (contact, (FolksPersona *) persona);
+          g_object_unref (persona);
         }
-
-      g_list_free (individuals);
-      g_object_unref (manager);
     }
 
   return priv->persona;
@@ -1558,7 +1628,7 @@ geocode_cb (GObject *source,
   EmpathyContactPriv *priv = GET_PRIV (contact);
   GError *error = NULL;
   GHashTable *new_location;
-  GHashTable *resolved;
+  GHashTable *resolved = NULL;
   gdouble latitude, longitude;
 
   if (priv->location == NULL)
@@ -1596,7 +1666,7 @@ geocode_cb (GObject *source,
   g_object_notify ((GObject *) contact, "location");
 
 out:
-  tp_clear_pointer (&result, g_hash_table_unref);
+  tp_clear_pointer (&resolved, g_hash_table_unref);
   g_object_unref (contact);
 }
 
@@ -1607,7 +1677,8 @@ update_geocode (EmpathyContact *contact)
   GHashTable *location;
 
   location = empathy_contact_get_location (contact);
-  if (location == NULL)
+  if (location == NULL ||
+      g_hash_table_size (location) == 0)
     return;
 
   /* No need to search for position if contact published it */
@@ -1616,6 +1687,8 @@ update_geocode (EmpathyContact *contact)
     return;
 
   geocode = geocode_object_new_for_params (location);
+  if (geocode == NULL)
+    return;
 
   geocode_object_resolve_async (geocode, NULL, geocode_cb,
       g_object_ref (contact));
@@ -1753,8 +1826,17 @@ contact_set_avatar_from_tp_contact (EmpathyContact *contact)
       gchar *data;
       gsize len;
       gchar *path;
+      GError *error = NULL;
+
+      if (!g_file_load_contents (file, NULL, &data, &len, NULL, &error))
+        {
+          DEBUG ("Failed to load avatar: %s", error->message);
+
+          g_error_free (error);
+          contact_set_avatar (contact, NULL);
+          return;
+        }
 
-      g_file_load_contents (file, NULL, &data, &len, NULL, NULL);
       path = g_file_get_path (file);
 
       avatar = empathy_avatar_new ((guchar *) data, len, mime, path);
@@ -1974,6 +2056,9 @@ empathy_contact_dup_best_for_action (FolksIndividual *individual,
         goto while_finish;
 
       tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona));
+      if (tp_contact == NULL)
+        goto while_finish;
+
       contact = empathy_contact_dup_from_tp_contact (tp_contact);
       empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
 
@@ -2001,3 +2086,53 @@ while_finish:
 
   return best_contact;
 }
+
+#define declare_contact_cb(name) \
+static void \
+contact_##name##_cb (GObject *source, \
+    GAsyncResult *result, \
+    gpointer user_data) \
+{ \
+  TpContact *contact = (TpContact *) source; \
+  GError *error = NULL; \
+  \
+  if (!tp_contact_##name##_finish (contact, result, &error)) \
+    { \
+      DEBUG ("Failed to ##name## on %s\n", \
+          tp_contact_get_identifier (contact)); \
+      g_error_free (error); \
+    } \
+}
+
+declare_contact_cb (request_subscription)
+declare_contact_cb (authorize_publication)
+declare_contact_cb (unblock)
+
+void
+empathy_contact_add_to_contact_list (EmpathyContact *self,
+    const gchar *message)
+{
+  EmpathyContactPriv *priv = GET_PRIV (self);
+
+  g_return_if_fail (priv->tp_contact != NULL);
+
+  tp_contact_request_subscription_async (priv->tp_contact, message,
+      contact_request_subscription_cb, NULL);
+
+  tp_contact_authorize_publication_async (priv->tp_contact,
+      contact_authorize_publication_cb, NULL);
+
+  tp_contact_unblock_async (priv->tp_contact, contact_unblock_cb, NULL);
+}
+
+declare_contact_cb (remove)
+
+void
+empathy_contact_remove_from_contact_list (EmpathyContact *self)
+{
+  EmpathyContactPriv *priv = GET_PRIV (self);
+
+  g_return_if_fail (priv->tp_contact != NULL);
+
+  tp_contact_remove_async (priv->tp_contact, contact_remove_cb, NULL);
+}