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