]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
Re-enable setting of groups for newly-added contacts
[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 (FOLKS_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       if (FOLKS_IS_GROUPS (persona))
828         folks_groups_set_groups (FOLKS_GROUPS (persona), priv->groups);
829
830       g_hash_table_destroy (priv->groups);
831       priv->groups = NULL;
832     }
833 }
834
835 TpConnection *
836 empathy_contact_get_connection (EmpathyContact *contact)
837 {
838   EmpathyContactPriv *priv;
839
840   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
841
842   priv = GET_PRIV (contact);
843
844   if (priv->tp_contact != NULL)
845     return tp_contact_get_connection (priv->tp_contact);
846
847   return NULL;
848 }
849
850 TpConnectionPresenceType
851 empathy_contact_get_presence (EmpathyContact *contact)
852 {
853   EmpathyContactPriv *priv;
854
855   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
856     TP_CONNECTION_PRESENCE_TYPE_UNSET);
857
858   priv = GET_PRIV (contact);
859
860   if (priv->tp_contact != NULL)
861     return tp_contact_get_presence_type (priv->tp_contact);
862
863   return priv->presence;
864 }
865
866 void
867 empathy_contact_set_presence (EmpathyContact *contact,
868                               TpConnectionPresenceType presence)
869 {
870   EmpathyContactPriv *priv;
871   TpConnectionPresenceType old_presence;
872
873   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
874
875   priv = GET_PRIV (contact);
876
877   if (presence == priv->presence)
878     return;
879
880   old_presence = priv->presence;
881   priv->presence = presence;
882
883   g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
884
885   g_object_notify (G_OBJECT (contact), "presence");
886 }
887
888 const gchar *
889 empathy_contact_get_presence_message (EmpathyContact *contact)
890 {
891   EmpathyContactPriv *priv;
892
893   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
894
895   priv = GET_PRIV (contact);
896
897   if (priv->tp_contact != NULL)
898     return tp_contact_get_presence_message (priv->tp_contact);
899
900   return priv->presence_message;
901 }
902
903 void
904 empathy_contact_set_presence_message (EmpathyContact *contact,
905                                       const gchar *message)
906 {
907   EmpathyContactPriv *priv = GET_PRIV (contact);
908
909   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
910
911   if (!tp_strdiff (message, priv->presence_message))
912     return;
913
914   g_free (priv->presence_message);
915   priv->presence_message = g_strdup (message);
916
917   g_object_notify (G_OBJECT (contact), "presence-message");
918 }
919
920 guint
921 empathy_contact_get_handle (EmpathyContact *contact)
922 {
923   EmpathyContactPriv *priv;
924
925   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
926
927   priv = GET_PRIV (contact);
928
929   if (priv->tp_contact != NULL)
930     return tp_contact_get_handle (priv->tp_contact);
931
932   return priv->handle;
933 }
934
935 void
936 empathy_contact_set_handle (EmpathyContact *contact,
937                             guint handle)
938 {
939   EmpathyContactPriv *priv;
940
941   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
942
943   priv = GET_PRIV (contact);
944
945   g_object_ref (contact);
946   if (handle != priv->handle)
947     {
948       priv->handle = handle;
949       g_object_notify (G_OBJECT (contact), "handle");
950     }
951   g_object_unref (contact);
952 }
953
954 EmpathyCapabilities
955 empathy_contact_get_capabilities (EmpathyContact *contact)
956 {
957   EmpathyContactPriv *priv;
958
959   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
960
961   priv = GET_PRIV (contact);
962
963   return priv->capabilities;
964 }
965
966 void
967 empathy_contact_set_capabilities (EmpathyContact *contact,
968                                   EmpathyCapabilities capabilities)
969 {
970   EmpathyContactPriv *priv;
971
972   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
973
974   priv = GET_PRIV (contact);
975
976   if (priv->capabilities == capabilities)
977     return;
978
979   priv->capabilities = capabilities;
980
981   g_object_notify (G_OBJECT (contact), "capabilities");
982 }
983
984 gboolean
985 empathy_contact_is_user (EmpathyContact *contact)
986 {
987   EmpathyContactPriv *priv;
988
989   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
990
991   priv = GET_PRIV (contact);
992
993   return priv->is_user;
994 }
995
996 void
997 empathy_contact_set_is_user (EmpathyContact *contact,
998                              gboolean is_user)
999 {
1000   EmpathyContactPriv *priv;
1001
1002   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1003
1004   priv = GET_PRIV (contact);
1005
1006   if (priv->is_user == is_user)
1007     return;
1008
1009   priv->is_user = is_user;
1010
1011   g_object_notify (G_OBJECT (contact), "is-user");
1012 }
1013
1014 gboolean
1015 empathy_contact_is_online (EmpathyContact *contact)
1016 {
1017   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1018
1019   switch (empathy_contact_get_presence (contact))
1020     {
1021       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
1022       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
1023       case TP_CONNECTION_PRESENCE_TYPE_ERROR:
1024         return FALSE;
1025       default:
1026         return TRUE;
1027     }
1028 }
1029
1030 const gchar *
1031 empathy_contact_get_status (EmpathyContact *contact)
1032 {
1033   const gchar *message;
1034
1035   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
1036
1037   message = empathy_contact_get_presence_message (contact);
1038   if (!EMP_STR_EMPTY (message))
1039     return message;
1040
1041   return empathy_presence_get_default_message (
1042       empathy_contact_get_presence (contact));
1043 }
1044
1045 gboolean
1046 empathy_contact_can_voip (EmpathyContact *contact)
1047 {
1048   EmpathyContactPriv *priv;
1049
1050   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1051
1052   priv = GET_PRIV (contact);
1053
1054   return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
1055       EMPATHY_CAPABILITIES_VIDEO);
1056 }
1057
1058 gboolean
1059 empathy_contact_can_voip_audio (EmpathyContact *contact)
1060 {
1061   EmpathyContactPriv *priv;
1062
1063   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1064
1065   priv = GET_PRIV (contact);
1066
1067   return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
1068 }
1069
1070 gboolean
1071 empathy_contact_can_voip_video (EmpathyContact *contact)
1072 {
1073   EmpathyContactPriv *priv;
1074
1075   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1076
1077   priv = GET_PRIV (contact);
1078
1079   return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
1080 }
1081
1082 gboolean
1083 empathy_contact_can_send_files (EmpathyContact *contact)
1084 {
1085   EmpathyContactPriv *priv;
1086
1087   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1088
1089   priv = GET_PRIV (contact);
1090
1091   return priv->capabilities & EMPATHY_CAPABILITIES_FT;
1092 }
1093
1094 gboolean
1095 empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact)
1096 {
1097   EmpathyContactPriv *priv;
1098
1099   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1100
1101   priv = GET_PRIV (contact);
1102
1103   return priv->capabilities & EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1104 }
1105
1106 static gchar *
1107 contact_get_avatar_filename (EmpathyContact *contact,
1108                              const gchar *token)
1109 {
1110   TpAccount *account;
1111   gchar *avatar_path;
1112   gchar *avatar_file;
1113   gchar *token_escaped;
1114
1115   if (EMP_STR_EMPTY (empathy_contact_get_id (contact)))
1116     return NULL;
1117
1118   token_escaped = tp_escape_as_identifier (token);
1119   account = empathy_contact_get_account (contact);
1120
1121   avatar_path = g_build_filename (g_get_user_cache_dir (),
1122       "telepathy",
1123       "avatars",
1124       tp_account_get_connection_manager (account),
1125       tp_account_get_protocol (account),
1126       NULL);
1127   g_mkdir_with_parents (avatar_path, 0700);
1128
1129   avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
1130
1131   g_free (token_escaped);
1132   g_free (avatar_path);
1133
1134   return avatar_file;
1135 }
1136
1137 gboolean
1138 empathy_contact_load_avatar_cache (EmpathyContact *contact,
1139                                    const gchar *token)
1140 {
1141   EmpathyAvatar *avatar = NULL;
1142   gchar *filename;
1143   gchar *data = NULL;
1144   gsize len;
1145   GError *error = NULL;
1146
1147   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1148   g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
1149
1150   /* Load the avatar from file if it exists */
1151   filename = contact_get_avatar_filename (contact, token);
1152   if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1153     {
1154       if (!g_file_get_contents (filename, &data, &len, &error))
1155         {
1156           DEBUG ("Failed to load avatar from cache: %s",
1157             error ? error->message : "No error given");
1158           g_clear_error (&error);
1159         }
1160     }
1161
1162   if (data)
1163     {
1164       DEBUG ("Avatar loaded from %s", filename);
1165       avatar = empathy_avatar_new ((guchar *) data, len, NULL, g_strdup (token),
1166           filename);
1167       empathy_contact_set_avatar (contact, avatar);
1168       empathy_avatar_unref (avatar);
1169     }
1170   else
1171     {
1172       g_free (filename);
1173     }
1174
1175   return data != NULL;
1176 }
1177
1178 GType
1179 empathy_avatar_get_type (void)
1180 {
1181   static GType type_id = 0;
1182
1183   if (!type_id)
1184     {
1185       type_id = g_boxed_type_register_static ("EmpathyAvatar",
1186           (GBoxedCopyFunc) empathy_avatar_ref,
1187           (GBoxedFreeFunc) empathy_avatar_unref);
1188     }
1189
1190   return type_id;
1191 }
1192
1193 /**
1194  * empathy_avatar_new:
1195  * @data: the avatar data
1196  * @len: the size of avatar data
1197  * @format: the mime type of the avatar image
1198  * @token: the token of the avatar
1199  * @filename: the filename where the avatar is stored in cache
1200  *
1201  * Create a #EmpathyAvatar from the provided data. This function takes the
1202  * ownership of @data, @format, @token and @filename.
1203  *
1204  * Returns: a new #EmpathyAvatar
1205  */
1206 EmpathyAvatar *
1207 empathy_avatar_new (guchar *data,
1208                     gsize len,
1209                     gchar *format,
1210                     gchar *token,
1211                     gchar *filename)
1212 {
1213   EmpathyAvatar *avatar;
1214
1215   avatar = g_slice_new0 (EmpathyAvatar);
1216   avatar->data = data;
1217   avatar->len = len;
1218   avatar->format = format;
1219   avatar->token = token;
1220   avatar->filename = filename;
1221   avatar->refcount = 1;
1222
1223   return avatar;
1224 }
1225
1226 void
1227 empathy_avatar_unref (EmpathyAvatar *avatar)
1228 {
1229   g_return_if_fail (avatar != NULL);
1230
1231   avatar->refcount--;
1232   if (avatar->refcount == 0)
1233     {
1234       g_free (avatar->data);
1235       g_free (avatar->format);
1236       g_free (avatar->token);
1237       g_free (avatar->filename);
1238       g_slice_free (EmpathyAvatar, avatar);
1239     }
1240 }
1241
1242 EmpathyAvatar *
1243 empathy_avatar_ref (EmpathyAvatar *avatar)
1244 {
1245   g_return_val_if_fail (avatar != NULL, NULL);
1246
1247   avatar->refcount++;
1248
1249   return avatar;
1250 }
1251
1252 /**
1253  * empathy_avatar_save_to_file:
1254  * @avatar: the avatar
1255  * @filename: name of a file to write avatar to
1256  * @error: return location for a GError, or NULL
1257  *
1258  * Save the avatar to a file named filename
1259  *
1260  * Returns: %TRUE on success, %FALSE if an error occurred
1261  */
1262 gboolean
1263 empathy_avatar_save_to_file (EmpathyAvatar *self,
1264                              const gchar *filename,
1265                              GError **error)
1266 {
1267   return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1268       error);
1269 }
1270
1271 /**
1272  * empathy_contact_get_location:
1273  * @contact: an #EmpathyContact
1274  *
1275  * Returns the user's location if available.  The keys are defined in
1276  * empathy-location.h. If the contact doesn't have location
1277  * information, the GHashTable will be empthy. Use #g_hash_table_unref when
1278  * you are done with the #GHashTable.
1279  *
1280  * It is composed of string keys and GValues.  Keys are
1281  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1282  * Example: a "city" key would have "Helsinki" as string GValue,
1283  *          a "latitude" would have 65.0 as double GValue.
1284  *
1285  * Returns: a #GHashTable of location values
1286  */
1287 GHashTable *
1288 empathy_contact_get_location (EmpathyContact *contact)
1289 {
1290   EmpathyContactPriv *priv;
1291
1292   g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
1293
1294   priv = GET_PRIV (contact);
1295
1296   return priv->location;
1297 }
1298
1299 /**
1300  * empathy_contact_set_location:
1301  * @contact: an #EmpathyContact
1302  * @location: a #GHashTable of the location
1303  *
1304  * Sets the user's location based on the location #GHashTable passed.
1305  * It is composed of string keys and GValues.  Keys are
1306  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1307  * Example: a "city" key would have "Helsinki" as string GValue,
1308  *          a "latitude" would have 65.0 as double GValue.
1309  */
1310 static void
1311 empathy_contact_set_location (EmpathyContact *contact,
1312     GHashTable *location)
1313 {
1314   EmpathyContactPriv *priv;
1315
1316   g_return_if_fail (EMPATHY_CONTACT (contact));
1317   g_return_if_fail (location != NULL);
1318
1319   priv = GET_PRIV (contact);
1320
1321   if (priv->location != NULL)
1322     g_hash_table_unref (priv->location);
1323
1324   priv->location = g_hash_table_ref (location);
1325 #if HAVE_GEOCLUE
1326   update_geocode (contact);
1327 #endif
1328   g_object_notify (G_OBJECT (contact), "location");
1329 }
1330
1331 /**
1332  * empathy_contact_equal:
1333  * @contact1: an #EmpathyContact
1334  * @contact2: an #EmpathyContact
1335  *
1336  * Returns FALSE if one of the contacts is NULL but the other is not.
1337  * Otherwise returns TRUE if both pointer are equal or if they bith
1338  * refer to the same id.
1339  * It's only necessary to call this function if your contact objects
1340  * come from logs where contacts are created dynamically and comparing
1341  * pointers is not enough.
1342  */
1343 gboolean
1344 empathy_contact_equal (gconstpointer contact1,
1345                        gconstpointer contact2)
1346 {
1347   EmpathyContact *c1;
1348   EmpathyContact *c2;
1349   const gchar *id1;
1350   const gchar *id2;
1351
1352   if ((contact1 == NULL) != (contact2 == NULL)) {
1353     return FALSE;
1354   }
1355   if (contact1 == contact2) {
1356     return TRUE;
1357   }
1358   c1 = EMPATHY_CONTACT (contact1);
1359   c2 = EMPATHY_CONTACT (contact2);
1360   id1 = empathy_contact_get_id (c1);
1361   id2 = empathy_contact_get_id (c2);
1362   if (!tp_strdiff (id1, id2)) {
1363     return TRUE;
1364   }
1365   return FALSE;
1366 }
1367
1368 #if HAVE_GEOCLUE
1369 #define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
1370 #define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
1371
1372 /* This callback is called by geoclue when it found a position
1373  * for the given address.  A position is necessary for a contact
1374  * to show up on the map
1375  */
1376 static void
1377 geocode_cb (GeoclueGeocode *geocode,
1378     GeocluePositionFields fields,
1379     double latitude,
1380     double longitude,
1381     double altitude,
1382     GeoclueAccuracy *accuracy,
1383     GError *error,
1384     gpointer contact)
1385 {
1386   EmpathyContactPriv *priv = GET_PRIV (contact);
1387   GHashTable *new_location;
1388
1389   if (priv->location == NULL)
1390     goto out;
1391
1392   if (error != NULL)
1393     {
1394       DEBUG ("Error geocoding location : %s", error->message);
1395       goto out;
1396     }
1397
1398   /* No need to change location if we didn't find the position */
1399   if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE))
1400     goto out;
1401
1402   if (!(fields & GEOCLUE_POSITION_FIELDS_LONGITUDE))
1403     goto out;
1404
1405   new_location = tp_asv_new (
1406       EMPATHY_LOCATION_LAT, G_TYPE_DOUBLE, latitude,
1407       EMPATHY_LOCATION_LON, G_TYPE_DOUBLE, longitude,
1408       NULL);
1409
1410   DEBUG ("\t - Latitude: %f", latitude);
1411   DEBUG ("\t - Longitude: %f", longitude);
1412
1413   /* Copy remaning fields. LAT and LON were not defined so we won't overwrite
1414    * the values we just set. */
1415   tp_g_hash_table_update (new_location, priv->location,
1416       (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup);
1417
1418   /* Set the altitude only if it wasn't defined before */
1419   if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE &&
1420       g_hash_table_lookup (new_location, EMPATHY_LOCATION_ALT) == NULL)
1421     {
1422       tp_asv_set_double (new_location, g_strdup (EMPATHY_LOCATION_ALT),
1423           altitude);
1424       DEBUG ("\t - Altitude: %f", altitude);
1425     }
1426
1427   /* Don't change the accuracy as we used an address to get this position */
1428   g_hash_table_unref (priv->location);
1429   priv->location = new_location;
1430   g_object_notify (contact, "location");
1431 out:
1432   g_object_unref (geocode);
1433   g_object_unref (contact);
1434 }
1435
1436 static gchar *
1437 get_dup_string (GHashTable *location,
1438     gchar *key)
1439 {
1440   GValue *value;
1441
1442   value = g_hash_table_lookup (location, key);
1443   if (value != NULL)
1444     return g_value_dup_string (value);
1445
1446   return NULL;
1447 }
1448
1449 static void
1450 update_geocode (EmpathyContact *contact)
1451 {
1452   static GeoclueGeocode *geocode;
1453   gchar *str;
1454   GHashTable *address;
1455   GHashTable *location;
1456
1457   location = empathy_contact_get_location (contact);
1458   if (location == NULL)
1459     return;
1460
1461   /* No need to search for position if contact published it */
1462   if (g_hash_table_lookup (location, EMPATHY_LOCATION_LAT) != NULL ||
1463       g_hash_table_lookup (location, EMPATHY_LOCATION_LON) != NULL)
1464     return;
1465
1466   if (geocode == NULL)
1467     {
1468       geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH);
1469       g_object_add_weak_pointer (G_OBJECT (geocode), (gpointer *) &geocode);
1470     }
1471   else
1472     {
1473       g_object_ref (geocode);
1474     }
1475
1476   address = geoclue_address_details_new ();
1477
1478   str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY_CODE);
1479   if (str != NULL)
1480     {
1481       g_hash_table_insert (address,
1482         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRYCODE), str);
1483       DEBUG ("\t - countrycode: %s", str);
1484     }
1485
1486   str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY);
1487   if (str != NULL)
1488     {
1489       g_hash_table_insert (address,
1490         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), str);
1491       DEBUG ("\t - country: %s", str);
1492     }
1493
1494   str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE);
1495   if (str != NULL)
1496     {
1497       g_hash_table_insert (address,
1498         g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), str);
1499       DEBUG ("\t - postalcode: %s", str);
1500     }
1501
1502   str = get_dup_string (location, EMPATHY_LOCATION_REGION);
1503   if (str != NULL)
1504     {
1505       g_hash_table_insert (address,
1506         g_strdup (GEOCLUE_ADDRESS_KEY_REGION), str);
1507       DEBUG ("\t - region: %s", str);
1508     }
1509
1510   str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY);
1511   if (str != NULL)
1512     {
1513       g_hash_table_insert (address,
1514         g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), str);
1515       DEBUG ("\t - locality: %s", str);
1516     }
1517
1518   str = get_dup_string (location, EMPATHY_LOCATION_STREET);
1519   if (str != NULL)
1520     {
1521       g_hash_table_insert (address,
1522         g_strdup (GEOCLUE_ADDRESS_KEY_STREET), str);
1523       DEBUG ("\t - street: %s", str);
1524     }
1525
1526   if (g_hash_table_size (address) > 0)
1527     {
1528       g_object_ref (contact);
1529
1530       geoclue_geocode_address_to_position_async (geocode, address,
1531           geocode_cb, contact);
1532     }
1533
1534   g_hash_table_unref (address);
1535 }
1536 #endif
1537
1538 static EmpathyCapabilities
1539 tp_caps_to_capabilities (TpCapabilities *caps)
1540 {
1541   EmpathyCapabilities capabilities = 0;
1542   guint i;
1543   GPtrArray *classes;
1544
1545   classes = tp_capabilities_get_channel_classes (caps);
1546
1547   for (i = 0; i < classes->len; i++)
1548     {
1549       GValueArray *class_struct;
1550       GHashTable *fixed_prop;
1551       GStrv allowed_prop;
1552       TpHandleType handle_type;
1553       const gchar *chan_type;
1554
1555       class_struct = g_ptr_array_index (classes, i);
1556       tp_value_array_unpack (class_struct, 2,
1557           &fixed_prop,
1558           &allowed_prop);
1559
1560       handle_type = tp_asv_get_uint32 (fixed_prop,
1561           TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL);
1562       if (handle_type != TP_HANDLE_TYPE_CONTACT)
1563         continue;
1564
1565       chan_type = tp_asv_get_string (fixed_prop,
1566           TP_PROP_CHANNEL_CHANNEL_TYPE);
1567
1568       if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
1569         {
1570           capabilities |= EMPATHY_CAPABILITIES_FT;
1571         }
1572       else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
1573         {
1574           const gchar *service;
1575
1576           service = tp_asv_get_string (fixed_prop,
1577               TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
1578
1579           if (!tp_strdiff (service, "rfb"))
1580             capabilities |= EMPATHY_CAPABILITIES_RFB_STREAM_TUBE;
1581         }
1582       else if (!tp_strdiff (chan_type,
1583         TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
1584         {
1585           guint j;
1586
1587           for (j = 0; allowed_prop[j] != NULL; j++)
1588             {
1589               if (!tp_strdiff (allowed_prop[j],
1590                     TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO))
1591                 capabilities |= EMPATHY_CAPABILITIES_AUDIO;
1592               else if (!tp_strdiff (allowed_prop[j],
1593                     TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO))
1594                 capabilities |= EMPATHY_CAPABILITIES_VIDEO;
1595             }
1596         }
1597     }
1598
1599   return capabilities;
1600 }
1601
1602 static void
1603 set_capabilities_from_tp_caps (EmpathyContact *self,
1604     TpCapabilities *caps)
1605 {
1606   EmpathyCapabilities capabilities;
1607
1608   if (caps == NULL)
1609     return;
1610
1611   capabilities = tp_caps_to_capabilities (caps);
1612   empathy_contact_set_capabilities (self, capabilities);
1613 }
1614
1615 static void
1616 contact_set_avatar_from_tp_contact (EmpathyContact *contact)
1617 {
1618   EmpathyContactPriv *priv = GET_PRIV (contact);
1619   const gchar *mime;
1620   const gchar *token;
1621   GFile *file;
1622
1623   token = tp_contact_get_avatar_token (priv->tp_contact);
1624   mime = tp_contact_get_avatar_mime_type (priv->tp_contact);
1625   file = tp_contact_get_avatar_file (priv->tp_contact);
1626
1627   if (file != NULL)
1628     {
1629       EmpathyAvatar *avatar;
1630       gchar *data;
1631       gsize len;
1632
1633       g_file_load_contents (file, NULL, &data, &len, NULL, NULL);
1634       avatar = empathy_avatar_new ((guchar *) data, len, g_strdup (mime), g_strdup (token),
1635           g_file_get_path (file));
1636       empathy_contact_set_avatar (contact, avatar);
1637       empathy_avatar_unref (avatar);
1638     }
1639   else
1640     {
1641       empathy_contact_set_avatar (contact, NULL);
1642     }
1643 }
1644
1645 EmpathyContact *
1646 empathy_contact_dup_from_tp_contact (TpContact *tp_contact)
1647 {
1648   EmpathyContact *contact = NULL;
1649
1650   g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
1651
1652   if (contacts_table == NULL)
1653     contacts_table = g_hash_table_new (g_direct_hash, g_direct_equal);
1654   else
1655     contact = g_hash_table_lookup (contacts_table, tp_contact);
1656
1657   if (contact == NULL)
1658     {
1659       contact = empathy_contact_new (tp_contact);
1660
1661       /* The hash table does not keep any ref.
1662        * contact keeps a ref to tp_contact, and is removed from the table in
1663        * contact_dispose() */
1664       g_hash_table_insert (contacts_table, tp_contact, contact);
1665     }
1666   else
1667     {
1668       g_object_ref (contact);
1669     }
1670
1671   return contact;
1672 }
1673