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