aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--libempathy/empathy-avatar.c106
-rw-r--r--libempathy/empathy-avatar.h15
-rw-r--r--libempathy/empathy-contact-factory.c179
-rw-r--r--libempathy/empathy-utils.c90
-rw-r--r--libempathy/empathy-utils.h5
6 files changed, 342 insertions, 61 deletions
diff --git a/ChangeLog b/ChangeLog
index be7fd0a00..582006347 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2007-09-26 Xavier Claessens <xclaesse@gmail.com>
+ * 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 <xclaesse@gmail.com>
+
* libempathy/empathy-contact-factory.c: Fix capabilities update, the NOT
operator is ~ and 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
@@ -318,6 +318,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,
gpointer user_data)
@@ -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,
@@ -353,31 +488,6 @@ contact_factory_avatar_updated_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);
- empathy_contact_set_avatar (contact, avatar);
- empathy_avatar_unref (avatar);
-}
-
-static void
contact_factory_update_capabilities (ContactFactoryAccountData *account_data,
guint handle,
const gchar *channel_type,
@@ -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__ */