From 8c22592a9a13ac09f8e7c0357fafcfd163910a4c Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 26 Sep 2007 20:16:00 +0000 Subject: Cache avatars and RequestAvatars only when needed. 2007-09-26 Xavier Claessens * libempathy/empathy-utils.c: * libempathy/empathy-utils.h: * libempathy/empathy-avatar.c: * libempathy/empathy-avatar.h: * libempathy/empathy-contact-factory.c: Cache avatars and RequestAvatars only when needed. svn path=/trunk/; revision=320 --- ChangeLog | 8 ++ libempathy/empathy-avatar.c | 106 +++++++++++++++++++-- libempathy/empathy-avatar.h | 15 +-- libempathy/empathy-contact-factory.c | 179 +++++++++++++++++++++++++++++------ libempathy/empathy-utils.c | 90 +++++++++++++++--- libempathy/empathy-utils.h | 5 +- 6 files changed, 342 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index be7fd0a00..582006347 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-09-26 Xavier Claessens + + * libempathy/empathy-utils.c: + * libempathy/empathy-utils.h: + * libempathy/empathy-avatar.c: + * libempathy/empathy-avatar.h: + * libempathy/empathy-contact-factory.c: + 2007-09-26 Xavier Claessens * libempathy/empathy-contact-factory.c: Fix capabilities update, the NOT diff --git a/libempathy/empathy-avatar.c b/libempathy/empathy-avatar.c index 425f23f55..e08dd28c4 100644 --- a/libempathy/empathy-avatar.c +++ b/libempathy/empathy-avatar.c @@ -24,6 +24,8 @@ #include "config.h" #include "empathy-avatar.h" +#include "empathy-utils.h" +#include "empathy-debug.h" #define DEBUG_DOMAIN "Avatar" @@ -41,26 +43,113 @@ empathy_avatar_get_type (void) return type_id; } -EmpathyAvatar * -empathy_avatar_new (guchar *data, - gsize len, - gchar *format) +static gchar * +avatar_get_filename (const gchar *token) { - EmpathyAvatar *avatar; + gchar *avatar_path; + gchar *avatar_file; + gchar *token_escaped; - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (len > 0, NULL); - g_return_val_if_fail (format != NULL, NULL); + avatar_path = g_build_filename (g_get_home_dir (), + ".gnome2", + PACKAGE_NAME, + "avatars", + NULL); + g_mkdir_with_parents (avatar_path, 0700); + + token_escaped = empathy_escape_as_identifier (token); + avatar_file = g_build_filename (avatar_path, token_escaped, NULL); + + g_free (token_escaped); + g_free (avatar_path); + + return avatar_file; +} + +static EmpathyAvatar * +avatar_new (const guchar *data, + const gsize len, + const gchar *format, + const gchar *token) +{ + EmpathyAvatar *avatar; avatar = g_slice_new0 (EmpathyAvatar); avatar->data = g_memdup (data, len); avatar->len = len; avatar->format = g_strdup (format); + avatar->token = g_strdup (token); avatar->refcount = 1; return avatar; } +EmpathyAvatar * +empathy_avatar_new (const guchar *data, + const gsize len, + const gchar *format, + const gchar *token) +{ + EmpathyAvatar *avatar; + gchar *filename; + GError *error = NULL; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (!G_STR_EMPTY (token), NULL); + + avatar = avatar_new (data, len, format, token); + + /* Save to cache if not yet in it */ + filename = avatar_get_filename (token); + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + if (!g_file_set_contents (filename, data, len, &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to save avatar in cache: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } else { + empathy_debug (DEBUG_DOMAIN, "Avatar saved to %s", filename); + } + } + g_free (filename); + + return avatar; +} + +EmpathyAvatar * +empathy_avatar_new_from_cache (const gchar *token) +{ + EmpathyAvatar *avatar = NULL; + gchar *filename; + gchar *data = NULL; + gsize len; + GError *error = NULL; + + g_return_val_if_fail (!G_STR_EMPTY (token), NULL); + + /* Load the avatar from file if it exists */ + filename = avatar_get_filename (token); + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + if (!g_file_get_contents (filename, &data, &len, &error)) { + empathy_debug (DEBUG_DOMAIN, + "Failed to load avatar from cache: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + } + + if (data) { + empathy_debug (DEBUG_DOMAIN, "Avatar loaded from %s", filename); + avatar = avatar_new (data, len, NULL, token); + } + + g_free (filename); + + return avatar; +} + void empathy_avatar_unref (EmpathyAvatar *avatar) { @@ -70,6 +159,7 @@ empathy_avatar_unref (EmpathyAvatar *avatar) if (avatar->refcount == 0) { g_free (avatar->data); g_free (avatar->format); + g_free (avatar->token); g_slice_free (EmpathyAvatar, avatar); } } diff --git a/libempathy/empathy-avatar.h b/libempathy/empathy-avatar.h index 6b28e8e7a..b3d69a8fe 100644 --- a/libempathy/empathy-avatar.h +++ b/libempathy/empathy-avatar.h @@ -33,15 +33,18 @@ struct _EmpathyAvatar { guchar *data; gsize len; gchar *format; + gchar *token; guint refcount; }; -GType empathy_avatar_get_type (void) G_GNUC_CONST; -EmpathyAvatar * empathy_avatar_new (guchar *avatar, - gsize len, - gchar *format); -EmpathyAvatar * empathy_avatar_ref (EmpathyAvatar *avatar); -void empathy_avatar_unref (EmpathyAvatar *avatar); +GType empathy_avatar_get_type (void) G_GNUC_CONST; +EmpathyAvatar * empathy_avatar_new (const guchar *avatar, + const gsize len, + const gchar *format, + const gchar *token); +EmpathyAvatar * empathy_avatar_new_from_cache (const gchar *token); +EmpathyAvatar * empathy_avatar_ref (EmpathyAvatar *avatar); +void empathy_avatar_unref (EmpathyAvatar *avatar); G_END_DECLS diff --git a/libempathy/empathy-contact-factory.c b/libempathy/empathy-contact-factory.c index ec0d7bac8..b468ccec2 100644 --- a/libempathy/empathy-contact-factory.c +++ b/libempathy/empathy-contact-factory.c @@ -317,6 +317,33 @@ contact_factory_aliases_changed_cb (DBusGProxy *proxy, } } +static void +contact_factory_avatar_retrieved_cb (DBusGProxy *proxy, + guint handle, + gchar *token, + GArray *avatar_data, + gchar *mime_type, + gpointer user_data) +{ + ContactFactoryAccountData *account_data = user_data; + EmpathyContact *contact; + EmpathyAvatar *avatar; + + contact = contact_factory_account_data_find_by_handle (account_data, + handle); + if (!contact) { + return; + } + + avatar = empathy_avatar_new (avatar_data->data, + avatar_data->len, + mime_type, + token); + + empathy_contact_set_avatar (contact, avatar); + empathy_avatar_unref (avatar); +} + static void contact_factory_request_avatars_cb (DBusGProxy *proxy, GError *error, @@ -332,6 +359,109 @@ contact_factory_request_avatars_cb (DBusGProxy *proxy, contact_factory_account_data_return_call (account_data); } +typedef struct { + ContactFactoryAccountData *account_data; + GArray *handles; +} TokensData; + +static gboolean +contact_factory_avatar_maybe_update (ContactFactoryAccountData *account_data, + guint handle, + const gchar *token) +{ + EmpathyContact *contact; + EmpathyAvatar *avatar; + + contact = contact_factory_account_data_find_by_handle (account_data, + handle); + if (!contact) { + return TRUE; + } + + /* Check if the avatar changed */ + avatar = empathy_contact_get_avatar (contact); + if (avatar && !empathy_strdiff (avatar->token, token)) { + return TRUE; + } + + /* Check if we have an avatar */ + if (G_STR_EMPTY (token)) { + empathy_contact_set_avatar (contact, NULL); + return TRUE; + } + + /* The avatar changed, search the new one in the cache */ + avatar = empathy_avatar_new_from_cache (token); + if (avatar) { + /* Got from cache, use it */ + empathy_contact_set_avatar (contact, avatar); + empathy_avatar_unref (avatar); + return TRUE; + } + + return FALSE; +} + +static void +contact_factory_get_known_avatar_tokens_cb (DBusGProxy *proxy, + GHashTable *tokens, + GError *error, + gpointer user_data) +{ + TokensData *data = user_data; + gint i; + + if (error) { + empathy_debug (DEBUG_DOMAIN, + "Error getting known avatars tokens: %s", + error->message); + goto OUT; + } + + /* Remove handles for which we have an avatar */ + for (i = 0; i < data->handles->len; i++) { + const gchar *token; + guint handle; + + handle = g_array_index (data->handles, guint, i); + token = g_hash_table_lookup (tokens, GUINT_TO_POINTER (handle)); + + /* If we have no token it means CM does not know what's the + * avatar for that contact, we need to request it. */ + if (!token) { + continue; + } + + /* If avatar is not updated it means we don't have it in + * the cache so we need to request it */ + if (!contact_factory_avatar_maybe_update (data->account_data, + handle, token)) { + continue; + } + + /* We don't need to request the avatar for this handle, + * remove it from the handles array. We need to check the handle + * that takes the place of the current index we are removing, + * so i-- is needed. */ + g_array_remove_index_fast (data->handles, i); + i--; + } + + /* Request needed avatars */ + if (data->handles->len > 0) { + data->account_data->nb_pending_calls++; + tp_conn_iface_avatars_request_avatars_async (data->account_data->avatars_iface, + data->handles, + contact_factory_request_avatars_cb, + data->account_data); + } + +OUT: + contact_factory_account_data_return_call (data->account_data); + g_array_free (data->handles, TRUE); + g_slice_free (TokensData, data); +} + static void contact_factory_avatar_updated_cb (DBusGProxy *proxy, guint handle, @@ -341,8 +471,13 @@ contact_factory_avatar_updated_cb (DBusGProxy *proxy, ContactFactoryAccountData *account_data = user_data; GArray *handles; - handles = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1); - g_array_insert_val (handles, 0, handle); + if (contact_factory_avatar_maybe_update (account_data, handle, new_token)) { + /* Avatar was cached, nothing to do */ + return; + } + + handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (handles, handle); account_data->nb_pending_calls++; tp_conn_iface_avatars_request_avatars_async (account_data->avatars_iface, @@ -352,31 +487,6 @@ contact_factory_avatar_updated_cb (DBusGProxy *proxy, g_array_free (handles, TRUE); } -static void -contact_factory_avatar_retrieved_cb (DBusGProxy *proxy, - guint handle, - gchar *token, - GArray *avatar_data, - gchar *mime_type, - gpointer user_data) -{ - ContactFactoryAccountData *account_data = user_data; - EmpathyContact *contact; - EmpathyAvatar *avatar; - - contact = contact_factory_account_data_find_by_handle (account_data, - handle); - if (!contact) { - return; - } - - avatar = empathy_avatar_new (avatar_data->data, - avatar_data->len, - mime_type); - empathy_contact_set_avatar (contact, avatar); - empathy_avatar_unref (avatar); -} - static void contact_factory_update_capabilities (ContactFactoryAccountData *account_data, guint handle, @@ -510,11 +620,18 @@ contact_factory_request_everything (ContactFactoryAccountData *account_data, } if (account_data->avatars_iface) { + TokensData *data; + account_data->nb_pending_calls++; - tp_conn_iface_avatars_request_avatars_async (account_data->avatars_iface, - handles, - contact_factory_request_avatars_cb, - account_data); + data = g_slice_new (TokensData); + data->account_data = account_data; + data->handles = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_vals (data->handles, handles->data, handles->len); + + tp_conn_iface_avatars_get_known_avatar_tokens_async (account_data->avatars_iface, + handles, + contact_factory_get_known_avatar_tokens_cb, + data); } if (account_data->capabilities_iface) { diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c index a1404f403..06442fe05 100644 --- a/libempathy/empathy-utils.c +++ b/libempathy/empathy-utils.c @@ -198,6 +198,82 @@ empathy_strncasecmp (const gchar *s1, return ret_val; } +/* Stolen from telepathy-glib */ +gboolean +empathy_strdiff (const gchar *left, const gchar *right) +{ + if ((NULL == left) != (NULL == right)) + return TRUE; + + else if (left == right) + return FALSE; + + else + return (0 != strcmp (left, right)); +} + +/* Stolen from telepathy-glib */ +static inline gboolean +_esc_ident_bad (gchar c, gboolean is_first) +{ + return ((c < 'a' || c > 'z') && + (c < 'A' || c > 'Z') && + (c < '0' || c > '9' || is_first)); +} + +/* Stolen from telepathy-glib */ +gchar * +empathy_escape_as_identifier (const gchar *name) +{ + gboolean bad = FALSE; + size_t len = 0; + GString *op; + const gchar *ptr, *first_ok; + + g_return_val_if_fail (name != NULL, NULL); + + for (ptr = name; *ptr; ptr++) + { + if (_esc_ident_bad (*ptr, ptr == name)) + { + bad = TRUE; + len += 3; + } + else + len++; + } + + /* fast path if it's clean */ + if (!bad) + return g_strdup (name); + + /* If strictly less than ptr, first_ok is the first uncopied safe character. + */ + first_ok = name; + op = g_string_sized_new (len); + for (ptr = name; *ptr; ptr++) + { + if (_esc_ident_bad (*ptr, ptr == name)) + { + /* copy preceding safe characters if any */ + if (first_ok < ptr) + { + g_string_append_len (op, first_ok, ptr - first_ok); + } + /* escape the unsafe character */ + g_string_append_printf (op, "_%02x", (unsigned char)(*ptr)); + /* restart after it */ + first_ok = ptr + 1; + } + } + /* copy trailing safe characters if any */ + if (first_ok < ptr) + { + g_string_append_len (op, first_ok, ptr - first_ok); + } + return g_string_free (op, FALSE); +} + gboolean empathy_xml_validate (xmlDoc *doc, const gchar *dtd_filename) @@ -385,17 +461,3 @@ empathy_inspect_handle (McAccount *account, return name; } -/* Stolen from telepathy-glib */ -gboolean -empathy_strdiff (const gchar *left, const gchar *right) -{ - if ((NULL == left) != (NULL == right)) - return TRUE; - - else if (left == right) - return FALSE; - - else - return (0 != strcmp (left, right)); -} - diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h index 5669d33d1..88d533728 100644 --- a/libempathy/empathy-utils.h +++ b/libempathy/empathy-utils.h @@ -66,6 +66,9 @@ gint empathy_strcasecmp (const gchar *s1, gint empathy_strncasecmp (const gchar *s1, const gchar *s2, gsize n); +gboolean empathy_strdiff (const gchar *left, + const gchar *right); +gchar * empathy_escape_as_identifier (const gchar *name); /* XML */ gboolean empathy_xml_validate (xmlDoc *doc, @@ -88,8 +91,6 @@ gchar * empathy_inspect_handle (McAccount *account, guint handle_type); gchar * empathy_inspect_channel (McAccount *account, TpChan *tp_chan); -gboolean empathy_strdiff (const gchar *left, - const gchar *right); G_END_DECLS #endif /* __EMPATHY_UTILS_H__ */ -- cgit v1.2.3