--- a/libpurple/buddyicon.c Tue Apr 24 03:56:16 2007 +0000 +++ b/libpurple/buddyicon.c Tue Apr 24 03:57:07 2007 +0000 @@ -28,30 +28,26 @@ #include "conversation.h" #include "dbus-maybe.h" #include "debug.h" +#include "imgstore.h" #include "util.h" typedef struct _PurpleBuddyIconData PurpleBuddyIconData; +/* NOTE: Instances of this struct are allocated without zeroing the memory, so + * NOTE: be sure to update purple_buddy_icon_new() if you add members. */ struct _PurpleBuddyIcon { - PurpleAccount *account; /**< The account the user is on. */ - char *username; /**< The username the icon belongs to. */ - PurpleBuddyIconData *protocol_icon; /**< The icon data. */ - PurpleBuddyIconData *custom_icon; /**< The data for a user-set custom icon. */ - int ref_count; /**< The buddy icon reference count. */ -}; - -struct _PurpleBuddyIconData -{ - guchar *image_data; /**< The buddy icon data. */ - size_t len; /**< The length of the buddy icon data. */ - char *filename; /**< The filename of the cache file. */ - int ref_count; /**< The buddy icon reference count. */ + PurpleAccount *account; /**< The account the user is on. */ + char *username; /**< The username the icon belongs to. */ + PurpleStoredImage *img; /**< The id of the stored image with the + the icon data. */ + int ref_count; /**< The buddy icon reference count. */ }; static GHashTable *account_cache = NULL; static GHashTable *icon_data_cache = NULL; static GHashTable *icon_file_cache = NULL; +static GHashTable *custom_icon_cache = NULL; static char *cache_dir = NULL; static gboolean icon_caching = TRUE; @@ -92,33 +88,6 @@ } } -static const char * -get_icon_type(guchar *icon_data, size_t icon_len) -{ - g_return_val_if_fail(icon_data != NULL, NULL); - g_return_val_if_fail(icon_len > 0, NULL); - - if (icon_len >= 4) - { - if (!strncmp((char *)icon_data, "BM", 2)) - return "bmp"; - else if (!strncmp((char *)icon_data, "GIF8", 4)) - return "gif"; - else if (!strncmp((char *)icon_data, "\xff\xd8\xff\xe0", 4)) - return "jpg"; - else if (!strncmp((char *)icon_data, "\x89PNG", 4)) - return "png"; - } - - return "icon"; -} - -static const char * -purple_buddy_icon_data_get_type(PurpleBuddyIconData *data) -{ - return get_icon_type(data->image_data, data->len); -} - static char * purple_buddy_icon_data_calculate_filename(guchar *icon_data, size_t icon_len) { @@ -143,21 +112,23 @@ /* Return the filename */ return g_strdup_printf("%s.%s", digest, - get_icon_type(icon_data, icon_len)); + purple_util_get_image_extension(icon_data, icon_len)); } static void -purple_buddy_icon_data_cache(PurpleBuddyIconData *data) +purple_buddy_icon_data_cache(PurpleStoredImage *img) { const char *dirname; char *path; FILE *file = NULL; + g_return_if_fail(img != NULL); + if (!purple_buddy_icons_is_caching()) return; dirname = purple_buddy_icons_get_cache_dir(); - path = g_build_filename(dirname, data->filename, NULL); + path = g_build_filename(dirname, purple_imgstore_get_filename(img), NULL); if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { @@ -173,7 +144,7 @@ if ((file = g_fopen(path, "wb")) != NULL) { - if (!fwrite(data->image_data, data->len, 1, file)) + if (!fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file)) { purple_debug_error("buddyicon", "Error writing %s: %s\n", path, strerror(errno)); @@ -194,20 +165,20 @@ } static void -purple_buddy_icon_data_uncache(PurpleBuddyIconData *data) +purple_buddy_icon_data_uncache_file(const char *filename) { const char *dirname; char *path; - g_return_if_fail(data != NULL); + g_return_if_fail(filename != NULL); /* It's possible that there are other references to this icon * cache file that are not currently loaded into memory. */ - if (g_hash_table_lookup(icon_file_cache, data->filename)) + if (g_hash_table_lookup(icon_file_cache, filename)) return; dirname = purple_buddy_icons_get_cache_dir(); - path = g_build_filename(dirname, data->filename, NULL); + path = g_build_filename(dirname, filename, NULL); if (g_file_test(path, G_FILE_TEST_EXISTS)) { @@ -223,69 +194,50 @@ g_free(path); } -static PurpleBuddyIconData * -purple_buddy_icon_data_ref(PurpleBuddyIconData *data) +static void +image_deleting_cb(PurpleStoredImage *img, gpointer data) { - g_return_val_if_fail(data != NULL, NULL); + const char *filename = purple_imgstore_get_filename(img); - data->ref_count++; - - return data; + if (img == g_hash_table_lookup(icon_data_cache, filename)) + { + purple_buddy_icon_data_uncache_file(filename); + g_hash_table_remove(icon_data_cache, filename); + } } -static PurpleBuddyIconData * -purple_buddy_icon_data_unref(PurpleBuddyIconData *data) +static PurpleStoredImage * +purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len, const char *filename) { - if (data == NULL) - return NULL; - - g_return_val_if_fail(data->ref_count > 0, NULL); - - data->ref_count--; - - if (data->ref_count == 0) - { - g_hash_table_remove(icon_data_cache, data->filename); - - purple_buddy_icon_data_uncache(data); - - g_free(data->image_data); - g_free(data->filename); - g_free(data); - - return NULL; - } - - return data; -} - -static PurpleBuddyIconData * -purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len) -{ - PurpleBuddyIconData *data; - char *filename; + char *file; + PurpleStoredImage *img; g_return_val_if_fail(icon_data != NULL, NULL); g_return_val_if_fail(icon_len > 0, NULL); - filename = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); if (filename == NULL) - return NULL; + { + file = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); + if (file == NULL) + return NULL; + } + else + file = g_strdup(filename); - if ((data = g_hash_table_lookup(icon_data_cache, filename))) + if ((img = g_hash_table_lookup(icon_data_cache, file))) { - g_free(filename); - return purple_buddy_icon_data_ref(data); + g_free(file); + return purple_imgstore_ref(img); } - data = g_new0(PurpleBuddyIconData, 1); - data->image_data = g_memdup(icon_data, icon_len); - data->len = icon_len; - data->filename = filename; + img = purple_imgstore_add(icon_data, icon_len, file); - purple_buddy_icon_data_cache(data); + /* This will take ownership of file and g_free it either now or later. */ + g_hash_table_insert(icon_data_cache, file, img); - return data; + purple_buddy_icon_data_cache(img); + + return img; } static PurpleBuddyIcon * @@ -294,7 +246,9 @@ PurpleBuddyIcon *icon; GHashTable *icon_cache; - icon = g_new0(PurpleBuddyIcon, 1); + /* This does not zero. See purple_buddy_icon_new() for + * information on which function allocates which member. */ + icon = g_slice_new(PurpleBuddyIcon); PURPLE_DBUS_REGISTER_POINTER(icon, PurpleBuddyIcon); icon->account = account; @@ -316,29 +270,26 @@ PurpleBuddyIcon * purple_buddy_icon_new(PurpleAccount *account, const char *username, - void *protocol_icon_data, size_t protocol_icon_len, - void *custom_icon_data, size_t custom_icon_len) + void *icon_data, size_t icon_len) { PurpleBuddyIcon *icon; g_return_val_if_fail(account != NULL, NULL); g_return_val_if_fail(username != NULL, NULL); - g_return_val_if_fail((protocol_icon_data != NULL && protocol_icon_len > 0) || - (custom_icon_data != NULL && custom_icon_len > 0), NULL); + g_return_val_if_fail(icon_data != NULL, NULL); + g_return_val_if_fail(icon_len > 0, NULL); icon = purple_buddy_icons_find(account, username); + /* purple_buddy_icon_create() sets account & username */ if (icon == NULL) icon = purple_buddy_icon_create(account, username); /* Take a reference for the caller of this function. */ - purple_buddy_icon_ref(icon); + icon->ref_count = 1; - if (protocol_icon_data != NULL && protocol_icon_len > 0) - purple_buddy_icon_set_protocol_data(icon, protocol_icon_data, protocol_icon_len); - - if (custom_icon_data != NULL && custom_icon_len > 0) - purple_buddy_icon_set_custom_data(icon, custom_icon_data, custom_icon_len); + /* purple_buddy_icon_set_data() sets img */ + purple_buddy_icon_set_data(icon, icon_data, icon_len); return icon; } @@ -371,11 +322,10 @@ g_hash_table_remove(icon_cache, purple_buddy_icon_get_username(icon)); g_free(icon->username); - purple_buddy_icon_data_unref(icon->protocol_icon); - purple_buddy_icon_data_unref(icon->custom_icon); + purple_imgstore_unref(icon->img); PURPLE_DBUS_UNREGISTER_POINTER(icon); - g_free(icon); + g_slice_free(PurpleBuddyIcon, icon); return NULL; } @@ -397,58 +347,39 @@ account = purple_buddy_icon_get_account(icon); username = purple_buddy_icon_get_username(icon); - /* If neither type of data exists, then call the functions below with - * NULL to unset the icon. They will then unref the icon and it - * should be destroyed. The only way it wouldn't be destroyed is if - * someone else is holding a reference to it, in which case they can - * kill the icon when they realize it has no data any more. */ - icon_to_set = (icon->protocol_icon || icon->custom_icon) ? icon : NULL; + /* If no data exists, then call the functions below with NULL to + * unset the icon. They will then unref the icon and it should be + * destroyed. The only way it wouldn't be destroyed is if someone + * else is holding a reference to it, in which case they can kill + * the icon when they realize it has no data. */ + icon_to_set = icon->img ? icon : NULL; for (list = sl = purple_find_buddies(account, username); sl != NULL; sl = sl->next) { PurpleBuddy *buddy = (PurpleBuddy *)sl->data; - const char *old_icon; + char *old_icon; purple_buddy_set_icon(buddy, icon_to_set); - old_icon = purple_blist_node_get_string((PurpleBlistNode *)buddy, - "buddy_icon"); - if (icon->protocol_icon) + old_icon = g_strdup(purple_blist_node_get_string((PurpleBlistNode *)buddy, + "buddy_icon")); + if (icon->img) { - old_icon = purple_blist_node_get_string((PurpleBlistNode *)buddy, - "buddy_icon"); + const char *filename = purple_imgstore_get_filename(icon->img); purple_blist_node_set_string((PurpleBlistNode *)buddy, "buddy_icon", - icon->protocol_icon->filename); - ref_filename(icon->protocol_icon->filename); + filename); + ref_filename(filename); } else { purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "buddy_icon"); } unref_filename(old_icon); - - - old_icon = purple_blist_node_get_string((PurpleBlistNode *)buddy, - "custom_buddy_icon"); - if (icon->custom_icon) - { - old_icon = purple_blist_node_get_string((PurpleBlistNode *)buddy, - "custom_buddy_icon"); - purple_blist_node_set_string((PurpleBlistNode *)buddy, - "custom_buddy_icon", - icon->custom_icon->filename); - ref_filename(icon->custom_icon->filename); - } - else - { - purple_blist_node_remove_setting((PurpleBlistNode *)buddy, - "custom_buddy_icon"); - } - unref_filename(old_icon); + g_free(old_icon); } g_slist_free(list); @@ -460,39 +391,21 @@ } void -purple_buddy_icon_set_custom_data(PurpleBuddyIcon *icon, guchar *data, size_t len) +purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data, size_t len) { - PurpleBuddyIconData *old_data; + PurpleStoredImage *old_img; g_return_if_fail(icon != NULL); - old_data = icon->custom_icon; - icon->custom_icon = NULL; + old_img = icon->img; + icon->img = NULL; if (data != NULL && len > 0) - icon->custom_icon = purple_buddy_icon_data_new(data, len); + icon->img = purple_buddy_icon_data_new(data, len, NULL); purple_buddy_icon_update(icon); - purple_buddy_icon_data_unref(icon->custom_icon); -} - -void -purple_buddy_icon_set_protocol_data(PurpleBuddyIcon *icon, guchar *data, size_t len) -{ - PurpleBuddyIconData *old_data; - - g_return_if_fail(icon != NULL); - - old_data = icon->protocol_icon; - icon->protocol_icon = NULL; - - if (data != NULL && len > 0) - icon->protocol_icon = purple_buddy_icon_data_new(data, len); - - purple_buddy_icon_update(icon); - - purple_buddy_icon_data_unref(old_data); + purple_imgstore_unref(old_img); } PurpleAccount * @@ -511,38 +424,27 @@ return icon->username; } -const guchar * +gconstpointer purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len) { g_return_val_if_fail(icon != NULL, NULL); - if (icon->custom_icon) + if (icon->img) { if (len != NULL) - *len = icon->custom_icon->len; - - return icon->custom_icon->image_data; - } + *len = purple_imgstore_get_size(icon->img); - if (icon->protocol_icon) - { - if (len != NULL) - *len = icon->protocol_icon->len; - - return icon->protocol_icon->image_data; + return purple_imgstore_get_data(icon->img); } return NULL; } const char * -purple_buddy_icon_get_type(const PurpleBuddyIcon *icon) +purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon) { - if (icon->custom_icon != NULL) - return purple_buddy_icon_data_get_type(icon->custom_icon); - - if (icon->protocol_icon != NULL) - return purple_buddy_icon_data_get_type(icon->protocol_icon); + if (icon->img != NULL) + return purple_imgstore_get_extension(icon->img); return NULL; } @@ -561,11 +463,11 @@ icon = purple_buddy_icons_find(account, username); if (icon != NULL) - purple_buddy_icon_set_protocol_data(icon, icon_data, icon_len); + purple_buddy_icon_set_data(icon, icon_data, icon_len); } else { - PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len, NULL, 0); + PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len); purple_buddy_icon_unref(icon); } } @@ -617,7 +519,6 @@ { PurpleBuddy *b = purple_find_buddy(account, username); const char *protocol_icon_file; - const char *custom_icon_file; const char *dirname; gboolean caching; guchar *data; @@ -627,9 +528,8 @@ return NULL; protocol_icon_file = purple_blist_node_get_string((PurpleBlistNode*)b, "buddy_icon"); - custom_icon_file = purple_blist_node_get_string((PurpleBlistNode*)b, "custom_buddy_icon"); - if (protocol_icon_file == NULL && custom_icon_file == NULL) + if (protocol_icon_file == NULL) return NULL; dirname = purple_buddy_icons_get_cache_dir(); @@ -640,17 +540,6 @@ * functions. */ purple_buddy_icons_set_caching(FALSE); - if (custom_icon_file != NULL) - { - char *path = g_build_filename(dirname, custom_icon_file, NULL); - if (read_icon_file(path, &data, &len)) - { - icon = purple_buddy_icon_create(account, username); - purple_buddy_icon_set_custom_data(icon, data, len); - } - g_free(path); - } - if (protocol_icon_file != NULL) { char *path = g_build_filename(dirname, protocol_icon_file, NULL); @@ -658,7 +547,7 @@ { if (icon == NULL) icon = purple_buddy_icon_create(account, username); - purple_buddy_icon_set_protocol_data(icon, data, len); + purple_buddy_icon_set_data(icon, data, len); } g_free(path); } @@ -669,6 +558,87 @@ return icon; } +gboolean +purple_buddy_icons_has_custom_icon(PurpleContact *contact) +{ + g_return_val_if_fail(contact != NULL, FALSE); + + return (purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon") != NULL); +} + +PurpleStoredImage * +purple_buddy_icons_find_custom_icon(PurpleContact *contact) +{ + PurpleStoredImage *img; + const char *custom_icon_file; + const char *dirname; + char *path; + guchar *data; + size_t len; + + g_return_val_if_fail(contact != NULL, NULL); + + if ((img = g_hash_table_lookup(custom_icon_cache, contact))) + { + return purple_imgstore_ref(img); + } + + custom_icon_file = purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon"); + + if (custom_icon_file == NULL) + return NULL; + + dirname = purple_buddy_icons_get_cache_dir(); + path = g_build_filename(dirname, custom_icon_file, NULL); + + if (read_icon_file(path, &data, &len)) + { + g_free(path); + img = purple_buddy_icon_data_new(data, len, custom_icon_file); + g_hash_table_insert(custom_icon_cache, contact, img); + return img; + } + g_free(path); + + return NULL; +} + +void +purple_buddy_icons_set_custom_icon(PurpleContact *contact, + guchar *icon_data, size_t icon_len) +{ + PurpleStoredImage *old_img; + PurpleStoredImage *img = NULL; + char *old_icon; + + old_img = g_hash_table_lookup(custom_icon_cache, contact); + + if (icon_data != NULL && icon_len > 0) + img = purple_buddy_icon_data_new(icon_data, icon_len, NULL); + + old_icon = g_strdup(purple_blist_node_get_string((PurpleBlistNode *)contact, + "custom_buddy_icon")); + if (img) + { + const char *filename = purple_imgstore_get_filename(img); + purple_blist_node_set_string((PurpleBlistNode *)contact, + "custom_buddy_icon", + filename); + ref_filename(filename); + } + else + { + purple_blist_node_remove_setting((PurpleBlistNode *)contact, + "custom_buddy_icon"); + } + unref_filename(old_icon); + g_free(old_icon); + + + g_hash_table_insert(custom_icon_cache, contact, img); + purple_imgstore_unref(old_img); +} + void purple_buddy_icon_set_old_icons_dir(const char *dirname) { @@ -762,6 +732,9 @@ PurpleBlistNode *node = purple_blist_get_root(); const char *dirname = purple_buddy_icons_get_cache_dir(); + // TODO: TEMP + old_icons_dir = g_strdup("/home/rlaager/.gaim/icons"); + /* Doing this once here saves having to check it inside a loop. */ if (old_icons_dir != NULL) { @@ -795,10 +768,18 @@ } else { - // TODO: If filename doesn't exist, drop the setting. + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) + { + purple_blist_node_remove_setting(node, + "buddy_icon"); + } ref_filename(filename); } } + } + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + { + const char *filename; filename = purple_blist_node_get_string(node, "custom_buddy_icon"); if (filename != NULL) @@ -811,7 +792,11 @@ } else { - // TODO: If filename doesn't exist, drop the setting. + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) + { + purple_blist_node_remove_setting(node, + "custom_buddy_icon"); + } ref_filename(filename); } } @@ -876,16 +861,24 @@ icon_data_cache = g_hash_table_new(g_str_hash, g_str_equal); icon_file_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + custom_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal); cache_dir = g_build_filename(purple_user_dir(), "icons", NULL); + + purple_signal_connect(purple_imgstore_get_handle(), "image-deleting", + purple_buddy_icons_get_handle(), + G_CALLBACK(image_deleting_cb), NULL); } void purple_buddy_icons_uninit() { + purple_signals_disconnect_by_handle(purple_buddy_icons_get_handle()); + g_hash_table_destroy(account_cache); g_hash_table_destroy(icon_data_cache); g_hash_table_destroy(icon_file_cache); + g_hash_table_destroy(custom_icon_cache); g_free(old_icons_dir); }