# HG changeset patch # User Richard Laager # Date 1177478772 0 # Node ID 0fa041c66957b76a217bdad791a29a7fd587d314 # Parent 58a09785938869e65aa245c8daa4f9bfb2672a8e# Parent 493ca924c19991233311ae9c92dc1fc3ea31d500 propagate from branch 'im.pidgin.pidgin' (head 4d02676110c034e401f4d42ed7304a7507d96929) to branch 'im.pidgin.rlaager.gaim_migration' (head cdf5792eae6b63870cd030ae85774d5385b7d428) diff -r 58a097859388 -r 0fa041c66957 finch/finch.c --- a/finch/finch.c Wed Apr 25 05:25:57 2007 +0000 +++ b/finch/finch.c Wed Apr 25 05:26:12 2007 +0000 @@ -272,9 +272,32 @@ * Fire up this baby. */ - /* Because we don't want debug-messages to show up and corrup the display */ + /* We don't want debug-messages to show up and corrupt the display */ purple_debug_set_enabled(debug_enabled); + /* If we're using a custom configuration directory, we + * do NOT want to migrate, or weird things will happen. */ + if (opt_config_dir_arg == NULL) + { + if (!purple_core_migrate()) + { + char *old = g_strconcat(purple_home_dir(), + G_DIR_SEPARATOR_S ".gaim", NULL); + char *text = g_strdup_printf(_( + "%s encountered errors migrating your settings " + "from %s to %s. Please investigate and complete the " + "migration by hand."), _("Finch"), + old, purple_user_dir()); + + g_free(old); + + purple_print_utf8_to_console(stderr, text); + g_free(text); + + return 0; + } + } + purple_core_set_ui_ops(gnt_core_get_ui_ops()); purple_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops()); purple_idle_set_ui_ops(finch_idle_get_ui_ops()); diff -r 58a097859388 -r 0fa041c66957 libpurple/blist.c --- a/libpurple/blist.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/blist.c Wed Apr 25 05:26:12 2007 +0000 @@ -605,6 +605,9 @@ } xmlnode_free(purple); + + /* This tells the buddy icon code to do its thing. */ + purple_buddy_icons_blist_loaded_cb(); } @@ -1141,20 +1144,12 @@ { g_return_if_fail(buddy != NULL); - if (buddy->icon != icon) { - if (buddy->icon != NULL) - purple_buddy_icon_unref(buddy->icon); - + if (buddy->icon != icon) + { + purple_buddy_icon_unref(buddy->icon); buddy->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL); } - if (buddy->icon) - purple_buddy_icon_cache(icon, buddy); - else - purple_buddy_icon_uncache(buddy); - - purple_blist_schedule_save(); - purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy); purple_blist_update_buddy_icon(buddy); @@ -1783,9 +1778,6 @@ contact = (PurpleContact *)cnode; group = (PurpleGroup *)gnode; - /* Delete any buddy icon. */ - purple_buddy_icon_uncache(buddy); - /* Remove the node from its parent */ if (node->prev) node->prev->next = node->next; @@ -1831,8 +1823,7 @@ purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy); /* Delete the node */ - if (buddy->icon != NULL) - purple_buddy_icon_unref(buddy->icon); + purple_buddy_icon_unref(buddy->icon); g_hash_table_destroy(buddy->node.settings); purple_presence_remove_buddy(buddy->presence, buddy); purple_presence_destroy(buddy->presence); diff -r 58a097859388 -r 0fa041c66957 libpurple/buddyicon.c --- a/libpurple/buddyicon.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/buddyicon.c Wed Apr 25 05:26:12 2007 +0000 @@ -24,26 +24,249 @@ */ #include "internal.h" #include "buddyicon.h" +#include "cipher.h" #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. */ + PurpleStoredImage *img; /**< The id of the stored image with the + the icon data. */ + char *username; /**< The username the icon belongs to. */ + char *checksum; /**< The protocol checksum. */ + 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; +/* For ~/.gaim to ~/.purple migration. */ +static char *old_icons_dir = NULL; + +static void +ref_filename(const char *filename) +{ + int refs; + + g_return_if_fail(filename != NULL); + + refs = GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename)); + + g_hash_table_insert(icon_file_cache, g_strdup(filename), + GINT_TO_POINTER(refs + 1)); +} + +static void +unref_filename(const char *filename) +{ + int refs; + + if (filename == NULL) + return; + + refs = GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename)); + + if (refs == 1) + { + g_hash_table_remove(icon_file_cache, filename); + } + else + { + g_hash_table_insert(icon_file_cache, g_strdup(filename), + GINT_TO_POINTER(refs - 1)); + } +} + +static char * +purple_buddy_icon_data_calculate_filename(guchar *icon_data, size_t icon_len) +{ + PurpleCipherContext *context; + gchar digest[41]; + + context = purple_cipher_context_new_by_name("sha1", NULL); + if (context == NULL) + { + purple_debug_error("buddyicon", "Could not find sha1 cipher\n"); + g_return_val_if_reached(NULL); + } + + /* Hash the icon data */ + purple_cipher_context_append(context, icon_data, icon_len); + if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL)) + { + purple_debug_error("buddyicon", "Failed to get SHA-1 digest.\n"); + g_return_val_if_reached(NULL); + } + purple_cipher_context_destroy(context); + + /* Return the filename */ + return g_strdup_printf("%s.%s", digest, + purple_util_get_image_extension(icon_data, icon_len)); +} + +static void +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, purple_imgstore_get_filename(img), NULL); + + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) + { + purple_debug_info("buddyicon", "Creating icon cache directory.\n"); + + if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + { + purple_debug_error("buddyicon", + "Unable to create directory %s: %s\n", + dirname, strerror(errno)); + } + } + + if ((file = g_fopen(path, "wb")) != NULL) + { + 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)); + } + else + purple_debug_info("buddyicon", "Wrote cache file: %s\n", path); + + fclose(file); + } + else + { + purple_debug_error("buddyicon", "Unable to create file %s: %s\n", + path, strerror(errno)); + g_free(path); + return; + } + g_free(path); +} + +static void +purple_buddy_icon_data_uncache_file(const char *filename) +{ + const char *dirname; + char *path; + + 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 (GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename))) + return; + + dirname = purple_buddy_icons_get_cache_dir(); + path = g_build_filename(dirname, filename, NULL); + + if (g_file_test(path, G_FILE_TEST_EXISTS)) + { + if (g_unlink(path)) + { + purple_debug_error("buddyicon", "Failed to delete %s: %s\n", + path, strerror(errno)); + } + else + { + purple_debug_info("buddyicon", "Deleted cache file: %s\n", path); + } + } + + g_free(path); +} + +static gboolean +value_equals(gpointer key, gpointer value, gpointer user_data) +{ + return (value == user_data); +} + +static void +image_deleting_cb(PurpleStoredImage *img, gpointer data) +{ + const char *filename = purple_imgstore_get_filename(img); + + 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); + + /* We could make this O(1) by using another hash table, but + * this is probably good enough. */ + g_hash_table_foreach_remove(custom_icon_cache, value_equals, img); + } +} + +static PurpleStoredImage * +purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len, const char *filename) +{ + char *file; + PurpleStoredImage *img; + + g_return_val_if_fail(icon_data != NULL, NULL); + g_return_val_if_fail(icon_len > 0, NULL); + + if (filename == NULL) + { + file = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); + if (file == NULL) + return NULL; + } + else + file = g_strdup(filename); + + if ((img = g_hash_table_lookup(icon_data_cache, file))) + { + g_free(file); + return purple_imgstore_ref(img); + } + + img = purple_imgstore_add(icon_data, icon_len, file); + + /* This will take ownership of file and g_free it either now or later. */ + g_hash_table_insert(icon_data_cache, file, img); + + purple_buddy_icon_data_cache(img); + + return img; +} + static PurpleBuddyIcon * purple_buddy_icon_create(PurpleAccount *account, const char *username) { 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); - purple_buddy_icon_set_account(icon, account); - purple_buddy_icon_set_username(icon, username); + icon->account = account; + icon->username = g_strdup(username); + icon->checksum = NULL; icon_cache = g_hash_table_lookup(account_cache, account); @@ -55,13 +278,14 @@ } g_hash_table_insert(icon_cache, - (char *)purple_buddy_icon_get_username(icon), icon); + (char *)purple_buddy_icon_get_username(icon), icon); return icon; } PurpleBuddyIcon * purple_buddy_icon_new(PurpleAccount *account, const char *username, - void *icon_data, size_t icon_len) + void *icon_data, size_t icon_len, + const char *checksum) { PurpleBuddyIcon *icon; @@ -70,76 +294,25 @@ g_return_val_if_fail(icon_data != NULL, NULL); g_return_val_if_fail(icon_len > 0, NULL); + /* purple_buddy_icons_find() does allocation, so be + * sure to update it as well when members are added. */ icon = purple_buddy_icons_find(account, username); + /* purple_buddy_icon_create() sets account & username */ if (icon == NULL) icon = purple_buddy_icon_create(account, username); - purple_buddy_icon_ref(icon); - purple_buddy_icon_set_data(icon, icon_data, icon_len); - purple_buddy_icon_set_path(icon, NULL); + /* Take a reference for the caller of this function. */ + icon->ref_count = 1; - /* purple_buddy_icon_set_data() makes blist.c or - * conversation.c, or both, take a reference. - * - * Plus, we leave one for the caller of this function. - */ + /* purple_buddy_icon_set_data() sets img, but it + * references img first, so we need to initialize it */ + icon->img = NULL; + purple_buddy_icon_set_data(icon, icon_data, icon_len, checksum); return icon; } -void -purple_buddy_icon_destroy(PurpleBuddyIcon *icon) -{ - PurpleConversation *conv; - PurpleAccount *account; - GHashTable *icon_cache; - const char *username; - GSList *sl, *list; - - g_return_if_fail(icon != NULL); - - if (icon->ref_count > 0) - { - /* If the ref count is greater than 0, then we weren't called from - * purple_buddy_icon_unref(). So we go through and ask everyone to - * unref us. Then we return, since we know somewhere along the - * line we got called recursively by one of the unrefs, and the - * icon is already destroyed. - */ - account = purple_buddy_icon_get_account(icon); - username = purple_buddy_icon_get_username(icon); - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, account); - if (conv != NULL) - purple_conv_im_set_icon(PURPLE_CONV_IM(conv), NULL); - - for (list = sl = purple_find_buddies(account, username); sl != NULL; - sl = sl->next) - { - PurpleBuddy *buddy = (PurpleBuddy *)sl->data; - - purple_buddy_set_icon(buddy, NULL); - } - - g_slist_free(list); - - return; - } - - icon_cache = g_hash_table_lookup(account_cache, - purple_buddy_icon_get_account(icon)); - - if (icon_cache != NULL) - g_hash_table_remove(icon_cache, purple_buddy_icon_get_username(icon)); - - g_free(icon->username); - g_free(icon->data); - g_free(icon->path); - PURPLE_DBUS_UNREGISTER_POINTER(icon); - g_free(icon); -} - PurpleBuddyIcon * purple_buddy_icon_ref(PurpleBuddyIcon *icon) { @@ -153,14 +326,26 @@ PurpleBuddyIcon * purple_buddy_icon_unref(PurpleBuddyIcon *icon) { - g_return_val_if_fail(icon != NULL, NULL); + if (icon == NULL) + return NULL; + g_return_val_if_fail(icon->ref_count > 0, NULL); icon->ref_count--; if (icon->ref_count == 0) { - purple_buddy_icon_destroy(icon); + GHashTable *icon_cache = g_hash_table_lookup(account_cache, purple_buddy_icon_get_account(icon)); + + if (icon_cache != NULL) + g_hash_table_remove(icon_cache, purple_buddy_icon_get_username(icon)); + + g_free(icon->username); + g_free(icon->checksum); + purple_imgstore_unref(icon->img); + + PURPLE_DBUS_UNREGISTER_POINTER(icon); + g_slice_free(PurpleBuddyIcon, icon); return NULL; } @@ -174,6 +359,7 @@ PurpleConversation *conv; PurpleAccount *account; const char *username; + PurpleBuddyIcon *icon_to_set; GSList *sl, *list; g_return_if_fail(icon != NULL); @@ -181,12 +367,52 @@ account = purple_buddy_icon_get_account(icon); username = purple_buddy_icon_get_username(icon); - for (list = sl = purple_find_buddies(account, username); sl != NULL; - sl = sl->next) + /* 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; + char *old_icon; - purple_buddy_set_icon(buddy, icon); + purple_buddy_set_icon(buddy, icon_to_set); + + + old_icon = g_strdup(purple_blist_node_get_string((PurpleBlistNode *)buddy, + "buddy_icon")); + if (icon->img && purple_buddy_icons_is_caching()) + { + const char *filename = purple_imgstore_get_filename(icon->img); + purple_blist_node_set_string((PurpleBlistNode *)buddy, + "buddy_icon", + filename); + + if (icon->checksum && *icon->checksum) + { + purple_blist_node_set_string((PurpleBlistNode *)buddy, + "icon_checksum", + icon->checksum); + } + else + { + purple_blist_node_remove_setting((PurpleBlistNode *)buddy, + "icon_checksum"); + } + ref_filename(filename); + } + else + { + purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "buddy_icon"); + purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "icon_checksum"); + } + unref_filename(old_icon); + g_free(old_icon); } g_slist_free(list); @@ -194,160 +420,28 @@ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, account); if (conv != NULL) - purple_conv_im_set_icon(PURPLE_CONV_IM(conv), icon); -} - -static void -delete_icon_cache_file(const char *dirname, const char *old_icon) -{ - struct stat st; - - g_return_if_fail(dirname != NULL); - g_return_if_fail(old_icon != NULL); - - if (g_stat(old_icon, &st) == 0) - g_unlink(old_icon); - else - { - char *filename = g_build_filename(dirname, old_icon, NULL); - if (g_stat(filename, &st) == 0) - g_unlink(filename); - g_free(filename); - } - purple_debug_info("buddyicon", "Uncached file %s\n", old_icon); + purple_conv_im_set_icon(PURPLE_CONV_IM(conv), icon_to_set); } void -purple_buddy_icon_cache(PurpleBuddyIcon *icon, PurpleBuddy *buddy) +purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data, + size_t len, const char *checksum) { - const guchar *data; - const char *dirname; - char *random; - char *filename; - const char *old_icon; - size_t len = 0; - FILE *file = NULL; - - g_return_if_fail(icon != NULL); - g_return_if_fail(buddy != NULL); - - if (!purple_buddy_icons_is_caching()) - return; - - data = purple_buddy_icon_get_data(icon, &len); - - random = g_strdup_printf("%x", g_random_int()); - dirname = purple_buddy_icons_get_cache_dir(); - filename = g_build_filename(dirname, random, NULL); - old_icon = purple_blist_node_get_string((PurpleBlistNode*)buddy, "buddy_icon"); - - if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) - { - purple_debug_info("buddyicon", "Creating icon cache directory.\n"); - - if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) - { - purple_debug_error("buddyicon", - "Unable to create directory %s: %s\n", - dirname, strerror(errno)); - } - } - - if ((file = g_fopen(filename, "wb")) != NULL) - { - fwrite(data, 1, len, file); - fclose(file); - purple_debug_info("buddyicon", "Wrote file %s\n", filename); - } - else - { - purple_debug_error("buddyicon", "Unable to create file %s: %s\n", - filename, strerror(errno)); - g_free(filename); - g_free(random); - return; - } - - g_free(filename); + PurpleStoredImage *old_img; - if (old_icon != NULL) - delete_icon_cache_file(dirname, old_icon); - - purple_blist_node_set_string((PurpleBlistNode *)buddy, "buddy_icon", random); - - g_free(random); -} - -void -purple_buddy_icon_uncache(PurpleBuddy *buddy) -{ - const char *old_icon; - - g_return_if_fail(buddy != NULL); - - old_icon = purple_blist_node_get_string((PurpleBlistNode *)buddy, "buddy_icon"); - - if (old_icon != NULL) - delete_icon_cache_file(purple_buddy_icons_get_cache_dir(), old_icon); - - purple_blist_node_remove_setting((PurpleBlistNode *)buddy, "buddy_icon"); - - /* Unset the icon in case this function is called from - * something other than purple_buddy_set_icon(). */ - if (buddy->icon != NULL) - { - purple_buddy_icon_unref(buddy->icon); - buddy->icon = NULL; - } -} - -void -purple_buddy_icon_set_account(PurpleBuddyIcon *icon, PurpleAccount *account) -{ - g_return_if_fail(icon != NULL); - g_return_if_fail(account != NULL); - - icon->account = account; -} - -void -purple_buddy_icon_set_username(PurpleBuddyIcon *icon, const char *username) -{ - g_return_if_fail(icon != NULL); - g_return_if_fail(username != NULL); - - g_free(icon->username); - icon->username = g_strdup(username); -} - -void -purple_buddy_icon_set_data(PurpleBuddyIcon *icon, void *data, size_t len) -{ g_return_if_fail(icon != NULL); - g_free(icon->data); + old_img = icon->img; + icon->img = NULL; if (data != NULL && len > 0) - { - icon->data = g_memdup(data, len); - icon->len = len; - } - else - { - icon->data = NULL; - icon->len = 0; - } + icon->img = purple_buddy_icon_data_new(data, len, NULL); + + icon->checksum = g_strdup(checksum); purple_buddy_icon_update(icon); -} -void -purple_buddy_icon_set_path(PurpleBuddyIcon *icon, const gchar *path) -{ - g_return_if_fail(icon != NULL); - - g_free(icon->path); - icon->path = g_strdup(path); + purple_imgstore_unref(old_img); } PurpleAccount * @@ -366,121 +460,457 @@ return icon->username; } -const guchar * +const char * +purple_buddy_icon_get_checksum(const PurpleBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + + return icon->checksum; +} + +gconstpointer purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len) { g_return_val_if_fail(icon != NULL, NULL); - if (len != NULL) - *len = icon->len; - - return icon->data; -} + if (icon->img) + { + if (len != NULL) + *len = purple_imgstore_get_size(icon->img); -const char * -purple_buddy_icon_get_path(PurpleBuddyIcon *icon) -{ - g_return_val_if_fail(icon != NULL, NULL); + return purple_imgstore_get_data(icon->img); + } - return icon->path; + return NULL; } const char * -purple_buddy_icon_get_type(const PurpleBuddyIcon *icon) +purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon) { - const void *data; - size_t len; - - g_return_val_if_fail(icon != NULL, NULL); - - data = purple_buddy_icon_get_data(icon, &len); - - /* TODO: Find a way to do this with GDK */ - if (len >= 4) - { - if (!strncmp(data, "BM", 2)) - return "bmp"; - else if (!strncmp(data, "GIF8", 4)) - return "gif"; - else if (!strncmp(data, "\xff\xd8\xff\xe0", 4)) - return "jpg"; - else if (!strncmp(data, "\x89PNG", 4)) - return "png"; - } + if (icon->img != NULL) + return purple_imgstore_get_extension(icon->img); return NULL; } void purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username, - void *icon_data, size_t icon_len) + void *icon_data, size_t icon_len, + const char *checksum) { + PurpleBuddyIcon *icon; + g_return_if_fail(account != NULL); g_return_if_fail(username != NULL); - if (icon_data == NULL || icon_len == 0) - { - PurpleBuddyIcon *buddy_icon; + icon = purple_buddy_icons_find(account, username); - buddy_icon = purple_buddy_icons_find(account, username); - - if (buddy_icon != NULL) - purple_buddy_icon_destroy(buddy_icon); - } + if (icon != NULL) + purple_buddy_icon_set_data(icon, icon_data, icon_len, checksum); else { - PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len); + PurpleBuddyIcon *icon = purple_buddy_icon_new(account, username, icon_data, icon_len, checksum); purple_buddy_icon_unref(icon); } } +const char * +purple_buddy_icons_get_checksum_for_user(PurpleBuddy *buddy) +{ + return purple_blist_node_get_string((PurpleBlistNode*)buddy, + "icon_checksum"); +} + +static gboolean +read_icon_file(const char *path, guchar **data, size_t *len) +{ + GError *err = NULL; + + if (!g_file_get_contents(path, (gchar **)data, len, &err)) + { + purple_debug_error("buddyicon", "Error reading %s: %s\n", + path, err->message); + g_error_free(err); + + return FALSE; + } + + return TRUE; +} + PurpleBuddyIcon * purple_buddy_icons_find(PurpleAccount *account, const char *username) { GHashTable *icon_cache; - PurpleBuddyIcon *ret = NULL; - char *filename = NULL; + PurpleBuddyIcon *icon = NULL; g_return_val_if_fail(account != NULL, NULL); g_return_val_if_fail(username != NULL, NULL); icon_cache = g_hash_table_lookup(account_cache, account); - if ((icon_cache == NULL) || ((ret = g_hash_table_lookup(icon_cache, username)) == NULL)) { - const char *file; - struct stat st; + if ((icon_cache == NULL) || ((icon = g_hash_table_lookup(icon_cache, username)) == NULL)) + { PurpleBuddy *b = purple_find_buddy(account, username); + const char *protocol_icon_file; + const char *dirname; + gboolean caching; + guchar *data; + size_t len; if (!b) return NULL; - if ((file = purple_blist_node_get_string((PurpleBlistNode*)b, "buddy_icon")) == NULL) + protocol_icon_file = purple_blist_node_get_string((PurpleBlistNode*)b, "buddy_icon"); + + if (protocol_icon_file == NULL) return NULL; - if (!g_stat(file, &st)) - filename = g_strdup(file); - else - filename = g_build_filename(purple_buddy_icons_get_cache_dir(), file, NULL); + dirname = purple_buddy_icons_get_cache_dir(); + + caching = purple_buddy_icons_is_caching(); + /* By disabling caching temporarily, we avoid a loop + * and don't have to add special code through several + * functions. */ + purple_buddy_icons_set_caching(FALSE); + + if (protocol_icon_file != NULL) + { + char *path = g_build_filename(dirname, protocol_icon_file, NULL); + if (read_icon_file(path, &data, &len)) + { + const char *checksum; + + if (icon == NULL) + icon = purple_buddy_icon_create(account, username); + icon->ref_count = 0; + icon->img = NULL; + checksum = g_strdup(purple_blist_node_get_string((PurpleBlistNode*)b, "icon_checksum")); + purple_buddy_icon_set_data(icon, data, len, checksum); + g_free(data); + } + g_free(path); + } + + purple_buddy_icons_set_caching(caching); + } + + 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_free(data); + 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; + PurpleBlistNode *child; + + 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 && purple_buddy_icons_is_caching()) + { + 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_hash_table_insert(custom_icon_cache, contact, img); + + for (child = contact->node.child ; child ; child = child->next) + { + PurpleBuddy *buddy; + PurpleConversation *conv; + + if (!PURPLE_BLIST_NODE_IS_BUDDY(child)) + continue; - if (!g_stat(filename, &st)) { - FILE *f = g_fopen(filename, "rb"); - if (f) { - char *data = g_malloc(st.st_size); - fread(data, 1, st.st_size, f); - fclose(f); - ret = purple_buddy_icon_create(account, username); - purple_buddy_icon_ref(ret); - purple_buddy_icon_set_data(ret, data, st.st_size); - purple_buddy_icon_unref(ret); - g_free(data); - g_free(filename); - return ret; + buddy = (PurpleBuddy *)child; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + purple_buddy_get_name(buddy), + purple_buddy_get_account(buddy)); + if (conv) + purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON); + + purple_blist_update_buddy_icon(buddy); + } + + if (old_img) + purple_imgstore_unref(old_img); + else + { + /* The old icon may not have been loaded into memory. In that + * case, we'll need to uncache the filename. The filenames + * are ref-counted, so this is safe. */ + purple_buddy_icon_data_uncache_file(old_icon); + } + g_free(old_icon); +} + +void +purple_buddy_icon_set_old_icons_dir(const char *dirname) +{ + old_icons_dir = g_strdup(dirname); +} + +static void +migrate_buddy_icon(PurpleBlistNode *node, const char *setting_name, + const char *dirname, const char *filename) +{ + char *path; + + if (filename[0] != '/') + { + path = g_build_filename(dirname, filename, NULL); + if (g_file_test(path, G_FILE_TEST_EXISTS)) + { + g_free(path); + return; + } + g_free(path); + + path = g_build_filename(old_icons_dir, filename, NULL); + } + else + path = g_strdup(filename); + + if (g_file_test(path, G_FILE_TEST_EXISTS)) + { + guchar *icon_data; + size_t icon_len; + FILE *file; + char *new_filename; + + if (!read_icon_file(path, &icon_data, &icon_len)) + { + g_free(path); + return; + } + + g_free(path); + + new_filename = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); + if (new_filename == NULL) + { + return; + } + + path = g_build_filename(dirname, new_filename, NULL); + if ((file = g_fopen(path, "wb")) != NULL) + { + if (!fwrite(icon_data, icon_len, 1, file)) + { + purple_debug_error("buddyicon", "Error writing %s: %s\n", + path, strerror(errno)); + } + else + purple_debug_info("buddyicon", "Wrote migrated cache file: %s\n", path); + + fclose(file); + } + else + { + purple_debug_error("buddyicon", "Unable to create file %s: %s\n", + path, strerror(errno)); + g_free(new_filename); + g_free(path); + return; + } + g_free(path); + + purple_blist_node_set_string(node, + setting_name, + new_filename); + ref_filename(new_filename); + + g_free(new_filename); + + if (!strcmp(setting_name, "buddy_icon")) + { + const char *hash; + + hash = purple_blist_node_get_string(node, "avatar_hash"); + if (hash != NULL) + { + purple_blist_node_set_string(node, "icon_checksum", hash); + purple_blist_node_remove_setting(node, "avatar_hash"); + } + else + { + int checksum = purple_blist_node_get_int(node, "icon_checksum"); + if (checksum != 0) + { + char *checksum_str = g_strdup_printf("%i", checksum); + purple_blist_node_remove_setting(node, "icon_checksum"); + purple_blist_node_set_string(node, "icon_checksum", checksum_str); + g_free(checksum_str); + } } } - g_free(filename); + } + else + { + /* If the icon is gone, drop the setting... */ + purple_blist_node_remove_setting(node, + setting_name); + + if (!strcmp(setting_name, "buddy_icon")) + { + purple_blist_node_remove_setting(node, + "avatar_hash"); + purple_blist_node_remove_setting(node, + "icon_checksum"); + } + g_free(path); + } +} + +void +purple_buddy_icons_blist_loaded_cb() +{ + 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) + { + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) + { + purple_debug_info("buddyicon", "Creating icon cache directory.\n"); + + if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + { + purple_debug_error("buddyicon", + "Unable to create directory %s: %s\n", + dirname, strerror(errno)); + } + } } - return ret; + while (node != NULL) + { + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) + { + const char *filename; + + filename = purple_blist_node_get_string(node, "buddy_icon"); + if (filename != NULL) + { + if (old_icons_dir != NULL) + { + migrate_buddy_icon(node, + "buddy_icon", + dirname, filename); + } + else + { + char *path = g_build_filename(dirname, filename, NULL); + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) + { + purple_blist_node_remove_setting(node, + "buddy_icon"); + purple_blist_node_remove_setting(node, + "icon_checksum"); + } + else + ref_filename(filename); + g_free(path); + } + } + } + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + { + const char *filename; + + filename = purple_blist_node_get_string(node, "custom_buddy_icon"); + if (filename != NULL) + { + if (old_icons_dir != NULL) + { + migrate_buddy_icon(node, + "custom_buddy_icon", + dirname, filename); + } + else + { + char *path = g_build_filename(dirname, filename, NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + { + purple_blist_node_remove_setting(node, + "custom_buddy_icon"); + } + else + ref_filename(filename); + g_free(path); + } + } + } + node = purple_blist_node_next(node, TRUE); + } } void @@ -510,6 +940,7 @@ return cache_dir; } +// TODO: Deal with this char *purple_buddy_icons_get_full_path(const char *icon) { if (icon == NULL) return NULL; @@ -535,13 +966,28 @@ g_direct_hash, g_direct_equal, NULL, (GFreeFunc)g_hash_table_destroy); + 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); } void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height) @@ -572,4 +1018,3 @@ *width = new_width; *height = new_height; } - diff -r 58a097859388 -r 0fa041c66957 libpurple/buddyicon.h --- a/libpurple/buddyicon.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/buddyicon.h Wed Apr 25 05:26:12 2007 +0000 @@ -29,51 +29,38 @@ #include "account.h" #include "blist.h" +#include "imgstore.h" #include "prpl.h" -struct _PurpleBuddyIcon -{ - PurpleAccount *account; /**< The account the user is on. */ - char *username; /**< The username the icon belongs to. */ - - void *data; /**< The buddy icon data. */ - size_t len; /**< The length of the buddy icon data. */ - char *path; /**< The buddy icon's non-cached path. */ - - int ref_count; /**< The buddy icon reference count. */ -}; - #ifdef __cplusplus extern "C" { #endif +// TODO: Deal with this. +char *purple_buddy_icons_get_full_path(const char *icon); + /**************************************************************************/ /** @name Buddy Icon API */ /**************************************************************************/ /*@{*/ /** - * Creates a new buddy icon structure. + * Creates a new buddy icon structure and populate it. + * + * If the buddy icon already exists, you'll get a reference to that structure, + * which will have been updated with the data supplied. * * @param account The account the user is on. * @param username The username the icon belongs to. * @param icon_data The buddy icon data. * @param icon_len The buddy icon length. + * @param checksum A protocol checksum from the prpl or @c NULL. * * @return The buddy icon structure. */ PurpleBuddyIcon *purple_buddy_icon_new(PurpleAccount *account, const char *username, - void *icon_data, size_t icon_len); - -/** - * Destroys a buddy icon structure. - * - * If the buddy icon's reference count is greater than 1, this will - * just decrease the reference count and return. - * - * @param icon The buddy icon structure to destroy. - */ -void purple_buddy_icon_destroy(PurpleBuddyIcon *icon); + void *icon_data, size_t icon_len, + const char *checksum); /** * Increments the reference count on a buddy icon. @@ -103,52 +90,16 @@ void purple_buddy_icon_update(PurpleBuddyIcon *icon); /** - * Caches a buddy icon associated with a specific buddy to disk. - * - * @param icon The buddy icon. - * @param buddy The buddy that this icon belongs to. - */ -void purple_buddy_icon_cache(PurpleBuddyIcon *icon, PurpleBuddy *buddy); - -/** - * Removes cached buddy icon for a specific buddy. - * - * @param buddy The buddy for which to remove the cached icon. - */ -void purple_buddy_icon_uncache(PurpleBuddy *buddy); - -/** - * Sets the buddy icon's account. - * - * @param icon The buddy icon. - * @param account The account. - */ -void purple_buddy_icon_set_account(PurpleBuddyIcon *icon, PurpleAccount *account); - -/** - * Sets the buddy icon's username. - * - * @param icon The buddy icon. - * @param username The username. - */ -void purple_buddy_icon_set_username(PurpleBuddyIcon *icon, const char *username); - -/** - * Sets the buddy icon's icon data. + * Sets the buddy icon's data that was received over the wire. * * @param icon The buddy icon. - * @param data The buddy icon data. - * @param len The length of the icon data. + * @param data The buddy icon data received over the wire. + * @param len The length of the data in @a data. + * @param checksum A protocol checksum from the prpl or @c NULL. */ -void purple_buddy_icon_set_data(PurpleBuddyIcon *icon, void *data, size_t len); - -/** - * Sets the buddy icon's path. - * - * @param icon The buddy icon. - * @param path The buddy icon's non-cached path. - */ -void purple_buddy_icon_set_path(PurpleBuddyIcon *icon, const gchar *path); +void +purple_buddy_icon_set_data(PurpleBuddyIcon *icon, guchar *data, + size_t len, const char *checksum); /** * Returns the buddy icon's account. @@ -169,32 +120,36 @@ const char *purple_buddy_icon_get_username(const PurpleBuddyIcon *icon); /** + * Returns the buddy icon's checksum. + * + * This function is really only for prpl use. + * + * @param icon The buddy icon. + * + * @return The checksum. + */ +const char *purple_buddy_icon_get_checksum(const PurpleBuddyIcon *icon); + +/** * Returns the buddy icon's data. * * @param icon The buddy icon. - * @param len The returned icon length. + * @param len If not @c NULL, the length of the icon data returned will be + * set in the location pointed to by this. * - * @return The icon data. + * @return A pointer to the icon data. */ -const guchar *purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len); - -/** - * Returns the buddy icon's path. - * - * @param icon The buddy icon. - * - * @return The buddy icon's non-cached path. - */ -const gchar *purple_buddy_icon_get_path(PurpleBuddyIcon *icon); +gconstpointer purple_buddy_icon_get_data(const PurpleBuddyIcon *icon, size_t *len); /** * Returns an extension corresponding to the buddy icon's file type. * * @param icon The buddy icon. * - * @return The icon's extension. + * @return The icon's extension, "icon" if unknown, or @c NULL if + * the image data has disappeared. */ -const char *purple_buddy_icon_get_type(const PurpleBuddyIcon *icon); +const char *purple_buddy_icon_get_extension(const PurpleBuddyIcon *icon); /*@}*/ @@ -210,11 +165,27 @@ * @param username The username of the user. * @param icon_data The icon data. * @param icon_len The length of the icon data. + * @param checksum A protocol checksum from the prpl or @c NULL. * * @return The buddy icon set, or NULL if no icon was set. */ -void purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username, - void *icon_data, size_t icon_len); +void +purple_buddy_icons_set_for_user(PurpleAccount *account, const char *username, + void *icon_data, size_t icon_len, + const char *checksum); + +/** + * Returns the checksum for the buddy icon of a specified buddy. + * + * This avoids loading the icon image data from the cache if it's + * not already loaded for some other reason. + * + * @param buddy The buddy + * + * @return The checksum. + */ +const char * +purple_buddy_icons_get_checksum_for_user(PurpleBuddy *buddy); /** * Returns the buddy icon information for a user. @@ -224,8 +195,46 @@ * * @return The icon data if found, or @c NULL if not found. */ -PurpleBuddyIcon *purple_buddy_icons_find(PurpleAccount *account, - const char *username); +PurpleBuddyIcon * +purple_buddy_icons_find(PurpleAccount *account, const char *username); + +/** + * Returns a boolean indicating if a given contact has a custom buddy icon. + * + * @param contact The contact + * + * @return A boolean indicating if @a contact has a custom buddy icon. + */ +gboolean +purple_buddy_icons_has_custom_icon(PurpleContact *contact); + +/** + * Returns the custom buddy icon image for a contact. + * + * This function deals with loading the icon from the cache, if + * needed, so it should be called in any case where you want the + * appropriate icon. + * + * @param contact The contact + * + * @return The custom buddy icon image. + */ +PurpleStoredImage * +purple_buddy_icons_find_custom_icon(PurpleContact *contact); + +/** + * Sets a custom buddy icon for a user. + * + * This function will deal with saving a record of the icon, + * caching the data, etc. + * + * @param contact The contact for which to set a custom icon. + * @param icon_data The image data of the icon. + * @param icon_len The length of the data in @a icon_data. + */ +void +purple_buddy_icons_set_custom_icon(PurpleContact *contact, + guchar *icon_data, size_t icon_len); /** * Sets whether or not buddy icon caching is enabled. @@ -263,18 +272,6 @@ const char *purple_buddy_icons_get_cache_dir(void); /** - * Takes a buddy icon and returns a full path. - * - * If @a icon is a full path to an existing file, a copy of - * @a icon is returned. Otherwise, a newly allocated string - * consiting of purple_buddy_icons_get_cache_dir() + @a icon is - * returned. - * - * @return The full path for an icon. - */ -char *purple_buddy_icons_get_full_path(const char *icon); - -/** * Returns the buddy icon subsystem handle. * * @return The subsystem handle. diff -r 58a097859388 -r 0fa041c66957 libpurple/conversation.c --- a/libpurple/conversation.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/conversation.c Wed Apr 25 05:26:12 2007 +0000 @@ -425,8 +425,7 @@ purple_conv_im_stop_typing_timeout(conv->u.im); purple_conv_im_stop_send_typed_timeout(conv->u.im); - if (conv->u.im->icon != NULL) - purple_buddy_icon_unref(conv->u.im->icon); + purple_buddy_icon_unref(conv->u.im->icon); conv->u.im->icon = NULL; PURPLE_DBUS_UNREGISTER_POINTER(conv->u.im); @@ -941,8 +940,7 @@ if (im->icon != icon) { - if (im->icon != NULL) - purple_buddy_icon_unref(im->icon); + purple_buddy_icon_unref(im->icon); im->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon)); } diff -r 58a097859388 -r 0fa041c66957 libpurple/core.c --- a/libpurple/core.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/core.c Wed Apr 25 05:26:12 2007 +0000 @@ -31,6 +31,7 @@ #include "dnsquery.h" #include "ft.h" #include "idle.h" +#include "imgstore.h" #include "network.h" #include "notify.h" #include "plugin.h" @@ -44,6 +45,7 @@ #include "sslconn.h" #include "status.h" #include "stun.h" +#include "util.h" #ifdef HAVE_DBUS # include "dbus-server.h" @@ -123,6 +125,9 @@ purple_plugins_init(); purple_plugins_probe(G_MODULE_SUFFIX); + /* The buddy icon code uses the imgstore, so init it early. */ + purple_imgstore_init(); + /* Accounts use status and buddy icons, so initialize these before accounts */ purple_status_init(); purple_buddy_icons_init(); @@ -189,6 +194,7 @@ purple_xfers_uninit(); purple_proxy_uninit(); purple_dnsquery_uninit(); + purple_imgstore_uninit(); purple_debug_info("main", "Unloading all plugins\n"); purple_plugins_destroy_all(); @@ -268,3 +274,376 @@ { return _ops; } + +static gboolean +move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative) +{ + char *new_name = g_build_filename(new_base, basename, NULL); +#ifndef _WIN32 + char *old_name; +#endif + if (g_rename(path, new_name)) + { + purple_debug_error("core", "Error renaming %s to %s: %s\n", + path, new_name, strerror(errno)); + g_free(new_name); + return FALSE; + } + g_free(new_name); + +#ifndef _WIN32 + /* NOTE: This new_name is relative. */ + new_name = g_build_filename(relative, basename, NULL); + old_name = g_build_filename(old_base, basename, NULL); + if (symlink(new_name, old_name)) + { + purple_debug_warning("core", "Error symlinking %s to %s: %s\n", + old_name, new_name, strerror(errno)); + } + g_free(old_name); + g_free(new_name); +#endif + + return TRUE; +} + +gboolean +purple_core_migrate(void) +{ + const char *user_dir = purple_user_dir(); + char *old_user_dir = g_strconcat(purple_home_dir(), + G_DIR_SEPARATOR_S ".gaim", NULL); + char *status_file; + FILE *fp; + GDir *dir; + GError *err; + const char *entry; +#ifndef _WIN32 + char *logs_dir; +#endif + char *old_icons_dir; + + if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS)) + { + /* ~/.gaim doesn't exist, so there's nothing to migrate. */ + g_free(old_user_dir); + return TRUE; + } + + status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL); + + if (g_file_test(user_dir, G_FILE_TEST_EXISTS)) + { + /* If we're here, we have both ~/.gaim and .purple. */ + + if (!g_file_test(status_file, G_FILE_TEST_EXISTS)) + { + /* There's no "migrating" status file, + * so ~/.purple is all up to date. */ + g_free(status_file); + g_free(old_user_dir); + return TRUE; + } + } + + /* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */ + + /* Ensure the user directory exists */ + if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR)) + { + if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + purple_debug_error("core", "Error creating directory %s: %s\n", + user_dir, strerror(errno)); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + } + + /* This writes ~/.purple/migrating, which allows us to detect + * incomplete migrations and properly retry. */ + if (!(fp = g_fopen(status_file, "w"))) + { + purple_debug_error("core", "Error opening file %s for writing: %s\n", + status_file, strerror(errno)); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + fclose(fp); + + /* Open ~/.gaim so we can loop over its contents. */ + err = NULL; + if (!(dir = g_dir_open(old_user_dir, 0, &err))) + { + purple_debug_error("core", "Error opening directory %s: %s\n", + status_file, + (err ? err->message : "Unknown error")); + if (err) + g_error_free(err); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + /* Loop over the contents of ~/.gaim */ + while ((entry = g_dir_read_name(dir))) + { + char *name = g_build_filename(old_user_dir, entry, NULL); + +#ifndef _WIN32 + /* Deal with symlinks... */ + if (g_file_test(name, G_FILE_TEST_IS_SYMLINK)) + { + /* We're only going to duplicate a logs symlink. */ + if (!strcmp(entry, "logs")) + { + char buf[MAXPATHLEN]; + + if (readlink(name, buf, sizeof(buf) - 1) == -1) + { + purple_debug_error("core", "Error reading symlink %s: %s\n", + name, strerror(errno)); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + buf[sizeof(buf) - 1] = '\0'; + + logs_dir = g_strconcat(user_dir, G_DIR_SEPARATOR_S "logs", NULL); + + if (!strcmp(buf, "../.purple/logs") || !strcmp(buf, logs_dir)) + { + /* If the symlink points to the new directory, we're + * likely just trying again after a failed migration, + * so there's no need to fail here. */ + g_free(logs_dir); + continue; + } + + /* In case we are trying again after a failed migration, we need + * to unlink any existing symlink. If it's a directory, this + * will fail, and so will the symlink below, which is good + * because the user should sort things out. */ + g_unlink(logs_dir); + + /* Relative links will most likely still be + * valid from ~/.purple, though not it's not + * guaranteed. Oh well. */ + if (symlink(buf, logs_dir)) + { + purple_debug_error("core", "Error symlinking %s to %s: %s\n", + logs_dir, buf, strerror(errno)); + g_free(name); + g_free(logs_dir); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + g_free(logs_dir); + continue; + } + + /* Ignore all other symlinks. */ + continue; + } +#endif + + /* Deal with directories... */ + if (g_file_test(name, G_FILE_TEST_IS_DIR)) + { + if (!strcmp(entry, "icons")) + { + /* This is a special case for the Album plugin, which + * stores data in the icons folder. We're not copying + * the icons directory over because previous bugs + * meant that it filled up with junk for many users. + * This is a great time to purge it. */ + + GDir *icons_dir; + char *new_icons_dir; + const char *icons_entry; + + err = NULL; + if (!(icons_dir = g_dir_open(name, 0, &err))) + { + purple_debug_error("core", "Error opening directory %s: %s\n", + name, + (err ? err->message : "Unknown error")); + if (err) + g_error_free(err); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + new_icons_dir = g_build_filename(user_dir, "icons", NULL); + /* Ensure the new icon directory exists */ + if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR)) + { + if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + purple_debug_error("core", "Error creating directory %s: %s\n", + new_icons_dir, strerror(errno)); + g_free(new_icons_dir); + g_dir_close(icons_dir); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + } + + while ((icons_entry = g_dir_read_name(icons_dir))) + { + char *icons_name = g_build_filename(name, icons_entry, NULL); + + if (g_file_test(icons_name, G_FILE_TEST_IS_DIR)) + { + if (!move_and_symlink_dir(icons_name, icons_entry, + name, new_icons_dir, "../../.purple/icons")) + { + g_free(icons_name); + g_free(new_icons_dir); + g_dir_close(icons_dir); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + } + g_free(icons_name); + } + + g_dir_close(icons_dir); + } + else if (!strcmp(entry, "plugins")) + { + /* Do nothing, because we broke plugin compatibility. + * This means that the plugins directory gets left behind. */ + } + else + { + /* All other directories are moved and symlinked. */ + if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple")) + { + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + } + } + else if (g_file_test(name, G_FILE_TEST_IS_REGULAR)) + { + /* Regular files are copied. */ + + char *new_name; + FILE *new_file; + + if (!(fp = g_fopen(name, "rb"))) + { + purple_debug_error("core", "Error opening file %s for reading: %s\n", + name, strerror(errno)); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + new_name = g_build_filename(user_dir, entry, NULL); + if (!(new_file = g_fopen(new_name, "wb"))) + { + purple_debug_error("core", "Error opening file %s for writing: %s\n", + new_name, strerror(errno)); + fclose(fp); + g_free(new_name); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + while (!feof(fp)) + { + unsigned char buf[256]; + size_t size; + + size = fread(buf, 1, sizeof(buf), fp); + if (size != sizeof(buf) && !feof(fp)) + { + purple_debug_error("core", "Error reading %s: %s\n", + name, strerror(errno)); + fclose(new_file); + fclose(fp); + g_free(new_name); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + + if (!fwrite(buf, size, 1, new_file)) + { + purple_debug_error("core", "Error writing %s: %s\n", + new_name, strerror(errno)); + fclose(new_file); + fclose(fp); + g_free(new_name); + g_free(name); + g_dir_close(dir); + g_free(status_file); + g_free(old_user_dir); + return FALSE; + } + } + + if (fclose(new_file)) + { + purple_debug_error("core", "Error writing: %s: %s\n", + new_name, strerror(errno)); + } + if (fclose(fp)) + { + purple_debug_warning("core", "Error closing %s: %s\n", + name, strerror(errno)); + } + g_free(new_name); + } + else + purple_debug_warning("core", "Not a regular file or directory: %s\n", name); + + g_free(name); + } + + /* The migration was successful, so delete the status file. */ + if (g_unlink(status_file)) + { + purple_debug_error("core", "Error unlinking file %s: %s\n", + status_file, strerror(errno)); + g_free(status_file); + return FALSE; + } + + old_icons_dir = g_build_filename(old_user_dir, "icons", NULL); + purple_buddy_icon_set_old_icons_dir(old_icons_dir); + g_free(old_icons_dir); + + g_free(old_user_dir); + + g_free(status_file); + return TRUE; +} diff -r 58a097859388 -r 0fa041c66957 libpurple/core.h --- a/libpurple/core.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/core.h Wed Apr 25 05:26:12 2007 +0000 @@ -106,6 +106,17 @@ */ PurpleCoreUiOps *purple_core_get_ui_ops(void); +/** + * Migrates from .gaim to .purple. + * + * UIs MUST NOT call this if they have been told to use a custom + * user directory. + * + * @return A boolean indicating success or migration failure. On failure, + * the application must display an error to the user and then exit. + */ +gboolean purple_core_migrate(void); + #ifdef __cplusplus } #endif diff -r 58a097859388 -r 0fa041c66957 libpurple/gaim-compat.h --- a/libpurple/gaim-compat.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/gaim-compat.h Wed Apr 25 05:26:12 2007 +0000 @@ -343,19 +343,15 @@ #define gaim_buddy_icon_ref purple_buddy_icon_ref #define gaim_buddy_icon_unref purple_buddy_icon_unref #define gaim_buddy_icon_update purple_buddy_icon_update -#define gaim_buddy_icon_cache purple_buddy_icon_cache -#define gaim_buddy_icon_uncache purple_buddy_icon_uncache #define gaim_buddy_icon_set_account purple_buddy_icon_set_account #define gaim_buddy_icon_set_username purple_buddy_icon_set_username -#define gaim_buddy_icon_set_data purple_buddy_icon_set_data -#define gaim_buddy_icon_set_path purple_buddy_icon_set_path +#define gaim_buddy_icon_set_data purple_buddy_icon_set_protocol_data #define gaim_buddy_icon_get_account purple_buddy_icon_get_account #define gaim_buddy_icon_get_username purple_buddy_icon_get_username #define gaim_buddy_icon_get_data purple_buddy_icon_get_data -#define gaim_buddy_icon_get_path purple_buddy_icon_get_path -#define gaim_buddy_icon_get_type purple_buddy_icon_get_type +#define gaim_buddy_icon_get_type purple_buddy_icon_get_extension #define gaim_buddy_icons_set_for_user purple_buddy_icons_set_for_user #define gaim_buddy_icons_find purple_buddy_icons_find @@ -363,7 +359,6 @@ #define gaim_buddy_icons_is_caching purple_buddy_icons_is_caching #define gaim_buddy_icons_set_cache_dir purple_buddy_icons_set_cache_dir #define gaim_buddy_icons_get_cache_dir purple_buddy_icons_get_cache_dir -#define gaim_buddy_icons_get_full_path purple_buddy_icons_get_full_path #define gaim_buddy_icons_get_handle purple_buddy_icons_get_handle #define gaim_buddy_icons_init purple_buddy_icons_init @@ -967,13 +962,13 @@ #define GaimStoredImage PurpleStoredImage -#define gaim_imgstore_add purple_imgstore_add -#define gaim_imgstore_get purple_imgstore_get +#define gaim_imgstore_add purple_imgstore_add_with_id +#define gaim_imgstore_get purple_imgstore_find_by_id #define gaim_imgstore_get_data purple_imgstore_get_data #define gaim_imgstore_get_size purple_imgstore_get_size #define gaim_imgstore_get_filename purple_imgstore_get_filename -#define gaim_imgstore_ref purple_imgstore_ref -#define gaim_imgstore_unref purple_imgstore_unref +#define gaim_imgstore_ref purple_imgstore_ref_by_id +#define gaim_imgstore_unref purple_imgstore_unref_by_id /* from log.h */ @@ -2237,7 +2232,7 @@ #define gaim_value_new_outgoing purple_value_new_outgoing #define gaim_value_destroy purple_value_destroy #define gaim_value_dup purple_value_dup -#define gaim_value_get_type purple_value_get_type +#define gaim_value_purple_buddy_icon_get_extensionget_type purple_value_get_type #define gaim_value_get_subtype purple_value_get_subtype #define gaim_value_get_specific_type purple_value_get_specific_type #define gaim_value_is_outgoing purple_value_is_outgoing diff -r 58a097859388 -r 0fa041c66957 libpurple/imgstore.c --- a/libpurple/imgstore.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/imgstore.c Wed Apr 25 05:26:12 2007 +0000 @@ -27,140 +27,162 @@ #include #include "debug.h" #include "imgstore.h" +#include "util.h" -static GSList *imgstore = NULL; +static GHashTable *imgstore; static int nextid = 0; /** * Stored image * - * Represents a single IM image awaiting display and/or transmission. - * Now that this type is basicly private too, these two structs could - * probably be combined. + * NOTE: purple_imgstore_add() creates these without zeroing the memory, so + * NOTE: make sure to update that function when adding members. */ struct _PurpleStoredImage { - char *data; /**< The image data. */ + int id; + guint8 refcount; size_t size; /**< The image data's size. */ char *filename; /**< The filename (for the UI) */ + gpointer data; /**< The image data. */ }; -typedef struct +PurpleStoredImage * +purple_imgstore_add(gconstpointer data, size_t size, const char *filename) { - int id; - int refcount; PurpleStoredImage *img; -} PurpleStoredImagePriv; -/* private functions */ + g_return_val_if_fail(data != NULL, 0); + g_return_val_if_fail(size > 0, 0); -static PurpleStoredImagePriv *purple_imgstore_get_priv(int id) { - GSList *tmp = imgstore; - PurpleStoredImagePriv *priv = NULL; + img = g_new(PurpleStoredImage, 1); + img->data = g_memdup(data, size); + img->size = size; + img->filename = g_strdup(filename); + img->refcount = 1; + img->id = 0; + + return img; +} - g_return_val_if_fail(id > 0, NULL); +int +purple_imgstore_add_with_id(gconstpointer data, size_t size, const char *filename) +{ + PurpleStoredImage *img = purple_imgstore_add(data, size, filename); + img->id = ++nextid; - while (tmp && !priv) { - PurpleStoredImagePriv *tmp_priv = tmp->data; + g_hash_table_insert(imgstore, GINT_TO_POINTER(img->id), img); - if (tmp_priv->id == id) - priv = tmp_priv; + return img->id; +} + +PurpleStoredImage *purple_imgstore_find_by_id(int id) { + PurpleStoredImage *img = g_hash_table_lookup(imgstore, GINT_TO_POINTER(id)); - tmp = tmp->next; - } + if (img != NULL) + purple_debug_misc("imgstore", "retrieved image id %d\n", img->id); + + return img; +} - if (!priv) - purple_debug(PURPLE_DEBUG_ERROR, "imgstore", "failed to find image id %d\n", id); +gconstpointer purple_imgstore_get_data(PurpleStoredImage *img) { + return img->data; +} - return priv; +size_t purple_imgstore_get_size(PurpleStoredImage *img) +{ + return img->size; } -static void purple_imgstore_free_priv(PurpleStoredImagePriv *priv) { - PurpleStoredImage *img = NULL; +const char *purple_imgstore_get_filename(PurpleStoredImage *img) +{ + return img->filename; +} + +const char *purple_imgstore_get_extension(PurpleStoredImage *img) +{ + return purple_util_get_image_extension(img->data, img->size); +} + +void purple_imgstore_ref_by_id(int id) +{ + PurpleStoredImage *img = purple_imgstore_find_by_id(id); + + g_return_if_fail(img != NULL); + + purple_imgstore_ref(img); +} + +void purple_imgstore_unref_by_id(int id) +{ + PurpleStoredImage *img = purple_imgstore_find_by_id(id); + + g_return_if_fail(img != NULL); + + purple_imgstore_unref(img); +} - g_return_if_fail(priv != NULL); +PurpleStoredImage * +purple_imgstore_ref(PurpleStoredImage *img) +{ + g_return_val_if_fail(img != NULL, NULL); + + img->refcount++; + + return img; +} - img = priv->img; - if (img) { +PurpleStoredImage * +purple_imgstore_unref(PurpleStoredImage *img) +{ + if (img == NULL) + return NULL; + + g_return_val_if_fail(img->refcount > 0, NULL); + + img->refcount--; + + if (img->refcount == 0) + { + purple_signal_emit(purple_imgstore_get_handle(), + "image-deleting", img); + if (img->id) + g_hash_table_remove(imgstore, GINT_TO_POINTER(img->id)); + g_free(img->data); g_free(img->filename); g_free(img); } - purple_debug(PURPLE_DEBUG_INFO, "imgstore", "freed image id %d\n", priv->id); - - g_free(priv); - - imgstore = g_slist_remove(imgstore, priv); + return img; } -/* public functions */ - -int purple_imgstore_add(const void *data, size_t size, const char *filename) { - PurpleStoredImagePriv *priv; - PurpleStoredImage *img; - - g_return_val_if_fail(data != NULL, 0); - g_return_val_if_fail(size > 0, 0); +void * +purple_imgstore_get_handle() +{ + static int handle; - img = g_new0(PurpleStoredImage, 1); - img->data = g_memdup(data, size); - img->size = size; - img->filename = g_strdup(filename); - - priv = g_new0(PurpleStoredImagePriv, 1); - priv->id = ++nextid; - priv->refcount = 1; - priv->img = img; - - imgstore = g_slist_append(imgstore, priv); - purple_debug(PURPLE_DEBUG_INFO, "imgstore", "added image id %d\n", priv->id); - - return priv->id; + return &handle; } -PurpleStoredImage *purple_imgstore_get(int id) { - PurpleStoredImagePriv *priv = purple_imgstore_get_priv(id); - - g_return_val_if_fail(priv != NULL, NULL); - - purple_debug(PURPLE_DEBUG_INFO, "imgstore", "retrieved image id %d\n", priv->id); - - return priv->img; -} +void +purple_imgstore_init() +{ + void *handle = purple_imgstore_get_handle(); -gpointer purple_imgstore_get_data(PurpleStoredImage *i) { - return i->data; -} + purple_signal_register(handle, "image-deleting", + purple_marshal_VOID__POINTER, NULL, + 1, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_STORED_IMAGE)); -size_t purple_imgstore_get_size(PurpleStoredImage *i) { - return i->size; -} - -const char *purple_imgstore_get_filename(PurpleStoredImage *i) { - return i->filename; + imgstore = g_hash_table_new(g_int_hash, g_int_equal); } -void purple_imgstore_ref(int id) { - PurpleStoredImagePriv *priv = purple_imgstore_get_priv(id); - - g_return_if_fail(priv != NULL); - - (priv->refcount)++; - - purple_debug(PURPLE_DEBUG_INFO, "imgstore", "referenced image id %d (count now %d)\n", priv->id, priv->refcount); -} +void +purple_imgstore_uninit() +{ + g_hash_table_destroy(imgstore); -void purple_imgstore_unref(int id) { - PurpleStoredImagePriv *priv = purple_imgstore_get_priv(id); - - g_return_if_fail(priv != NULL); - g_return_if_fail(priv->refcount > 0); - - (priv->refcount)--; - - purple_debug(PURPLE_DEBUG_INFO, "imgstore", "unreferenced image id %d (count now %d)\n", priv->id, priv->refcount); - - if (priv->refcount == 0) - purple_imgstore_free_priv(priv); + purple_signals_unregister_by_instance(purple_blist_get_handle()); } diff -r 58a097859388 -r 0fa041c66957 libpurple/imgstore.h --- a/libpurple/imgstore.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/imgstore.h Wed Apr 25 05:26:12 2007 +0000 @@ -34,9 +34,29 @@ #endif /** - * Add an image to the store. The caller owns a reference - * to the image in the store, and must dereference the image - * with purple_imgstore_unref for it to be freed. + * Add an image to the store. + * + * The caller owns a reference to the image in the store, and must dereference + * the image with purple_imgstore_unref() for it to be freed. + * + * No ID is allocated when using this function. If you need to reference the + * image by an ID, use purple_imgstore_add_with_id() instead. + * + * @param data Pointer to the image data. + * @param size Image data's size. + * @param filename Filename associated with image. + * + * @return The stored image. + */ +PurpleStoredImage * +purple_imgstore_add(gconstpointer data, size_t size, const char *filename); + +/** + * Add an image to the store, allocating an ID. + * + * The caller owns a reference to the image in the store, and must dereference + * the image with purple_imgstore_unref_by_id() or purple_imgstore_unref() + * for it to be freed. * * @param data Pointer to the image data. * @param size Image data's size. @@ -44,7 +64,7 @@ * @return ID for the image. */ -int purple_imgstore_add(const void *data, size_t size, const char *filename); +int purple_imgstore_add_with_id(gconstpointer data, size_t size, const char *filename); /** * Retrieve an image from the store. The caller does not own a @@ -54,22 +74,22 @@ * * @return A pointer to the requested image, or NULL if it was not found. */ -PurpleStoredImage *purple_imgstore_get(int id); +PurpleStoredImage *purple_imgstore_find_by_id(int id); /** * Retrieves a pointer to the image's data. * - * @param i The Image + * @param img The Image * * @return A pointer to the data, which must not * be freed or modified. */ -gpointer purple_imgstore_get_data(PurpleStoredImage *i); +gconstpointer purple_imgstore_get_data(PurpleStoredImage *img); /** * Retrieves the length of the image's data. * - * @param i The Image + * @param img The Image * * @return The size of the data that the pointer returned by * purple_imgstore_get_data points to. @@ -79,30 +99,82 @@ /** * Retrieves a pointer to the image's filename. * - * @param i The Image + * @param img The image * * @return A pointer to the filename, which must not * be freed or modified. */ -const char *purple_imgstore_get_filename(PurpleStoredImage *i); +const char *purple_imgstore_get_filename(PurpleStoredImage *img); + +/** + * Returns an extension corresponding to the image's file type. + * + * @param img The image. + * + * @return The icon's extension or "icon" if unknown. + */ +const char *purple_imgstore_get_extension(PurpleStoredImage *img); /** - * Increment the reference count for an image in the store. The - * image will be removed from the store when the reference count - * is zero. + * Increment the reference count. + * + * @param img The image. + * + * @return @a img + */ +PurpleStoredImage * +purple_imgstore_ref(PurpleStoredImage *img); + +/** + * Decrement the reference count. + * + * If the reference count reaches zero, the image will be freed. + * + * @param img The image. + * + * @return @a img or @c NULL if the reference count reached zero. + */ +PurpleStoredImage * +purple_imgstore_unref(PurpleStoredImage *img); + +/** + * Increment the reference count using an ID. + * + * This is a convience wrapper for purple_imgstore_find_by_id() and + * purple_imgstore_ref(), so if you have a PurpleStoredImage, it'll + * be more efficient to call purple_imgstore_ref() directly. * * @param id The ID for the image. */ -void purple_imgstore_ref(int id); +void purple_imgstore_ref_by_id(int id); /** - * Decrement the reference count for an image in the store. The - * image will be removed from the store when the reference count - * is zero. + * Decrement the reference count using an ID. + * + * This is a convience wrapper for purple_imgstore_find_by_id() and + * purple_imgstore_unref(), so if you have a PurpleStoredImage, it'll + * be more efficient to call purple_imgstore_unref() directly. * * @param id The ID for the image. */ -void purple_imgstore_unref(int id); +void purple_imgstore_unref_by_id(int id); + +/** + * Returns the image store subsystem handle. + * + * @return The subsystem handle. + */ +void *purple_imgstore_get_handle(void); + +/** + * Initializes the image store subsystem. + */ +void purple_imgstore_init(void); + +/** + * Uninitializes the image store subsystem. + */ +void purple_imgstore_uninit(void); #ifdef __cplusplus } diff -r 58a097859388 -r 0fa041c66957 libpurple/internal.h --- a/libpurple/internal.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/internal.h Wed Apr 25 05:26:12 2007 +0000 @@ -184,4 +184,15 @@ #define PURPLE_WEBSITE "http://pidgin.im/" +/* This is for the buddy list to notify the buddy icon code that + * it's done loading. We may want to replace this with a signal. */ +void +purple_buddy_icons_blist_loaded_cb(void); + +/* This is for the purple_core_migrate() code to tell the buddy + * icon subsystem about the old icons directory so it can + * migrate any icons in use. */ +void +purple_buddy_icon_set_old_icons_dir(const char *dirname); + #endif /* _PURPLE_INTERNAL_H_ */ diff -r 58a097859388 -r 0fa041c66957 libpurple/plugins/perl/common/BuddyIcon.xs --- a/libpurple/plugins/perl/common/BuddyIcon.xs Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/plugins/perl/common/BuddyIcon.xs Wed Apr 25 05:26:12 2007 +0000 @@ -3,10 +3,6 @@ MODULE = Purple::Buddy::Icon PACKAGE = Purple::Buddy::Icon PREFIX = purple_buddy_icon_ PROTOTYPES: ENABLE -void -purple_buddy_icon_destroy(icon) - Purple::Buddy::Icon icon - Purple::Buddy::Icon purple_buddy_icon_ref(icon) Purple::Buddy::Icon icon @@ -20,22 +16,13 @@ Purple::Buddy::Icon icon void -purple_buddy_icon_cache(icon, buddy) +purple_buddy_icon_set_custom_data(icon, data, len) Purple::Buddy::Icon icon - Purple::BuddyList::Buddy buddy + void * data + size_t len void -purple_buddy_icon_set_account(icon, account) - Purple::Buddy::Icon icon - Purple::Account account - -void -purple_buddy_icon_set_username(icon, username) - Purple::Buddy::Icon icon - const char * username - -void -purple_buddy_icon_set_data(icon, data, len) +purple_buddy_icon_set_protocol_data(icon, data, len) Purple::Buddy::Icon icon void * data size_t len diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/jabber/buddy.c Wed Apr 25 05:26:12 2007 +0000 @@ -714,7 +714,7 @@ purple_notify_user_info_destroy(user_info); while(jbi->vcard_imgids) { - purple_imgstore_unref(GPOINTER_TO_INT(jbi->vcard_imgids->data)); + purple_imgstore_unref_by_id(GPOINTER_TO_INT(jbi->vcard_imgids->data)); jbi->vcard_imgids = g_slist_delete_link(jbi->vcard_imgids, jbi->vcard_imgids); } @@ -959,21 +959,20 @@ data = purple_base64_decode(bintext, &size); - jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add(data, size, "logo.png"))); + jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(data, size, "logo.png"))); g_string_append_printf(info_text, "%s:
", photo ? _("Photo") : _("Logo"), GPOINTER_TO_INT(jbi->vcard_imgids->data)); - purple_buddy_icons_set_for_user(js->gc->account, bare_jid, - data, size); - purple_cipher_digest_region("sha1", (guchar *)data, size, sizeof(hashval), hashval, NULL); p = hash; for(i=0; i<20; i++, p+=2) snprintf(p, 3, "%02x", hashval[i]); - purple_blist_node_set_string((PurpleBlistNode*)b, "avatar_hash", hash); + + purple_buddy_icons_set_for_user(js->gc->account, bare_jid, + data, size, hash); g_free(data); g_free(bintext); diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Wed Apr 25 05:26:12 2007 +0000 @@ -201,7 +201,6 @@ static void jabber_vcard_parse_avatar(JabberStream *js, xmlnode *packet, gpointer blah) { JabberBuddy *jb = NULL; - PurpleBuddy *b = NULL; xmlnode *vcard, *photo, *binval; char *text; guchar *data; @@ -221,21 +220,20 @@ (( (binval = xmlnode_get_child(photo, "BINVAL")) && (text = xmlnode_get_data(binval))) || (text = xmlnode_get_data(photo)))) { + unsigned char hashval[20]; + char hash[41], *p; + int i; + data = purple_base64_decode(text, &size); - purple_buddy_icons_set_for_user(js->gc->account, from, data, size); - if((b = purple_find_buddy(js->gc->account, from))) { - unsigned char hashval[20]; - char hash[41], *p; - int i; + purple_cipher_digest_region("sha1", data, size, + sizeof(hashval), hashval, NULL); + p = hash; + for(i=0; i<20; i++, p+=2) + snprintf(p, 3, "%02x", hashval[i]); - purple_cipher_digest_region("sha1", data, size, - sizeof(hashval), hashval, NULL); - p = hash; - for(i=0; i<20; i++, p+=2) - snprintf(p, 3, "%02x", hashval[i]); - purple_blist_node_set_string((PurpleBlistNode*)b, "avatar_hash", hash); - } + purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash); + g_free(data); g_free(text); } @@ -520,7 +518,7 @@ } if(avatar_hash) { - const char *avatar_hash2 = purple_blist_node_get_string((PurpleBlistNode*)b, "avatar_hash"); + const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) { JabberIq *iq; xmlnode *vcard; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Wed Apr 25 05:26:12 2007 +0000 @@ -1869,7 +1869,7 @@ { char buf[1024]; purple_debug_info("msn", "%s is %d bytes\n", photo_url_text, len); - id = purple_imgstore_add(url_text, len, NULL); + id = purple_imgstore_add_with_id(url_text, len, NULL); g_snprintf(buf, sizeof(buf), "
", id); purple_notify_user_info_prepend_pair(user_info, NULL, buf); } @@ -1888,7 +1888,7 @@ g_free(photo_url_text); g_free(info2_data); if (id != -1) - purple_imgstore_unref(id); + purple_imgstore_unref_by_id(id); #endif } diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/msn/slp.c --- a/libpurple/protocols/msn/slp.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/msn/slp.c Wed Apr 25 05:26:12 2007 +0000 @@ -870,7 +870,7 @@ if (buddy == NULL) return FALSE; - old = purple_blist_node_get_string((PurpleBlistNode *)buddy, "icon_checksum"); + old = purple_buddy_icons_get_checksum_for_user(buddy); new = msn_object_get_sha1(obj); if (new == NULL) @@ -878,7 +878,7 @@ /* If the old and new checksums are the same, and the file actually exists, * then return TRUE */ - if (old != NULL && !strcmp(old, new) && (purple_buddy_icons_find(account, purple_buddy_get_name(buddy)) != NULL)) + if (old != NULL && !strcmp(old, new)) return TRUE; return FALSE; @@ -956,22 +956,7 @@ if (obj == NULL) { - /* It seems the user has not set a msnobject */ - GSList *sl, *list; - - list = purple_find_buddies(account, user->passport); - - for (sl = list; sl != NULL; sl = sl->next) - { - PurpleBuddy *buddy = (PurpleBuddy *)sl->data; - if (buddy->icon) - purple_blist_node_remove_setting((PurpleBlistNode*)buddy, "icon_checksum"); - } - g_slist_free(list); - - /* TODO: I think we need better buddy icon core functions. */ - purple_buddy_icons_set_for_user(account, user->passport, NULL, 0); - + purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL); return; } @@ -1001,7 +986,6 @@ MsnUserList *userlist; const char *info; PurpleAccount *account; - GSList *sl, *list; g_return_if_fail(slpcall != NULL); @@ -1013,18 +997,8 @@ userlist = slpcall->slplink->session->userlist; account = slpcall->slplink->session->account; - /* TODO: I think we need better buddy icon core functions. */ purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user, - (void *)data, size); - - list = purple_find_buddies(account, slpcall->slplink->remote_user); - - for (sl = list; sl != NULL; sl = sl->next) - { - PurpleBuddy *buddy = (PurpleBuddy *)sl->data; - purple_blist_node_set_string((PurpleBlistNode*)buddy, "icon_checksum", info); - } - g_slist_free(list); + (void *)data, size, info); #if 0 /* Free one window slot */ @@ -1103,7 +1077,6 @@ MsnObject *my_obj = NULL; gchar *data = NULL; gsize len = 0; - GSList *sl, *list; #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Requesting our own user display\n"); @@ -1119,19 +1092,9 @@ g_file_get_contents(filename, &data, &len, NULL); } - /* TODO: I think we need better buddy icon core functions. */ - purple_buddy_icons_set_for_user(account, user->passport, (void *)data, len); + purple_buddy_icons_set_for_user(account, user->passport, (void *)data, len, info); g_free(data); - list = purple_find_buddies(account, user->passport); - - for (sl = list; sl != NULL; sl = sl->next) - { - PurpleBuddy *buddy = (PurpleBuddy *)sl->data; - purple_blist_node_set_string((PurpleBlistNode*)buddy, "icon_checksum", info); - } - g_slist_free(list); - /* Free one window slot */ session->userlist->buddy_icon_window++; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/oscar/odc.c --- a/libpurple/protocols/oscar/odc.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/oscar/odc.c Wed Apr 25 05:26:12 2007 +0000 @@ -353,7 +353,7 @@ if ((embedded_data != NULL) && (embedded_data->size == size)) { - imgid = purple_imgstore_add(embedded_data->data, size, src); + imgid = purple_imgstore_add_with_id(embedded_data->data, size, src); /* Record the image number */ images = g_slist_append(images, GINT_TO_POINTER(imgid)); @@ -406,7 +406,7 @@ { GSList *l; for (l = images; l != NULL; l = l->next) - purple_imgstore_unref(GPOINTER_TO_INT(l->data)); + purple_imgstore_unref_by_id(GPOINTER_TO_INT(l->data)); g_slist_free(images); } diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Apr 25 05:26:12 2007 +0000 @@ -1841,34 +1841,14 @@ bi->ipaddr = info->icqinfo.ipaddr; if (info->iconcsumlen) { - const char *filename, *saved_b16 = NULL; - char *b16 = NULL, *filepath = NULL; + const char *saved_b16 = NULL; + char *b16 = NULL; PurpleBuddy *b = NULL; b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen); b = purple_find_buddy(account, info->sn); - /* - * If for some reason the checksum is valid, but cached file is not.. - * we want to know. - */ if (b != NULL) - filename = purple_blist_node_get_string((PurpleBlistNode*)b, "buddy_icon"); - else - filename = NULL; - if (filename != NULL) { - if (g_file_test(filename, G_FILE_TEST_EXISTS)) - saved_b16 = purple_blist_node_get_string((PurpleBlistNode*)b, - "icon_checksum"); - else { - filepath = g_build_filename(purple_buddy_icons_get_cache_dir(), - filename, NULL); - if (g_file_test(filepath, G_FILE_TEST_EXISTS)) - saved_b16 = purple_blist_node_get_string((PurpleBlistNode*)b, - "icon_checksum"); - g_free(filepath); - } - } else - saved_b16 = NULL; + saved_b16 = purple_buddy_icons_get_checksum_for_user(b); if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) { GSList *cur = od->requesticon; @@ -2177,7 +2157,8 @@ { purple_buddy_icons_set_for_user(account, userinfo->sn, args->info.icon.icon, - args->info.icon.length); + args->info.icon.length, + NULL); } else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY) @@ -3273,16 +3254,10 @@ * no icon is set. Ignore these. */ if ((iconlen > 0) && (iconlen != 90)) { - char *b16; - PurpleBuddy *b; + char *b16 = purple_base16_encode(iconcsum, iconcsumlen); purple_buddy_icons_set_for_user(purple_connection_get_account(gc), - sn, icon, iconlen); - b16 = purple_base16_encode(iconcsum, iconcsumlen); - b = purple_find_buddy(gc->account, sn); - if ((b16 != NULL) && (b != NULL)) { - purple_blist_node_set_string((PurpleBlistNode*)b, "icon_checksum", b16); - g_free(b16); - } + sn, icon, iconlen, b16); + g_free(b16); } cur = od->requesticon; @@ -4140,11 +4115,11 @@ id = g_datalist_get_data(&attribs, "id"); /* ... if it refers to a valid purple image ... */ - if (id && (image = purple_imgstore_get(atoi(id)))) { + if (id && (image = purple_imgstore_find_by_id(atoi(id)))) { /* ... append the message from start to the tag ... */ unsigned long size = purple_imgstore_get_size(image); const char *filename = purple_imgstore_get_filename(image); - gpointer imgdata = purple_imgstore_get_data(image); + gconstpointer imgdata = purple_imgstore_get_data(image); oscar_id++; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/qq/buddy_info.c --- a/libpurple/protocols/qq/buddy_info.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Wed Apr 25 05:26:12 2007 +0000 @@ -531,22 +531,17 @@ qq_send_packet_get_info(gc, qd->uid, FALSE); } -void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *iconfile) +void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile) { - FILE *file; - struct stat st; + gchar *data; + gsize len; - g_return_if_fail(g_stat(iconfile, &st) == 0); - file = g_fopen(iconfile, "rb"); - if (file) { - PurpleBuddyIcon *icon; - size_t data_len; - gchar *data = g_new(gchar, st.st_size + 1); - data_len = fread(data, 1, st.st_size, file); - fclose(file); - purple_buddy_icons_set_for_user(account, who, data, data_len); - icon = purple_buddy_icons_find(account, who); - purple_buddy_icon_set_path(icon, iconfile); + if (!g_file_get_contents(iconfile, &data, &len, NULL)) + g_return_if_reached(); + else + { + purple_buddy_icons_set_for_user(account, who, data, len, icon_num); + g_free(data); } } @@ -600,25 +595,29 @@ /* tell server my icon changed */ _qq_send_packet_modify_face(gc, icon_num); /* display in blist */ - qq_set_buddy_icon_for_user(account, account->username, icon_path); + qq_set_buddy_icon_for_user(account, account->username, icon, icon_path); } static void _qq_update_buddy_icon(PurpleAccount *account, const gchar *name, gint face) { - gchar *icon_path; PurpleBuddyIcon *icon = purple_buddy_icons_find(account, name); gchar *icon_num_str = face_to_icon_str(face); - const gchar *old_path = purple_buddy_icon_get_path(icon); - const gchar *buddy_icon_dir = qq_buddy_icon_dir(); + const gchar *old_icon_num = purple_buddy_icon_get_checksum(icon); + + if (icon == NULL || old_icon_num == NULL || + strcmp(icon_num_str, old_icon_num)) + { + gchar *icon_path; - icon_path = g_strconcat(buddy_icon_dir, G_DIR_SEPARATOR_S, QQ_ICON_PREFIX, - icon_num_str, QQ_ICON_SUFFIX, NULL); - if (icon == NULL || old_path == NULL - || g_ascii_strcasecmp(icon_path, old_path) != 0) - qq_set_buddy_icon_for_user(account, name, icon_path); + icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S, + QQ_ICON_PREFIX, icon_num_str, + QQ_ICON_SUFFIX, NULL); + + qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path); + g_free(icon_path); + } g_free(icon_num_str); - g_free(icon_path); } /* after getting info or modify myself, refresh the buddy list accordingly */ diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/qq/buddy_info.h --- a/libpurple/protocols/qq/buddy_info.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/qq/buddy_info.h Wed Apr 25 05:26:12 2007 +0000 @@ -87,7 +87,7 @@ void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc); void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window); void qq_set_my_buddy_icon(PurpleConnection *gc, const gchar *iconfile); -void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *iconfile); +void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile); void qq_prepare_modify_info(PurpleConnection *gc); void qq_process_modify_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); void qq_process_get_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/sametime/sametime.c Wed Apr 25 05:26:12 2007 +0000 @@ -2698,7 +2698,7 @@ cid = make_cid(cid); /* add image to the purple image store */ - img = purple_imgstore_add(d_dat, d_len, cid); + img = purple_imgstore_add_with_id(d_dat, d_len, cid); g_free(d_dat); /* map the cid to the image store identifier */ @@ -2772,7 +2772,7 @@ /* dereference all the imgages */ while(images) { - purple_imgstore_unref(GPOINTER_TO_INT(images->data)); + purple_imgstore_unref_by_id(GPOINTER_TO_INT(images->data)); images = g_list_delete_link(images, images); } } @@ -3856,7 +3856,7 @@ /* find the imgstore data by the id tag */ id = g_datalist_get_data(&attr, "id"); if(id && *id) - img = purple_imgstore_get(atoi(id)); + img = purple_imgstore_find_by_id(atoi(id)); if(img) { char *cid; @@ -3882,9 +3882,8 @@ /* obtain and base64 encode the image data, and put it in the mime part */ - data = purple_imgstore_get_data(img); size = purple_imgstore_get_size(img); - data = purple_base64_encode(data, (gsize) size); + data = purple_base64_encode(purple_imgstore_get_data(img), (gsize) size); purple_mime_part_set_data(part, data); g_free(data); diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/silc/buddy.c --- a/libpurple/protocols/silc/buddy.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/silc/buddy.c Wed Apr 25 05:26:12 2007 +0000 @@ -1716,7 +1716,7 @@ } t = purple_buddy_icon_get_type((const PurpleBuddyIcon *)&ic); - if (!t) { + if (!t || !strcmp(t, "icon")) { g_free(ic.data); silc_mime_free(mime); return; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/silc/ops.c --- a/libpurple/protocols/silc/ops.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/silc/ops.c Wed Apr 25 05:26:12 2007 +0000 @@ -161,7 +161,7 @@ if (channel && !convo) goto out; - imgid = purple_imgstore_add(data, data_len, ""); + imgid = purple_imgstore_add_with_id(data, data_len, ""); if (imgid) { cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV; g_snprintf(tmp, sizeof(tmp), "", imgid); @@ -177,7 +177,7 @@ sender->nickname : "", tmp, cflags, time(NULL)); - purple_imgstore_unref(imgid); + purple_imgstore_unref_by_id(imgid); cflags = 0; } goto out; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Wed Apr 25 05:26:12 2007 +0000 @@ -342,6 +342,8 @@ } case 192: /* Pictures, aka Buddy Icons, checksum */ { + /* FIXME: Please, if you know this protocol, + * FIXME: fix up the strtol() stuff if possible. */ int cksum = strtol(pair->value, NULL, 10); PurpleBuddy *b; @@ -353,9 +355,7 @@ if (!cksum || (cksum == -1)) { if (f) yahoo_friend_set_buddy_icon_need_request(f, TRUE); - purple_buddy_icons_set_for_user(gc->account, name, NULL, 0); - if (b) - purple_blist_node_remove_setting((PurpleBlistNode *)b, YAHOO_ICON_CHECKSUM_KEY); + purple_buddy_icons_set_for_user(gc->account, name, NULL, 0, NULL); break; } @@ -363,7 +363,7 @@ break; yahoo_friend_set_buddy_icon_need_request(f, FALSE); - if (b && cksum != purple_blist_node_get_int((PurpleBlistNode*)b, YAHOO_ICON_CHECKSUM_KEY)) + if (b && cksum != strtol(purple_buddy_icons_get_checksum_for_user(b), NULL, 10)) yahoo_send_picture_request(gc, name); break; diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Wed Apr 25 05:26:12 2007 +0000 @@ -48,7 +48,6 @@ #define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg" -#define YAHOO_ICON_CHECKSUM_KEY "icon_checksum" #define YAHOO_PICURL_SETTING "picture_url" #define YAHOO_PICCKSUM_SETTING "picture_checksum" #define YAHOO_PICEXPIRE_SETTING "picture_expire" diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/yahoo/yahoo_picture.c --- a/libpurple/protocols/yahoo/yahoo_picture.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Wed Apr 25 05:26:12 2007 +0000 @@ -49,7 +49,6 @@ { struct yahoo_fetch_picture_data *d; struct yahoo_data *yd; - PurpleBuddy *b; d = user_data; yd = d->gc->proto_data; @@ -60,10 +59,9 @@ } else if (len == 0) { purple_debug_error("yahoo", "Fetched an icon with length 0. Strange.\n"); } else { - purple_buddy_icons_set_for_user(purple_connection_get_account(d->gc), d->who, (void *)pic_data, len); - b = purple_find_buddy(purple_connection_get_account(d->gc), d->who); - if (b) - purple_blist_node_set_int((PurpleBlistNode*)b, YAHOO_ICON_CHECKSUM_KEY, d->checksum); + char *checksum = g_strdup_printf("%i", d->checksum); + purple_buddy_icons_set_for_user(purple_connection_get_account(d->gc), d->who, (void *)pic_data, len, checksum); + g_free(checksum); } g_free(d->who); @@ -117,7 +115,9 @@ PurpleUtilFetchUrlData *url_data; struct yahoo_fetch_picture_data *data; PurpleBuddy *b = purple_find_buddy(gc->account, who); - if (b && (checksum == purple_blist_node_get_int((PurpleBlistNode*)b, YAHOO_ICON_CHECKSUM_KEY))) + + /* FIXME: Cleanup this strtol() stuff if possible. */ + if (b && (checksum == strtol(purple_buddy_icons_get_checksum_for_user(b), NULL, 10))) return; data = g_new0(struct yahoo_fetch_picture_data, 1); @@ -166,11 +166,8 @@ if (icon == 2) yahoo_send_picture_request(gc, who); else if ((icon == 0) || (icon == 1)) { - PurpleBuddy *b = purple_find_buddy(gc->account, who); YahooFriend *f; - purple_buddy_icons_set_for_user(gc->account, who, NULL, 0); - if (b) - purple_blist_node_remove_setting((PurpleBlistNode *)b, YAHOO_ICON_CHECKSUM_KEY); + purple_buddy_icons_set_for_user(gc->account, who, NULL, 0, NULL); if ((f = yahoo_friend_find(gc, who))) yahoo_friend_set_buddy_icon_need_request(f, TRUE); purple_debug_misc("yahoo", "Setting user %s's icon to NULL.\n", who); @@ -203,7 +200,9 @@ if (who) { PurpleBuddy *b = purple_find_buddy(gc->account, who); - if (b && (checksum != purple_blist_node_get_int((PurpleBlistNode*)b, YAHOO_ICON_CHECKSUM_KEY))) + + /* FIXME: Cleanup this strtol() stuff if possible. */ + if (b && (checksum != strtol(purple_buddy_icons_get_checksum_for_user(b), NULL, 10))) yahoo_send_picture_request(gc, who); } } @@ -276,11 +275,8 @@ if (avatar == 2) yahoo_send_picture_request(gc, who); else if ((avatar == 0) || (avatar == 1)) { - PurpleBuddy *b = purple_find_buddy(gc->account, who); YahooFriend *f; - purple_buddy_icons_set_for_user(gc->account, who, NULL, 0); - if (b) - purple_blist_node_remove_setting((PurpleBlistNode *)b, YAHOO_ICON_CHECKSUM_KEY); + purple_buddy_icons_set_for_user(gc->account, who, NULL, 0, NULL); if ((f = yahoo_friend_find(gc, who))) yahoo_friend_set_buddy_icon_need_request(f, TRUE); purple_debug_misc("yahoo", "Setting user %s's icon to NULL.\n", who); diff -r 58a097859388 -r 0fa041c66957 libpurple/protocols/yahoo/yahoo_profile.c --- a/libpurple/protocols/yahoo/yahoo_profile.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Wed Apr 25 05:26:12 2007 +0000 @@ -1022,7 +1022,7 @@ photo_url_text, url_text); } else { purple_debug_info("yahoo", "%s is %d bytes\n", photo_url_text, len); - id = purple_imgstore_add(url_text, len, NULL); + id = purple_imgstore_add_with_id(url_text, len, NULL); tmp = g_strdup_printf("
", id); purple_notify_user_info_add_pair(user_info, NULL, tmp); @@ -1234,7 +1234,7 @@ g_free(photo_url_text); g_free(info2_data); if (id != -1) - purple_imgstore_unref(id); + purple_imgstore_unref_by_id(id); #endif } diff -r 58a097859388 -r 0fa041c66957 libpurple/util.c --- a/libpurple/util.c Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/util.c Wed Apr 25 05:26:12 2007 +0000 @@ -65,7 +65,7 @@ }; static char custom_home_dir[MAXPATHLEN]; -static char home_dir[MAXPATHLEN]; +static char home_dir[MAXPATHLEN] = ""; PurpleMenuAction * purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data, @@ -2245,25 +2245,17 @@ #endif } -/* Returns the argument passed to -c IFF it was present, or ~/.gaim IFF it - * exists, else ~/.purple. */ +/* Returns the argument passed to -c IFF it was present, or ~/.purple. */ const char * purple_user_dir(void) { - if (custom_home_dir != NULL && strlen(custom_home_dir) > 0) { + if (custom_home_dir != NULL && *custom_home_dir) { strcpy ((char*) &home_dir, (char*) &custom_home_dir); - } else { + } else if (!*home_dir) { const gchar *hd = purple_home_dir(); if (hd) { g_strlcpy((char*) &home_dir, hd, sizeof(home_dir)); - g_strlcat((char*) &home_dir, G_DIR_SEPARATOR_S ".gaim", - sizeof(home_dir)); - - if (g_file_test(home_dir, G_FILE_TEST_EXISTS)) - return home_dir; - - g_strlcpy((char*) &home_dir, hd, sizeof(home_dir)); g_strlcat((char*) &home_dir, G_DIR_SEPARATOR_S ".purple", sizeof(home_dir)); } @@ -2577,6 +2569,27 @@ return fp; } +const char * +purple_util_get_image_extension(gpointer data, size_t len) +{ + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(len > 0, NULL); + + if (len >= 4) + { + if (!strncmp((char *)data, "BM", 2)) + return "bmp"; + else if (!strncmp((char *)data, "GIF8", 4)) + return "gif"; + else if (!strncmp((char *)data, "\xff\xd8\xff\xe0", 4)) + return "jpg"; + else if (!strncmp((char *)data, "\x89PNG", 4)) + return "png"; + } + + return "icon"; +} + gboolean purple_program_is_valid(const char *program) { diff -r 58a097859388 -r 0fa041c66957 libpurple/util.h --- a/libpurple/util.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/util.h Wed Apr 25 05:26:12 2007 +0000 @@ -598,6 +598,25 @@ FILE *purple_mkstemp(char **path, gboolean binary); /** + * Returns an extension corresponding to the image data's file type. + * + * @param data A pointer to the image data + * @param len The length of the image data + * + * @return The appropriate extension, or "icon" if unknown. + */ +const char * +purple_util_get_image_extension(gpointer data, size_t len); + +/*@}*/ + + +/**************************************************************************/ +/** @name Environment Detection Functions */ +/**************************************************************************/ +/*@{*/ + +/** * Checks if the given program name is valid and executable. * * @param program The file name of the application. @@ -1118,6 +1137,7 @@ * inherit the handlers of the parent. */ void purple_restore_default_signal_handlers(void); + #ifdef __cplusplus } #endif diff -r 58a097859388 -r 0fa041c66957 libpurple/value.h --- a/libpurple/value.h Wed Apr 25 05:25:57 2007 +0000 +++ b/libpurple/value.h Wed Apr 25 05:26:12 2007 +0000 @@ -76,7 +76,8 @@ PURPLE_SUBTYPE_XFER, PURPLE_SUBTYPE_SAVEDSTATUS, PURPLE_SUBTYPE_XMLNODE, - PURPLE_SUBTYPE_USERINFO + PURPLE_SUBTYPE_USERINFO, + PURPLE_SUBTYPE_STORED_IMAGE } PurpleSubType; /** diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkaccount.c Wed Apr 25 05:26:12 2007 +0000 @@ -302,8 +302,10 @@ dialog = data; +#if 0 if (filename != NULL) set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, filename), g_strdup(filename)); +#endif dialog->icon_filesel = NULL; } @@ -342,7 +344,9 @@ } if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n'))) *rtmp = '\0'; +#if 0 set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, tmp), g_strdup(tmp)); +#endif g_free(tmp); } gtk_drag_finish(dc, TRUE, FALSE, t); @@ -1159,10 +1163,12 @@ else if (purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon") && icon_change) { const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon"); +#if 0 char *icon = pidgin_convert_buddy_icon(dialog->plugin, filename); purple_account_set_buddy_icon_path(account, filename); purple_account_set_buddy_icon(account, icon); g_free(icon); +#endif } } diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkblist.c Wed Apr 25 05:26:12 2007 +0000 @@ -2155,7 +2155,7 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node, - gboolean scaled, gboolean greyed, gboolean custom) + gboolean scaled, gboolean greyed) { GdkPixbuf *buf, *ret = NULL; GdkPixbufLoader *loader; @@ -2166,6 +2166,7 @@ PurpleChat *chat = NULL; PurpleAccount *account = NULL; PurplePluginProtocolInfo *prpl_info = NULL; + PurpleStoredImage *custom_img; if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { buddy = purple_contact_get_priority_buddy((PurpleContact*)node); @@ -2190,19 +2191,11 @@ return NULL; #endif - if (custom) { - const char *file = purple_blist_node_get_string((PurpleBlistNode*)purple_buddy_get_contact(buddy), - "custom_buddy_icon"); - if (file && *file) { - char *contents; - GError *err = NULL; - if (!g_file_get_contents(file, &contents, &len, &err)) { - purple_debug_info("custom -icon", "Could not open custom-icon %s for %s\n", - file, purple_buddy_get_name(buddy), err->message); - g_error_free(err); - } else - data = (const guchar*)contents; - } + custom_img = purple_buddy_icons_find_custom_icon(purple_buddy_get_contact(buddy)); + if (custom_img) + { + data = purple_imgstore_get_data(custom_img); + len = purple_imgstore_get_size(custom_img); } if (data == NULL) { @@ -2211,8 +2204,9 @@ if (!(icon = purple_buddy_icons_find(buddy->account, buddy->name))) /* Not sure I like this...*/ return NULL; data = purple_buddy_icon_get_data(icon, &len); + if (data == NULL) + return NULL; } - custom = FALSE; /* We are not using the custom icon */ } if(data == NULL) @@ -2221,13 +2215,14 @@ loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, data, len, NULL); gdk_pixbuf_loader_close(loader, NULL); + + purple_imgstore_unref(custom_img); + buf = gdk_pixbuf_loader_get_pixbuf(loader); if (buf) g_object_ref(G_OBJECT(buf)); g_object_unref(G_OBJECT(loader)); - if (custom) - g_free((void*)data); if (buf) { int orig_width, orig_height; int scale_width, scale_height; @@ -2335,7 +2330,7 @@ } td->status_icon = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_LARGE); - td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE, TRUE); + td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE); td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); tooltip_text = pidgin_get_tooltip_text(node, full); td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); @@ -4893,7 +4888,7 @@ status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy, PIDGIN_STATUS_ICON_SMALL); - avatar = pidgin_blist_get_buddy_icon((PurpleBlistNode *)buddy, TRUE, TRUE, TRUE); + avatar = pidgin_blist_get_buddy_icon((PurpleBlistNode *)buddy, TRUE, TRUE); if (!avatar) { g_object_ref(G_OBJECT(gtkblist->empty_avatar)); avatar = gtkblist->empty_avatar; @@ -5078,7 +5073,7 @@ status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL); emblem = pidgin_blist_get_emblem(node); - avatar = pidgin_blist_get_buddy_icon(node, TRUE, FALSE, TRUE); + avatar = pidgin_blist_get_buddy_icon(node, TRUE, FALSE); mark = g_markup_escape_text(purple_chat_get_name(chat), -1); diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkconv.c Wed Apr 25 05:26:12 2007 +0000 @@ -2482,23 +2482,6 @@ fclose(fp); } -static const char * -custom_icon_pref_name(PidginConversation *gtkconv) -{ - PurpleConversation *conv; - PurpleAccount *account; - PurpleBuddy *buddy; - - conv = gtkconv->active_conv; - account = purple_conversation_get_account(conv); - buddy = purple_find_buddy(account, purple_conversation_get_name(conv)); - if (buddy) { - PurpleContact *contact = purple_buddy_get_contact(buddy); - return purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon"); - } - return NULL; -} - static void custom_icon_sel_cb(const char *filename, gpointer data) { @@ -2538,9 +2521,7 @@ g_return_if_fail(conv != NULL); - ext = purple_buddy_icon_get_type(purple_conv_im_get_icon(PURPLE_CONV_IM(conv))); - if (ext == NULL) - ext = "icon"; + ext = purple_buddy_icon_get_extension(purple_conv_im_get_icon(PURPLE_CONV_IM(conv))); buf = g_strdup_printf("%s.%s", purple_normalize(conv->account, conv->name), ext); @@ -2576,7 +2557,8 @@ icon_menu(GtkObject *obj, GdkEventButton *e, PidginConversation *gtkconv) { static GtkWidget *menu = NULL; - const char *pref; + PurpleConversation *conv; + PurpleBuddy *buddy; if (e->button != 3 || e->type != GDK_BUTTON_PRESS) return FALSE; @@ -2610,11 +2592,18 @@ 0, 0, NULL); /* Is there a custom icon for this person? */ - pref = custom_icon_pref_name(gtkconv); - if (pref && *pref) { - pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL, - G_CALLBACK(remove_custom_icon_cb), gtkconv, - 0, 0, NULL); + conv = gtkconv->active_conv; + buddy = purple_find_buddy(purple_conversation_get_account(conv), + purple_conversation_get_name(conv)); + if (buddy) + { + PurpleContact *contact = purple_buddy_get_contact(buddy); + if (contact && purple_buddy_icons_has_custom_icon(contact)) + { + pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL, + G_CALLBACK(remove_custom_icon_cb), gtkconv, + 0, 0, NULL); + } } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time); @@ -6177,12 +6166,14 @@ PidginConversation *gtkconv; PidginWindow *win; + PurpleBuddy *buddy; + GdkPixbufLoader *loader; GdkPixbufAnimation *anim; GError *err = NULL; - const char *custom = NULL; - const void *data = NULL; + PurpleStoredImage *custom_img = NULL; + gconstpointer data = NULL; size_t len; GdkPixbuf *buf; @@ -6239,17 +6230,18 @@ if (purple_conversation_get_gc(conv) == NULL) return; - custom = custom_icon_pref_name(gtkconv); - if (custom) { - /* There is a custom icon for this user */ - char *contents = NULL; - if (!g_file_get_contents(custom, &contents, &len, &err)) { - purple_debug_warning("custom icon", "could not load custom icon %s for %s\n", - custom, purple_conversation_get_name(conv)); - g_error_free(err); - err = NULL; - } else - data = contents; + buddy = purple_find_buddy(account, purple_conversation_get_name(conv)); + if (buddy) + { + PurpleContact *contact = purple_buddy_get_contact(buddy); + if (contact) { + custom_img = purple_buddy_icons_find_custom_icon(contact); + if (custom_img) { + /* There is a custom icon for this user */ + data = purple_imgstore_get_data(custom_img); + len = purple_imgstore_get_size(custom_img); + } + } } if (data == NULL) { @@ -6259,20 +6251,22 @@ return; data = purple_buddy_icon_get_data(icon, &len); - custom = NULL; + + if (data == NULL) + return; } loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, data, len, NULL); gdk_pixbuf_loader_close(loader, &err); + + purple_imgstore_unref(custom_img); + anim = gdk_pixbuf_loader_get_animation(loader); if (anim) g_object_ref(G_OBJECT(anim)); g_object_unref(loader); - if (custom) - g_free((void*)data); - if (!anim) return; gtkconv->u.im->anim = anim; @@ -6283,9 +6277,6 @@ g_error_free(err); } - if (!gtkconv->u.im->anim) - return; - if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) { gtkconv->u.im->iter = NULL; buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Wed Apr 25 05:26:12 2007 +0000 @@ -480,7 +480,7 @@ name = strrchr(filename, G_DIR_SEPARATOR) + 1; - id = purple_imgstore_add(filedata, size, name); + id = purple_imgstore_add_with_id(filedata, size, name); g_free(filedata); if (id == 0) { @@ -499,7 +499,7 @@ gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins); gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter); - purple_imgstore_unref(id); + purple_imgstore_unref_by_id(id); } diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkmain.c Wed Apr 25 05:26:12 2007 +0000 @@ -145,7 +145,7 @@ static void sighandler(int sig); /** - * Reap all our dead children. Sometimes Purple forks off a separate + * Reap all our dead children. Sometimes libpurple forks off a separate * process to do some stuff. When that process exits we are * informed about it so that we can call waitpid() and let it * stop being a zombie. @@ -160,7 +160,7 @@ * it continues with the initialization process. This means that * we have a race condition where GStreamer is waitpid()ing for its * child to die and we're catching the SIGCHLD signal. If GStreamer - * is awarded the zombied process then everything is ok. But if Purple + * is awarded the zombied process then everything is ok. But if libpurple * reaps the zombie process then the GStreamer initialization sequence * fails. * @@ -446,6 +446,7 @@ int opt; gboolean gui_check; gboolean debug_enabled; + gboolean migration_failed = FALSE; struct option long_options[] = { {"config", required_argument, NULL, 'c'}, @@ -639,6 +640,15 @@ purple_debug_set_enabled(debug_enabled); + /* If we're using a custom configuration directory, we + * do NOT want to migrate, or weird things will happen. */ + if (opt_config_dir_arg == NULL) + { + if (!purple_core_migrate()) + { + migration_failed = TRUE; + } + } search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL); gtk_rc_add_default_file(search_path); @@ -663,6 +673,37 @@ winpidgin_init(hint); #endif + if (migration_failed) + { + char *old = g_strconcat(purple_home_dir(), + G_DIR_SEPARATOR_S ".gaim", NULL); + const char *text = _( + "%s encountered errors migrating your settings " + "from %s to %s. Please investigate and complete the " + "migration by hand."); + GtkWidget *dialog; + + dialog = gtk_message_dialog_new(NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + text, PIDGIN_NAME, + old, purple_user_dir()); + g_free(old); + + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_main_quit), NULL); + + gtk_widget_show_all(dialog); + + gtk_main(); + +#ifdef HAVE_SIGNAL_H + g_free(segfault_message); +#endif + return 0; + } + purple_core_set_ui_ops(pidgin_core_get_ui_ops()); purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops()); diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkstatusbox.c Wed Apr 25 05:26:12 2007 +0000 @@ -1439,8 +1439,10 @@ PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug); if (prplinfo && prplinfo->icon_spec.format) { char *icon = NULL; +#if 0 if (filename) icon = pidgin_convert_buddy_icon(plug, filename); +#endif purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL)); purple_account_set_ui_string(box->account, PIDGIN_UI, "non-global-buddyicon-cached-path", icon); purple_account_set_buddy_icon_path(box->account, filename); @@ -1459,8 +1461,10 @@ purple_account_get_bool(account, "use-global-buddyicon", TRUE) && prplinfo->icon_spec.format) { char *icon = NULL; +#if 0 if (filename) icon = pidgin_convert_buddy_icon(plug, filename); +#endif purple_account_set_buddy_icon_path(account, filename); purple_account_set_buddy_icon(account, icon); g_free(icon); diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkutils.c Wed Apr 25 05:26:12 2007 +0000 @@ -78,12 +78,12 @@ } static GtkIMHtmlFuncs gtkimhtml_cbs = { - (GtkIMHtmlGetImageFunc)purple_imgstore_get, + (GtkIMHtmlGetImageFunc)purple_imgstore_find_by_id, (GtkIMHtmlGetImageDataFunc)purple_imgstore_get_data, (GtkIMHtmlGetImageSizeFunc)purple_imgstore_get_size, (GtkIMHtmlGetImageFilenameFunc)purple_imgstore_get_filename, - purple_imgstore_ref, - purple_imgstore_unref, + purple_imgstore_ref_by_id, + purple_imgstore_unref_by_id, }; void @@ -1350,13 +1350,13 @@ return; } - id = purple_imgstore_add(filedata, size, data->filename); + id = purple_imgstore_add_with_id(filedata, size, data->filename); g_free(filedata); gtk_text_buffer_get_iter_at_mark(GTK_IMHTML(gtkconv->entry)->text_buffer, &iter, gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer)); gtk_imhtml_insert_image_at_iter(GTK_IMHTML(gtkconv->entry), id, &iter); - purple_imgstore_unref(id); + purple_imgstore_unref_by_id(id); break; } @@ -2409,15 +2409,14 @@ } #endif -char * -pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path) +gpointer +pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len) { PurplePluginProtocolInfo *prpl_info; #if GTK_CHECK_VERSION(2,2,0) char **prpl_formats; int width, height; char **pixbuf_formats = NULL; - struct stat st; GdkPixbufFormat *format; GdkPixbuf *pixbuf; #if !GTK_CHECK_VERSION(2,4,0) @@ -2426,28 +2425,11 @@ #endif gchar *contents; gsize length; - const char *dirname; - char *random; - char *filename; prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL); - dirname = purple_buddy_icons_get_cache_dir(); - if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { - purple_debug_info("buddyicon", "Creating icon cache directory.\n"); - - if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) { - purple_debug_error("buddyicon", - "Unable to create directory %s: %s\n", - dirname, strerror(errno)); - return NULL; - } - } - - random = g_strdup_printf("%x", g_random_int()); - filename = g_build_filename(dirname, random, NULL); #if GTK_CHECK_VERSION(2,2,0) #if GTK_CHECK_VERSION(2,4,0) @@ -2478,47 +2460,21 @@ prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */ #endif { - FILE *image; - #if GTK_CHECK_VERSION(2,2,0) g_strfreev(prpl_formats); g_strfreev(pixbuf_formats); #endif - - /* We don't need to scale the image, so copy it to the cache folder verbatim */ + /* We don't need to scale the image. */ contents = NULL; - if (!g_file_get_contents(path, &contents, &length, NULL) || - (image = g_fopen(filename, "wb")) == NULL) + if (!g_file_get_contents(path, &contents, &length, NULL)) { - g_free(random); - g_free(filename); g_free(contents); #if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(pixbuf)); #endif return NULL; } - - if (fwrite(contents, 1, length, image) != length) - { - fclose(image); - g_unlink(filename); - - g_free(random); - g_free(filename); - g_free(contents); -#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); -#endif - return NULL; - } - fclose(image); - g_free(contents); - -#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); -#endif } #if GTK_CHECK_VERSION(2,2,0) else @@ -2527,6 +2483,7 @@ GError *error = NULL; GdkPixbuf *scale; gboolean success = FALSE; + char *filename = NULL; g_strfreev(pixbuf_formats); @@ -2534,8 +2491,6 @@ if (error) { purple_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message); g_error_free(error); - g_free(random); - g_free(filename); g_strfreev(prpl_formats); return NULL; } @@ -2558,6 +2513,17 @@ } for (i = 0; prpl_formats[i]; i++) { + FILE *fp; + + g_free(filename); + fp = purple_mkstemp(&filename, TRUE); + if (!fp) + { + g_free(filename); + return NULL; + } + fclose(fp); + purple_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename); /* The "compression" param wasn't supported until gdk-pixbuf 2.8. * Using it in previous versions causes the save to fail (and an assert message). */ @@ -2586,29 +2552,33 @@ g_object_unref(G_OBJECT(pixbuf)); if (!success) { purple_debug_error("buddyicon", "Could not convert icon to usable format.\n"); - g_free(random); + return NULL; + } + + contents = NULL; + if (!g_file_get_contents(filename, &contents, &length, NULL)) + { + purple_debug_error("buddyicon", + "Could not read '%s', which we just wrote to disk.\n", + filename); + + g_free(contents); g_free(filename); return NULL; } + + g_unlink(filename); + g_free(filename); } - if (g_stat(filename, &st) != 0) { - purple_debug_error("buddyicon", - "Could not stat '%s', which we just wrote to disk: %s\n", - filename, strerror(errno)); - g_free(random); - g_free(filename); - return NULL; - } - - /* Check the file size */ + /* Check the image size */ /* * TODO: If the file is too big, it would be cool if we checked if * the prpl supported jpeg, and then we could convert to that * and use a lower quality setting. */ if ((prpl_info->icon_spec.max_filesize != 0) && - (st.st_size > prpl_info->icon_spec.max_filesize)) + (length > prpl_info->icon_spec.max_filesize)) { gchar *tmp; tmp = g_strdup_printf(_("The file '%s' is too large for %s. Please try a smaller image.\n"), @@ -2618,16 +2588,15 @@ purple_debug_info("buddyicon", "'%s' was converted to an image which is %" G_GSIZE_FORMAT " bytes, but the maximum icon size for %s is %" G_GSIZE_FORMAT - " bytes\n", path, st.st_size, plugin->info->name, + " bytes\n", path, length, plugin->info->name, prpl_info->icon_spec.max_filesize); g_free(tmp); - g_free(random); - g_free(filename); return NULL; } - g_free(filename); - return random; + if (len) + *len = length; + return contents; #else /* * The chosen icon wasn't the right size, and we're using @@ -2780,10 +2749,10 @@ void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename) { - PurpleConversation *conv; PurpleBuddy *buddy; - PurpleBlistNode *node; - char *path = NULL; + PurpleContact *contact; + gpointer data = NULL; + size_t len = 0; buddy = purple_find_buddy(account, who); if (!buddy) { @@ -2791,35 +2760,20 @@ return; } - node = (PurpleBlistNode*)purple_buddy_get_contact(buddy); - path = (char*)purple_blist_node_get_string(node, "custom_buddy_icon"); - if (path) { - struct stat st; - if (g_stat(path, &st) == 0) - g_unlink(path); - path = NULL; - } + contact = purple_buddy_get_contact(buddy); if (filename) { - char *newfile; - - newfile = pidgin_convert_buddy_icon(purple_find_prpl(purple_account_get_protocol_id(account)), - filename); - path = purple_buddy_icons_get_full_path(newfile); - g_free(newfile); + const char *prpl_id = purple_account_get_protocol_id(account); + PurplePlugin *prpl = purple_find_prpl(prpl_id); + + data = pidgin_convert_buddy_icon(prpl, filename, &len); + + /* We don't want to delete the old icon if the new one didn't load. */ + if (data == NULL) + return; } - purple_blist_node_set_string(node, "custom_buddy_icon", path); - g_free(path); - - /* Update the conversation */ - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account); - if (conv) - purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON); - - /* Update the buddylist */ - if (buddy) - purple_blist_update_buddy_icon(buddy); + purple_buddy_icons_set_custom_icon(contact, data, len); } char *pidgin_make_pretty_arrows(const char *str) diff -r 58a097859388 -r 0fa041c66957 pidgin/gtkutils.h --- a/pidgin/gtkutils.h Wed Apr 25 05:25:57 2007 +0000 +++ b/pidgin/gtkutils.h Wed Apr 25 05:26:12 2007 +0000 @@ -445,11 +445,13 @@ /** * Converts a buddy icon to the required size and format * - * @param plugin The prpl to conver the icon - * @param path The path of a buddy icon to convert - * @return The name of a new buddy icon + * @param plugin The prpl to convert the icon + * @param path The path of a file to convert + * @param len If not @c NULL, the length of the returned data will be set here. + * + * @return The converted image data, or @c NULL if an error occurred. */ -char* pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path); +gpointer pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len); #if !GTK_CHECK_VERSION(2,6,0) /**