]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
empathy_contact_set_handle: remove the handle ready flag when the handle is set to 0
[empathy.git] / libempathy / empathy-contact.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3  * Copyright (C) 2004 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Mikael Hallendal <micke@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  *          Xavier Claessens <xclaesse@gmail.com>
24  *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
25  */
26
27 #include "config.h"
28
29 #include <string.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include <telepathy-glib/util.h>
34 #include <libmissioncontrol/mc-enum-types.h>
35
36 #include "empathy-contact.h"
37 #include "empathy-contact-factory.h"
38 #include "empathy-utils.h"
39 #include "empathy-enum-types.h"
40 #include "empathy-marshal.h"
41
42 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
43 #include "empathy-debug.h"
44
45 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
46 typedef struct {
47   EmpathyContactFactory *factory;
48   gchar *id;
49   gchar *name;
50   EmpathyAvatar *avatar;
51   McAccount *account;
52   McPresence presence;
53   gchar *presence_message;
54   guint handle;
55   EmpathyCapabilities capabilities;
56   gboolean is_user;
57   guint hash;
58   EmpathyContactReady ready;
59   GList *ready_callbacks;
60 } EmpathyContactPriv;
61
62 typedef struct {
63     EmpathyContactReady ready;
64     EmpathyContactReadyCb *callback;
65     gpointer user_data;
66     GDestroyNotify destroy;
67     GObject *weak_object;
68 } ReadyCbData;
69
70 static void contact_finalize (GObject *object);
71 static void contact_get_property (GObject *object, guint param_id,
72     GValue *value, GParamSpec *pspec);
73 static void contact_set_property (GObject *object, guint param_id,
74     const GValue *value, GParamSpec *pspec);
75
76 G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT);
77
78 enum
79 {
80   PROP_0,
81   PROP_ID,
82   PROP_NAME,
83   PROP_AVATAR,
84   PROP_ACCOUNT,
85   PROP_PRESENCE,
86   PROP_PRESENCE_MESSAGE,
87   PROP_HANDLE,
88   PROP_CAPABILITIES,
89   PROP_IS_USER,
90   PROP_READY
91 };
92
93 enum {
94   PRESENCE_CHANGED,
95   LAST_SIGNAL
96 };
97
98 static guint signals[LAST_SIGNAL];
99
100 static void
101 contact_dispose (GObject *object)
102 {
103   EmpathyContactPriv *priv = GET_PRIV (object);
104
105   if (priv->account)
106     g_object_unref (priv->account);
107   priv->account = NULL;
108
109   if (priv->factory)
110     g_object_unref (priv->factory);
111   priv->factory = NULL;
112
113   G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object);
114 }
115
116 static void
117 empathy_contact_class_init (EmpathyContactClass *class)
118 {
119   GObjectClass *object_class;
120
121   object_class = G_OBJECT_CLASS (class);
122
123   object_class->finalize = contact_finalize;
124   object_class->dispose = contact_dispose;
125   object_class->get_property = contact_get_property;
126   object_class->set_property = contact_set_property;
127
128   g_object_class_install_property (object_class,
129       PROP_ID,
130       g_param_spec_string ("id",
131         "Contact id",
132         "String identifying contact",
133         NULL,
134         G_PARAM_READWRITE));
135
136   g_object_class_install_property (object_class,
137       PROP_NAME,
138       g_param_spec_string ("name",
139         "Contact Name",
140         "The name of the contact",
141         NULL,
142         G_PARAM_READWRITE));
143
144   g_object_class_install_property (object_class,
145       PROP_AVATAR,
146       g_param_spec_boxed ("avatar",
147         "Avatar image",
148         "The avatar image",
149         EMPATHY_TYPE_AVATAR,
150         G_PARAM_READWRITE));
151
152   g_object_class_install_property (object_class,
153       PROP_ACCOUNT,
154       g_param_spec_object ("account",
155         "Contact Account",
156         "The account associated with the contact",
157         MC_TYPE_ACCOUNT,
158         G_PARAM_READWRITE));
159
160   g_object_class_install_property (object_class,
161       PROP_PRESENCE,
162       g_param_spec_uint ("presence",
163         "Contact presence",
164         "Presence of contact",
165         MC_PRESENCE_UNSET,
166         LAST_MC_PRESENCE,
167         MC_PRESENCE_UNSET,
168         G_PARAM_READWRITE));
169
170   g_object_class_install_property (object_class,
171       PROP_PRESENCE_MESSAGE,
172       g_param_spec_string ("presence-message",
173         "Contact presence message",
174         "Presence message of contact",
175         NULL,
176         G_PARAM_READWRITE));
177
178   g_object_class_install_property (object_class,
179       PROP_HANDLE,
180       g_param_spec_uint ("handle",
181         "Contact Handle",
182         "The handle of the contact",
183         0,
184         G_MAXUINT,
185         0,
186         G_PARAM_READWRITE));
187
188   g_object_class_install_property (object_class,
189       PROP_CAPABILITIES,
190       g_param_spec_flags ("capabilities",
191         "Contact Capabilities",
192         "Capabilities of the contact",
193         EMPATHY_TYPE_CAPABILITIES,
194         EMPATHY_CAPABILITIES_UNKNOWN,
195         G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
196
197   g_object_class_install_property (object_class,
198       PROP_IS_USER,
199       g_param_spec_boolean ("is-user",
200         "Contact is-user",
201         "Is contact the user",
202         FALSE,
203         G_PARAM_READWRITE));
204
205   g_object_class_install_property (object_class,
206       PROP_READY,
207       g_param_spec_flags ("ready",
208         "Contact ready flags",
209         "Flags for ready properties",
210         EMPATHY_TYPE_CONTACT_READY,
211         EMPATHY_CONTACT_READY_NONE,
212         G_PARAM_READABLE));
213
214   signals[PRESENCE_CHANGED] =
215     g_signal_new ("presence-changed",
216                   G_TYPE_FROM_CLASS (class),
217                   G_SIGNAL_RUN_LAST,
218                   0,
219                   NULL, NULL,
220                   _empathy_marshal_VOID__ENUM_ENUM,
221                   G_TYPE_NONE,
222                   2, MC_TYPE_PRESENCE,
223                   MC_TYPE_PRESENCE);
224
225   g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
226 }
227
228 static void
229 empathy_contact_init (EmpathyContact *contact)
230 {
231   EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
232     EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
233
234   contact->priv = priv;
235
236   /* Keep a ref to the factory to be sure it is not finalized while there is
237    * still contacts alive. */
238   priv->factory = empathy_contact_factory_dup_singleton ();
239 }
240
241 static void
242 contact_finalize (GObject *object)
243 {
244   EmpathyContactPriv *priv;
245   GList *l;
246
247   priv = GET_PRIV (object);
248
249   DEBUG ("finalize: %p", object);
250
251   g_free (priv->name);
252   g_free (priv->id);
253   g_free (priv->presence_message);
254
255   for (l = priv->ready_callbacks; l != NULL; l = g_list_next (l))
256     {
257       ReadyCbData *d = (ReadyCbData *)l->data;
258
259       if (d->destroy != NULL)
260         d->destroy (d->user_data);
261       g_slice_free (ReadyCbData, d);
262     }
263
264   g_list_free (priv->ready_callbacks);
265   priv->ready_callbacks = NULL;
266
267   if (priv->avatar)
268       empathy_avatar_unref (priv->avatar);
269
270   G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
271 }
272
273 static void
274 contact_get_property (GObject *object,
275                       guint param_id,
276                       GValue *value,
277                       GParamSpec *pspec)
278 {
279   EmpathyContactPriv *priv;
280
281   priv = GET_PRIV (object);
282
283   switch (param_id)
284     {
285       case PROP_ID:
286         g_value_set_string (value, priv->id);
287         break;
288       case PROP_NAME:
289         g_value_set_string (value,
290             empathy_contact_get_name (EMPATHY_CONTACT (object)));
291         break;
292       case PROP_AVATAR:
293         g_value_set_boxed (value, priv->avatar);
294         break;
295       case PROP_ACCOUNT:
296         g_value_set_object (value, priv->account);
297         break;
298       case PROP_PRESENCE:
299         g_value_set_uint (value, priv->presence);
300         break;
301       case PROP_PRESENCE_MESSAGE:
302         g_value_set_string (value, priv->presence_message);
303         break;
304       case PROP_HANDLE:
305         g_value_set_uint (value, priv->handle);
306         break;
307       case PROP_CAPABILITIES:
308         g_value_set_flags (value, priv->capabilities);
309         break;
310       case PROP_IS_USER:
311         g_value_set_boolean (value, priv->is_user);
312         break;
313       case PROP_READY:
314         g_value_set_flags (value, priv->ready);
315         break;
316       default:
317         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
318         break;
319     };
320 }
321
322 static void
323 contact_set_property (GObject *object,
324                       guint param_id,
325                       const GValue *value,
326                       GParamSpec *pspec)
327 {
328   EmpathyContactPriv *priv;
329
330   priv = GET_PRIV (object);
331
332   switch (param_id)
333     {
334       case PROP_ID:
335         empathy_contact_set_id (EMPATHY_CONTACT (object),
336         g_value_get_string (value));
337         break;
338       case PROP_NAME:
339         empathy_contact_set_name (EMPATHY_CONTACT (object),
340         g_value_get_string (value));
341         break;
342       case PROP_AVATAR:
343         empathy_contact_set_avatar (EMPATHY_CONTACT (object),
344         g_value_get_boxed (value));
345         break;
346       case PROP_ACCOUNT:
347         empathy_contact_set_account (EMPATHY_CONTACT (object),
348         MC_ACCOUNT (g_value_get_object (value)));
349         break;
350       case PROP_PRESENCE:
351         empathy_contact_set_presence (EMPATHY_CONTACT (object),
352         g_value_get_uint (value));
353         break;
354       case PROP_PRESENCE_MESSAGE:
355         empathy_contact_set_presence_message (EMPATHY_CONTACT (object),
356         g_value_get_string (value));
357         break;
358       case PROP_HANDLE:
359         empathy_contact_set_handle (EMPATHY_CONTACT (object),
360         g_value_get_uint (value));
361         break;
362       case PROP_CAPABILITIES:
363         empathy_contact_set_capabilities (EMPATHY_CONTACT (object),
364         g_value_get_flags (value));
365         break;
366       case PROP_IS_USER:
367         empathy_contact_set_is_user (EMPATHY_CONTACT (object),
368         g_value_get_boolean (value));
369         break;
370       default:
371         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
372         break;
373     };
374 }
375
376 static gboolean
377 contact_is_ready (EmpathyContact *contact, EmpathyContactReady ready)
378 {
379   EmpathyContactPriv *priv = GET_PRIV (contact);
380
381   /* When the name is NULL, empathy_contact_get_name() fallback to the id.
382    * When the caller want to wait the name to be ready, it also want to wait
383    * the id to be ready in case of fallback. */
384   if ((ready & EMPATHY_CONTACT_READY_NAME) && EMP_STR_EMPTY (priv->name))
385       ready |= EMPATHY_CONTACT_READY_ID;
386
387   return (priv->ready & ready) == ready;
388 }
389
390 static void
391 contact_weak_object_notify (gpointer data, GObject *old_object)
392 {
393   EmpathyContact *contact = EMPATHY_CONTACT (data);
394   EmpathyContactPriv *priv = GET_PRIV (contact);
395
396   GList *l, *ln;
397
398   for (l = priv->ready_callbacks ; l != NULL ; l = ln )
399     {
400       ReadyCbData *d = (ReadyCbData *)l->data;
401       ln = g_list_next (l);
402
403       if (d->weak_object == old_object)
404         {
405           if (d->destroy != NULL)
406             d->destroy (d->user_data);
407
408           priv->ready_callbacks = g_list_delete_link (priv->ready_callbacks,
409             l);
410
411           g_slice_free (ReadyCbData, d);
412         }
413     }
414 }
415
416 static void
417 contact_call_ready_callback (EmpathyContact *contact, const GError *error,
418   ReadyCbData *data)
419 {
420   data->callback (contact, error, data->user_data, data->weak_object);
421   if (data->destroy != NULL)
422     data->destroy (data->user_data);
423
424   if (data->weak_object)
425     g_object_weak_unref (data->weak_object,
426       contact_weak_object_notify, contact);
427 }
428
429
430 static void
431 contact_set_ready_flag (EmpathyContact *contact,
432                         EmpathyContactReady flag)
433 {
434   EmpathyContactPriv *priv = GET_PRIV (contact);
435
436   if (!(priv->ready & flag))
437     {
438       GList *l, *ln;
439
440       priv->ready |= flag;
441       g_object_notify (G_OBJECT (contact), "ready");
442
443       for (l = priv->ready_callbacks ; l != NULL ; l = ln )
444         {
445           ReadyCbData *d = (ReadyCbData *)l->data;
446           ln = g_list_next (l);
447
448           if (contact_is_ready (contact, d->ready))
449             {
450               contact_call_ready_callback (contact, NULL, d);
451               priv->ready_callbacks = g_list_delete_link
452                 (priv->ready_callbacks, l);
453               g_slice_free (ReadyCbData, d);
454             }
455         }
456     }
457 }
458
459 static void
460 contact_remove_ready_flag (EmpathyContact *contact,
461                            EmpathyContactReady flag)
462 {
463   EmpathyContactPriv *priv = GET_PRIV (contact);
464
465   if (priv->ready & flag)
466     {
467       priv->ready ^= flag;
468       g_object_notify (G_OBJECT (contact), "ready");
469     }
470 }
471
472 EmpathyContact *
473 empathy_contact_new (McAccount *account)
474 {
475   return g_object_new (EMPATHY_TYPE_CONTACT,
476       "account", account,
477       NULL);
478 }
479
480 EmpathyContact *
481 empathy_contact_new_full (McAccount  *account,
482                           const gchar *id,
483                           const gchar *name)
484 {
485   return g_object_new (EMPATHY_TYPE_CONTACT,
486       "account", account,
487        "name", name,
488        "id", id,
489        NULL);
490 }
491
492 const gchar *
493 empathy_contact_get_id (EmpathyContact *contact)
494 {
495   EmpathyContactPriv *priv;
496
497   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
498
499   priv = GET_PRIV (contact);
500
501   return priv->id;
502 }
503
504 void
505 empathy_contact_set_id (EmpathyContact *contact,
506                         const gchar *id)
507 {
508   EmpathyContactPriv *priv;
509
510   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
511   g_return_if_fail (id != NULL);
512
513   priv = GET_PRIV (contact);
514
515   /* We temporally ref the contact because it could be destroyed
516    * during the signal emition */
517   g_object_ref (contact);
518   if (tp_strdiff (id, priv->id))
519     {
520       g_free (priv->id);
521       priv->id = g_strdup (id);
522
523       g_object_notify (G_OBJECT (contact), "id");
524       if (EMP_STR_EMPTY (priv->name))
525           g_object_notify (G_OBJECT (contact), "name");
526     }
527   contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_ID);
528
529   g_object_unref (contact);
530 }
531
532 const gchar *
533 empathy_contact_get_name (EmpathyContact *contact)
534 {
535   EmpathyContactPriv *priv;
536
537   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
538
539   priv = GET_PRIV (contact);
540
541   if (EMP_STR_EMPTY (priv->name))
542       return empathy_contact_get_id (contact);
543
544   return priv->name;
545 }
546
547 void
548 empathy_contact_set_name (EmpathyContact *contact,
549                           const gchar *name)
550 {
551   EmpathyContactPriv *priv;
552
553   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
554
555   priv = GET_PRIV (contact);
556
557   g_object_ref (contact);
558   if (tp_strdiff (name, priv->name))
559     {
560       g_free (priv->name);
561       priv->name = g_strdup (name);
562       g_object_notify (G_OBJECT (contact), "name");
563     }
564   contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_NAME);
565   g_object_unref (contact);
566 }
567
568 EmpathyAvatar *
569 empathy_contact_get_avatar (EmpathyContact *contact)
570 {
571   EmpathyContactPriv *priv;
572
573   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
574
575   priv = GET_PRIV (contact);
576
577   return priv->avatar;
578 }
579
580 void
581 empathy_contact_set_avatar (EmpathyContact *contact,
582                             EmpathyAvatar *avatar)
583 {
584   EmpathyContactPriv *priv;
585
586   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
587
588   priv = GET_PRIV (contact);
589
590   if (priv->avatar == avatar)
591     return;
592
593   if (priv->avatar)
594     {
595       empathy_avatar_unref (priv->avatar);
596       priv->avatar = NULL;
597     }
598
599   if (avatar)
600       priv->avatar = empathy_avatar_ref (avatar);
601
602   g_object_notify (G_OBJECT (contact), "avatar");
603 }
604
605 McAccount *
606 empathy_contact_get_account (EmpathyContact *contact)
607 {
608   EmpathyContactPriv *priv;
609
610   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
611
612   priv = GET_PRIV (contact);
613
614   return priv->account;
615 }
616
617 void
618 empathy_contact_set_account (EmpathyContact *contact,
619                              McAccount *account)
620 {
621   EmpathyContactPriv *priv;
622
623   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
624   g_return_if_fail (MC_IS_ACCOUNT (account));
625
626   priv = GET_PRIV (contact);
627
628   if (account == priv->account)
629     return;
630
631   if (priv->account)
632       g_object_unref (priv->account);
633   priv->account = g_object_ref (account);
634
635   g_object_notify (G_OBJECT (contact), "account");
636 }
637
638 McPresence
639 empathy_contact_get_presence (EmpathyContact *contact)
640 {
641   EmpathyContactPriv *priv;
642
643   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), MC_PRESENCE_UNSET);
644
645   priv = GET_PRIV (contact);
646
647   return priv->presence;
648 }
649
650 void
651 empathy_contact_set_presence (EmpathyContact *contact,
652                               McPresence presence)
653 {
654   EmpathyContactPriv *priv;
655   McPresence old_presence;
656
657   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
658
659   priv = GET_PRIV (contact);
660
661   if (presence == priv->presence)
662     return;
663
664   old_presence = priv->presence;
665   priv->presence = presence;
666
667   g_signal_emit (contact, signals[PRESENCE_CHANGED], 0, presence, old_presence);
668
669   g_object_notify (G_OBJECT (contact), "presence");
670 }
671
672 const gchar *
673 empathy_contact_get_presence_message (EmpathyContact *contact)
674 {
675   EmpathyContactPriv *priv;
676
677   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
678
679   priv = GET_PRIV (contact);
680
681   return priv->presence_message;
682 }
683
684 void
685 empathy_contact_set_presence_message (EmpathyContact *contact,
686                                       const gchar *message)
687 {
688   EmpathyContactPriv *priv = GET_PRIV (contact);
689
690   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
691
692   if (!tp_strdiff (message, priv->presence_message))
693     return;
694
695   g_free (priv->presence_message);
696   priv->presence_message = g_strdup (message);
697
698   g_object_notify (G_OBJECT (contact), "presence-message");
699 }
700
701 guint
702 empathy_contact_get_handle (EmpathyContact *contact)
703 {
704   EmpathyContactPriv *priv;
705
706   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
707
708   priv = GET_PRIV (contact);
709
710   return priv->handle;
711 }
712
713 void
714 empathy_contact_set_handle (EmpathyContact *contact,
715                             guint handle)
716 {
717   EmpathyContactPriv *priv;
718
719   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
720
721   priv = GET_PRIV (contact);
722
723   g_object_ref (contact);
724   if (handle != priv->handle)
725     {
726       priv->handle = handle;
727       g_object_notify (G_OBJECT (contact), "handle");
728     }
729
730   if (handle != 0)
731     contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
732   else
733     contact_remove_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
734
735   g_object_unref (contact);
736 }
737
738 EmpathyCapabilities
739 empathy_contact_get_capabilities (EmpathyContact *contact)
740 {
741   EmpathyContactPriv *priv;
742
743   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
744
745   priv = GET_PRIV (contact);
746
747   return priv->capabilities;
748 }
749
750 void
751 empathy_contact_set_capabilities (EmpathyContact *contact,
752                                   EmpathyCapabilities capabilities)
753 {
754   EmpathyContactPriv *priv;
755
756   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
757
758   priv = GET_PRIV (contact);
759
760   if (priv->capabilities == capabilities)
761     return;
762
763   priv->capabilities = capabilities;
764
765   g_object_notify (G_OBJECT (contact), "capabilities");
766 }
767
768 gboolean
769 empathy_contact_is_user (EmpathyContact *contact)
770 {
771   EmpathyContactPriv *priv;
772
773   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
774
775   priv = GET_PRIV (contact);
776
777   return priv->is_user;
778 }
779
780 void
781 empathy_contact_set_is_user (EmpathyContact *contact,
782                              gboolean is_user)
783 {
784   EmpathyContactPriv *priv;
785
786   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
787
788   priv = GET_PRIV (contact);
789
790   if (priv->is_user == is_user)
791     return;
792
793   priv->is_user = is_user;
794
795   g_object_notify (G_OBJECT (contact), "is-user");
796 }
797
798 gboolean
799 empathy_contact_is_online (EmpathyContact *contact)
800 {
801   EmpathyContactPriv *priv;
802
803   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
804
805   priv = GET_PRIV (contact);
806
807   return (priv->presence > MC_PRESENCE_OFFLINE);
808 }
809
810 const gchar *
811 empathy_contact_get_status (EmpathyContact *contact)
812 {
813   EmpathyContactPriv *priv;
814
815   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
816
817   priv = GET_PRIV (contact);
818
819   if (priv->presence_message)
820     return priv->presence_message;
821
822   return empathy_presence_get_default_message (priv->presence);
823 }
824
825 gboolean
826 empathy_contact_can_voip (EmpathyContact *contact)
827 {
828   EmpathyContactPriv *priv;
829
830   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
831
832   priv = GET_PRIV (contact);
833
834   return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
835       EMPATHY_CAPABILITIES_VIDEO);
836 }
837
838 gboolean
839 empathy_contact_can_send_files (EmpathyContact *contact)
840 {
841   EmpathyContactPriv *priv;
842
843   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
844
845   priv = GET_PRIV (contact);
846
847   return priv->capabilities & EMPATHY_CAPABILITIES_FT;
848 }
849
850 EmpathyContactReady
851 empathy_contact_get_ready (EmpathyContact *contact)
852 {
853   EmpathyContactPriv *priv;
854
855   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
856
857   priv = GET_PRIV (contact);
858
859   return priv->ready;
860 }
861
862 gboolean
863 empathy_contact_equal (gconstpointer v1,
864                        gconstpointer v2)
865 {
866   McAccount *account_a;
867   McAccount *account_b;
868   const gchar *id_a;
869   const gchar *id_b;
870
871   g_return_val_if_fail (EMPATHY_IS_CONTACT (v1), FALSE);
872   g_return_val_if_fail (EMPATHY_IS_CONTACT (v2), FALSE);
873
874   account_a = empathy_contact_get_account (EMPATHY_CONTACT (v1));
875   account_b = empathy_contact_get_account (EMPATHY_CONTACT (v2));
876
877   id_a = empathy_contact_get_id (EMPATHY_CONTACT (v1));
878   id_b = empathy_contact_get_id (EMPATHY_CONTACT (v2));
879
880   return empathy_account_equal (account_a, account_b) &&
881       !tp_strdiff (id_a, id_b);
882 }
883
884 guint
885 empathy_contact_hash (gconstpointer key)
886 {
887   EmpathyContactPriv *priv;
888
889   g_return_val_if_fail (EMPATHY_IS_CONTACT (key), +1);
890
891   priv = GET_PRIV (EMPATHY_CONTACT (key));
892
893   if (priv->hash == 0)
894     {
895       priv->hash = empathy_account_hash (priv->account) ^
896           g_str_hash (priv->id);
897     }
898
899   return priv->hash;
900 }
901
902 void empathy_contact_call_when_ready (EmpathyContact *contact,
903   EmpathyContactReady ready, EmpathyContactReadyCb *callback,
904   gpointer user_data, GDestroyNotify destroy, GObject *weak_object)
905 {
906   EmpathyContactPriv *priv = GET_PRIV (contact);
907
908   g_return_if_fail (contact != NULL);
909   g_return_if_fail (callback != NULL);
910
911   if (contact_is_ready (contact, ready))
912     {
913       callback (contact, NULL, user_data, weak_object);
914       if (destroy != NULL)
915         destroy (user_data);
916     }
917   else
918     {
919       ReadyCbData *d = g_slice_new0 (ReadyCbData);
920       d->ready = ready;
921       d->callback = callback;
922       d->user_data = user_data;
923       d->destroy = destroy;
924       d->weak_object = weak_object;
925
926       if (weak_object != NULL)
927         g_object_weak_ref (weak_object, contact_weak_object_notify, contact);
928
929       priv->ready_callbacks = g_list_prepend (priv->ready_callbacks, d);
930     }
931 }
932
933 static gboolean
934 contact_is_ready_func (GObject *contact,
935                        gpointer user_data)
936 {
937   return contact_is_ready (EMPATHY_CONTACT (contact),
938     GPOINTER_TO_UINT (user_data));
939 }
940
941 void
942 empathy_contact_run_until_ready (EmpathyContact *contact,
943                                  EmpathyContactReady ready,
944                                  GMainLoop **loop)
945 {
946   empathy_run_until_ready_full (contact, "notify::ready",
947       contact_is_ready_func, GUINT_TO_POINTER (ready),
948       loop);
949 }
950
951 static gchar *
952 contact_get_avatar_filename (EmpathyContact *contact,
953                              const gchar *token)
954 {
955   EmpathyContactPriv *priv = GET_PRIV (contact);
956   gchar *avatar_path;
957   gchar *avatar_file;
958   gchar *token_escaped;
959   gchar *contact_escaped;
960
961   if (EMP_STR_EMPTY (priv->id))
962     return NULL;
963
964   contact_escaped = tp_escape_as_identifier (priv->id);
965   token_escaped = tp_escape_as_identifier (token);
966
967   avatar_path = g_build_filename (g_get_user_cache_dir (),
968       PACKAGE_NAME,
969       "avatars",
970       mc_account_get_unique_name (priv->account),
971       contact_escaped,
972       NULL);
973   g_mkdir_with_parents (avatar_path, 0700);
974
975   avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
976
977   g_free (contact_escaped);
978   g_free (token_escaped);
979   g_free (avatar_path);
980
981   return avatar_file;
982 }
983
984 void
985 empathy_contact_load_avatar_data (EmpathyContact *contact,
986                                   const guchar  *data,
987                                   const gsize len,
988                                   const gchar *format,
989                                   const gchar *token)
990 {
991   EmpathyAvatar *avatar;
992   gchar *filename;
993   GError *error = NULL;
994
995   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
996   g_return_if_fail (data != NULL);
997   g_return_if_fail (len > 0);
998   g_return_if_fail (format != NULL);
999   g_return_if_fail (!EMP_STR_EMPTY (token));
1000
1001   /* Load and set the avatar */
1002   avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
1003       g_strdup (token));
1004   empathy_contact_set_avatar (contact, avatar);
1005   empathy_avatar_unref (avatar);
1006
1007   /* Save to cache if not yet in it */
1008   filename = contact_get_avatar_filename (contact, token);
1009   if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
1010     {
1011       if (!empathy_avatar_save_to_file (avatar, filename, &error))
1012         {
1013           DEBUG ("Failed to save avatar in cache: %s",
1014             error ? error->message : "No error given");
1015           g_clear_error (&error);
1016         }
1017       else
1018           DEBUG ("Avatar saved to %s", filename);
1019     }
1020   g_free (filename);
1021 }
1022
1023 gboolean
1024 empathy_contact_load_avatar_cache (EmpathyContact *contact,
1025                                    const gchar *token)
1026 {
1027   EmpathyAvatar *avatar = NULL;
1028   gchar *filename;
1029   gchar *data = NULL;
1030   gsize len;
1031   GError *error = NULL;
1032
1033   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
1034   g_return_val_if_fail (!EMP_STR_EMPTY (token), FALSE);
1035
1036   /* Load the avatar from file if it exists */
1037   filename = contact_get_avatar_filename (contact, token);
1038   if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
1039     {
1040       if (!g_file_get_contents (filename, &data, &len, &error))
1041         {
1042           DEBUG ("Failed to load avatar from cache: %s",
1043             error ? error->message : "No error given");
1044           g_clear_error (&error);
1045         }
1046     }
1047
1048   if (data)
1049     {
1050       DEBUG ("Avatar loaded from %s", filename);
1051       avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
1052       empathy_contact_set_avatar (contact, avatar);
1053       empathy_avatar_unref (avatar);
1054     }
1055
1056   g_free (filename);
1057
1058   return data != NULL;
1059 }
1060
1061 GType
1062 empathy_avatar_get_type (void)
1063 {
1064   static GType type_id = 0;
1065
1066   if (!type_id)
1067     {
1068       type_id = g_boxed_type_register_static ("EmpathyAvatar",
1069           (GBoxedCopyFunc) empathy_avatar_ref,
1070           (GBoxedFreeFunc) empathy_avatar_unref);
1071     }
1072
1073   return type_id;
1074 }
1075
1076 EmpathyAvatar *
1077 empathy_avatar_new (guchar *data,
1078                     gsize len,
1079                     gchar *format,
1080                     gchar *token)
1081 {
1082   EmpathyAvatar *avatar;
1083
1084   avatar = g_slice_new0 (EmpathyAvatar);
1085   avatar->data = data;
1086   avatar->len = len;
1087   avatar->format = format;
1088   avatar->token = token;
1089   avatar->refcount = 1;
1090
1091   return avatar;
1092 }
1093
1094 void
1095 empathy_avatar_unref (EmpathyAvatar *avatar)
1096 {
1097   g_return_if_fail (avatar != NULL);
1098
1099   avatar->refcount--;
1100   if (avatar->refcount == 0)
1101     {
1102       g_free (avatar->data);
1103       g_free (avatar->format);
1104       g_free (avatar->token);
1105       g_slice_free (EmpathyAvatar, avatar);
1106     }
1107 }
1108
1109 EmpathyAvatar *
1110 empathy_avatar_ref (EmpathyAvatar *avatar)
1111 {
1112   g_return_val_if_fail (avatar != NULL, NULL);
1113
1114   avatar->refcount++;
1115
1116   return avatar;
1117 }
1118
1119 /**
1120  * empathy_avatar_save_to_file:
1121  * @avatar: the avatar
1122  * @filename: name of a file to write avatar to
1123  * @error: return location for a GError, or NULL
1124  *
1125  * Save the avatar to a file named filename
1126  *
1127  * Returns: %TRUE on success, %FALSE if an error occurred 
1128  */
1129 gboolean
1130 empathy_avatar_save_to_file (EmpathyAvatar *self,
1131                              const gchar *filename,
1132                              GError **error)
1133 {
1134   return g_file_set_contents (filename, self->data, self->len, error);
1135 }
1136