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