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