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