]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
Merge branch 'import-617971'
[empathy.git] / libempathy / empathy-contact.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3  * Copyright (C) 2007-2009 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include <glib/gi18n-lib.h>
27
28 #include <telepathy-glib/account-manager.h>
29 #include <telepathy-glib/interfaces.h>
30 #include <telepathy-glib/util.h>
31 #ifdef ENABEL_TPL
32 #include <telepathy-logger/contact.h>
33 #endif /* ENABLE_TPL */
34
35 #if HAVE_GEOCLUE
36 #include <geoclue/geoclue-geocode.h>
37 #endif
38
39 #include "empathy-contact.h"
40 #include "empathy-utils.h"
41 #include "empathy-enum-types.h"
42 #include "empathy-marshal.h"
43 #include "empathy-location.h"
44
45 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
46 #include "empathy-debug.h"
47
48 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
49 typedef struct {
50   TpContact *tp_contact;
51   TpAccount *account;
52   gchar *id;
53   gchar *name;
54   EmpathyAvatar *avatar;
55   TpConnectionPresenceType presence;
56   gchar *presence_message;
57   guint handle;
58   EmpathyCapabilities capabilities;
59   gboolean is_user;
60   guint hash;
61   /* Location is composed of string keys and GValues.
62    * Example: a "city" key would have "Helsinki" as string GValue,
63    *          a "latitude" would have 65.0 as double GValue.
64    *
65    * This is a super set of the location stored in TpContact as we can try add
66    * more fields by searching the address using geoclue.
67    */
68   GHashTable *location;
69 } EmpathyContactPriv;
70
71 static void contact_finalize (GObject *object);
72 static void contact_get_property (GObject *object, guint param_id,
73     GValue *value, GParamSpec *pspec);
74 static void contact_set_property (GObject *object, guint param_id,
75     const GValue *value, GParamSpec *pspec);
76
77 #if HAVE_GEOCLUE
78 static void update_geocode (EmpathyContact *contact);
79 #endif
80
81 static void empathy_contact_set_location (EmpathyContact *contact,
82     GHashTable *location);
83
84 static void set_capabilities_from_tp_caps (EmpathyContact *self,
85     TpCapabilities *caps);
86
87 static void contact_set_avatar_from_tp_contact (EmpathyContact *contact);
88
89 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
90
91 enum
92 {
93   PROP_0,
94   PROP_TP_CONTACT,
95   PROP_ACCOUNT,
96   PROP_ID,
97   PROP_NAME,
98   PROP_AVATAR,
99   PROP_PRESENCE,
100   PROP_PRESENCE_MESSAGE,
101   PROP_HANDLE,
102   PROP_CAPABILITIES,
103   PROP_IS_USER,
104   PROP_LOCATION
105 };
106
107 enum {
108   PRESENCE_CHANGED,
109   LAST_SIGNAL
110 };
111
112 static guint signals[LAST_SIGNAL];
113
114 /* TpContact* -> EmpathyContact*, both borrowed ref */
115 static GHashTable *contacts_table = NULL;
116
117 static void
118 tp_contact_notify_cb (TpContact *tp_contact,
119                       GParamSpec *param,
120                       GObject *contact)
121 {
122   EmpathyContactPriv *priv = GET_PRIV (contact);
123
124   /* Forward property notifications */
125   if (!tp_strdiff (param->name, "alias"))
126     g_object_notify (contact, "name");
127   else if (!tp_strdiff (param->name, "presence-type")) {
128     TpConnectionPresenceType presence;
129
130     presence = empathy_contact_get_presence (EMPATHY_CONTACT (contact));
131     g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence,
132       priv->presence);
133     priv->presence = presence;
134     g_object_notify (contact, "presence");
135   }
136   else if (!tp_strdiff (param->name, "presence-message"))
137     g_object_notify (contact, "presence-message");
138   else if (!tp_strdiff (param->name, "identifier"))
139     g_object_notify (contact, "id");
140   else if (!tp_strdiff (param->name, "handle"))
141     g_object_notify (contact, "handle");
142   else if (!tp_strdiff (param->name, "location"))
143     {
144       GHashTable *location;
145
146       location = tp_contact_get_location (tp_contact);
147       /* This will start a geoclue search to find the address if needed */
148       empathy_contact_set_location (EMPATHY_CONTACT (contact), location);
149     }
150   else if (!tp_strdiff (param->name, "capabilities"))
151     {
152       set_capabilities_from_tp_caps (EMPATHY_CONTACT (contact),
153           tp_contact_get_capabilities (tp_contact));
154     }
155   else if (!tp_strdiff (param->name, "avatar-file"))
156     {
157       contact_set_avatar_from_tp_contact (EMPATHY_CONTACT (contact));
158     }
159 }
160
161 static void
162 contact_dispose (GObject *object)
163 {
164   EmpathyContactPriv *priv = GET_PRIV (object);
165
166   if (priv->tp_contact)
167     {
168       g_hash_table_remove (contacts_table, priv->tp_contact);
169       g_signal_handlers_disconnect_by_func (priv->tp_contact,
170           tp_contact_notify_cb, object);
171       g_object_unref (priv->tp_contact);
172     }
173   priv->tp_contact = NULL;
174
175   if (priv->account)
176     g_object_unref (priv->account);
177   priv->account = NULL;
178
179   if (priv->avatar != NULL)
180     {
181       empathy_avatar_unref (priv->avatar);
182       priv->avatar = NULL;
183     }
184
185   if (priv->location != NULL)
186     {
187       g_hash_table_unref (priv->location);
188       priv->location = NULL;
189     }
190
191   G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
192 }
193
194 static void
195 empathy_contact_class_init (EmpathyContactClass *class)
196 {
197   GObjectClass *object_class;
198
199   object_class = G_OBJECT_CLASS (class);
200
201   object_class->finalize = contact_finalize;
202   object_class->dispose = contact_dispose;
203   object_class->get_property = contact_get_property;
204   object_class->set_property = contact_set_property;
205
206   g_object_class_install_property (object_class,
207       PROP_TP_CONTACT,
208       g_param_spec_object ("tp-contact",
209         "TpContact",
210         "The TpContact associated with the contact",
211         TP_TYPE_CONTACT,
212         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
213
214   g_object_class_install_property (object_class,
215       PROP_ACCOUNT,
216       g_param_spec_object ("account",
217         "The account",
218         "The account associated with the contact",
219         TP_TYPE_ACCOUNT,
220         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221
222   g_object_class_install_property (object_class,
223       PROP_ID,
224       g_param_spec_string ("id",
225         "Contact id",
226         "String identifying contact",
227         NULL,
228         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
229
230   g_object_class_install_property (object_class,
231       PROP_NAME,
232       g_param_spec_string ("name",
233         "Contact Name",
234         "The name of the contact",
235         NULL,
236         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
237
238   g_object_class_install_property (object_class,
239       PROP_AVATAR,
240       g_param_spec_boxed ("avatar",
241         "Avatar image",
242         "The avatar image",
243         EMPATHY_TYPE_AVATAR,
244         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245
246   g_object_class_install_property (object_class,
247       PROP_PRESENCE,
248       g_param_spec_uint ("presence",
249         "Contact presence",
250         "Presence of contact",
251         TP_CONNECTION_PRESENCE_TYPE_UNSET,
252         NUM_TP_CONNECTION_PRESENCE_TYPES,
253         TP_CONNECTION_PRESENCE_TYPE_UNSET,
254         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256   g_object_class_install_property (object_class,
257       PROP_PRESENCE_MESSAGE,
258       g_param_spec_string ("presence-message",
259         "Contact presence message",
260         "Presence message of contact",
261         NULL,
262         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
263
264   g_object_class_install_property (object_class,
265       PROP_HANDLE,
266       g_param_spec_uint ("handle",
267         "Contact Handle",
268         "The handle of the contact",
269         0,
270         G_MAXUINT,
271         0,
272         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273
274   g_object_class_install_property (object_class,
275       PROP_CAPABILITIES,
276       g_param_spec_flags ("capabilities",
277         "Contact Capabilities",
278         "Capabilities of the contact",
279         EMPATHY_TYPE_CAPABILITIES,
280         EMPATHY_CAPABILITIES_UNKNOWN,
281         G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282
283   g_object_class_install_property (object_class,
284       PROP_IS_USER,
285       g_param_spec_boolean ("is-user",
286         "Contact is-user",
287         "Is contact the user",
288         FALSE,
289         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290
291
292   g_object_class_install_property (object_class,
293       PROP_LOCATION,
294       g_param_spec_boxed ("location",
295         "Contact location",
296         "Physical location of the contact",
297         G_TYPE_HASH_TABLE,
298         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
299
300   signals[PRESENCE_CHANGED] =
301     g_signal_new ("presence-changed",
302                   G_TYPE_FROM_CLASS (class),
303                   G_SIGNAL_RUN_LAST,
304                   0,
305                   NULL, NULL,
306                   _empathy_marshal_VOID__UINT_UINT,
307                   G_TYPE_NONE,
308                   2, G_TYPE_UINT,
309                   G_TYPE_UINT);
310
311   g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
312 }
313
314 static void
315 empathy_contact_init (EmpathyContact *contact)
316 {
317   EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
318     EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
319
320   contact->priv = priv;
321
322   priv->location = NULL;
323 }
324
325 static void
326 contact_finalize (GObject *object)
327 {
328   EmpathyContactPriv *priv;
329
330   priv = GET_PRIV (object);
331
332   DEBUG ("finalize: %p", object);
333
334   g_free (priv->name);
335   g_free (priv->id);
336   g_free (priv->presence_message);
337
338   G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
339 }
340
341 static void
342 set_tp_contact (EmpathyContact *contact,
343                 TpContact *tp_contact)
344 {
345   EmpathyContactPriv *priv = GET_PRIV (contact);
346   GHashTable *location;
347   TpHandle self_handle;
348   TpHandle handle;
349
350   if (tp_contact == NULL)
351     return;
352
353   g_assert (priv->tp_contact == NULL);
354   priv->tp_contact = g_object_ref (tp_contact);
355   priv->presence = empathy_contact_get_presence (contact);
356
357   location = tp_contact_get_location (tp_contact);
358   if (location != NULL)
359     empathy_contact_set_location (contact, location);
360
361   set_capabilities_from_tp_caps (contact,
362       tp_contact_get_capabilities (tp_contact));
363
364   contact_set_avatar_from_tp_contact (contact);
365
366   /* Set is-user property. Note that it could still be the handle is
367    * different from the connection's self handle, in the case the handle
368    * comes from a group interface. */
369   self_handle = tp_connection_get_self_handle (
370       tp_contact_get_connection (tp_contact));
371   handle = tp_contact_get_handle (tp_contact);
372   empathy_contact_set_is_user (contact, self_handle == handle);
373
374   g_signal_connect (priv->tp_contact, "notify",
375     G_CALLBACK (tp_contact_notify_cb), contact);
376 }
377
378 static void
379 contact_get_property (GObject *object,
380                       guint param_id,
381                       GValue *value,
382                       GParamSpec *pspec)
383 {
384   EmpathyContact *contact = EMPATHY_CONTACT (object);
385
386   switch (param_id)
387     {
388       case PROP_TP_CONTACT:
389         g_value_set_object (value, empathy_contact_get_tp_contact (contact));
390         break;
391       case PROP_ACCOUNT:
392         g_value_set_object (value, empathy_contact_get_account (contact));
393         break;
394       case PROP_ID:
395         g_value_set_string (value, empathy_contact_get_id (contact));
396         break;
397       case PROP_NAME:
398         g_value_set_string (value, empathy_contact_get_name (contact));
399         break;
400       case PROP_AVATAR:
401         g_value_set_boxed (value, empathy_contact_get_avatar (contact));
402         break;
403       case PROP_PRESENCE:
404         g_value_set_uint (value, empathy_contact_get_presence (contact));
405         break;
406       case PROP_PRESENCE_MESSAGE:
407         g_value_set_string (value, empathy_contact_get_presence_message (contact));
408         break;
409       case PROP_HANDLE:
410         g_value_set_uint (value, empathy_contact_get_handle (contact));
411         break;
412       case PROP_CAPABILITIES:
413         g_value_set_flags (value, empathy_contact_get_capabilities (contact));
414         break;
415       case PROP_IS_USER:
416         g_value_set_boolean (value, empathy_contact_is_user (contact));
417         break;
418       default:
419         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
420         break;
421     };
422 }
423
424 static void
425 contact_set_property (GObject *object,
426                       guint param_id,
427                       const GValue *value,
428                       GParamSpec *pspec)
429 {
430   EmpathyContact *contact = EMPATHY_CONTACT (object);
431   EmpathyContactPriv *priv = GET_PRIV (object);
432
433   switch (param_id)
434     {
435       case PROP_TP_CONTACT:
436         set_tp_contact (contact, g_value_get_object (value));
437         break;
438       case PROP_ACCOUNT:
439         g_assert (priv->account == NULL);
440         priv->account = g_value_dup_object (value);
441         break;
442       case PROP_ID:
443         empathy_contact_set_id (contact, g_value_get_string (value));
444         break;
445       case PROP_NAME:
446         empathy_contact_set_name (contact, g_value_get_string (value));
447         break;
448       case PROP_AVATAR:
449         empathy_contact_set_avatar (contact, g_value_get_boxed (value));
450         break;
451       case PROP_PRESENCE:
452         empathy_contact_set_presence (contact, g_value_get_uint (value));
453         break;
454       case PROP_PRESENCE_MESSAGE:
455         empathy_contact_set_presence_message (contact, g_value_get_string (value));
456         break;
457       case PROP_HANDLE:
458         empathy_contact_set_handle (contact, g_value_get_uint (value));
459         break;
460       case PROP_CAPABILITIES:
461         empathy_contact_set_capabilities (contact, g_value_get_flags (value));
462         break;
463       case PROP_IS_USER:
464         empathy_contact_set_is_user (contact, g_value_get_boolean (value));
465         break;
466       default:
467         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
468         break;
469     };
470 }
471
472 EmpathyContact *
473 empathy_contact_new (TpContact *tp_contact)
474 {
475   g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
476
477   return g_object_new (EMPATHY_TYPE_CONTACT,
478       "tp-contact", tp_contact,
479       NULL);
480 }
481
482 #ifdef ENABLE_TPL
483 EmpathyContact *
484 empathy_contact_from_tpl_contact (TpAccount *account,
485     TplContact *tpl_contact)
486 {
487   EmpathyContact *retval;
488   gboolean is_user;
489
490   g_return_val_if_fail (TPL_IS_CONTACT (tpl_contact), NULL);
491
492   is_user = (TPL_CONTACT_USER == tpl_contact_get_contact_type (tpl_contact));
493
494   retval = g_object_new (EMPATHY_TYPE_CONTACT,
495       "id", tpl_contact_get_alias (tpl_contact),
496       "name", tpl_contact_get_identifier (tpl_contact),
497       "account", account,
498       "is-user", is_user,
499       NULL);
500
501   if (!EMP_STR_EMPTY (tpl_contact_get_avatar_token (tpl_contact)))
502     empathy_contact_load_avatar_cache (retval,
503         tpl_contact_get_avatar_token (tpl_contact));
504
505   return retval;
506 }
507 #endif /* ENABLE_TPL */
508
509 EmpathyContact *
510 empathy_contact_new_for_log (TpAccount *account,
511                              const gchar *id,
512                              const gchar *name,
513                              gboolean is_user)
514 {
515   g_return_val_if_fail (id != NULL, NULL);
516   g_assert (account != NULL);
517
518   return g_object_new (EMPATHY_TYPE_CONTACT,
519       "account", account,
520       "id", id,
521       "name", name,
522       "is-user", is_user,
523       NULL);
524 }
525
526 TpContact *
527 empathy_contact_get_tp_contact (EmpathyContact *contact)
528 {
529   EmpathyContactPriv *priv;
530
531   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
532
533   priv = GET_PRIV (contact);
534
535   return priv->tp_contact;
536 }
537
538 const gchar *
539 empathy_contact_get_id (EmpathyContact *contact)
540 {
541   EmpathyContactPriv *priv;
542
543   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
544
545   priv = GET_PRIV (contact);
546
547   if (priv->tp_contact != NULL)
548     return tp_contact_get_identifier (priv->tp_contact);
549
550   return priv->id;
551 }
552
553 void
554 empathy_contact_set_id (EmpathyContact *contact,
555                         const gchar *id)
556 {
557   EmpathyContactPriv *priv;
558
559   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
560   g_return_if_fail (id != NULL);
561
562   priv = GET_PRIV (contact);
563
564   /* We temporally ref the contact because it could be destroyed
565    * during the signal emition */
566   g_object_ref (contact);
567   if (tp_strdiff (id, priv->id))
568     {
569       g_free (priv->id);
570       priv->id = g_strdup (id);
571
572       g_object_notify (G_OBJECT (contact), "id");
573       if (EMP_STR_EMPTY (priv->name))
574           g_object_notify (G_OBJECT (contact), "name");
575     }
576
577   g_object_unref (contact);
578 }
579
580 const gchar *
581 empathy_contact_get_name (EmpathyContact *contact)
582 {
583   EmpathyContactPriv *priv;
584   const gchar        *alias;
585
586   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
587
588   priv = GET_PRIV (contact);
589
590   if (priv->tp_contact != NULL)
591     alias = tp_contact_get_alias (priv->tp_contact);
592   else
593     alias = priv->name;
594
595   if (!EMP_STR_EMPTY (alias))
596     return alias;
597   else
598     return empathy_contact_get_id (contact);
599 }
600
601 void
602 empathy_contact_set_name (EmpathyContact *contact,
603                           const gchar *name)
604 {
605   EmpathyContactPriv *priv;
606
607   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
608
609   priv = GET_PRIV (contact);
610
611   g_object_ref (contact);
612   if (tp_strdiff (name, priv->name))
613     {
614       g_free (priv->name);
615       priv->name = g_strdup (name);
616       g_object_notify (G_OBJECT (contact), "name");
617     }
618   g_object_unref (contact);
619 }
620
621 static void
622 contact_set_aliases_cb (TpConnection *connection,
623     const GError *error,
624     gpointer user_data,
625     GObject *weak_object)
626 {
627         if (error)
628                 DEBUG ("Error: %s", error->message);
629 }
630
631 void
632 empathy_contact_set_alias (EmpathyContact *contact,
633     const gchar *alias)
634 {
635         TpConnection *connection;
636         GHashTable *new_alias;
637         guint handle;
638
639         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
640
641         handle = empathy_contact_get_handle (contact);
642
643         DEBUG ("Setting alias for contact %s (%d) to %s",
644                 empathy_contact_get_id (contact),
645                 handle, alias);
646
647         new_alias = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
648             g_free);
649
650         g_hash_table_insert (new_alias, GUINT_TO_POINTER (handle), g_strdup (alias));
651
652         connection = empathy_contact_get_connection (contact);
653         tp_cli_connection_interface_aliasing_call_set_aliases (connection, -1,
654             new_alias, contact_set_aliases_cb, NULL, NULL, NULL);
655
656         g_hash_table_destroy (new_alias);
657 }
658
659 EmpathyAvatar *
660 empathy_contact_get_avatar (EmpathyContact *contact)
661 {
662   EmpathyContactPriv *priv;
663
664   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
665
666   priv = GET_PRIV (contact);
667
668   return priv->avatar;
669 }
670
671 void
672 empathy_contact_set_avatar (EmpathyContact *contact,
673                             EmpathyAvatar *avatar)
674 {
675   EmpathyContactPriv *priv;
676
677   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
678
679   priv = GET_PRIV (contact);
680
681   if (priv->avatar == avatar)
682     return;
683
684   if (priv->avatar)
685     {
686       empathy_avatar_unref (priv->avatar);
687       priv->avatar = NULL;
688     }
689
690   if (avatar)
691       priv->avatar = empathy_avatar_ref (avatar);
692
693   g_object_notify (G_OBJECT (contact), "avatar");
694 }
695
696 TpAccount *
697 empathy_contact_get_account (EmpathyContact *contact)
698 {
699   EmpathyContactPriv *priv;
700
701   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
702
703   priv = GET_PRIV (contact);
704
705   if (priv->account == NULL && priv->tp_contact != NULL)
706     {
707       TpConnection *connection;
708
709       /* FIXME: This assume the account manager already exists */
710       connection = tp_contact_get_connection (priv->tp_contact);
711       priv->account =
712         g_object_ref (empathy_get_account_for_connection (connection));
713     }
714
715   return priv->account;
716 }
717
718 TpConnection *
719 empathy_contact_get_connection (EmpathyContact *contact)
720 {
721   EmpathyContactPriv *priv;
722
723   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
724
725   priv = GET_PRIV (contact);
726
727   if (priv->tp_contact != NULL)
728     return tp_contact_get_connection (priv->tp_contact);
729
730   return NULL;
731 }
732
733 TpConnectionPresenceType
734 empathy_contact_get_presence (EmpathyContact *contact)
735 {
736   EmpathyContactPriv *priv;
737
738   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
739     TP_CONNECTION_PRESENCE_TYPE_UNSET);
740
741   priv = GET_PRIV (contact);
742
743   if (priv->tp_contact != NULL)
744     return tp_contact_get_presence_type (priv->tp_contact);
745
746   return priv->presence;
747 }
748
749 void
750 empathy_contact_set_presence (EmpathyContact *contact,
751                               TpConnectionPresenceType presence)
752 {
753   EmpathyContactPriv *priv;
754   TpConnectionPresenceType old_presence;
755
756   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
757
758   priv = GET_PRIV (contact);
759
760   if (presence == priv->presence)
761     return;
762
763   old_presence = priv->presence;
764   priv->presence = presence;
765
766   g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
767
768   g_object_notify (G_OBJECT (contact), "presence");
769 }
770
771 const gchar *
772 empathy_contact_get_presence_message (EmpathyContact *contact)
773 {
774   EmpathyContactPriv *priv;
775
776   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
777
778   priv = GET_PRIV (contact);
779
780   if (priv->tp_contact != NULL)
781     return tp_contact_get_presence_message (priv->tp_contact);
782
783   return priv->presence_message;
784 }
785
786 void
787 empathy_contact_set_presence_message (EmpathyContact *contact,
788                                       const gchar *message)
789 {
790   EmpathyContactPriv *priv = GET_PRIV (contact);
791
792   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
793
794   if (!tp_strdiff (message, priv->presence_message))
795     return;
796
797   g_free (priv->presence_message);
798   priv->presence_message = g_strdup (message);
799
800   g_object_notify (G_OBJECT (contact), "presence-message");
801 }
802
803 guint
804 empathy_contact_get_handle (EmpathyContact *contact)
805 {
806   EmpathyContactPriv *priv;
807
808   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
809
810   priv = GET_PRIV (contact);
811
812   if (priv->tp_contact != NULL)
813     return tp_contact_get_handle (priv->tp_contact);
814
815   return priv->handle;
816 }
817
818 void
819 empathy_contact_set_handle (EmpathyContact *contact,
820                             guint handle)
821 {
822   EmpathyContactPriv *priv;
823
824   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
825
826   priv = GET_PRIV (contact);
827
828   g_object_ref (contact);
829   if (handle != priv->handle)
830     {
831       priv->handle = handle;
832       g_object_notify (G_OBJECT (contact), "handle");
833     }
834   g_object_unref (contact);
835 }
836
837 EmpathyCapabilities
838 empathy_contact_get_capabilities (EmpathyContact *contact)
839 {
840   EmpathyContactPriv *priv;
841
842   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
843
844   priv = GET_PRIV (contact);
845
846   return priv->capabilities;
847 }
848
849 void
850 empathy_contact_set_capabilities (EmpathyContact *contact,
851                                   EmpathyCapabilities capabilities)
852 {
853   EmpathyContactPriv *priv;
854
855   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
856
857   priv = GET_PRIV (contact);
858
859   if (priv->capabilities == capabilities)
860     return;
861
862   priv->capabilities = capabilities;
863
864   g_object_notify (G_OBJECT (contact), "capabilities");
865 }
866
867 gboolean
868 empathy_contact_is_user (EmpathyContact *contact)
869 {
870   EmpathyContactPriv *priv;
871
872   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
873
874   priv = GET_PRIV (contact);
875
876   return priv->is_user;
877 }
878
879 void
880 empathy_contact_set_is_user (EmpathyContact *contact,
881                              gboolean is_user)
882 {
883   EmpathyContactPriv *priv;
884
885   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
886
887   priv = GET_PRIV (contact);
888
889   if (priv->is_user == is_user)
890     return;
891
892   priv->is_user = is_user;
893
894   g_object_notify (G_OBJECT (contact), "is-user");
895 }
896
897 gboolean
898 empathy_contact_is_online (EmpathyContact *contact)
899 {
900   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
901
902   switch (empathy_contact_get_presence (contact))
903     {
904       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
905       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
906       case TP_CONNECTION_PRESENCE_TYPE_ERROR:
907         return FALSE;
908       default:
909         return TRUE;
910     }
911 }
912
913 const gchar *
914 empathy_contact_get_status (EmpathyContact *contact)
915 {
916   const gchar *message;
917
918   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
919
920   message = empathy_contact_get_presence_message (contact);
921   if (!EMP_STR_EMPTY (message))
922     return message;
923
924   return empathy_presence_get_default_message (
925       empathy_contact_get_presence (contact));
926 }
927
928 gboolean
929 empathy_contact_can_voip (EmpathyContact *contact)
930 {
931   EmpathyContactPriv *priv;
932
933   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
934
935   priv = GET_PRIV (contact);
936
937   return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
938       EMPATHY_CAPABILITIES_VIDEO);
939 }
940
941 gboolean
942 empathy_contact_can_voip_audio (EmpathyContact *contact)
943 {
944   EmpathyContactPriv *priv;
945
946   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
947
948   priv = GET_PRIV (contact);
949
950   return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
951 }
952
953 gboolean
954 empathy_contact_can_voip_video (EmpathyContact *contact)
955 {
956   EmpathyContactPriv *priv;
957
958   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
959
960   priv = GET_PRIV (contact);
961
962   return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
963 }
964
965 gboolean
966 empathy_contact_can_send_files (EmpathyContact *contact)
967 {
968   EmpathyContactPriv *priv;
969
970   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
971
972   priv = GET_PRIV (contact);
973
974   return priv->capabilities & EMPATHY_CAPABILITIES_FT;
975 }
976
977 gboolean
978 empathy_contact_can_use_stream_tube (EmpathyContact *contact)
979 {
980   EmpathyContactPriv *priv;
981
982   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
983
984   priv = GET_PRIV (contact);
985
986   return priv->capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE;
987 }
988
989 static gchar *
990 contact_get_avatar_filename (EmpathyContact *contact,
991                              const gchar *token)
992 {
993   TpAccount *account;
994   gchar *avatar_path;
995   gchar *avatar_file;
996   gchar *token_escaped;
997
998   if (EMP_STR_EMPTY (empathy_contact_get_id (contact)))
999     return NULL;
1000
1001   token_escaped = tp_escape_as_identifier (token);
1002   account = empathy_contact_get_account (contact);
1003
1004   avatar_path = g_build_filename (g_get_user_cache_dir (),
1005       "telepathy",
1006       "avatars",
1007       tp_account_get_connection_manager (account),
1008       tp_account_get_protocol (account),
1009       NULL);
1010   g_mkdir_with_parents (avatar_path, 0700);
1011
1012   avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
1013
1014   g_free (token_escaped);
1015   g_free (avatar_path);
1016
1017   return avatar_file;
1018 }
1019
1020 gboolean
1021 empathy_contact_load_avatar_cache (EmpathyContact *contact,
1022                                    const gchar *token)
1023 {
1024   EmpathyAvatar *avatar = NULL;
1025   gchar *filename;
1026   gchar *data = NULL;
1027   gsize len;
1028   GError *error = NULL;
1029
1030   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1031   g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
1032
1033   /* Load the avatar from file if it exists */
1034   filename = contact_get_avatar_filename (contact, token);
1035   if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1036     {
1037       if (!g_file_get_contents (filename, &data, &len, &error))
1038         {
1039           DEBUG ("Failed to load avatar from cache: %s",
1040             error ? error->message : "No error given");
1041           g_clear_error (&error);
1042         }
1043     }
1044
1045   if (data)
1046     {
1047       DEBUG ("Avatar loaded from %s", filename);
1048       avatar = empathy_avatar_new ((guchar *) data, len, NULL, g_strdup (token),
1049           filename);
1050       empathy_contact_set_avatar (contact, avatar);
1051       empathy_avatar_unref (avatar);
1052     }
1053   else
1054     {
1055       g_free (filename);
1056     }
1057
1058   return data != NULL;
1059 }
1060
1061 GType
1062 empathy_avatar_get_type (void)
1063 {
1064   static GType type_id = 0;
1065
1066   if (!type_id)
1067     {
1068       type_id = g_boxed_type_register_static ("EmpathyAvatar",
1069           (GBoxedCopyFunc) empathy_avatar_ref,
1070           (GBoxedFreeFunc) empathy_avatar_unref);
1071     }
1072
1073   return type_id;
1074 }
1075
1076 /**
1077  * empathy_avatar_new:
1078  * @data: the avatar data
1079  * @len: the size of avatar data
1080  * @format: the mime type of the avatar image
1081  * @token: the token of the avatar
1082  * @filename: the filename where the avatar is stored in cache
1083  *
1084  * Create a #EmpathyAvatar from the provided data. This function takes the
1085  * ownership of @data, @format, @token and @filename.
1086  *
1087  * Returns: a new #EmpathyAvatar
1088  */
1089 EmpathyAvatar *
1090 empathy_avatar_new (guchar *data,
1091                     gsize len,
1092                     gchar *format,
1093                     gchar *token,
1094                     gchar *filename)
1095 {
1096   EmpathyAvatar *avatar;
1097
1098   avatar = g_slice_new0 (EmpathyAvatar);
1099   avatar->data = data;
1100   avatar->len = len;
1101   avatar->format = format;
1102   avatar->token = token;
1103   avatar->filename = filename;
1104   avatar->refcount = 1;
1105
1106   return avatar;
1107 }
1108
1109 void
1110 empathy_avatar_unref (EmpathyAvatar *avatar)
1111 {
1112   g_return_if_fail (avatar != NULL);
1113
1114   avatar->refcount--;
1115   if (avatar->refcount == 0)
1116     {
1117       g_free (avatar->data);
1118       g_free (avatar->format);
1119       g_free (avatar->token);
1120       g_slice_free (EmpathyAvatar, avatar);
1121     }
1122 }
1123
1124 EmpathyAvatar *
1125 empathy_avatar_ref (EmpathyAvatar *avatar)
1126 {
1127   g_return_val_if_fail (avatar != NULL, NULL);
1128
1129   avatar->refcount++;
1130
1131   return avatar;
1132 }
1133
1134 /**
1135  * empathy_avatar_save_to_file:
1136  * @avatar: the avatar
1137  * @filename: name of a file to write avatar to
1138  * @error: return location for a GError, or NULL
1139  *
1140  * Save the avatar to a file named filename
1141  *
1142  * Returns: %TRUE on success, %FALSE if an error occurred
1143  */
1144 gboolean
1145 empathy_avatar_save_to_file (EmpathyAvatar *self,
1146                              const gchar *filename,
1147                              GError **error)
1148 {
1149   return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1150       error);
1151 }
1152
1153 /**
1154  * empathy_contact_get_location:
1155  * @contact: an #EmpathyContact
1156  *
1157  * Returns the user's location if available.  The keys are defined in
1158  * empathy-location.h. If the contact doesn't have location
1159  * information, the GHashTable will be empthy. Use #g_hash_table_unref when
1160  * you are done with the #GHashTable.
1161  *
1162  * It is composed of string keys and GValues.  Keys are
1163  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1164  * Example: a "city" key would have "Helsinki" as string GValue,
1165  *          a "latitude" would have 65.0 as double GValue.
1166  *
1167  * Returns: a #GHashTable of location values
1168  */
1169 GHashTable *
1170 empathy_contact_get_location (EmpathyContact *contact)
1171 {
1172   EmpathyContactPriv *priv;
1173
1174   g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
1175
1176   priv = GET_PRIV (contact);
1177
1178   return priv->location;
1179 }
1180
1181 /**
1182  * empathy_contact_set_location:
1183  * @contact: an #EmpathyContact
1184  * @location: a #GHashTable of the location
1185  *
1186  * Sets the user's location based on the location #GHashTable passed.
1187  * It is composed of string keys and GValues.  Keys are
1188  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1189  * Example: a "city" key would have "Helsinki" as string GValue,
1190  *          a "latitude" would have 65.0 as double GValue.
1191  */
1192 static void
1193 empathy_contact_set_location (EmpathyContact *contact,
1194     GHashTable *location)
1195 {
1196   EmpathyContactPriv *priv;
1197
1198   g_return_if_fail (EMPATHY_CONTACT (contact));
1199   g_return_if_fail (location != NULL);
1200
1201   priv = GET_PRIV (contact);
1202
1203   if (priv->location != NULL)
1204     g_hash_table_unref (priv->location);
1205
1206   priv->location = g_hash_table_ref (location);
1207 #if HAVE_GEOCLUE
1208   update_geocode (contact);
1209 #endif
1210   g_object_notify (G_OBJECT (contact), "location");
1211 }
1212
1213 /**
1214  * empathy_contact_equal:
1215  * @contact1: an #EmpathyContact
1216  * @contact2: an #EmpathyContact
1217  *
1218  * Returns FALSE if one of the contacts is NULL but the other is not.
1219  * Otherwise returns TRUE if both pointer are equal or if they bith
1220  * refer to the same id.
1221  * It's only necessary to call this function if your contact objects
1222  * come from logs where contacts are created dynamically and comparing
1223  * pointers is not enough.
1224  */
1225 gboolean
1226 empathy_contact_equal (gconstpointer contact1,
1227                        gconstpointer contact2)
1228 {
1229   EmpathyContact *c1;
1230   EmpathyContact *c2;
1231   const gchar *id1;
1232   const gchar *id2;
1233
1234   if ((contact1 == NULL) != (contact2 == NULL)) {
1235     return FALSE;
1236   }
1237   if (contact1 == contact2) {
1238     return TRUE;
1239   }
1240   c1 = EMPATHY_CONTACT (contact1);
1241   c2 = EMPATHY_CONTACT (contact2);
1242   id1 = empathy_contact_get_id (c1);
1243   id2 = empathy_contact_get_id (c2);
1244   if (!tp_strdiff (id1, id2)) {
1245     return TRUE;
1246   }
1247   return FALSE;
1248 }
1249
1250 #if HAVE_GEOCLUE
1251 #define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
1252 #define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
1253
1254 /* This callback is called by geoclue when it found a position
1255  * for the given address.  A position is necessary for a contact
1256  * to show up on the map
1257  */
1258 static void
1259 geocode_cb (GeoclueGeocode *geocode,
1260     GeocluePositionFields fields,
1261     double latitude,
1262     double longitude,
1263     double altitude,
1264     GeoclueAccuracy *accuracy,
1265     GError *error,
1266     gpointer contact)
1267 {
1268   EmpathyContactPriv *priv = GET_PRIV (contact);
1269   GHashTable *new_location;
1270
1271   if (priv->location == NULL)
1272     goto out;
1273
1274   if (error != NULL)
1275     {
1276       DEBUG ("Error geocoding location : %s", error->message);
1277       goto out;
1278     }
1279
1280   /* No need to change location if we didn't find the position */
1281   if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE))
1282     goto out;
1283
1284   if (!(fields & GEOCLUE_POSITION_FIELDS_LONGITUDE))
1285     goto out;
1286
1287   new_location = tp_asv_new (
1288       EMPATHY_LOCATION_LAT, G_TYPE_DOUBLE, latitude,
1289       EMPATHY_LOCATION_LON, G_TYPE_DOUBLE, longitude,
1290       NULL);
1291
1292   DEBUG ("\t - Latitude: %f", latitude);
1293   DEBUG ("\t - Longitude: %f", longitude);
1294
1295   /* Copy remaning fields. LAT and LON were not defined so we won't overwrite
1296    * the values we just set. */
1297   tp_g_hash_table_update (new_location, priv->location,
1298       (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup);
1299
1300   /* Set the altitude only if it wasn't defined before */
1301   if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE &&
1302       g_hash_table_lookup (new_location, EMPATHY_LOCATION_ALT) == NULL)
1303     {
1304       tp_asv_set_double (new_location, g_strdup (EMPATHY_LOCATION_ALT),
1305           altitude);
1306       DEBUG ("\t - Altitude: %f", altitude);
1307     }
1308
1309   /* Don't change the accuracy as we used an address to get this position */
1310   g_hash_table_unref (priv->location);
1311   priv->location = new_location;
1312   g_object_notify (contact, "location");
1313 out:
1314   g_object_unref (geocode);
1315   g_object_unref (contact);
1316 }
1317
1318 static gchar *
1319 get_dup_string (GHashTable *location,
1320     gchar *key)
1321 {
1322   GValue *value;
1323
1324   value = g_hash_table_lookup (location, key);
1325   if (value != NULL)
1326     return g_value_dup_string (value);
1327
1328   return NULL;
1329 }
1330
1331 static void
1332 update_geocode (EmpathyContact *contact)
1333 {
1334   static GeoclueGeocode *geocode;
1335   gchar *str;
1336   GHashTable *address;
1337   GHashTable *location;
1338
1339   location = empathy_contact_get_location (contact);
1340   if (location == NULL)
1341     return;
1342
1343   /* No need to search for position if contact published it */
1344   if (g_hash_table_lookup (location, EMPATHY_LOCATION_LAT) != NULL ||
1345       g_hash_table_lookup (location, EMPATHY_LOCATION_LON) != NULL)
1346     return;
1347
1348   if (geocode == NULL)
1349     {
1350       geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH);
1351       g_object_add_weak_pointer (G_OBJECT (geocode), (gpointer *) &geocode);
1352     }
1353   else
1354     {
1355       g_object_ref (geocode);
1356     }
1357
1358   address = geoclue_address_details_new ();
1359
1360   str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY_CODE);
1361   if (str != NULL)
1362     {
1363       g_hash_table_insert (address,
1364         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRYCODE), str);
1365       DEBUG ("\t - countrycode: %s", str);
1366     }
1367
1368   str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY);
1369   if (str != NULL)
1370     {
1371       g_hash_table_insert (address,
1372         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), str);
1373       DEBUG ("\t - country: %s", str);
1374     }
1375
1376   str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE);
1377   if (str != NULL)
1378     {
1379       g_hash_table_insert (address,
1380         g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), str);
1381       DEBUG ("\t - postalcode: %s", str);
1382     }
1383
1384   str = get_dup_string (location, EMPATHY_LOCATION_REGION);
1385   if (str != NULL)
1386     {
1387       g_hash_table_insert (address,
1388         g_strdup (GEOCLUE_ADDRESS_KEY_REGION), str);
1389       DEBUG ("\t - region: %s", str);
1390     }
1391
1392   str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY);
1393   if (str != NULL)
1394     {
1395       g_hash_table_insert (address,
1396         g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), str);
1397       DEBUG ("\t - locality: %s", str);
1398     }
1399
1400   str = get_dup_string (location, EMPATHY_LOCATION_STREET);
1401   if (str != NULL)
1402     {
1403       g_hash_table_insert (address,
1404         g_strdup (GEOCLUE_ADDRESS_KEY_STREET), str);
1405       DEBUG ("\t - street: %s", str);
1406     }
1407
1408   if (g_hash_table_size (address) > 0)
1409     {
1410       g_object_ref (contact);
1411
1412       geoclue_geocode_address_to_position_async (geocode, address,
1413           geocode_cb, contact);
1414     }
1415
1416   g_hash_table_unref (address);
1417 }
1418 #endif
1419
1420 static EmpathyCapabilities
1421 tp_caps_to_capabilities (TpCapabilities *caps)
1422 {
1423   EmpathyCapabilities capabilities = 0;
1424   guint i;
1425   GPtrArray *classes;
1426
1427   classes = tp_capabilities_get_channel_classes (caps);
1428
1429   for (i = 0; i < classes->len; i++)
1430     {
1431       GValueArray *class_struct;
1432       GHashTable *fixed_prop;
1433       GStrv allowed_prop;
1434       TpHandleType handle_type;
1435       const gchar *chan_type;
1436
1437       class_struct = g_ptr_array_index (classes, i);
1438       tp_value_array_unpack (class_struct, 2,
1439           &fixed_prop,
1440           &allowed_prop);
1441
1442       handle_type = tp_asv_get_uint32 (fixed_prop,
1443           TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL);
1444       if (handle_type != TP_HANDLE_TYPE_CONTACT)
1445         continue;
1446
1447       chan_type = tp_asv_get_string (fixed_prop,
1448           TP_PROP_CHANNEL_CHANNEL_TYPE);
1449
1450       if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
1451         {
1452           capabilities |= EMPATHY_CAPABILITIES_FT;
1453         }
1454       else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
1455         {
1456           capabilities |= EMPATHY_CAPABILITIES_STREAM_TUBE;
1457         }
1458       else if (!tp_strdiff (chan_type,
1459         TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
1460         {
1461           guint j;
1462
1463           for (j = 0; allowed_prop[j] != NULL; j++)
1464             {
1465               if (!tp_strdiff (allowed_prop[j],
1466                     TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO))
1467                 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1468               else if (!tp_strdiff (allowed_prop[j],
1469                     TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO))
1470                 capabilities |= EMPATHY_CAPABILITIES_VIDEO;
1471             }
1472         }
1473     }
1474
1475   return capabilities;
1476 }
1477
1478 static void
1479 set_capabilities_from_tp_caps (EmpathyContact *self,
1480     TpCapabilities *caps)
1481 {
1482   EmpathyCapabilities capabilities;
1483
1484   if (caps == NULL)
1485     return;
1486
1487   capabilities = tp_caps_to_capabilities (caps);
1488   empathy_contact_set_capabilities (self, capabilities);
1489 }
1490
1491 static void
1492 contact_set_avatar_from_tp_contact (EmpathyContact *contact)
1493 {
1494   EmpathyContactPriv *priv = GET_PRIV (contact);
1495   const gchar *mime;
1496   const gchar *token;
1497   GFile *file;
1498
1499   token = tp_contact_get_avatar_token (priv->tp_contact);
1500   mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
1501   file = tp_contact_get_avatar_file (priv->tp_contact);
1502
1503   if (file != NULL)
1504     {
1505       EmpathyAvatar *avatar;
1506       gchar *data;
1507       gsize len;
1508
1509       g_file_load_contents (file, NULL, &data, &len, NULL, NULL);
1510       avatar = empathy_avatar_new ((guchar *) data, len, g_strdup (mime), g_strdup (token),
1511           g_file_get_path (file));
1512       empathy_contact_set_avatar (contact, avatar);
1513       empathy_avatar_unref (avatar);
1514     }
1515   else
1516     {
1517       empathy_contact_set_avatar (contact, NULL);
1518     }
1519 }
1520
1521 EmpathyContact *
1522 empathy_contact_dup_from_tp_contact (TpContact *tp_contact)
1523 {
1524   EmpathyContact *contact = NULL;
1525
1526   g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
1527
1528   if (contacts_table == NULL)
1529     contacts_table = g_hash_table_new (g_direct_hash, g_direct_equal);
1530   else
1531     contact = g_hash_table_lookup (contacts_table, tp_contact);
1532
1533   if (contact == NULL)
1534     {
1535       contact = empathy_contact_new (tp_contact);
1536
1537       /* The hash table does not keep any ref.
1538        * contact keeps a ref to tp_contact, and is removed from the table in
1539        * contact_dispose() */
1540       g_hash_table_insert (contacts_table, tp_contact, contact);
1541     }
1542   else
1543     {
1544       g_object_ref (contact);
1545     }
1546
1547   return contact;
1548 }
1549