]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
Get location update from TpContact (#599162)
[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/util.h>
30 #ifdef ENABEL_TPL
31 #include <telepathy-logger/contact.h>
32 #endif /* ENABLE_TPL */
33
34 #if HAVE_GEOCLUE
35 #include <geoclue/geoclue-geocode.h>
36 #endif
37
38 #include "empathy-contact.h"
39 #include "empathy-utils.h"
40 #include "empathy-enum-types.h"
41 #include "empathy-marshal.h"
42 #include "empathy-location.h"
43
44 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
45 #include "empathy-debug.h"
46
47 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
48 typedef struct {
49   TpContact *tp_contact;
50   TpAccount *account;
51   gchar *id;
52   gchar *name;
53   EmpathyAvatar *avatar;
54   TpConnectionPresenceType presence;
55   gchar *presence_message;
56   guint handle;
57   EmpathyCapabilities capabilities;
58   gboolean is_user;
59   guint hash;
60   /* Location is composed of string keys and GValues.
61    * Example: a "city" key would have "Helsinki" as string GValue,
62    *          a "latitude" would have 65.0 as double GValue.
63    *
64    * This is a super set of the location stored in TpContact as we can try add
65    * more fields by searching the address using geoclue.
66    */
67   GHashTable *location;
68 } EmpathyContactPriv;
69
70 static void contact_finalize (GObject *object);
71 static void contact_get_property (GObject *object, guint param_id,
72     GValue *value, GParamSpec *pspec);
73 static void contact_set_property (GObject *object, guint param_id,
74     const GValue *value, GParamSpec *pspec);
75
76 static void update_geocode (EmpathyContact *contact);
77
78 static void empathy_contact_set_location (EmpathyContact *contact,
79     GHashTable *location);
80
81 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
82
83 enum
84 {
85   PROP_0,
86   PROP_TP_CONTACT,
87   PROP_ACCOUNT,
88   PROP_ID,
89   PROP_NAME,
90   PROP_AVATAR,
91   PROP_PRESENCE,
92   PROP_PRESENCE_MESSAGE,
93   PROP_HANDLE,
94   PROP_CAPABILITIES,
95   PROP_IS_USER,
96   PROP_LOCATION
97 };
98
99 enum {
100   PRESENCE_CHANGED,
101   LAST_SIGNAL
102 };
103
104 static guint signals[LAST_SIGNAL];
105
106 static void
107 tp_contact_notify_cb (TpContact *tp_contact,
108                       GParamSpec *param,
109                       GObject *contact)
110 {
111   EmpathyContactPriv *priv = GET_PRIV (contact);
112
113   /* Forward property notifications */
114   if (!tp_strdiff (param->name, "alias"))
115     g_object_notify (contact, "name");
116   else if (!tp_strdiff (param->name, "presence-type")) {
117     TpConnectionPresenceType presence;
118
119     presence = empathy_contact_get_presence (EMPATHY_CONTACT (contact));
120     g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence,
121       priv->presence);
122     priv->presence = presence;
123     g_object_notify (contact, "presence");
124   }
125   else if (!tp_strdiff (param->name, "presence-message"))
126     g_object_notify (contact, "presence-message");
127   else if (!tp_strdiff (param->name, "identifier"))
128     g_object_notify (contact, "id");
129   else if (!tp_strdiff (param->name, "handle"))
130     g_object_notify (contact, "handle");
131   else if (!tp_strdiff (param->name, "location"))
132     {
133       GHashTable *location;
134
135       location = tp_contact_get_location (tp_contact);
136       /* This will start a geoclue search to find the address if needed */
137       empathy_contact_set_location (EMPATHY_CONTACT (contact), location);
138     }
139 }
140
141 static void
142 contact_dispose (GObject *object)
143 {
144   EmpathyContactPriv *priv = GET_PRIV (object);
145
146   if (priv->tp_contact)
147     {
148       g_signal_handlers_disconnect_by_func (priv->tp_contact,
149           tp_contact_notify_cb, object);
150       g_object_unref (priv->tp_contact);
151     }
152   priv->tp_contact = NULL;
153
154   if (priv->account)
155     g_object_unref (priv->account);
156   priv->account = NULL;
157
158   G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
159 }
160
161 static void
162 empathy_contact_class_init (EmpathyContactClass *class)
163 {
164   GObjectClass *object_class;
165
166   object_class = G_OBJECT_CLASS (class);
167
168   object_class->finalize = contact_finalize;
169   object_class->dispose = contact_dispose;
170   object_class->get_property = contact_get_property;
171   object_class->set_property = contact_set_property;
172
173   g_object_class_install_property (object_class,
174       PROP_TP_CONTACT,
175       g_param_spec_object ("tp-contact",
176         "TpContact",
177         "The TpContact associated with the contact",
178         TP_TYPE_CONTACT,
179         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180
181   g_object_class_install_property (object_class,
182       PROP_ACCOUNT,
183       g_param_spec_object ("account",
184         "The account",
185         "The account associated with the contact",
186         TP_TYPE_ACCOUNT,
187         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188
189   g_object_class_install_property (object_class,
190       PROP_ID,
191       g_param_spec_string ("id",
192         "Contact id",
193         "String identifying contact",
194         NULL,
195         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196
197   g_object_class_install_property (object_class,
198       PROP_NAME,
199       g_param_spec_string ("name",
200         "Contact Name",
201         "The name of the contact",
202         NULL,
203         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204
205   g_object_class_install_property (object_class,
206       PROP_AVATAR,
207       g_param_spec_boxed ("avatar",
208         "Avatar image",
209         "The avatar image",
210         EMPATHY_TYPE_AVATAR,
211         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212
213   g_object_class_install_property (object_class,
214       PROP_PRESENCE,
215       g_param_spec_uint ("presence",
216         "Contact presence",
217         "Presence of contact",
218         TP_CONNECTION_PRESENCE_TYPE_UNSET,
219         NUM_TP_CONNECTION_PRESENCE_TYPES,
220         TP_CONNECTION_PRESENCE_TYPE_UNSET,
221         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222
223   g_object_class_install_property (object_class,
224       PROP_PRESENCE_MESSAGE,
225       g_param_spec_string ("presence-message",
226         "Contact presence message",
227         "Presence message of contact",
228         NULL,
229         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230
231   g_object_class_install_property (object_class,
232       PROP_HANDLE,
233       g_param_spec_uint ("handle",
234         "Contact Handle",
235         "The handle of the contact",
236         0,
237         G_MAXUINT,
238         0,
239         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240
241   g_object_class_install_property (object_class,
242       PROP_CAPABILITIES,
243       g_param_spec_flags ("capabilities",
244         "Contact Capabilities",
245         "Capabilities of the contact",
246         EMPATHY_TYPE_CAPABILITIES,
247         EMPATHY_CAPABILITIES_UNKNOWN,
248         G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
249
250   g_object_class_install_property (object_class,
251       PROP_IS_USER,
252       g_param_spec_boolean ("is-user",
253         "Contact is-user",
254         "Is contact the user",
255         FALSE,
256         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
257
258
259   g_object_class_install_property (object_class,
260       PROP_LOCATION,
261       g_param_spec_boxed ("location",
262         "Contact location",
263         "Physical location of the contact",
264         G_TYPE_HASH_TABLE,
265         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
266
267   signals[PRESENCE_CHANGED] =
268     g_signal_new ("presence-changed",
269                   G_TYPE_FROM_CLASS (class),
270                   G_SIGNAL_RUN_LAST,
271                   0,
272                   NULL, NULL,
273                   _empathy_marshal_VOID__UINT_UINT,
274                   G_TYPE_NONE,
275                   2, G_TYPE_UINT,
276                   G_TYPE_UINT);
277
278   g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
279 }
280
281 static void
282 empathy_contact_init (EmpathyContact *contact)
283 {
284   EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
285     EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
286
287   contact->priv = priv;
288
289   priv->location = NULL;
290 }
291
292 static void
293 contact_finalize (GObject *object)
294 {
295   EmpathyContactPriv *priv;
296
297   priv = GET_PRIV (object);
298
299   DEBUG ("finalize: %p", object);
300
301   g_free (priv->name);
302   g_free (priv->id);
303   g_free (priv->presence_message);
304
305   if (priv->avatar)
306       empathy_avatar_unref (priv->avatar);
307
308   if (priv->location != NULL)
309       g_hash_table_unref (priv->location);
310
311   G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
312 }
313
314 static void
315 set_tp_contact (EmpathyContact *contact,
316                 TpContact *tp_contact)
317 {
318   EmpathyContactPriv *priv = GET_PRIV (contact);
319   GHashTable *location;
320
321   if (tp_contact == NULL)
322     return;
323
324   g_assert (priv->tp_contact == NULL);
325   priv->tp_contact = g_object_ref (tp_contact);
326   priv->presence = empathy_contact_get_presence (contact);
327
328   location = tp_contact_get_location (tp_contact);
329   if (location != NULL)
330     empathy_contact_set_location (contact, location);
331
332   g_signal_connect (priv->tp_contact, "notify",
333     G_CALLBACK (tp_contact_notify_cb), contact);
334 }
335
336 static void
337 contact_get_property (GObject *object,
338                       guint param_id,
339                       GValue *value,
340                       GParamSpec *pspec)
341 {
342   EmpathyContact *contact = EMPATHY_CONTACT (object);
343
344   switch (param_id)
345     {
346       case PROP_TP_CONTACT:
347         g_value_set_object (value, empathy_contact_get_tp_contact (contact));
348         break;
349       case PROP_ACCOUNT:
350         g_value_set_object (value, empathy_contact_get_account (contact));
351         break;
352       case PROP_ID:
353         g_value_set_string (value, empathy_contact_get_id (contact));
354         break;
355       case PROP_NAME:
356         g_value_set_string (value, empathy_contact_get_name (contact));
357         break;
358       case PROP_AVATAR:
359         g_value_set_boxed (value, empathy_contact_get_avatar (contact));
360         break;
361       case PROP_PRESENCE:
362         g_value_set_uint (value, empathy_contact_get_presence (contact));
363         break;
364       case PROP_PRESENCE_MESSAGE:
365         g_value_set_string (value, empathy_contact_get_presence_message (contact));
366         break;
367       case PROP_HANDLE:
368         g_value_set_uint (value, empathy_contact_get_handle (contact));
369         break;
370       case PROP_CAPABILITIES:
371         g_value_set_flags (value, empathy_contact_get_capabilities (contact));
372         break;
373       case PROP_IS_USER:
374         g_value_set_boolean (value, empathy_contact_is_user (contact));
375         break;
376       default:
377         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
378         break;
379     };
380 }
381
382 static void
383 contact_set_property (GObject *object,
384                       guint param_id,
385                       const GValue *value,
386                       GParamSpec *pspec)
387 {
388   EmpathyContact *contact = EMPATHY_CONTACT (object);
389   EmpathyContactPriv *priv = GET_PRIV (object);
390
391   switch (param_id)
392     {
393       case PROP_TP_CONTACT:
394         set_tp_contact (contact, g_value_get_object (value));
395         break;
396       case PROP_ACCOUNT:
397         g_assert (priv->account == NULL);
398         priv->account = g_value_dup_object (value);
399         break;
400       case PROP_ID:
401         empathy_contact_set_id (contact, g_value_get_string (value));
402         break;
403       case PROP_NAME:
404         empathy_contact_set_name (contact, g_value_get_string (value));
405         break;
406       case PROP_AVATAR:
407         empathy_contact_set_avatar (contact, g_value_get_boxed (value));
408         break;
409       case PROP_PRESENCE:
410         empathy_contact_set_presence (contact, g_value_get_uint (value));
411         break;
412       case PROP_PRESENCE_MESSAGE:
413         empathy_contact_set_presence_message (contact, g_value_get_string (value));
414         break;
415       case PROP_HANDLE:
416         empathy_contact_set_handle (contact, g_value_get_uint (value));
417         break;
418       case PROP_CAPABILITIES:
419         empathy_contact_set_capabilities (contact, g_value_get_flags (value));
420         break;
421       case PROP_IS_USER:
422         empathy_contact_set_is_user (contact, g_value_get_boolean (value));
423         break;
424       default:
425         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
426         break;
427     };
428 }
429
430 EmpathyContact *
431 empathy_contact_new (TpContact *tp_contact)
432 {
433   g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
434
435   return g_object_new (EMPATHY_TYPE_CONTACT,
436       "tp-contact", tp_contact,
437       NULL);
438 }
439
440 #ifdef ENABLE_TPL
441 EmpathyContact *
442 empathy_contact_from_tpl_contact (TpAccount *account,
443     TplContact *tpl_contact)
444 {
445   EmpathyContact *retval;
446   gboolean is_user;
447
448   g_return_val_if_fail (TPL_IS_CONTACT (tpl_contact), NULL);
449
450   is_user = (TPL_CONTACT_USER == tpl_contact_get_contact_type (tpl_contact));
451
452   retval = g_object_new (EMPATHY_TYPE_CONTACT,
453       "id", tpl_contact_get_alias (tpl_contact),
454       "name", tpl_contact_get_identifier (tpl_contact),
455       "account", account,
456       "is-user", is_user,
457       NULL);
458
459   if (!EMP_STR_EMPTY (tpl_contact_get_avatar_token (tpl_contact)))
460     empathy_contact_load_avatar_cache (retval,
461         tpl_contact_get_avatar_token (tpl_contact));
462
463   return retval;
464 }
465 #endif /* ENABLE_TPL */
466
467 EmpathyContact *
468 empathy_contact_new_for_log (TpAccount *account,
469                              const gchar *id,
470                              const gchar *name,
471                              gboolean is_user)
472 {
473   g_return_val_if_fail (id != NULL, NULL);
474   g_assert (account != NULL);
475
476   return g_object_new (EMPATHY_TYPE_CONTACT,
477       "account", account,
478       "id", id,
479       "name", name,
480       "is-user", is_user,
481       NULL);
482 }
483
484 TpContact *
485 empathy_contact_get_tp_contact (EmpathyContact *contact)
486 {
487   EmpathyContactPriv *priv;
488
489   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
490
491   priv = GET_PRIV (contact);
492
493   return priv->tp_contact;
494 }
495
496 const gchar *
497 empathy_contact_get_id (EmpathyContact *contact)
498 {
499   EmpathyContactPriv *priv;
500
501   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
502
503   priv = GET_PRIV (contact);
504
505   if (priv->tp_contact != NULL)
506     return tp_contact_get_identifier (priv->tp_contact);
507
508   return priv->id;
509 }
510
511 void
512 empathy_contact_set_id (EmpathyContact *contact,
513                         const gchar *id)
514 {
515   EmpathyContactPriv *priv;
516
517   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
518   g_return_if_fail (id != NULL);
519
520   priv = GET_PRIV (contact);
521
522   /* We temporally ref the contact because it could be destroyed
523    * during the signal emition */
524   g_object_ref (contact);
525   if (tp_strdiff (id, priv->id))
526     {
527       g_free (priv->id);
528       priv->id = g_strdup (id);
529
530       g_object_notify (G_OBJECT (contact), "id");
531       if (EMP_STR_EMPTY (priv->name))
532           g_object_notify (G_OBJECT (contact), "name");
533     }
534
535   g_object_unref (contact);
536 }
537
538 const gchar *
539 empathy_contact_get_name (EmpathyContact *contact)
540 {
541   EmpathyContactPriv *priv;
542   const gchar        *alias;
543
544   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
545
546   priv = GET_PRIV (contact);
547
548   if (priv->tp_contact != NULL)
549     alias = tp_contact_get_alias (priv->tp_contact);
550   else
551     alias = priv->name;
552
553   if (!EMP_STR_EMPTY (alias))
554     return alias;
555   else
556     return empathy_contact_get_id (contact);
557 }
558
559 void
560 empathy_contact_set_name (EmpathyContact *contact,
561                           const gchar *name)
562 {
563   EmpathyContactPriv *priv;
564
565   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
566
567   priv = GET_PRIV (contact);
568
569   g_object_ref (contact);
570   if (tp_strdiff (name, priv->name))
571     {
572       g_free (priv->name);
573       priv->name = g_strdup (name);
574       g_object_notify (G_OBJECT (contact), "name");
575     }
576   g_object_unref (contact);
577 }
578
579 EmpathyAvatar *
580 empathy_contact_get_avatar (EmpathyContact *contact)
581 {
582   EmpathyContactPriv *priv;
583
584   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
585
586   priv = GET_PRIV (contact);
587
588   return priv->avatar;
589 }
590
591 void
592 empathy_contact_set_avatar (EmpathyContact *contact,
593                             EmpathyAvatar *avatar)
594 {
595   EmpathyContactPriv *priv;
596
597   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
598
599   priv = GET_PRIV (contact);
600
601   if (priv->avatar == avatar)
602     return;
603
604   if (priv->avatar)
605     {
606       empathy_avatar_unref (priv->avatar);
607       priv->avatar = NULL;
608     }
609
610   if (avatar)
611       priv->avatar = empathy_avatar_ref (avatar);
612
613   g_object_notify (G_OBJECT (contact), "avatar");
614 }
615
616 TpAccount *
617 empathy_contact_get_account (EmpathyContact *contact)
618 {
619   EmpathyContactPriv *priv;
620
621   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
622
623   priv = GET_PRIV (contact);
624
625   if (priv->account == NULL && priv->tp_contact != NULL)
626     {
627       TpConnection *connection;
628
629       /* FIXME: This assume the account manager already exists */
630       connection = tp_contact_get_connection (priv->tp_contact);
631       priv->account =
632         g_object_ref (empathy_get_account_for_connection (connection));
633     }
634
635   return priv->account;
636 }
637
638 TpConnection *
639 empathy_contact_get_connection (EmpathyContact *contact)
640 {
641   EmpathyContactPriv *priv;
642
643   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
644
645   priv = GET_PRIV (contact);
646
647   if (priv->tp_contact != NULL)
648     return tp_contact_get_connection (priv->tp_contact);
649
650   return NULL;
651 }
652
653 TpConnectionPresenceType
654 empathy_contact_get_presence (EmpathyContact *contact)
655 {
656   EmpathyContactPriv *priv;
657
658   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
659     TP_CONNECTION_PRESENCE_TYPE_UNSET);
660
661   priv = GET_PRIV (contact);
662
663   if (priv->tp_contact != NULL)
664     return tp_contact_get_presence_type (priv->tp_contact);
665
666   return priv->presence;
667 }
668
669 void
670 empathy_contact_set_presence (EmpathyContact *contact,
671                               TpConnectionPresenceType presence)
672 {
673   EmpathyContactPriv *priv;
674   TpConnectionPresenceType old_presence;
675
676   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
677
678   priv = GET_PRIV (contact);
679
680   if (presence == priv->presence)
681     return;
682
683   old_presence = priv->presence;
684   priv->presence = presence;
685
686   g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
687
688   g_object_notify (G_OBJECT (contact), "presence");
689 }
690
691 const gchar *
692 empathy_contact_get_presence_message (EmpathyContact *contact)
693 {
694   EmpathyContactPriv *priv;
695
696   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
697
698   priv = GET_PRIV (contact);
699
700   if (priv->tp_contact != NULL)
701     return tp_contact_get_presence_message (priv->tp_contact);
702
703   return priv->presence_message;
704 }
705
706 void
707 empathy_contact_set_presence_message (EmpathyContact *contact,
708                                       const gchar *message)
709 {
710   EmpathyContactPriv *priv = GET_PRIV (contact);
711
712   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
713
714   if (!tp_strdiff (message, priv->presence_message))
715     return;
716
717   g_free (priv->presence_message);
718   priv->presence_message = g_strdup (message);
719
720   g_object_notify (G_OBJECT (contact), "presence-message");
721 }
722
723 guint
724 empathy_contact_get_handle (EmpathyContact *contact)
725 {
726   EmpathyContactPriv *priv;
727
728   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
729
730   priv = GET_PRIV (contact);
731
732   if (priv->tp_contact != NULL)
733     return tp_contact_get_handle (priv->tp_contact);
734
735   return priv->handle;
736 }
737
738 void
739 empathy_contact_set_handle (EmpathyContact *contact,
740                             guint handle)
741 {
742   EmpathyContactPriv *priv;
743
744   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
745
746   priv = GET_PRIV (contact);
747
748   g_object_ref (contact);
749   if (handle != priv->handle)
750     {
751       priv->handle = handle;
752       g_object_notify (G_OBJECT (contact), "handle");
753     }
754   g_object_unref (contact);
755 }
756
757 EmpathyCapabilities
758 empathy_contact_get_capabilities (EmpathyContact *contact)
759 {
760   EmpathyContactPriv *priv;
761
762   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
763
764   priv = GET_PRIV (contact);
765
766   return priv->capabilities;
767 }
768
769 void
770 empathy_contact_set_capabilities (EmpathyContact *contact,
771                                   EmpathyCapabilities capabilities)
772 {
773   EmpathyContactPriv *priv;
774
775   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
776
777   priv = GET_PRIV (contact);
778
779   if (priv->capabilities == capabilities)
780     return;
781
782   priv->capabilities = capabilities;
783
784   g_object_notify (G_OBJECT (contact), "capabilities");
785 }
786
787 gboolean
788 empathy_contact_is_user (EmpathyContact *contact)
789 {
790   EmpathyContactPriv *priv;
791
792   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
793
794   priv = GET_PRIV (contact);
795
796   return priv->is_user;
797 }
798
799 void
800 empathy_contact_set_is_user (EmpathyContact *contact,
801                              gboolean is_user)
802 {
803   EmpathyContactPriv *priv;
804
805   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
806
807   priv = GET_PRIV (contact);
808
809   if (priv->is_user == is_user)
810     return;
811
812   priv->is_user = is_user;
813
814   g_object_notify (G_OBJECT (contact), "is-user");
815 }
816
817 gboolean
818 empathy_contact_is_online (EmpathyContact *contact)
819 {
820   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
821
822   switch (empathy_contact_get_presence (contact))
823     {
824       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
825       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
826       case TP_CONNECTION_PRESENCE_TYPE_ERROR:
827         return FALSE;
828       default:
829         return TRUE;
830     }
831 }
832
833 const gchar *
834 empathy_contact_get_status (EmpathyContact *contact)
835 {
836   const gchar *message;
837
838   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
839
840   message = empathy_contact_get_presence_message (contact);
841   if (!EMP_STR_EMPTY (message))
842     return message;
843
844   return empathy_presence_get_default_message (
845       empathy_contact_get_presence (contact));
846 }
847
848 gboolean
849 empathy_contact_can_voip (EmpathyContact *contact)
850 {
851   EmpathyContactPriv *priv;
852
853   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
854
855   priv = GET_PRIV (contact);
856
857   return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
858       EMPATHY_CAPABILITIES_VIDEO);
859 }
860
861 gboolean
862 empathy_contact_can_voip_audio (EmpathyContact *contact)
863 {
864   EmpathyContactPriv *priv;
865
866   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
867
868   priv = GET_PRIV (contact);
869
870   return priv->capabilities & EMPATHY_CAPABILITIES_AUDIO;
871 }
872
873 gboolean
874 empathy_contact_can_voip_video (EmpathyContact *contact)
875 {
876   EmpathyContactPriv *priv;
877
878   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
879
880   priv = GET_PRIV (contact);
881
882   return priv->capabilities & EMPATHY_CAPABILITIES_VIDEO;
883 }
884
885 gboolean
886 empathy_contact_can_send_files (EmpathyContact *contact)
887 {
888   EmpathyContactPriv *priv;
889
890   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
891
892   priv = GET_PRIV (contact);
893
894   return priv->capabilities & EMPATHY_CAPABILITIES_FT;
895 }
896
897 gboolean
898 empathy_contact_can_use_stream_tube (EmpathyContact *contact)
899 {
900   EmpathyContactPriv *priv;
901
902   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
903
904   priv = GET_PRIV (contact);
905
906   return priv->capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE;
907 }
908
909 static gchar *
910 contact_get_avatar_filename (EmpathyContact *contact,
911                              const gchar *token)
912 {
913   TpAccount *account;
914   gchar *avatar_path;
915   gchar *avatar_file;
916   gchar *token_escaped;
917
918   if (EMP_STR_EMPTY (empathy_contact_get_id (contact)))
919     return NULL;
920
921   token_escaped = tp_escape_as_identifier (token);
922   account = empathy_contact_get_account (contact);
923
924   avatar_path = g_build_filename (g_get_user_cache_dir (),
925       "telepathy",
926       "avatars",
927       tp_account_get_connection_manager (account),
928       tp_account_get_protocol (account),
929       NULL);
930   g_mkdir_with_parents (avatar_path, 0700);
931
932   avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
933
934   g_free (token_escaped);
935   g_free (avatar_path);
936
937   return avatar_file;
938 }
939
940 void
941 empathy_contact_load_avatar_data (EmpathyContact *contact,
942                                   const guchar *data,
943                                   const gsize len,
944                                   const gchar *format,
945                                   const gchar *token)
946 {
947   EmpathyAvatar *avatar;
948   gchar *filename;
949   GError *error = NULL;
950
951   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
952   g_return_if_fail (data != NULL);
953   g_return_if_fail (len > 0);
954   g_return_if_fail (format != NULL);
955   g_return_if_fail (!EMP_STR_EMPTY (token));
956
957   /* Load and set the avatar */
958   filename = contact_get_avatar_filename (contact, token);
959   avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
960       g_strdup (token), filename);
961   empathy_contact_set_avatar (contact, avatar);
962   empathy_avatar_unref (avatar);
963
964   /* Save to cache if not yet in it */
965   if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
966     {
967       if (!empathy_avatar_save_to_file (avatar, filename, &error))
968         {
969           DEBUG ("Failed to save avatar in cache: %s",
970             error ? error->message : "No error given");
971           g_clear_error (&error);
972         }
973       else
974           DEBUG ("Avatar saved to %s", filename);
975     }
976 }
977
978 gboolean
979 empathy_contact_load_avatar_cache (EmpathyContact *contact,
980                                    const gchar *token)
981 {
982   EmpathyAvatar *avatar = NULL;
983   gchar *filename;
984   gchar *data = NULL;
985   gsize len;
986   GError *error = NULL;
987
988   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
989   g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
990
991   /* Load the avatar from file if it exists */
992   filename = contact_get_avatar_filename (contact, token);
993   if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
994     {
995       if (!g_file_get_contents (filename, &data, &len, &error))
996         {
997           DEBUG ("Failed to load avatar from cache: %s",
998             error ? error->message : "No error given");
999           g_clear_error (&error);
1000         }
1001     }
1002
1003   if (data)
1004     {
1005       DEBUG ("Avatar loaded from %s", filename);
1006       avatar = empathy_avatar_new ((guchar *) data, len, NULL, g_strdup (token),
1007           filename);
1008       empathy_contact_set_avatar (contact, avatar);
1009       empathy_avatar_unref (avatar);
1010     }
1011   else
1012     {
1013       g_free (filename);
1014     }
1015
1016   return data != NULL;
1017 }
1018
1019 GType
1020 empathy_avatar_get_type (void)
1021 {
1022   static GType type_id = 0;
1023
1024   if (!type_id)
1025     {
1026       type_id = g_boxed_type_register_static ("EmpathyAvatar",
1027           (GBoxedCopyFunc) empathy_avatar_ref,
1028           (GBoxedFreeFunc) empathy_avatar_unref);
1029     }
1030
1031   return type_id;
1032 }
1033
1034 /**
1035  * empathy_avatar_new:
1036  * @data: the avatar data
1037  * @len: the size of avatar data
1038  * @format: the mime type of the avatar image
1039  * @token: the token of the avatar
1040  * @filename: the filename where the avatar is stored in cache
1041  *
1042  * Create a #EmpathyAvatar from the provided data. This function takes the
1043  * ownership of @data, @format, @token and @filename.
1044  *
1045  * Returns: a new #EmpathyAvatar
1046  */
1047 EmpathyAvatar *
1048 empathy_avatar_new (guchar *data,
1049                     gsize len,
1050                     gchar *format,
1051                     gchar *token,
1052                     gchar *filename)
1053 {
1054   EmpathyAvatar *avatar;
1055
1056   avatar = g_slice_new0 (EmpathyAvatar);
1057   avatar->data = data;
1058   avatar->len = len;
1059   avatar->format = format;
1060   avatar->token = token;
1061   avatar->filename = filename;
1062   avatar->refcount = 1;
1063
1064   return avatar;
1065 }
1066
1067 void
1068 empathy_avatar_unref (EmpathyAvatar *avatar)
1069 {
1070   g_return_if_fail (avatar != NULL);
1071
1072   avatar->refcount--;
1073   if (avatar->refcount == 0)
1074     {
1075       g_free (avatar->data);
1076       g_free (avatar->format);
1077       g_free (avatar->token);
1078       g_slice_free (EmpathyAvatar, avatar);
1079     }
1080 }
1081
1082 EmpathyAvatar *
1083 empathy_avatar_ref (EmpathyAvatar *avatar)
1084 {
1085   g_return_val_if_fail (avatar != NULL, NULL);
1086
1087   avatar->refcount++;
1088
1089   return avatar;
1090 }
1091
1092 /**
1093  * empathy_avatar_save_to_file:
1094  * @avatar: the avatar
1095  * @filename: name of a file to write avatar to
1096  * @error: return location for a GError, or NULL
1097  *
1098  * Save the avatar to a file named filename
1099  *
1100  * Returns: %TRUE on success, %FALSE if an error occurred
1101  */
1102 gboolean
1103 empathy_avatar_save_to_file (EmpathyAvatar *self,
1104                              const gchar *filename,
1105                              GError **error)
1106 {
1107   return g_file_set_contents (filename, (const gchar *) self->data, self->len,
1108       error);
1109 }
1110
1111 /**
1112  * empathy_contact_get_location:
1113  * @contact: an #EmpathyContact
1114  *
1115  * Returns the user's location if available.  The keys are defined in
1116  * empathy-location.h. If the contact doesn't have location
1117  * information, the GHashTable will be empthy. Use #g_hash_table_unref when
1118  * you are done with the #GHashTable.
1119  *
1120  * It is composed of string keys and GValues.  Keys are
1121  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1122  * Example: a "city" key would have "Helsinki" as string GValue,
1123  *          a "latitude" would have 65.0 as double GValue.
1124  *
1125  * Returns: a #GHashTable of location values
1126  */
1127 GHashTable *
1128 empathy_contact_get_location (EmpathyContact *contact)
1129 {
1130   EmpathyContactPriv *priv;
1131
1132   g_return_val_if_fail (EMPATHY_CONTACT (contact), NULL);
1133
1134   priv = GET_PRIV (contact);
1135
1136   return priv->location;
1137 }
1138
1139 /**
1140  * empathy_contact_set_location:
1141  * @contact: an #EmpathyContact
1142  * @location: a #GHashTable of the location
1143  *
1144  * Sets the user's location based on the location #GHashTable passed.
1145  * It is composed of string keys and GValues.  Keys are
1146  * defined in empathy-location.h such as #EMPATHY_LOCATION_COUNTRY.
1147  * Example: a "city" key would have "Helsinki" as string GValue,
1148  *          a "latitude" would have 65.0 as double GValue.
1149  */
1150 static void
1151 empathy_contact_set_location (EmpathyContact *contact,
1152     GHashTable *location)
1153 {
1154   EmpathyContactPriv *priv;
1155
1156   g_return_if_fail (EMPATHY_CONTACT (contact));
1157   g_return_if_fail (location != NULL);
1158
1159   priv = GET_PRIV (contact);
1160
1161   if (priv->location != NULL)
1162     g_hash_table_unref (priv->location);
1163
1164   priv->location = g_hash_table_ref (location);
1165   update_geocode (contact);
1166   g_object_notify (G_OBJECT (contact), "location");
1167 }
1168
1169 /**
1170  * empathy_contact_equal:
1171  * @contact1: an #EmpathyContact
1172  * @contact2: an #EmpathyContact
1173  *
1174  * Returns FALSE if one of the contacts is NULL but the other is not.
1175  * Otherwise returns TRUE if both pointer are equal or if they bith
1176  * refer to the same id.
1177  * It's only necessary to call this function if your contact objects
1178  * come from logs where contacts are created dynamically and comparing
1179  * pointers is not enough.
1180  */
1181 gboolean
1182 empathy_contact_equal (gconstpointer contact1,
1183                        gconstpointer contact2)
1184 {
1185   EmpathyContact *c1;
1186   EmpathyContact *c2;
1187   const gchar *id1;
1188   const gchar *id2;
1189
1190   if ((contact1 == NULL) != (contact2 == NULL)) {
1191     return FALSE;
1192   }
1193   if (contact1 == contact2) {
1194     return TRUE;
1195   }
1196   c1 = EMPATHY_CONTACT (contact1);
1197   c2 = EMPATHY_CONTACT (contact2);
1198   id1 = empathy_contact_get_id (c1);
1199   id2 = empathy_contact_get_id (c2);
1200   if (!tp_strdiff (id1, id2)) {
1201     return TRUE;
1202   }
1203   return FALSE;
1204 }
1205
1206 #if HAVE_GEOCLUE
1207 #define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo"
1208 #define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
1209
1210 /* This callback is called by geoclue when it found a position
1211  * for the given address.  A position is necessary for a contact
1212  * to show up on the map
1213  */
1214 static void
1215 geocode_cb (GeoclueGeocode *geocode,
1216             GeocluePositionFields fields,
1217             double latitude,
1218             double longitude,
1219             double altitude,
1220             GeoclueAccuracy *accuracy,
1221             GError *error,
1222             gpointer contact)
1223 {
1224         GValue *new_value;
1225         GHashTable *location;
1226
1227         location = empathy_contact_get_location (EMPATHY_CONTACT (contact));
1228
1229         if (error != NULL) {
1230                 DEBUG ("Error geocoding location : %s", error->message);
1231                 g_object_unref (geocode);
1232                 g_object_unref (contact);
1233                 return;
1234         }
1235
1236         if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) {
1237                 new_value = tp_g_value_slice_new_double (latitude);
1238                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_LAT),
1239                         new_value);
1240                 DEBUG ("\t - Latitude: %f", latitude);
1241         }
1242         if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
1243                 new_value = tp_g_value_slice_new_double (longitude);
1244                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_LON),
1245                         new_value);
1246                 DEBUG ("\t - Longitude: %f", longitude);
1247         }
1248         if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) {
1249                 new_value = tp_g_value_slice_new_double (altitude);
1250                 g_hash_table_replace (location, g_strdup (EMPATHY_LOCATION_ALT),
1251                         new_value);
1252                 DEBUG ("\t - Altitude: %f", altitude);
1253         }
1254
1255         /* Don't change the accuracy as we used an address to get this position */
1256         g_object_notify (contact, "location");
1257         g_object_unref (geocode);
1258         g_object_unref (contact);
1259 }
1260 #endif
1261
1262 #if HAVE_GEOCLUE
1263 static gchar *
1264 get_dup_string (GHashTable *location,
1265     gchar *key)
1266 {
1267   GValue *value;
1268
1269   value = g_hash_table_lookup (location, key);
1270   if (value != NULL)
1271     return g_value_dup_string (value);
1272
1273   return NULL;
1274 }
1275 #endif
1276
1277 static void
1278 update_geocode (EmpathyContact *contact)
1279 {
1280 #if HAVE_GEOCLUE
1281         static GeoclueGeocode *geocode;
1282         gchar *str;
1283         GHashTable *address;
1284         GValue* value;
1285         GHashTable *location;
1286
1287         location = empathy_contact_get_location (contact);
1288         if (location == NULL)
1289                 return;
1290
1291         value = g_hash_table_lookup (location, EMPATHY_LOCATION_LAT);
1292         if (value != NULL)
1293                 return;
1294
1295         if (geocode == NULL) {
1296                 geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH);
1297                 g_object_add_weak_pointer (G_OBJECT (geocode), (gpointer *) &geocode);
1298         }
1299         else
1300                 g_object_ref (geocode);
1301
1302         address = geoclue_address_details_new ();
1303
1304         str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY_CODE);
1305         if (str != NULL) {
1306                 g_hash_table_insert (address,
1307                         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRYCODE), str);
1308                 DEBUG ("\t - countrycode: %s", str);
1309         }
1310
1311         str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY);
1312         if (str != NULL) {
1313                 g_hash_table_insert (address,
1314                         g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), str);
1315                 DEBUG ("\t - country: %s", str);
1316         }
1317
1318         str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE);
1319         if (str != NULL) {
1320                 g_hash_table_insert (address,
1321                         g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), str);
1322                 DEBUG ("\t - postalcode: %s", str);
1323         }
1324
1325         str = get_dup_string (location, EMPATHY_LOCATION_REGION);
1326         if (str != NULL) {
1327                 g_hash_table_insert (address,
1328                         g_strdup (GEOCLUE_ADDRESS_KEY_REGION), str);
1329                 DEBUG ("\t - region: %s", str);
1330         }
1331
1332         str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY);
1333         if (str != NULL) {
1334                 g_hash_table_insert (address,
1335                         g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), str);
1336                 DEBUG ("\t - locality: %s", str);
1337         }
1338
1339         str = get_dup_string (location, EMPATHY_LOCATION_STREET);
1340         if (str != NULL) {
1341                 g_hash_table_insert (address,
1342                         g_strdup (GEOCLUE_ADDRESS_KEY_STREET), str);
1343                 DEBUG ("\t - street: %s", str);
1344         }
1345
1346         g_object_ref (contact);
1347         geoclue_geocode_address_to_position_async (geocode, address,
1348                 geocode_cb, contact);
1349
1350         g_hash_table_unref (address);
1351 #endif
1352 }