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