]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
Added ft-related functions to EmpathyContact. (Jonny Lamb)
[empathy.git] / libempathy / empathy-contact.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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  */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #include <glib/gi18n.h>
31
32 #include <telepathy-glib/util.h>
33
34 #include "empathy-contact.h"
35 #include "empathy-utils.h"
36 #include "empathy-enum-types.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
39 #include "empathy-debug.h"
40
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact)
42 typedef struct {
43   gchar *id;
44   gchar *name;
45   EmpathyAvatar *avatar;
46   McAccount *account;
47   McPresence presence;
48   gchar *presence_message;
49   guint handle;
50   EmpathyCapabilities capabilities;
51   gboolean is_user;
52   guint hash;
53   EmpathyContactReady ready;
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_ID,
68   PROP_NAME,
69   PROP_AVATAR,
70   PROP_ACCOUNT,
71   PROP_PRESENCE,
72   PROP_PRESENCE_MESSAGE,
73   PROP_HANDLE,
74   PROP_CAPABILITIES,
75   PROP_IS_USER,
76   PROP_READY
77 };
78
79 static void
80 empathy_contact_class_init (EmpathyContactClass *class)
81 {
82   GObjectClass *object_class;
83
84   object_class = G_OBJECT_CLASS (class);
85
86   object_class->finalize = contact_finalize;
87   object_class->get_property = contact_get_property;
88   object_class->set_property = contact_set_property;
89
90   g_object_class_install_property (object_class,
91       PROP_ID,
92       g_param_spec_string ("id",
93         "Contact id",
94         "String identifying contact",
95         NULL,
96         G_PARAM_READWRITE));
97
98   g_object_class_install_property (object_class,
99       PROP_NAME,
100       g_param_spec_string ("name",
101         "Contact Name",
102         "The name of the contact",
103         NULL,
104         G_PARAM_READWRITE));
105
106   g_object_class_install_property (object_class,
107       PROP_AVATAR,
108       g_param_spec_boxed ("avatar",
109         "Avatar image",
110         "The avatar image",
111         EMPATHY_TYPE_AVATAR,
112         G_PARAM_READWRITE));
113
114   g_object_class_install_property (object_class,
115       PROP_ACCOUNT,
116       g_param_spec_object ("account",
117         "Contact Account",
118         "The account associated with the contact",
119         MC_TYPE_ACCOUNT,
120         G_PARAM_READWRITE));
121
122   g_object_class_install_property (object_class,
123       PROP_PRESENCE,
124       g_param_spec_uint ("presence",
125         "Contact presence",
126         "Presence of contact",
127         MC_PRESENCE_UNSET,
128         LAST_MC_PRESENCE,
129         MC_PRESENCE_UNSET,
130         G_PARAM_READWRITE));
131
132   g_object_class_install_property (object_class,
133       PROP_PRESENCE_MESSAGE,
134       g_param_spec_string ("presence-message",
135         "Contact presence message",
136         "Presence message of contact",
137         NULL,
138         G_PARAM_READWRITE));
139
140   g_object_class_install_property (object_class,
141       PROP_HANDLE,
142       g_param_spec_uint ("handle",
143         "Contact Handle",
144         "The handle of the contact",
145         0,
146         G_MAXUINT,
147         0,
148         G_PARAM_READWRITE));
149
150   g_object_class_install_property (object_class,
151       PROP_CAPABILITIES,
152       g_param_spec_flags ("capabilities",
153         "Contact Capabilities",
154         "Capabilities of the contact",
155         EMPATHY_TYPE_CAPABILITIES,
156         EMPATHY_CAPABILITIES_UNKNOWN,
157         G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
158
159   g_object_class_install_property (object_class,
160       PROP_IS_USER,
161       g_param_spec_boolean ("is-user",
162         "Contact is-user",
163         "Is contact the user",
164         FALSE,
165         G_PARAM_READWRITE));
166
167   g_object_class_install_property (object_class,
168       PROP_READY,
169       g_param_spec_flags ("ready",
170         "Contact ready flags",
171         "Flags for ready properties",
172         EMPATHY_TYPE_CONTACT_READY,
173         EMPATHY_CONTACT_READY_NONE,
174         G_PARAM_READABLE));
175
176   g_type_class_add_private (object_class, sizeof (EmpathyContactPriv));
177 }
178
179 static void
180 empathy_contact_init (EmpathyContact *contact)
181 {
182   EmpathyContactPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (contact,
183     EMPATHY_TYPE_CONTACT, EmpathyContactPriv);
184
185   contact->priv = priv;
186 }
187
188 static void
189 contact_finalize (GObject *object)
190 {
191   EmpathyContactPriv *priv;
192
193   priv = GET_PRIV (object);
194
195   DEBUG ("finalize: %p", object);
196
197   g_free (priv->name);
198   g_free (priv->id);
199   g_free (priv->presence_message);
200
201   if (priv->avatar)
202       empathy_avatar_unref (priv->avatar);
203
204   if (priv->account)
205       g_object_unref (priv->account);
206
207   G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
208 }
209
210 static void
211 contact_get_property (GObject *object,
212                       guint param_id,
213                       GValue *value,
214                       GParamSpec *pspec)
215 {
216   EmpathyContactPriv *priv;
217
218   priv = GET_PRIV (object);
219
220   switch (param_id)
221     {
222       case PROP_ID:
223         g_value_set_string (value, priv->id);
224         break;
225       case PROP_NAME:
226         g_value_set_string (value,
227             empathy_contact_get_name (EMPATHY_CONTACT (object)));
228         break;
229       case PROP_AVATAR:
230         g_value_set_boxed (value, priv->avatar);
231         break;
232       case PROP_ACCOUNT:
233         g_value_set_object (value, priv->account);
234         break;
235       case PROP_PRESENCE:
236         g_value_set_uint (value, priv->presence);
237         break;
238       case PROP_PRESENCE_MESSAGE:
239         g_value_set_string (value, priv->presence_message);
240         break;
241       case PROP_HANDLE:
242         g_value_set_uint (value, priv->handle);
243         break;
244       case PROP_CAPABILITIES:
245         g_value_set_flags (value, priv->capabilities);
246         break;
247       case PROP_IS_USER:
248         g_value_set_boolean (value, priv->is_user);
249         break;
250       case PROP_READY:
251         g_value_set_flags (value, priv->ready);
252         break;
253       default:
254         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
255         break;
256     };
257 }
258
259 static void
260 contact_set_property (GObject *object,
261                       guint param_id,
262                       const GValue *value,
263                       GParamSpec *pspec)
264 {
265   EmpathyContactPriv *priv;
266
267   priv = GET_PRIV (object);
268
269   switch (param_id)
270     {
271       case PROP_ID:
272         empathy_contact_set_id (EMPATHY_CONTACT (object),
273         g_value_get_string (value));
274         break;
275       case PROP_NAME:
276         empathy_contact_set_name (EMPATHY_CONTACT (object),
277         g_value_get_string (value));
278         break;
279       case PROP_AVATAR:
280         empathy_contact_set_avatar (EMPATHY_CONTACT (object),
281         g_value_get_boxed (value));
282         break;
283       case PROP_ACCOUNT:
284         empathy_contact_set_account (EMPATHY_CONTACT (object),
285         MC_ACCOUNT (g_value_get_object (value)));
286         break;
287       case PROP_PRESENCE:
288         empathy_contact_set_presence (EMPATHY_CONTACT (object),
289         g_value_get_uint (value));
290         break;
291       case PROP_PRESENCE_MESSAGE:
292         empathy_contact_set_presence_message (EMPATHY_CONTACT (object),
293         g_value_get_string (value));
294         break;
295       case PROP_HANDLE:
296         empathy_contact_set_handle (EMPATHY_CONTACT (object),
297         g_value_get_uint (value));
298         break;
299       case PROP_CAPABILITIES:
300         empathy_contact_set_capabilities (EMPATHY_CONTACT (object),
301         g_value_get_flags (value));
302         break;
303       case PROP_IS_USER:
304         empathy_contact_set_is_user (EMPATHY_CONTACT (object),
305         g_value_get_boolean (value));
306         break;
307       default:
308         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
309         break;
310     };
311 }
312
313 static void
314 contact_set_ready_flag (EmpathyContact *contact,
315                         EmpathyContactReady flag)
316 {
317   EmpathyContactPriv *priv = GET_PRIV (contact);
318
319   if (!(priv->ready & flag))
320     {
321       priv->ready |= flag;
322       g_object_notify (G_OBJECT (contact), "ready");
323     }
324 }
325
326 EmpathyContact *
327 empathy_contact_new (McAccount *account)
328 {
329   return g_object_new (EMPATHY_TYPE_CONTACT,
330       "account", account,
331       NULL);
332 }
333
334 EmpathyContact *
335 empathy_contact_new_full (McAccount  *account,
336                           const gchar *id,
337                           const gchar *name)
338 {
339   return g_object_new (EMPATHY_TYPE_CONTACT,
340       "account", account,
341        "name", name,
342        "id", id,
343        NULL);
344 }
345
346 const gchar *
347 empathy_contact_get_id (EmpathyContact *contact)
348 {
349   EmpathyContactPriv *priv;
350
351   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
352
353   priv = GET_PRIV (contact);
354
355   return priv->id;
356 }
357
358 void
359 empathy_contact_set_id (EmpathyContact *contact,
360                         const gchar *id)
361 {
362   EmpathyContactPriv *priv;
363
364   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
365   g_return_if_fail (id != NULL);
366
367   priv = GET_PRIV (contact);
368
369   /* We temporally ref the contact because it could be destroyed
370    * during the signal emition */
371   g_object_ref (contact);
372   if (tp_strdiff (id, priv->id))
373     {
374       g_free (priv->id);
375       priv->id = g_strdup (id);
376
377       g_object_notify (G_OBJECT (contact), "id");
378       if (G_STR_EMPTY (priv->name))
379           g_object_notify (G_OBJECT (contact), "name");
380     }
381   contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_ID);
382
383   g_object_unref (contact);
384 }
385
386 const gchar *
387 empathy_contact_get_name (EmpathyContact *contact)
388 {
389   EmpathyContactPriv *priv;
390
391   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
392
393   priv = GET_PRIV (contact);
394
395   if (G_STR_EMPTY (priv->name))
396       return empathy_contact_get_id (contact);
397
398   return priv->name;
399 }
400
401 void
402 empathy_contact_set_name (EmpathyContact *contact,
403                           const gchar *name)
404 {
405   EmpathyContactPriv *priv;
406
407   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
408
409   priv = GET_PRIV (contact);
410
411   g_object_ref (contact);
412   if (tp_strdiff (name, priv->name))
413     {
414       g_free (priv->name);
415       priv->name = g_strdup (name);
416       g_object_notify (G_OBJECT (contact), "name");
417     }
418   contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_NAME);
419   g_object_unref (contact);
420 }
421
422 EmpathyAvatar *
423 empathy_contact_get_avatar (EmpathyContact *contact)
424 {
425   EmpathyContactPriv *priv;
426
427   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
428
429   priv = GET_PRIV (contact);
430
431   return priv->avatar;
432 }
433
434 void
435 empathy_contact_set_avatar (EmpathyContact *contact,
436                             EmpathyAvatar *avatar)
437 {
438   EmpathyContactPriv *priv;
439
440   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
441
442   priv = GET_PRIV (contact);
443
444   if (priv->avatar == avatar)
445     return;
446
447   if (priv->avatar)
448     {
449       empathy_avatar_unref (priv->avatar);
450       priv->avatar = NULL;
451     }
452
453   if (avatar)
454       priv->avatar = empathy_avatar_ref (avatar);
455
456   g_object_notify (G_OBJECT (contact), "avatar");
457 }
458
459 McAccount *
460 empathy_contact_get_account (EmpathyContact *contact)
461 {
462   EmpathyContactPriv *priv;
463
464   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
465
466   priv = GET_PRIV (contact);
467
468   return priv->account;
469 }
470
471 void
472 empathy_contact_set_account (EmpathyContact *contact,
473                              McAccount *account)
474 {
475   EmpathyContactPriv *priv;
476
477   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
478   g_return_if_fail (MC_IS_ACCOUNT (account));
479
480   priv = GET_PRIV (contact);
481
482   if (account == priv->account)
483     return;
484
485   if (priv->account)
486       g_object_unref (priv->account);
487   priv->account = g_object_ref (account);
488
489   g_object_notify (G_OBJECT (contact), "account");
490 }
491
492 McPresence
493 empathy_contact_get_presence (EmpathyContact *contact)
494 {
495   EmpathyContactPriv *priv;
496
497   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), MC_PRESENCE_UNSET);
498
499   priv = GET_PRIV (contact);
500
501   return priv->presence;
502 }
503
504 void
505 empathy_contact_set_presence (EmpathyContact *contact,
506                               McPresence presence)
507 {
508   EmpathyContactPriv *priv;
509
510   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
511
512   priv = GET_PRIV (contact);
513
514   if (presence == priv->presence)
515     return;
516
517   priv->presence = presence;
518
519   g_object_notify (G_OBJECT (contact), "presence");
520 }
521
522 const gchar *
523 empathy_contact_get_presence_message (EmpathyContact *contact)
524 {
525   EmpathyContactPriv *priv;
526
527   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
528
529   priv = GET_PRIV (contact);
530
531   return priv->presence_message;
532 }
533
534 void
535 empathy_contact_set_presence_message (EmpathyContact *contact,
536                                       const gchar *message)
537 {
538   EmpathyContactPriv *priv = GET_PRIV (contact);
539
540   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
541
542   if (!tp_strdiff (message, priv->presence_message))
543     return;
544
545   g_free (priv->presence_message);
546   priv->presence_message = g_strdup (message);
547
548   g_object_notify (G_OBJECT (contact), "presence-message");
549 }
550
551 guint
552 empathy_contact_get_handle (EmpathyContact *contact)
553 {
554   EmpathyContactPriv *priv;
555
556   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
557
558   priv = GET_PRIV (contact);
559
560   return priv->handle;
561 }
562
563 static gboolean
564 is_salut (EmpathyContact *contact)
565 {
566   McAccount *account;
567   McProfile *profile;
568   const gchar *name;
569
570   account = empathy_contact_get_account (contact);
571   profile = mc_account_get_profile (account);
572   name = mc_profile_get_protocol_name (profile);
573
574   return (strcmp (name, "local-xmpp") == 0);
575 }
576
577 void
578 empathy_contact_set_handle (EmpathyContact *contact,
579                             guint handle)
580 {
581   EmpathyContactPriv *priv;
582
583   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
584
585   priv = GET_PRIV (contact);
586
587   g_object_ref (contact);
588   if (handle != priv->handle)
589     {
590       priv->handle = handle;
591       g_object_notify (G_OBJECT (contact), "handle");
592
593       /* FIXME salut does not yet support the Capabilities interface, so for
594        * now we use this hack.
595        */
596       if (is_salut (contact))
597         {
598           EmpathyCapabilities caps;
599
600           caps = empathy_contact_get_capabilities (contact);
601           caps |= EMPATHY_CAPABILITIES_FT;
602
603           empathy_contact_set_capabilities (contact, caps);
604         }
605     }
606   contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
607   g_object_unref (contact);
608 }
609
610 EmpathyCapabilities
611 empathy_contact_get_capabilities (EmpathyContact *contact)
612 {
613   EmpathyContactPriv *priv;
614
615   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
616
617   priv = GET_PRIV (contact);
618
619   return priv->capabilities;
620 }
621
622 void
623 empathy_contact_set_capabilities (EmpathyContact *contact,
624                                   EmpathyCapabilities capabilities)
625 {
626   EmpathyContactPriv *priv;
627
628   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
629
630   priv = GET_PRIV (contact);
631
632   if (priv->capabilities == capabilities)
633     return;
634
635   priv->capabilities = capabilities;
636
637   g_object_notify (G_OBJECT (contact), "capabilities");
638 }
639
640 gboolean
641 empathy_contact_is_user (EmpathyContact *contact)
642 {
643   EmpathyContactPriv *priv;
644
645   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
646
647   priv = GET_PRIV (contact);
648
649   return priv->is_user;
650 }
651
652 void
653 empathy_contact_set_is_user (EmpathyContact *contact,
654                              gboolean is_user)
655 {
656   EmpathyContactPriv *priv;
657
658   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
659
660   priv = GET_PRIV (contact);
661
662   if (priv->is_user == is_user)
663     return;
664
665   priv->is_user = is_user;
666
667   g_object_notify (G_OBJECT (contact), "is-user");
668 }
669
670 gboolean
671 empathy_contact_is_online (EmpathyContact *contact)
672 {
673   EmpathyContactPriv *priv;
674
675   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
676
677   priv = GET_PRIV (contact);
678
679   return (priv->presence > MC_PRESENCE_OFFLINE);
680 }
681
682 const gchar *
683 empathy_contact_get_status (EmpathyContact *contact)
684 {
685   EmpathyContactPriv *priv;
686
687   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
688
689   priv = GET_PRIV (contact);
690
691   if (priv->presence_message)
692     return priv->presence_message;
693
694   return empathy_presence_get_default_message (priv->presence);
695 }
696
697 gboolean
698 empathy_contact_can_voip (EmpathyContact *contact)
699 {
700   EmpathyContactPriv *priv;
701
702   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
703
704   priv = GET_PRIV (contact);
705
706   return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
707       EMPATHY_CAPABILITIES_VIDEO);
708 }
709
710 gboolean
711 empathy_contact_can_send_files (EmpathyContact *contact)
712 {
713   EmpathyContactPriv *priv;
714
715   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
716
717   priv = GET_PRIV (contact);
718
719   return priv->capabilities & EMPATHY_CAPABILITIES_FT;
720 }
721
722 EmpathyContactReady
723 empathy_contact_get_ready (EmpathyContact *contact)
724 {
725   EmpathyContactPriv *priv;
726
727   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
728
729   priv = GET_PRIV (contact);
730
731   return priv->ready;
732 }
733
734 gboolean
735 empathy_contact_equal (gconstpointer v1,
736                        gconstpointer v2)
737 {
738   McAccount *account_a;
739   McAccount *account_b;
740   const gchar *id_a;
741   const gchar *id_b;
742
743   g_return_val_if_fail (EMPATHY_IS_CONTACT (v1), FALSE);
744   g_return_val_if_fail (EMPATHY_IS_CONTACT (v2), FALSE);
745
746   account_a = empathy_contact_get_account (EMPATHY_CONTACT (v1));
747   account_b = empathy_contact_get_account (EMPATHY_CONTACT (v2));
748
749   id_a = empathy_contact_get_id (EMPATHY_CONTACT (v1));
750   id_b = empathy_contact_get_id (EMPATHY_CONTACT (v2));
751
752   return empathy_account_equal (account_a, account_b) &&
753       !tp_strdiff (id_a, id_b);
754 }
755
756 guint
757 empathy_contact_hash (gconstpointer key)
758 {
759   EmpathyContactPriv *priv;
760
761   g_return_val_if_fail (EMPATHY_IS_CONTACT (key), +1);
762
763   priv = GET_PRIV (EMPATHY_CONTACT (key));
764
765   if (priv->hash == 0)
766     {
767       priv->hash = empathy_account_hash (priv->account) ^
768           g_str_hash (priv->id);
769     }
770
771   return priv->hash;
772 }
773
774 static gboolean
775 contact_is_ready_func (GObject *contact,
776                        gpointer user_data)
777 {
778   EmpathyContactPriv *priv = GET_PRIV (contact);
779   EmpathyContactReady ready;
780
781   ready = GPOINTER_TO_UINT (user_data);
782
783   /* When the name is NULL, empathy_contact_get_name() fallback to the id.
784    * When the caller want to wait the name to be ready, it also want to wait
785    * the id to be ready in case of fallback. */
786   if ((ready & EMPATHY_CONTACT_READY_NAME) && G_STR_EMPTY (priv->name))
787       ready |= EMPATHY_CONTACT_READY_ID;
788
789   return (priv->ready & ready) == ready;
790 }
791
792 void
793 empathy_contact_run_until_ready (EmpathyContact *contact,
794                                  EmpathyContactReady ready,
795                                  GMainLoop **loop)
796 {
797   empathy_run_until_ready_full (contact, "notify::ready",
798       contact_is_ready_func, GUINT_TO_POINTER (ready),
799       loop);
800 }
801
802 static gchar *
803 contact_get_avatar_filename (EmpathyContact *contact,
804                              const gchar *token)
805 {
806   EmpathyContactPriv *priv = GET_PRIV (contact);
807   gchar *avatar_path;
808   gchar *avatar_file;
809   gchar *token_escaped;
810   gchar *contact_escaped;
811
812   if (G_STR_EMPTY (priv->id))
813     return NULL;
814
815   contact_escaped = tp_escape_as_identifier (priv->id);
816   token_escaped = tp_escape_as_identifier (token);
817
818   avatar_path = g_build_filename (g_get_user_cache_dir (),
819       PACKAGE_NAME,
820       "avatars",
821       mc_account_get_unique_name (priv->account),
822       contact_escaped,
823       NULL);
824   g_mkdir_with_parents (avatar_path, 0700);
825
826   avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
827
828   g_free (contact_escaped);
829   g_free (token_escaped);
830   g_free (avatar_path);
831
832   return avatar_file;
833 }
834
835 void
836 empathy_contact_load_avatar_data (EmpathyContact *contact,
837                                   const guchar  *data,
838                                   const gsize len,
839                                   const gchar *format,
840                                   const gchar *token)
841 {
842   EmpathyAvatar *avatar;
843   gchar *filename;
844   GError *error = NULL;
845
846   g_return_if_fail (EMPATHY_IS_CONTACT (contact));
847   g_return_if_fail (data != NULL);
848   g_return_if_fail (len > 0);
849   g_return_if_fail (format != NULL);
850   g_return_if_fail (!G_STR_EMPTY (token));
851
852   /* Load and set the avatar */
853   avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
854       g_strdup (token));
855   empathy_contact_set_avatar (contact, avatar);
856   empathy_avatar_unref (avatar);
857
858   /* Save to cache if not yet in it */
859   filename = contact_get_avatar_filename (contact, token);
860   if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
861     {
862       if (!empathy_avatar_save_to_file (avatar, filename, &error))
863         {
864           DEBUG ("Failed to save avatar in cache: %s",
865             error ? error->message : "No error given");
866           g_clear_error (&error);
867         }
868       else
869           DEBUG ("Avatar saved to %s", filename);
870     }
871   g_free (filename);
872 }
873
874 gboolean
875 empathy_contact_load_avatar_cache (EmpathyContact *contact,
876                                    const gchar *token)
877 {
878   EmpathyAvatar *avatar = NULL;
879   gchar *filename;
880   gchar *data = NULL;
881   gsize len;
882   GError *error = NULL;
883
884   g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
885   g_return_val_if_fail (!G_STR_EMPTY (token), FALSE);
886
887   /* Load the avatar from file if it exists */
888   filename = contact_get_avatar_filename (contact, token);
889   if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
890     {
891       if (!g_file_get_contents (filename, &data, &len, &error))
892         {
893           DEBUG ("Failed to load avatar from cache: %s",
894             error ? error->message : "No error given");
895           g_clear_error (&error);
896         }
897     }
898
899   if (data)
900     {
901       DEBUG ("Avatar loaded from %s", filename);
902       avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
903       empathy_contact_set_avatar (contact, avatar);
904       empathy_avatar_unref (avatar);
905     }
906
907   g_free (filename);
908
909   return data != NULL;
910 }
911
912 GType
913 empathy_avatar_get_type (void)
914 {
915   static GType type_id = 0;
916
917   if (!type_id)
918     {
919       type_id = g_boxed_type_register_static ("EmpathyAvatar",
920           (GBoxedCopyFunc) empathy_avatar_ref,
921           (GBoxedFreeFunc) empathy_avatar_unref);
922     }
923
924   return type_id;
925 }
926
927 EmpathyAvatar *
928 empathy_avatar_new (guchar *data,
929                     gsize len,
930                     gchar *format,
931                     gchar *token)
932 {
933   EmpathyAvatar *avatar;
934
935   avatar = g_slice_new0 (EmpathyAvatar);
936   avatar->data = data;
937   avatar->len = len;
938   avatar->format = format;
939   avatar->token = token;
940   avatar->refcount = 1;
941
942   return avatar;
943 }
944
945 void
946 empathy_avatar_unref (EmpathyAvatar *avatar)
947 {
948   g_return_if_fail (avatar != NULL);
949
950   avatar->refcount--;
951   if (avatar->refcount == 0)
952     {
953       g_free (avatar->data);
954       g_free (avatar->format);
955       g_free (avatar->token);
956       g_slice_free (EmpathyAvatar, avatar);
957     }
958 }
959
960 EmpathyAvatar *
961 empathy_avatar_ref (EmpathyAvatar *avatar)
962 {
963   g_return_val_if_fail (avatar != NULL, NULL);
964
965   avatar->refcount++;
966
967   return avatar;
968 }
969
970 /**
971  * empathy_avatar_save_to_file:
972  * @avatar: the avatar
973  * @filename: name of a file to write avatar to
974  * @error: return location for a GError, or NULL
975  *
976  * Save the avatar to a file named filename
977  *
978  * Returns: %TRUE on success, %FALSE if an error occurred 
979  */
980 gboolean
981 empathy_avatar_save_to_file (EmpathyAvatar *self,
982                              const gchar *filename,
983                              GError **error)
984 {
985   return g_file_set_contents (filename, self->data, self->len, error);
986 }
987