]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact.c
switch empathy-contact to Collabora coding style (Guillaume Desmottes)
[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     {
203       empathy_avatar_unref (priv->avatar);
204     }
205
206         if (priv->account)
207     {
208       g_object_unref (priv->account);
209     }
210
211         G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object);
212 }
213
214 static void
215 contact_get_property (GObject *object,
216                       guint param_id,
217                       GValue *value,
218                       GParamSpec *pspec)
219 {
220         EmpathyContactPriv *priv;
221
222         priv = GET_PRIV (object);
223
224         switch (param_id) {
225         case PROP_ID:
226                 g_value_set_string (value, priv->id);
227                 break;
228         case PROP_NAME:
229                 g_value_set_string (value,
230         empathy_contact_get_name (EMPATHY_CONTACT (object)));
231                 break;
232         case PROP_AVATAR:
233                 g_value_set_boxed (value, priv->avatar);
234                 break;
235         case PROP_ACCOUNT:
236                 g_value_set_object (value, priv->account);
237                 break;
238         case PROP_PRESENCE:
239                 g_value_set_uint (value, priv->presence);
240                 break;
241         case PROP_PRESENCE_MESSAGE:
242                 g_value_set_string (value, priv->presence_message);
243                 break;
244         case PROP_HANDLE:
245                 g_value_set_uint (value, priv->handle);
246                 break;
247         case PROP_CAPABILITIES:
248                 g_value_set_flags (value, priv->capabilities);
249                 break;
250         case PROP_IS_USER:
251                 g_value_set_boolean (value, priv->is_user);
252                 break;
253         case PROP_READY:
254                 g_value_set_flags (value, priv->ready);
255                 break;
256         default:
257                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
258                 break;
259         };
260 }
261
262 static void
263 contact_set_property (GObject *object,
264                       guint param_id,
265                       const GValue *value,
266                       GParamSpec *pspec)
267 {
268         EmpathyContactPriv *priv;
269
270         priv = GET_PRIV (object);
271
272         switch (param_id) {
273         case PROP_ID:
274                 empathy_contact_set_id (EMPATHY_CONTACT (object),
275         g_value_get_string (value));
276                 break;
277         case PROP_NAME:
278                 empathy_contact_set_name (EMPATHY_CONTACT (object),
279         g_value_get_string (value));
280                 break;
281         case PROP_AVATAR:
282                 empathy_contact_set_avatar (EMPATHY_CONTACT (object),
283         g_value_get_boxed (value));
284                 break;
285         case PROP_ACCOUNT:
286                 empathy_contact_set_account (EMPATHY_CONTACT (object),
287         MC_ACCOUNT (g_value_get_object (value)));
288                 break;
289         case PROP_PRESENCE:
290                 empathy_contact_set_presence (EMPATHY_CONTACT (object),
291         g_value_get_uint (value));
292                 break;
293         case PROP_PRESENCE_MESSAGE:
294                 empathy_contact_set_presence_message (EMPATHY_CONTACT (object),
295         g_value_get_string (value));
296                 break;
297         case PROP_HANDLE:
298                 empathy_contact_set_handle (EMPATHY_CONTACT (object),
299         g_value_get_uint (value));
300                 break;
301         case PROP_CAPABILITIES:
302                 empathy_contact_set_capabilities (EMPATHY_CONTACT (object),
303         g_value_get_flags (value));
304                 break;
305         case PROP_IS_USER:
306                 empathy_contact_set_is_user (EMPATHY_CONTACT (object),
307                                 g_value_get_boolean (value));
308                 break;
309         default:
310                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
311                 break;
312         };
313 }
314
315 static void
316 contact_set_ready_flag (EmpathyContact *contact,
317                         EmpathyContactReady flag)
318 {
319         EmpathyContactPriv *priv = GET_PRIV (contact);
320
321         if (!(priv->ready & flag))
322     {
323       priv->ready |= flag;
324       g_object_notify (G_OBJECT (contact), "ready");
325     }
326 }
327
328 EmpathyContact *
329 empathy_contact_new (McAccount *account)
330 {
331         return g_object_new (EMPATHY_TYPE_CONTACT,
332       "account", account,
333                         NULL);
334 }
335
336 EmpathyContact *
337 empathy_contact_new_full (McAccount  *account,
338                           const gchar *id,
339                           const gchar *name)
340 {
341   return g_object_new (EMPATHY_TYPE_CONTACT,
342       "account", account,
343        "name", name,
344        "id", id,
345        NULL);
346 }
347
348 const gchar *
349 empathy_contact_get_id (EmpathyContact *contact)
350 {
351         EmpathyContactPriv *priv;
352
353         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
354
355         priv = GET_PRIV (contact);
356
357         return priv->id;
358 }
359
360 void
361 empathy_contact_set_id (EmpathyContact *contact,
362                                     const gchar *id)
363 {
364         EmpathyContactPriv *priv;
365
366         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
367         g_return_if_fail (id != NULL);
368
369         priv = GET_PRIV (contact);
370
371         /* We temporally ref the contact because it could be destroyed
372          * during the signal emition */
373         g_object_ref (contact);
374         if (tp_strdiff (id, priv->id))
375     {
376       g_free (priv->id);
377       priv->id = g_strdup (id);
378
379       g_object_notify (G_OBJECT (contact), "id");
380       if (G_STR_EMPTY (priv->name))
381         {
382           g_object_notify (G_OBJECT (contact), "name");
383         }
384     }
385         contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_ID);
386
387         g_object_unref (contact);
388 }
389
390 const gchar *
391 empathy_contact_get_name (EmpathyContact *contact)
392 {
393         EmpathyContactPriv *priv;
394
395         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
396
397         priv = GET_PRIV (contact);
398
399         if (G_STR_EMPTY (priv->name))
400     {
401       return empathy_contact_get_id (contact);
402     }
403
404         return priv->name;
405 }
406
407 void
408 empathy_contact_set_name (EmpathyContact *contact,
409                           const gchar *name)
410 {
411         EmpathyContactPriv *priv;
412
413         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
414
415         priv = GET_PRIV (contact);
416
417         g_object_ref (contact);
418         if (tp_strdiff (name, priv->name))
419     {
420       g_free (priv->name);
421       priv->name = g_strdup (name);
422       g_object_notify (G_OBJECT (contact), "name");
423     }
424         contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_NAME);
425         g_object_unref (contact);
426 }
427
428 EmpathyAvatar *
429 empathy_contact_get_avatar (EmpathyContact *contact)
430 {
431         EmpathyContactPriv *priv;
432
433         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
434
435         priv = GET_PRIV (contact);
436
437         return priv->avatar;
438 }
439
440 void
441 empathy_contact_set_avatar (EmpathyContact *contact,
442                                               EmpathyAvatar *avatar)
443 {
444         EmpathyContactPriv *priv;
445
446         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
447
448         priv = GET_PRIV (contact);
449
450         if (priv->avatar == avatar)
451                 return;
452
453         if (priv->avatar)
454     {
455       empathy_avatar_unref (priv->avatar);
456       priv->avatar = NULL;
457     }
458
459         if (avatar)
460     {
461       priv->avatar = empathy_avatar_ref (avatar);
462     }
463
464         g_object_notify (G_OBJECT (contact), "avatar");
465 }
466
467 McAccount *
468 empathy_contact_get_account (EmpathyContact *contact)
469 {
470         EmpathyContactPriv *priv;
471
472         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
473
474         priv = GET_PRIV (contact);
475
476         return priv->account;
477 }
478
479 void
480 empathy_contact_set_account (EmpathyContact *contact,
481                                                McAccount *account)
482 {
483         EmpathyContactPriv *priv;
484
485         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
486         g_return_if_fail (MC_IS_ACCOUNT (account));
487
488         priv = GET_PRIV (contact);
489
490         if (account == priv->account)
491                 return;
492
493         if (priv->account)
494     {
495       g_object_unref (priv->account);
496     }
497         priv->account = g_object_ref (account);
498
499         g_object_notify (G_OBJECT (contact), "account");
500 }
501
502 McPresence
503 empathy_contact_get_presence (EmpathyContact *contact)
504 {
505         EmpathyContactPriv *priv;
506
507         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), MC_PRESENCE_UNSET);
508
509         priv = GET_PRIV (contact);
510
511         return priv->presence;
512 }
513
514 void
515 empathy_contact_set_presence (EmpathyContact *contact,
516                                                 McPresence presence)
517 {
518         EmpathyContactPriv *priv;
519
520         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
521
522         priv = GET_PRIV (contact);
523
524         if (presence == priv->presence)
525                 return;
526
527         priv->presence = presence;
528
529         g_object_notify (G_OBJECT (contact), "presence");
530 }
531
532 const gchar *
533 empathy_contact_get_presence_message (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         return priv->presence_message;
542 }
543
544 void
545 empathy_contact_set_presence_message (EmpathyContact *contact,
546                                                               const gchar *message)
547 {
548         EmpathyContactPriv *priv = GET_PRIV (contact);
549
550         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
551
552         if (!tp_strdiff (message, priv->presence_message))
553                 return;
554
555         g_free (priv->presence_message);
556         priv->presence_message = g_strdup (message);
557
558         g_object_notify (G_OBJECT (contact), "presence-message");
559 }
560
561 guint
562 empathy_contact_get_handle (EmpathyContact *contact)
563 {
564         EmpathyContactPriv *priv;
565
566         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
567
568         priv = GET_PRIV (contact);
569
570         return priv->handle;
571 }
572
573 void
574 empathy_contact_set_handle (EmpathyContact *contact,
575                                               guint handle)
576 {
577         EmpathyContactPriv *priv;
578
579         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
580
581         priv = GET_PRIV (contact);
582
583         g_object_ref (contact);
584         if (handle != priv->handle)
585     {
586       priv->handle = handle;
587       g_object_notify (G_OBJECT (contact), "handle");
588     }
589         contact_set_ready_flag (contact, EMPATHY_CONTACT_READY_HANDLE);
590         g_object_unref (contact);
591 }
592
593 EmpathyCapabilities
594 empathy_contact_get_capabilities (EmpathyContact *contact)
595 {
596         EmpathyContactPriv *priv;
597
598         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), 0);
599
600         priv = GET_PRIV (contact);
601
602         return priv->capabilities;
603 }
604
605 void
606 empathy_contact_set_capabilities (EmpathyContact *contact,
607                                   EmpathyCapabilities capabilities)
608 {
609         EmpathyContactPriv *priv;
610
611         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
612
613         priv = GET_PRIV (contact);
614
615         if (priv->capabilities == capabilities)
616                 return;
617
618         priv->capabilities = capabilities;
619
620         g_object_notify (G_OBJECT (contact), "capabilities");
621 }
622
623 gboolean
624 empathy_contact_is_user (EmpathyContact *contact)
625 {
626         EmpathyContactPriv *priv;
627
628         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
629
630         priv = GET_PRIV (contact);
631
632         return priv->is_user;
633 }
634
635 void
636 empathy_contact_set_is_user (EmpathyContact *contact,
637                                                gboolean is_user)
638 {
639         EmpathyContactPriv *priv;
640
641         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
642
643         priv = GET_PRIV (contact);
644
645         if (priv->is_user == is_user)
646                 return;
647
648         priv->is_user = is_user;
649
650         g_object_notify (G_OBJECT (contact), "is-user");
651 }
652
653 gboolean
654 empathy_contact_is_online (EmpathyContact *contact)
655 {
656         EmpathyContactPriv *priv;
657
658         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
659
660         priv = GET_PRIV (contact);
661
662         return (priv->presence > MC_PRESENCE_OFFLINE);
663 }
664
665 const gchar *
666 empathy_contact_get_status (EmpathyContact *contact)
667 {
668         EmpathyContactPriv *priv;
669
670         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), "");
671
672         priv = GET_PRIV (contact);
673
674         if (priv->presence_message)
675                 return priv->presence_message;
676
677         return empathy_presence_get_default_message (priv->presence);
678 }
679
680 gboolean
681 empathy_contact_can_voip (EmpathyContact *contact)
682 {
683         EmpathyContactPriv *priv;
684
685         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
686
687         priv = GET_PRIV (contact);
688
689         return priv->capabilities & (EMPATHY_CAPABILITIES_AUDIO |
690                                      EMPATHY_CAPABILITIES_VIDEO);
691 }
692
693 EmpathyContactReady
694 empathy_contact_get_ready (EmpathyContact *contact)
695 {
696         EmpathyContactPriv *priv;
697
698         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
699
700         priv = GET_PRIV (contact);
701
702         return priv->ready;
703 }
704
705 gboolean
706 empathy_contact_equal (gconstpointer v1,
707                                    gconstpointer v2)
708 {
709         McAccount *account_a;
710         McAccount *account_b;
711         const gchar *id_a;
712         const gchar *id_b;
713
714         g_return_val_if_fail (EMPATHY_IS_CONTACT (v1), FALSE);
715         g_return_val_if_fail (EMPATHY_IS_CONTACT (v2), FALSE);
716
717         account_a = empathy_contact_get_account (EMPATHY_CONTACT (v1));
718         account_b = empathy_contact_get_account (EMPATHY_CONTACT (v2));
719
720         id_a = empathy_contact_get_id (EMPATHY_CONTACT (v1));
721         id_b = empathy_contact_get_id (EMPATHY_CONTACT (v2));
722
723         return empathy_account_equal (account_a, account_b) &&
724                !tp_strdiff (id_a, id_b);
725 }
726
727 guint
728 empathy_contact_hash (gconstpointer key)
729 {
730         EmpathyContactPriv *priv;
731
732         g_return_val_if_fail (EMPATHY_IS_CONTACT (key), +1);
733
734         priv = GET_PRIV (EMPATHY_CONTACT (key));
735
736         if (priv->hash == 0) {
737                 priv->hash = empathy_account_hash (priv->account) ^
738                              g_str_hash (priv->id);
739         }
740
741         return priv->hash;
742 }
743
744 static gboolean
745 contact_is_ready_func (GObject *contact,
746                        gpointer user_data)
747 {
748         EmpathyContactPriv *priv = GET_PRIV (contact);
749         EmpathyContactReady ready;
750
751         ready = GPOINTER_TO_UINT (user_data);
752
753         /* When the name is NULL, empathy_contact_get_name() fallback to the id.
754          * When the caller want to wait the name to be ready, it also want to wait
755          * the id to be ready in case of fallback. */
756         if ((ready & EMPATHY_CONTACT_READY_NAME) && G_STR_EMPTY (priv->name)) {
757                 ready |= EMPATHY_CONTACT_READY_ID;
758         }
759
760         return (priv->ready & ready) == ready;
761 }
762
763 void
764 empathy_contact_run_until_ready (EmpathyContact *contact,
765                                  EmpathyContactReady ready,
766                                  GMainLoop **loop)
767 {
768         empathy_run_until_ready_full (contact, "notify::ready",
769       contact_is_ready_func, GUINT_TO_POINTER (ready),
770       loop);
771 }
772
773 static gchar *
774 contact_get_avatar_filename (EmpathyContact *contact,
775                              const gchar *token)
776 {
777         EmpathyContactPriv *priv = GET_PRIV (contact);
778         gchar *avatar_path;
779         gchar *avatar_file;
780         gchar *token_escaped;
781         gchar *contact_escaped;
782
783         if (G_STR_EMPTY (priv->id))
784                 return NULL;
785
786         contact_escaped = tp_escape_as_identifier (priv->id);
787         token_escaped = tp_escape_as_identifier (token);
788
789         avatar_path = g_build_filename (g_get_user_cache_dir (),
790       PACKAGE_NAME,
791       "avatars",
792       mc_account_get_unique_name (priv->account),
793       contact_escaped,
794       NULL);
795         g_mkdir_with_parents (avatar_path, 0700);
796
797         avatar_file = g_build_filename (avatar_path, token_escaped, NULL);
798
799         g_free (contact_escaped);
800         g_free (token_escaped);
801         g_free (avatar_path);
802
803         return avatar_file;
804 }
805
806 void
807 empathy_contact_load_avatar_data (EmpathyContact *contact,
808                                   const guchar  *data,
809                                                           const gsize len,
810                                                           const gchar *format,
811                                                           const gchar *token)
812 {
813         EmpathyAvatar *avatar;
814         gchar *filename;
815         GError *error = NULL;
816
817         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
818         g_return_if_fail (data != NULL);
819         g_return_if_fail (len > 0);
820         g_return_if_fail (format != NULL);
821         g_return_if_fail (!G_STR_EMPTY (token));
822
823         /* Load and set the avatar */
824         avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format),
825       g_strdup (token));
826         empathy_contact_set_avatar (contact, avatar);
827         empathy_avatar_unref (avatar);
828
829         /* Save to cache if not yet in it */
830         filename = contact_get_avatar_filename (contact, token);
831         if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS))
832     {
833       if (!empathy_avatar_save_to_file (avatar, filename, &error))
834         {
835           DEBUG ("Failed to save avatar in cache: %s",
836             error ? error->message : "No error given");
837           g_clear_error (&error);
838         }
839       else
840         {
841           DEBUG ("Avatar saved to %s", filename);
842         }
843     }
844         g_free (filename);
845 }
846
847 gboolean
848 empathy_contact_load_avatar_cache (EmpathyContact *contact,
849                                                            const gchar *token)
850 {
851         EmpathyAvatar *avatar = NULL;
852         gchar *filename;
853         gchar *data = NULL;
854         gsize len;
855         GError *error = NULL;
856
857         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
858         g_return_val_if_fail (!G_STR_EMPTY (token), FALSE);
859
860         /* Load the avatar from file if it exists */
861         filename = contact_get_avatar_filename (contact, token);
862         if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
863     {
864       if (!g_file_get_contents (filename, &data, &len, &error))
865         {
866           DEBUG ("Failed to load avatar from cache: %s",
867             error ? error->message : "No error given");
868           g_clear_error (&error);
869         }
870     }
871
872         if (data)
873     {
874       DEBUG ("Avatar loaded from %s", filename);
875       avatar = empathy_avatar_new (data, len, NULL, g_strdup (token));
876       empathy_contact_set_avatar (contact, avatar);
877       empathy_avatar_unref (avatar);
878     }
879
880         g_free (filename);
881
882         return data != NULL;
883 }
884
885 GType
886 empathy_avatar_get_type (void)
887 {
888         static GType type_id = 0;
889
890         if (!type_id)
891     {
892       type_id = g_boxed_type_register_static ("EmpathyAvatar",
893           (GBoxedCopyFunc) empathy_avatar_ref,
894           (GBoxedFreeFunc) empathy_avatar_unref);
895     }
896
897         return type_id;
898 }
899
900 EmpathyAvatar *
901 empathy_avatar_new (guchar *data,
902                     gsize len,
903                                 gchar *format,
904                                 gchar *token)
905 {
906         EmpathyAvatar *avatar;
907
908         avatar = g_slice_new0 (EmpathyAvatar);
909         avatar->data = data;
910         avatar->len = len;
911         avatar->format = format;
912         avatar->token = token;
913         avatar->refcount = 1;
914
915         return avatar;
916 }
917
918 void
919 empathy_avatar_unref (EmpathyAvatar *avatar)
920 {
921         g_return_if_fail (avatar != NULL);
922
923         avatar->refcount--;
924         if (avatar->refcount == 0)
925     {
926       g_free (avatar->data);
927       g_free (avatar->format);
928       g_free (avatar->token);
929       g_slice_free (EmpathyAvatar, avatar);
930     }
931 }
932
933 EmpathyAvatar *
934 empathy_avatar_ref (EmpathyAvatar *avatar)
935 {
936         g_return_val_if_fail (avatar != NULL, NULL);
937
938         avatar->refcount++;
939
940         return avatar;
941 }
942
943 /**
944  * empathy_avatar_save_to_file:
945  * @avatar: the avatar
946  * @filename: name of a file to write avatar to
947  * @error: return location for a GError, or NULL
948  *
949  * Save the avatar to a file named filename
950  *
951  * Returns: %TRUE on success, %FALSE if an error occurred 
952  */
953 gboolean
954 empathy_avatar_save_to_file (EmpathyAvatar *self,
955                              const gchar *filename,
956                              GError **error)
957 {
958   return g_file_set_contents (filename, self->data, self->len, error);
959 }
960