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