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