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