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