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