changeset 16500:74daf9fc9cf3

merge of '3ee86d1e423ebfd391c26b73ae7ed1b75353d2cf' and '53e71fc52c4d4be3947b2404fe6332ae8d52dc55'
author Ethan Blanton <elb@pidgin.im>
date Fri, 27 Apr 2007 04:03:04 +0000
parents e331baecd661 (current diff) 5f57cd067579 (diff)
children e40d216f1368
files pidgin/pixmaps/status/16/rtl/chat.png pidgin/pixmaps/status/22/rtl/chat.png pidgin/pixmaps/status/32/rtl/chat.png pidgin/pixmaps/status/48/rtl/chat.png
diffstat 100 files changed, 2783 insertions(+), 1604 deletions(-) [+]
line wrap: on
line diff
--- a/doc/pidgin-arch.fig	Fri Apr 27 04:02:51 2007 +0000
+++ b/doc/pidgin-arch.fig	Fri Apr 27 04:03:04 2007 +0000
@@ -119,7 +119,7 @@
 	 5550 2400 5550 1800
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
 	1 1 1.00 60.00 120.00
-	 4200 1800 5100 2925
+	 5100 2925 4200 1800
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
 	1 1 1.00 60.00 120.00
 	 1050 2100 1050 2700
@@ -130,12 +130,12 @@
 	 6900 3600 2400 3600
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
 	1 1 1.00 60.00 120.00
-	 7200 1575 6300 1050
+	 6300 1050 7200 1575
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
 	1 1 1.00 60.00 120.00
-	 7200 1725 6300 3000
+	 6300 3000 7200 1725
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
 	1 1 1.00 60.00 120.00
-	 7200 3750 6300 3150
+	 6300 3150 7200 3750
 4 1 0 50 -1 0 12 0.0000 4 135 1110 1125 1275 User Interface\001
 4 0 0 50 -1 0 12 0.0000 4 135 390 3225 3375 Core\001
--- a/finch/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/finch/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,5 +1,3 @@
-if ENABLE_GNT
-
 EXTRA_DIST = \
 		getopt.c \
 		getopt.h \
@@ -7,8 +5,12 @@
 
 SUBDIRS = libgnt plugins
 
+if ENABLE_GNT
+
 bin_PROGRAMS = finch
 
+endif
+
 finch_SOURCES = \
 	gntaccount.c \
 	gntblist.c \
@@ -74,4 +76,3 @@
 	$(DBUS_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(GNT_CFLAGS)
-endif
--- a/finch/finch.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/finch/finch.c	Fri Apr 27 04:03:04 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());
--- a/finch/gntaccount.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/finch/gntaccount.c	Fri Apr 27 04:03:04 2007 +0000
@@ -611,10 +611,10 @@
 	prompt = g_strdup_printf(_("Are you sure you want to delete %s?"),
 			purple_account_get_username(account));
 
-	purple_request_action(account, _("Delete Account"), prompt, NULL, 0, account, 2,
-			_("Delete"), really_delete_account, _("Cancel"),
-			account, NULL, NULL,
-			NULL);
+	purple_request_action(account, _("Delete Account"), prompt, NULL, 0,
+						  account, NULL, NULL, account, 2,
+						  _("Delete"), really_delete_account,
+						  _("Cancel"), NULL);
 	g_free(prompt);
 }
 
--- a/libpurple/account.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/account.c	Fri Apr 27 04:03:04 2007 +0000
@@ -347,12 +347,6 @@
 		xmlnode_insert_data(child, tmp, -1);
 	}
 
-	if ((tmp = purple_account_get_buddy_icon(account)) != NULL)
-	{
-		child = xmlnode_new_child(node, "buddyicon");
-		xmlnode_insert_data(child, tmp, -1);
-	}
-
 	if (g_hash_table_size(account->settings) > 0)
 	{
 		child = xmlnode_new_child(node, "settings");
@@ -742,11 +736,21 @@
 		g_free(data);
 	}
 
-	/* Read the buddyicon */
+	/* Read an old buddyicon */
 	child = xmlnode_get_child(node, "buddyicon");
 	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
 	{
-		purple_account_set_buddy_icon(ret, data);
+		const char *dirname = purple_buddy_icons_get_cache_dir();
+		char *filename = g_build_filename(dirname, data, NULL);
+		gchar *contents;
+		gsize len;
+
+		if (g_file_get_contents(filename, &contents, &len, NULL))
+		{
+			purple_buddy_icons_set_account_icon(ret, (guchar *)contents, len);
+		}
+
+		g_free(filename);
 		g_free(data);
 	}
 
@@ -880,7 +884,6 @@
 	g_free(account->alias);
 	g_free(account->password);
 	g_free(account->user_info);
-	g_free(account->buddy_icon);
 	g_free(account->buddy_icon_path);
 	g_free(account->protocol_id);
 
@@ -1320,56 +1323,6 @@
 	schedule_accounts_save();
 }
 
-void
-purple_account_set_buddy_icon(PurpleAccount *account, const char *icon)
-{
-	g_return_if_fail(account != NULL);
-
-	/* Delete an existing icon from the cache. */
-	if (account->buddy_icon != NULL && (icon == NULL || strcmp(account->buddy_icon, icon)))
-	{
-		const char *dirname = purple_buddy_icons_get_cache_dir();
-
-		if (g_file_test(account->buddy_icon, G_FILE_TEST_IS_REGULAR))
-		{
-			/* The file exists. This is a full path. */
-
-			/* XXX: This is a hack so we only delete the file if it's
-			 * in the cache dir. Otherwise, people who upgrade (who
-			 * may have buddy icon filenames set outside of the cache
-			 * dir) could lose files. */
-			if (!strncmp(dirname, account->buddy_icon, strlen(dirname)))
-				g_unlink(account->buddy_icon);
-		}
-		else
-		{
-			char *filename = g_build_filename(dirname, account->buddy_icon, NULL);
-			g_unlink(filename);
-			g_free(filename);
-		}
-	}
-
-	g_free(account->buddy_icon);
-	account->buddy_icon = g_strdup(icon);
-	if (purple_account_is_connected(account))
-	{
-		PurpleConnection *gc;
-		PurplePluginProtocolInfo *prpl_info;
-
-		gc = purple_account_get_connection(account);
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-		if (prpl_info && prpl_info->set_buddy_icon)
-		{
-			char *cached_path = purple_buddy_icons_get_full_path(icon);
-			prpl_info->set_buddy_icon(gc, cached_path);
-			g_free(cached_path);
-		}
-	}
-
-	schedule_accounts_save();
-}
-
 void purple_account_set_buddy_icon_path(PurpleAccount *account, const char *path)
 {
 	g_return_if_fail(account != NULL);
@@ -1745,14 +1698,6 @@
 }
 
 const char *
-purple_account_get_buddy_icon(const PurpleAccount *account)
-{
-	g_return_val_if_fail(account != NULL, NULL);
-
-	return account->buddy_icon;
-}
-
-const char *
 purple_account_get_buddy_icon_path(const PurpleAccount *account)
 {
 	g_return_val_if_fail(account != NULL, NULL);
@@ -2279,7 +2224,7 @@
 	purple_pounce_destroy_all_by_account(account);
 
 	/* This will cause the deletion of an old buddy icon. */
-	purple_account_set_buddy_icon(account, NULL);
+	purple_buddy_icons_set_account_icon(account, NULL, 0);
 
 	purple_account_destroy(account);
 }
--- a/libpurple/account.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/account.h	Fri Apr 27 04:03:04 2007 +0000
@@ -75,7 +75,6 @@
 	char *password;             /**< The account password.                  */
 	char *user_info;            /**< User information.                      */
 
-	char *buddy_icon;           /**< The buddy icon's cached path.          */
 	char *buddy_icon_path;      /**< The buddy icon's non-cached path.      */
 
 	gboolean remember_pass;     /**< Remember the password.                 */
@@ -284,14 +283,6 @@
 void purple_account_set_user_info(PurpleAccount *account, const char *user_info);
 
 /**
- * Sets the account's buddy icon.
- *
- * @param account The account.
- * @param icon    The buddy icon file.
- */
-void purple_account_set_buddy_icon(PurpleAccount *account, const char *icon);
-
-/**
  * Sets the account's buddy icon path.
  *
  * @param account The account.
@@ -525,15 +516,6 @@
 const char *purple_account_get_user_info(const PurpleAccount *account);
 
 /**
- * Returns the account's buddy icon filename.
- *
- * @param account The account.
- *
- * @return The buddy icon filename.
- */
-const char *purple_account_get_buddy_icon(const PurpleAccount *account);
-
-/**
  * Gets the account's buddy icon path.
  *
  * @param account The account.
--- a/libpurple/blist.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/blist.c	Fri Apr 27 04:03:04 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);
--- a/libpurple/buddyicon.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/buddyicon.c	Fri Apr 27 04:03:04 2007 +0000
@@ -24,26 +24,257 @@
  */
 #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;
+
+/* This one is used for both custom buddy icons
+ * on PurpleContacts and account icons. */
+static GHashTable *pointer_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(pointer_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)
+		{
+			g_free(icon_data);
+			return NULL;
+		}
+	}
+	else
+		file = g_strdup(filename);
+
+	if ((img = g_hash_table_lookup(icon_data_cache, file)))
+	{
+		g_free(file);
+		g_free(icon_data);
+		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 +286,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 +302,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 +334,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 +367,7 @@
 	PurpleConversation *conv;
 	PurpleAccount *account;
 	const char *username;
+	PurpleBuddyIcon *icon_to_set;
 	GSList *sl, *list;
 
 	g_return_if_fail(icon != NULL);
@@ -181,12 +375,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 +428,33 @@
 	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;
+	PurpleStoredImage *old_img;
 
-	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");
+	g_return_if_fail(icon != 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));
-		}
-	}
+	old_img = icon->img;
+	icon->img = NULL;
 
-	if ((file = g_fopen(filename, "wb")) != NULL)
+	if (data != 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;
+		if (len > 0)
+			icon->img = purple_buddy_icon_data_new(data, len, NULL);
+		else
+			g_free(data);
 	}
 
-	g_free(filename);
-
-	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);
-
-	if (data != NULL && len > 0)
-	{
-		icon->data = g_memdup(data, len);
-		icon->len  = len;
-	}
-	else
-	{
-		icon->data = NULL;
-		icon->len  = 0;
-	}
+	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 +473,574 @@
 	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);
 	}
 }
 
+char *purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon)
+{
+	char *path;
+
+	g_return_val_if_fail(icon != NULL, NULL);
+
+	if (icon->img == NULL)
+		return NULL;
+
+	path = g_build_filename(purple_buddy_icons_get_cache_dir(),
+	                        purple_imgstore_get_filename(icon->img), NULL);
+	if (!g_file_test(path, G_FILE_TEST_EXISTS))
+	{
+		g_free(path);
+		return NULL;
+	}
+	return path;
+}
+
+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(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_account_icon(PurpleAccount *account)
+{
+	PurpleStoredImage *img;
+	const char *account_icon_file;
+	const char *dirname;
+	char *path;
+	guchar *data;
+	size_t len;
+
+	g_return_val_if_fail(account != NULL, NULL);
+
+	if ((img = g_hash_table_lookup(pointer_icon_cache, account)))
+	{
+		return purple_imgstore_ref(img);
+	}
+
+	account_icon_file = purple_account_get_string(account, "buddy_icon", NULL);
+
+	if (account_icon_file == NULL)
+		return NULL;
+
+	dirname = purple_buddy_icons_get_cache_dir();
+	path = g_build_filename(dirname, account_icon_file, NULL);
+
+	if (read_icon_file(path, &data, &len))
+	{
+		g_free(path);
+		img = purple_buddy_icon_data_new(data, len, account_icon_file);
+		g_hash_table_insert(pointer_icon_cache, account, img);
+		return img;
+	}
+	g_free(path);
+
+	return NULL;
+}
+
+PurpleStoredImage *
+purple_buddy_icons_set_account_icon(PurpleAccount *account,
+                                    guchar *icon_data, size_t icon_len)
+{
+	PurpleStoredImage *old_img;
+	PurpleStoredImage *img = NULL;
+	char *old_icon;
+
+	old_img = g_hash_table_lookup(pointer_icon_cache, account);
+
+	if (icon_data != NULL && icon_len > 0)
+	{
+		img = purple_buddy_icon_data_new(icon_data, icon_len, NULL);
+	}
+
+	old_icon = g_strdup(purple_account_get_string(account, "buddy_icon", NULL));
+	if (img && purple_buddy_icons_is_caching())
+	{
+		const char *filename = purple_imgstore_get_filename(img);
+		purple_account_set_string(account, "buddy_icon", filename);
+		ref_filename(filename);
+	}
+	else
+	{
+		purple_account_set_string(account, "buddy_icon", NULL);
+	}
+	unref_filename(old_icon);
+
+	if (img)
+		g_hash_table_insert(pointer_icon_cache, account, img);
+	else
+		g_hash_table_remove(pointer_icon_cache, account);
+
+	if (purple_account_is_connected(account))
+	{
+		PurpleConnection *gc;
+		PurplePluginProtocolInfo *prpl_info;
+
+		gc = purple_account_get_connection(account);
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+		if (prpl_info && prpl_info->set_buddy_icon)
+			prpl_info->set_buddy_icon(gc, img);
+	}
+
+	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);
+
+	return img;
+}
+
+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(pointer_icon_cache, contact)))
+	{
+		return purple_imgstore_ref(img);
+	}
+
+	custom_icon_file = purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon");
+
+	if (custom_icon_file == NULL)
+		return NULL;
+
+	dirname = purple_buddy_icons_get_cache_dir();
+	path = g_build_filename(dirname, custom_icon_file, NULL);
+
+	if (read_icon_file(path, &data, &len))
+	{
+		g_free(path);
+		img = purple_buddy_icon_data_new(data, len, custom_icon_file);
+		g_hash_table_insert(pointer_icon_cache, contact, img);
+		return img;
+	}
+	g_free(path);
 
-		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;
+	return NULL;
+}
+
+PurpleStoredImage *
+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(pointer_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);
+
+	if (img)
+		g_hash_table_insert(pointer_icon_cache, contact, img);
+	else
+		g_hash_table_remove(pointer_icon_cache, contact);
+
+	for (child = contact->node.child ; child ; child = child->next)
+	{
+		PurpleBuddy *buddy;
+		PurpleConversation *conv;
+
+		if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+			continue;
+
+		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);
+
+	return img;
+}
+
+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();
+
+	/* 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,16 +1070,6 @@
 	return cache_dir;
 }
 
-char *purple_buddy_icons_get_full_path(const char *icon) {
-	if (icon == NULL)
-		return NULL;
-
-	if (g_file_test(icon, G_FILE_TEST_IS_REGULAR))
-		return g_strdup(icon);
-	else
-		return g_build_filename(purple_buddy_icons_get_cache_dir(), icon, NULL);
-}
-
 void *
 purple_buddy_icons_get_handle()
 {
@@ -535,13 +1085,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);
+	pointer_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(pointer_icon_cache);
+	g_free(old_icons_dir);
 }
 
 void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height)
@@ -572,4 +1137,3 @@
 	*width = new_width;
 	*height = new_height;
 }
-
--- a/libpurple/buddyicon.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/buddyicon.h	Fri Apr 27 04:03:04 2007 +0000
@@ -29,51 +29,36 @@
 
 #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
 
+
 /**************************************************************************/
 /** @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 +88,17 @@
 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.
  *
  * @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, which the buddy icon code
+ *             takes ownership of and will free.
+ * @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 +119,52 @@
 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);
+
+/**
+ * Returns a full path to an icon.
+ *
+ * If the icon has data and the file exists in the cache, this will return
+ * a full path to the cache file.
+ *
+ * In general, it is not appropriate to be poking in the icon cache
+ * directly.  If you find yourself wanting to use this function, think
+ * very long and hard about it, and then don't.
+ *
+ * @param icon The buddy icon
+ *
+ * @return A full path to the file, or @c NULL under various conditions.
+ */
+char *purple_buddy_icon_get_full_path(PurpleBuddyIcon *icon);
 
 /*@}*/
 
@@ -208,13 +178,30 @@
  *
  * @param account   The account the user is on.
  * @param username  The username of the user.
- * @param icon_data The icon data.
+ * @param icon_data The buddy icon data, which the buddy icon code
+ *                  takes ownership of and will free.
  * @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 +211,90 @@
  *
  * @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 buddy icon image for an account.
+ *
+ * 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.
+ *
+ * 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 account The account
+ *
+ * @return The account's buddy icon image.
+ */
+PurpleStoredImage *
+purple_buddy_icons_find_account_icon(PurpleAccount *account);
+
+/**
+ * Sets a buddy icon for an account.
+ *
+ * This function will deal with saving a record of the icon,
+ * caching the data, etc.
+ *
+ * @param account   The account for which to set a custom icon.
+ * @param icon_data The image data of the icon, which the
+ *                  buddy icon code will free.
+ * @param icon_len  The length of the data in @a icon_data.
+ *
+ * @return The icon that was set.  The caller does NOT own
+ *         a reference to this, and must call purple_imgstore_ref()
+ *         if it wants one.
+ */
+PurpleStoredImage *
+purple_buddy_icons_set_account_icon(PurpleAccount *account,
+                                    guchar *icon_data, size_t icon_len);
+
+/**
+ * Returns the custom buddy icon image for a contact.
+ *
+ * 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.
+ *
+ * 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, which the
+ *                  buddy icon code will free.
+ * @param icon_len  The length of the data in @a icon_data.
+ *
+ * @return The icon that was set.  The caller does NOT own
+ *         a reference to this, and must call purple_imgstore_ref()
+ *         if it wants one.
+ */
+PurpleStoredImage *
+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 +332,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.
--- a/libpurple/conversation.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/conversation.c	Fri Apr 27 04:03:04 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));
 	}
--- a/libpurple/core.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/core.c	Fri Apr 27 04:03:04 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,377 @@
 {
 	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)
+{
+#error You probably want to back up your .gaim directory right now. This revision is going to destroy it, potentially not in the way we've intended
+	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) && ferror(new_file) != 0)
+				{
+					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;
+}
--- a/libpurple/core.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/core.h	Fri Apr 27 04:03:04 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
--- a/libpurple/gaim-compat.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/gaim-compat.h	Fri Apr 27 04:03:04 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
--- a/libpurple/imgstore.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/imgstore.c	Fri Apr 27 04:03:04 2007 +0000
@@ -27,140 +27,170 @@
 #include <glib.h>
 #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(gpointer 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 = data;
+	img->size = size;
+	img->filename = g_strdup(filename);
+	img->refcount = 1;
+	img->id = 0;
+
+	return img;
+}
+
+int
+purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename)
+{
+	PurpleStoredImage *img = purple_imgstore_add(data, size, filename);
+	img->id = ++nextid;
 
-	g_return_val_if_fail(id > 0, NULL);
+	g_hash_table_insert(imgstore, GINT_TO_POINTER(img->id), img);
+
+	return img->id;
+}
 
-	while (tmp && !priv) {
-		PurpleStoredImagePriv *tmp_priv = tmp->data;
+PurpleStoredImage *purple_imgstore_find_by_id(int id) {
+	PurpleStoredImage *img = g_hash_table_lookup(imgstore, GINT_TO_POINTER(id));
 
-		if (tmp_priv->id == id)
-			priv = tmp_priv;
+	if (img != NULL)
+		purple_debug_misc("imgstore", "retrieved image id %d\n", img->id);
 
-		tmp = tmp->next;
-	}
+	return img;
+}
+
+gconstpointer purple_imgstore_get_data(PurpleStoredImage *img) {
+	g_return_val_if_fail(img != NULL, NULL);
 
-	if (!priv)
-		purple_debug(PURPLE_DEBUG_ERROR, "imgstore", "failed to find image id %d\n", id);
+	return img->data;
+}
 
-	return priv;
+size_t purple_imgstore_get_size(PurpleStoredImage *img)
+{
+	g_return_val_if_fail(img != NULL, 0);
+
+	return img->size;
 }
 
-static void purple_imgstore_free_priv(PurpleStoredImagePriv *priv) {
-	PurpleStoredImage *img = NULL;
+const char *purple_imgstore_get_filename(PurpleStoredImage *img)
+{
+	g_return_val_if_fail(img != NULL, NULL);
+
+	return img->filename;
+}
+
+const char *purple_imgstore_get_extension(PurpleStoredImage *img)
+{
+	g_return_val_if_fail(img != NULL, NULL);
+
+	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);
 
-	g_return_if_fail(priv != NULL);
+	purple_imgstore_unref(img);
+}
+
+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());
 }
--- a/libpurple/imgstore.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/imgstore.h	Fri Apr 27 04:03:04 2007 +0000
@@ -26,6 +26,8 @@
 #ifndef _PURPLE_IMGSTORE_H_
 #define _PURPLE_IMGSTORE_H_
 
+#include <glib.h>
+
 struct _PurpleStoredImage;
 typedef struct _PurpleStoredImage PurpleStoredImage;
 
@@ -34,17 +36,41 @@
 #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, which the imgstore will take
+ *                      ownership of and free as appropriate.  If you want a
+ *                      copy of the data, make it before calling this function.
+ * @param size		Image data's size.
+ * @param filename	Filename associated with image.
  *
- * @param data		Pointer to the image data.
+ * @return The stored image.
+ */
+PurpleStoredImage *
+purple_imgstore_add(gpointer 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, which the imgstore will take
+ *                      ownership of and free as appropriate.  If you want a
+ *                      copy of the data, make it before calling this function.
  * @param size		Image data's size.
  * @param filename	Filename associated with image.
 
  * @return ID for the image.
  */
-int purple_imgstore_add(const void *data, size_t size, const char *filename);
+int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename);
 
 /**
  * Retrieve an image from the store. The caller does not own a
@@ -54,22 +80,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 +105,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
 }
--- a/libpurple/internal.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/internal.h	Fri Apr 27 04:03:04 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_ */
--- a/libpurple/plugins/perl/common/Account.xs	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Fri Apr 27 04:03:04 2007 +0000
@@ -59,7 +59,7 @@
     const char *user_info
 
 void
-purple_account_set_buddy_icon(account, icon)
+purple_account_set_buddy_icon_path(account, icon)
     Purple::Account account
     const char *icon
 
@@ -144,7 +144,7 @@
     Purple::Account account
 
 const char *
-purple_account_get_buddy_icon(account)
+purple_account_get_buddy_icon_path(account)
     Purple::Account account
 
 const char *
--- a/libpurple/plugins/perl/common/BuddyIcon.xs	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/plugins/perl/common/BuddyIcon.xs	Fri Apr 27 04:03:04 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,25 +16,11 @@
 	Purple::Buddy::Icon icon
 
 void
-purple_buddy_icon_cache(icon, buddy)
-	Purple::Buddy::Icon icon
-	Purple::BuddyList::Buddy buddy
-
-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_data(icon, data, len, checksum)
 	Purple::Buddy::Icon icon
 	void * data
 	size_t len
+	char *checksum
 
 Purple::Account
 purple_buddy_icon_get_account(icon)
@@ -54,7 +36,7 @@
 	size_t &len
 
 const char *
-purple_buddy_icon_get_type(icon)
+purple_buddy_icon_get_extension(icon)
 	Purple::Buddy::Icon icon
 
 void
--- a/libpurple/plugins/perl/common/ImgStore.xs	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/plugins/perl/common/ImgStore.xs	Fri Apr 27 04:03:04 2007 +0000
@@ -3,17 +3,23 @@
 MODULE = Purple::ImgStore  PACKAGE = Purple::ImgStore  PREFIX = purple_imgstore_
 PROTOTYPES: ENABLE
 
-int 
+Purple::StoredImage
 purple_imgstore_add(data, size, filename)
 	void *data
 	size_t size
 	const char *filename
 
+int
+purple_imgstore_add_with_id(data, size, filename)
+	void *data
+	size_t size
+	const char *filename
+
 Purple::StoredImage
-purple_imgstore_get(id)
+purple_imgstore_find_by_id(id)
 	int id
 
-gpointer 
+gconstpointer 
 purple_imgstore_get_data(i)
 	Purple::StoredImage i
 
@@ -25,11 +31,23 @@
 purple_imgstore_get_size(i)
 	Purple::StoredImage i
 
-void 
+const char *
+purple_imgstore_get_extension(i)
+	Purple::StoredImage i
+
+Purple::StoredImage
 purple_imgstore_ref(id)
+	Purple::StoredImage id
+
+Purple::StoredImage
+purple_imgstore_unref(id)
+	Purple::StoredImage id
+
+void
+purple_imgstore_ref_by_id(id)
 	int id
 
-void 
-purple_imgstore_unref(id)
+void
+purple_imgstore_unref_by_id(id)
 	int id
 
--- a/libpurple/protocols/jabber/buddy.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Fri Apr 27 04:03:04 2007 +0000
@@ -383,7 +383,6 @@
 	JabberIq *iq;
 	JabberStream *js = gc->proto_data;
 	xmlnode *vc_node;
-	char *avatar_file = NULL;
 	struct tag_attr *tag_attr;
 
 	g_free(js->avatar_hash);
@@ -393,7 +392,6 @@
 	 * Send only if there's actually any *information* to send
 	 */
 	vc_node = info ? xmlnode_from_str(info, -1) : NULL;
-	avatar_file = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(gc->account));
 
 	if(!vc_node) {
 		vc_node = xmlnode_new("vCard");
@@ -403,27 +401,29 @@
 
 	if (vc_node->name &&
 			!g_ascii_strncasecmp(vc_node->name, "vCard", 5)) {
-		GError *error = NULL;
-		gchar *avatar_data_tmp;
-		guchar *avatar_data;
-		gsize avatar_len;
+		PurpleStoredImage *img;
 
-		if(avatar_file && g_file_get_contents(avatar_file, &avatar_data_tmp, &avatar_len, &error)) {
+		if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
+			gconstpointer avatar_data;
+			gsize avatar_len;
 			xmlnode *photo, *binval;
 			gchar *enc;
 			int i;
 			unsigned char hashval[20];
 			char *p, hash[41];
 
-			avatar_data = (guchar *) avatar_data_tmp;
+			avatar_data = purple_imgstore_get_data(img);
+			avatar_len = purple_imgstore_get_size(img);
 			photo = xmlnode_new_child(vc_node, "PHOTO");
 			binval = xmlnode_new_child(photo, "BINVAL");
 			enc = purple_base64_encode(avatar_data, avatar_len);
 
-			purple_cipher_digest_region("sha1", (guchar *)avatar_data,
+			purple_cipher_digest_region("sha1", avatar_data,
 									  avatar_len, sizeof(hashval),
 									  hashval, NULL);
 
+			purple_imgstore_unref(img);
+
 			p = hash;
 			for(i=0; i<20; i++, p+=2)
 				snprintf(p, 3, "%02x", hashval[i]);
@@ -431,11 +431,7 @@
 
 			xmlnode_insert_data(binval, enc, -1);
 			g_free(enc);
-			g_free(avatar_data);
-		} else if (error != NULL) {
-			g_error_free(error);
 		}
-		g_free(avatar_file);
 
 		iq = jabber_iq_new(js, JABBER_IQ_SET);
 		xmlnode_insert_child(iq->node, vc_node);
@@ -445,7 +441,7 @@
 	}
 }
 
-void jabber_set_buddy_icon(PurpleConnection *gc, const char *iconfile)
+void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	PurplePresence *gpresence;
 	PurpleStatus *status;
@@ -715,7 +711,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);
 	}
 
@@ -960,23 +956,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(g_memdup(data, size), size, "logo.png")));
 					g_string_append_printf(info_text,
 							"<b>%s:</b> <img id='%d'><br/>",
 							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);
 
-					g_free(data);
+					purple_buddy_icons_set_for_user(js->gc->account, bare_jid,
+							data, size, hash);
 					g_free(bintext);
 				}
 			}
--- a/libpurple/protocols/jabber/buddy.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Fri Apr 27 04:03:04 2007 +0000
@@ -91,7 +91,7 @@
 
 void jabber_set_info(PurpleConnection *gc, const char *info);
 void jabber_setup_set_info(PurplePluginAction *action);
-void jabber_set_buddy_icon(PurpleConnection *gc, const char *iconfile);
+void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 
 const char *jabber_buddy_state_get_name(JabberBuddyState state);
 const char *jabber_buddy_state_get_status_id(JabberBuddyState state);
--- a/libpurple/protocols/jabber/presence.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Fri Apr 27 04:03:04 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,22 +220,19 @@
 				(( (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);
-			}
-			g_free(data);
+			purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
 			g_free(text);
 		}
 	}
@@ -521,7 +517,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;
--- a/libpurple/protocols/msn/msn.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Fri Apr 27 04:03:04 2007 +0000
@@ -1301,7 +1301,7 @@
 }
 
 static void
-msn_set_buddy_icon(PurpleConnection *gc, const char *filename)
+msn_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	MsnSession *session;
 	MsnUser *user;
@@ -1309,7 +1309,7 @@
 	session = gc->proto_data;
 	user = session->user;
 
-	msn_user_set_buddy_icon(user, filename);
+	msn_user_set_buddy_icon(user, img);
 
 	msn_change_status(session);
 }
@@ -1880,7 +1880,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(g_memdup(url_text, len), len, NULL);
 			g_snprintf(buf, sizeof(buf), "<img id=\"%d\"><br>", id);
 			purple_notify_user_info_prepend_pair(user_info, NULL, buf);
 		}
@@ -1899,7 +1899,7 @@
 	g_free(photo_url_text);
 	g_free(info2_data);
 	if (id != -1)
-		purple_imgstore_unref(id);
+		purple_imgstore_unref_by_id(id);
 #endif
 }
 
--- a/libpurple/protocols/msn/object.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/object.c	Fri Apr 27 04:03:04 2007 +0000
@@ -111,11 +111,12 @@
 
 	g_free(obj->creator);
 	g_free(obj->location);
-	g_free(obj->real_location);
 	g_free(obj->friendly);
 	g_free(obj->sha1d);
 	g_free(obj->sha1c);
 
+	purple_imgstore_unref(obj->img);
+
 	if (obj->local)
 		local_objs = g_list_remove(local_objs, obj);
 
@@ -317,21 +318,19 @@
 }
 
 void
-msn_object_set_real_location(MsnObject *obj, const char *real_location)
+msn_object_set_image(MsnObject *obj, PurpleStoredImage *img)
 {
 	g_return_if_fail(obj != NULL);
+	g_return_if_fail(img != NULL);
 
 	/* obj->local = TRUE; */
 
-	if (obj->real_location != NULL)
-		g_free(obj->real_location);
-
-	obj->real_location =
-		(real_location == NULL ? NULL : g_strdup(real_location));
+	purple_imgstore_unref(obj->img);
+	obj->img = purple_imgstore_ref(img);
 }
 
-const char *
-msn_object_get_real_location(const MsnObject *obj)
+PurpleStoredImage *
+msn_object_get_image(const MsnObject *obj)
 {
 	MsnObject *local_obj;
 
@@ -340,7 +339,7 @@
 	local_obj = msn_object_find_local(msn_object_get_sha1(obj));
 
 	if (local_obj != NULL)
-		return local_obj->real_location;
+		return local_obj->img;
 
 	return NULL;
 }
--- a/libpurple/protocols/msn/object.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/object.h	Fri Apr 27 04:03:04 2007 +0000
@@ -24,6 +24,8 @@
 #ifndef _MSN_OBJECT_H_
 #define _MSN_OBJECT_H_
 
+#include "imgstore.h"
+
 #include "internal.h"
 
 typedef enum
@@ -44,7 +46,7 @@
 	char *creator;
 	int size;
 	MsnObjectType type;
-	char *real_location;
+	PurpleStoredImage *img;
 	char *location;
 	char *friendly;
 	char *sha1d;
@@ -134,6 +136,14 @@
 void msn_object_set_sha1c(MsnObject *obj, const char *sha1c);
 
 /**
+ * Associates an image with a MsnObject.
+ *
+ * @param obj The object.
+ * @param img The image to associate.
+ */
+void msn_object_set_image(MsnObject *obj, PurpleStoredImage *img);
+
+/**
  * Returns a MsnObject's creator value.
  *
  * @param obj The object.
@@ -205,9 +215,15 @@
  */
 const char *msn_object_get_sha1(const MsnObject *obj);
 
+/**
+ * Returns the image associated with the MsnObject.
+ *
+ * @param obj The object.
+ *
+ * @return The associated image.
+ */
+PurpleStoredImage *msn_object_get_image(const MsnObject *obj);
+
 void msn_object_set_local(MsnObject *obj);
-const char *msn_object_get_real_location(const MsnObject *obj);
-void msn_object_set_real_location(MsnObject *obj,
-								  const char *real_location);
 
 #endif /* _MSN_OBJECT_H_ */
--- a/libpurple/protocols/msn/session.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Fri Apr 27 04:03:04 2007 +0000
@@ -384,7 +384,7 @@
 {
 	PurpleAccount *account;
 	PurpleConnection *gc;
-	char *icon;
+	PurpleStoredImage *img;
 
 	if (session->logged_in)
 		return;
@@ -392,9 +392,9 @@
 	account = session->account;
 	gc = purple_account_get_connection(account);
 
-	icon = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(session->account));
-	msn_user_set_buddy_icon(session->user, icon);
-	g_free(icon);
+	img = purple_buddy_icons_find_account_icon(session->account);
+	msn_user_set_buddy_icon(session->user, img);
+	purple_imgstore_unref(img);
 
 	session->logged_in = TRUE;
 
--- a/libpurple/protocols/msn/slp.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Fri Apr 27 04:03:04 2007 +0000
@@ -248,14 +248,14 @@
 	if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"))
 	{
 		/* Emoticon or UserDisplay */
+		char *content;
+		gsize len;
 		MsnSlpSession *slpsession;
 		MsnSlpLink *slplink;
 		MsnSlpMessage *slpmsg;
 		MsnObject *obj;
 		char *msnobj_data;
-		const char *file_name;
-		char *content;
-		gsize len;
+		PurpleStoredImage *img;
 		int type;
 
 		/* Send Ok */
@@ -281,9 +281,8 @@
 			g_return_if_reached();
 		}
 
-		file_name = msn_object_get_real_location(obj);
-
-		if (file_name == NULL)
+		img = msn_object_get_image(obj);
+		if (img == NULL)
 		{
 			purple_debug_error("msn", "Wrong object.\n");
 			msn_object_destroy(obj);
@@ -314,7 +313,7 @@
 #ifdef MSN_DEBUG_SLP
 		slpmsg->info = "SLP DATA";
 #endif
-		msn_slpmsg_open_file(slpmsg, file_name);
+		msn_slpmsg_set_image(slpmsg, img);
 		msn_slplink_queue_slpmsg(slplink, slpmsg);
 	}
 	else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683"))
@@ -870,7 +869,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 +877,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 +955,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 +985,6 @@
 	MsnUserList *userlist;
 	const char *info;
 	PurpleAccount *account;
-	GSList *sl, *list;
 
 	g_return_if_fail(slpcall != NULL);
 
@@ -1013,18 +996,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);
+								  g_memdup(data, size), size, info);
 
 #if 0
 	/* Free one window slot */
@@ -1101,9 +1074,8 @@
 	else
 	{
 		MsnObject *my_obj = NULL;
-		gchar *data = NULL;
-		gsize len = 0;
-		GSList *sl, *list;
+		gconstpointer data = NULL;
+		size_t len = 0;
 
 #ifdef MSN_DEBUG_UD
 		purple_debug_info("msn", "Requesting our own user display\n");
@@ -1113,24 +1085,12 @@
 
 		if (my_obj != NULL)
 		{
-			const char *filename = msn_object_get_real_location(my_obj);
-
-			if (filename != NULL)
-				g_file_get_contents(filename, &data, &len, NULL);
+			PurpleStoredImage *img = msn_object_get_image(my_obj);
+			data = purple_imgstore_get_data(img);
+			len = purple_imgstore_get_size(img);
 		}
 
-		/* TODO: I think we need better buddy icon core functions. */
-		purple_buddy_icons_set_for_user(account, user->passport, (void *)data, len);
-		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);
+		purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
 
 		/* Free one window slot */
 		session->userlist->buddy_icon_window++;
--- a/libpurple/protocols/msn/slpmsg.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/slpmsg.c	Fri Apr 27 04:03:04 2007 +0000
@@ -65,7 +65,11 @@
 	if (slpmsg->fp != NULL)
 		fclose(slpmsg->fp);
 
-	if (slpmsg->buffer != NULL)
+	purple_imgstore_unref(slpmsg->img);
+
+	/* We don't want to free the data of the PurpleStoredImage,
+	 * but to avoid code duplication, it's sharing buffer. */
+	if (slpmsg->img == NULL)
 		g_free(slpmsg->buffer);
 
 #ifdef MSN_DEBUG_SLP
@@ -101,6 +105,11 @@
 msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
 						 long long size)
 {
+	/* We can only have one data source at a time. */
+	g_return_if_fail(slpmsg->buffer);
+	g_return_if_fail(slpmsg->img);
+	g_return_if_fail(slpmsg->fp);
+
 	if (body != NULL)
 		slpmsg->buffer = g_memdup(body, size);
 	else
@@ -110,10 +119,28 @@
 }
 
 void
+msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img)
+{
+	/* We can only have one data source at a time. */
+	g_return_if_fail(slpmsg->buffer);
+	g_return_if_fail(slpmsg->img);
+	g_return_if_fail(slpmsg->fp);
+
+	slpmsg->img = purple_imgstore_ref(img);
+	slpmsg->buffer = (guchar *)purple_imgstore_get_data(img);
+	slpmsg->size = purple_imgstore_get_size(img);
+}
+
+void
 msn_slpmsg_open_file(MsnSlpMessage *slpmsg, const char *file_name)
 {
 	struct stat st;
 
+	/* We can only have one data source at a time. */
+	g_return_if_fail(slpmsg->buffer);
+	g_return_if_fail(slpmsg->img);
+	g_return_if_fail(slpmsg->fp);
+
 	slpmsg->fp = g_fopen(file_name, "rb");
 
 	if (g_stat(file_name, &st) == 0)
--- a/libpurple/protocols/msn/slpmsg.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/slpmsg.h	Fri Apr 27 04:03:04 2007 +0000
@@ -26,6 +26,8 @@
 
 typedef struct _MsnSlpMessage MsnSlpMessage;
 
+#include "imgstore.h"
+
 #include "slpsession.h"
 #include "slpcall.h"
 #include "slplink.h"
@@ -57,6 +59,7 @@
 	long flags;
 
 	FILE *fp;
+	PurpleStoredImage *img;
 	guchar *buffer;
 	long long offset;
 	long long size;
@@ -90,6 +93,7 @@
 
 void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
 						 long long size);
+void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img);
 void msn_slpmsg_open_file(MsnSlpMessage *slpmsg,
 						  const char *file_name);
 MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
--- a/libpurple/protocols/msn/user.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Fri Apr 27 04:03:04 2007 +0000
@@ -151,23 +151,20 @@
 }
 
 void
-msn_user_set_buddy_icon(MsnUser *user, const char *filename)
+msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
 {
-	struct stat st;
-	FILE *fp;
 	MsnObject *msnobj = msn_user_get_object(user);
 
 	g_return_if_fail(user != NULL);
 
-	if (filename == NULL || g_stat(filename, &st) == -1)
-	{
+	if (img == NULL)
 		msn_user_set_object(user, NULL);
-	}
-	else if ((fp = g_fopen(filename, "rb")) != NULL)
+	else
 	{
 		PurpleCipherContext *ctx;
 		char *buf;
-		gsize len;
+		gconstpointer data = purple_imgstore_get_data(img);
+		size_t size = purple_imgstore_get_size(img);
 		char *base64;
 		unsigned char digest[20];
 
@@ -182,26 +179,20 @@
 			msn_user_set_object(user, msnobj);
 		}
 
-		msn_object_set_real_location(msnobj, filename);
-
-		buf = g_malloc(st.st_size);
-		len = fread(buf, 1, st.st_size, fp);
-
-		fclose(fp);
+		msn_object_set_image(msnobj, img);
 
 		/* Compute the SHA1D field. */
 		memset(digest, 0, sizeof(digest));
 
 		ctx = purple_cipher_context_new_by_name("sha1", NULL);
-		purple_cipher_context_append(ctx, (const guchar *)buf, st.st_size);
+		purple_cipher_context_append(ctx, data, size);
 		purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
-		g_free(buf);
 
 		base64 = purple_base64_encode(digest, sizeof(digest));
 		msn_object_set_sha1d(msnobj, base64);
 		g_free(base64);
 
-		msn_object_set_size(msnobj, st.st_size);
+		msn_object_set_size(msnobj, size);
 
 		/* Compute the SHA1C field. */
 		buf = g_strdup_printf(
@@ -216,7 +207,7 @@
 		memset(digest, 0, sizeof(digest));
 
 		purple_cipher_context_reset(ctx, NULL);
-		purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf));
+		purple_cipher_context_append(ctx, data, strlen((char *)data));
 		purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
 		purple_cipher_context_destroy(ctx);
 		g_free(buf);
@@ -225,11 +216,6 @@
 		msn_object_set_sha1c(msnobj, base64);
 		g_free(base64);
 	}
-	else
-	{
-		purple_debug_error("msn", "Unable to open buddy icon %s!\n", filename);
-		msn_user_set_object(user, NULL);
-	}
 }
 
 void
--- a/libpurple/protocols/msn/user.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Fri Apr 27 04:03:04 2007 +0000
@@ -138,9 +138,9 @@
  * Sets the buddy icon for a local user.
  *
  * @param user     The user.
- * @param filename The path to the buddy icon.
+ * @param img      The buddy icon image
  */
-void msn_user_set_buddy_icon(MsnUser *user, const char *filename);
+void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img);
 
 /**
  * Sets the group ID list for a user.
--- a/libpurple/protocols/oscar/odc.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/oscar/odc.c	Fri Apr 27 04:03:04 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(g_memdup(embedded_data->data, size), 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);
 	}
 
--- a/libpurple/protocols/oscar/oscar.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Apr 27 04:03:04 2007 +0000
@@ -1842,34 +1842,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;
@@ -1916,7 +1896,7 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleMessageFlags flags = 0;
 	struct buddyinfo *bi;
-	char *iconfile;
+	PurpleStoredImage *img;
 	GString *message;
 	gchar *tmp;
 	aim_mpmsg_section_t *curpart;
@@ -1953,33 +1933,19 @@
 		}
 	}
 
-	iconfile = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(account));
-	if ((iconfile != NULL) &&
+	img = purple_buddy_icons_find_account_icon(account);
+	if ((img != NULL) &&
 	    (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
-		FILE *file;
-		struct stat st;
-
-		if (!g_stat(iconfile, &st)) {
-			guchar *buf = g_malloc(st.st_size);
-			file = g_fopen(iconfile, "rb");
-			if (file) {
-				/* XXX - Use g_file_get_contents() */
-				/* g_file_get_contents(iconfile, &data, &len, NULL); */
-				int len = fread(buf, 1, st.st_size, file);
-				purple_debug_info("oscar",
-						   "Sending buddy icon to %s (%d bytes, "
-						   "%lu reported)\n",
-						   userinfo->sn, len, st.st_size);
-				aim_im_sendch2_icon(od, userinfo->sn, buf, st.st_size,
-					st.st_mtime, aimutil_iconsum(buf, st.st_size));
-				fclose(file);
-			} else
-				purple_debug_error("oscar", "Can't open buddy icon file!\n");
-			g_free(buf);
-		} else
-			purple_debug_error("oscar", "Can't stat buddy icon file!\n");
-	}
-	g_free(iconfile);
+		gconstpointer data = purple_imgstore_get_data(img);
+		size_t len = purple_imgstore_get_size(img);
+		purple_debug_info("oscar",
+				   "Sending buddy icon to %s (%d bytes)\n",
+				   userinfo->sn, len);
+		/* TODO: XXX: FIXME: Does this actually need the mtime of the file? */
+		aim_im_sendch2_icon(od, userinfo->sn, data, len,
+			time(NULL), aimutil_iconsum(data, len));
+	}
+	purple_imgstore_unref(img);
 
 	message = g_string_new("");
 	curpart = args->mpmsg.parts;
@@ -2177,8 +2143,9 @@
 	else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
 	{
 		purple_buddy_icons_set_for_user(account, userinfo->sn,
-									  args->info.icon.icon,
-									  args->info.icon.length);
+									  g_memdup(args->info.icon.icon, args->info.icon.length),
+									  args->info.icon.length,
+									  NULL);
 	}
 
 	else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
@@ -3278,16 +3245,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, g_memdup(icon, iconlen), iconlen, b16);
+		g_free(b16);
 	}
 
 	cur = od->requesticon;
@@ -3325,29 +3286,17 @@
 	}
 
 	if (od->set_icon) {
-		struct stat st;
-		char *iconfile = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(purple_connection_get_account(gc)));
-		if (iconfile == NULL) {
+		PurpleAccount *account = purple_connection_get_account(gc);
+		PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+		if (img == NULL) {
 			aim_ssi_delicon(od);
-		} else if (!g_stat(iconfile, &st)) {
-			guchar *buf = g_malloc(st.st_size);
-			FILE *file = g_fopen(iconfile, "rb");
-			if (file) {
-				/* XXX - Use g_file_get_contents()? */
-				fread(buf, 1, st.st_size, file);
-				fclose(file);
-				purple_debug_info("oscar",
-					   "Uploading icon to icon server\n");
-				aim_bart_upload(od, buf, st.st_size);
-			} else
-				purple_debug_error("oscar",
-					   "Can't open buddy icon file!\n");
-			g_free(buf);
 		} else {
-			purple_debug_error("oscar",
-				   "Can't stat buddy icon file!\n");
+			purple_debug_info("oscar",
+				   "Uploading icon to icon server\n");
+			aim_bart_upload(od, purple_imgstore_get_data(img), 
+			                purple_imgstore_get_size(img));
+			purple_imgstore_unref(img);
 		}
-		g_free(iconfile);
 		od->set_icon = FALSE;
 	}
 
@@ -4145,11 +4094,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++;
 
@@ -4208,13 +4157,11 @@
 	PurpleAccount *account;
 	PeerConnection *conn;
 	int ret;
-	char *iconfile;
 	char *tmp1, *tmp2;
 
 	od = (OscarData *)gc->proto_data;
 	account = purple_connection_get_account(gc);
 	ret = 0;
-	iconfile = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(account));
 
 	if (imflags & PURPLE_MESSAGE_AUTO_RESP)
 		tmp1 = purple_str_sub_away_formatters(message, name);
@@ -4229,9 +4176,9 @@
 	} else {
 		struct buddyinfo *bi;
 		struct aim_sendimext_args args;
-		struct stat st;
 		gsize len;
 		PurpleConversation *conv;
+		PurpleStoredImage *img;
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
 
@@ -4280,44 +4227,38 @@
 			bi->ico_need = FALSE;
 		}
 
-		if (iconfile && !g_stat(iconfile, &st)) {
-			FILE *file = g_fopen(iconfile, "rb");
-			if (file) {
-				guchar *buf = g_malloc(st.st_size);
-				/* TODO: Use g_file_get_contents()? */
-				fread(buf, 1, st.st_size, file);
-				fclose(file);
-
-				args.iconlen   = st.st_size;
-				args.iconsum   = aimutil_iconsum(buf, st.st_size);
-				args.iconstamp = st.st_mtime;
-
-				if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
-					bi->ico_informed = FALSE;
-					bi->ico_sent     = FALSE;
-				}
-
-				/*
-				 * TODO:
-				 * For some reason sending our icon to people only works
-				 * when we're the ones who initiated the conversation.  If
-				 * the other person sends the first IM then they never get
-				 * the icon.  We should fix that.
-				 */
-				if (!bi->ico_informed) {
-					purple_debug_info("oscar",
-							   "Claiming to have a buddy icon\n");
-					args.flags |= AIM_IMFLAGS_HASICON;
-					bi->ico_me_len = args.iconlen;
-					bi->ico_me_csum = args.iconsum;
-					bi->ico_me_time = args.iconstamp;
-					bi->ico_informed = TRUE;
-				}
-
-				g_free(buf);
+		img = purple_buddy_icons_find_account_icon(account);
+		if (img) {
+			gconstpointer data = purple_imgstore_get_data(img);
+			args.iconlen   = purple_imgstore_get_size(img);
+			args.iconsum   = aimutil_iconsum(data, args.iconlen);
+			/* TODO: XXX: FIXME: Deal with the timestamp issue. */
+			args.iconstamp = time(NULL);
+
+			if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
+				bi->ico_informed = FALSE;
+				bi->ico_sent     = FALSE;
 			}
+
+			/*
+			 * TODO:
+			 * For some reason sending our icon to people only works
+			 * when we're the ones who initiated the conversation.  If
+			 * the other person sends the first IM then they never get
+			 * the icon.  We should fix that.
+			 */
+			if (!bi->ico_informed) {
+				purple_debug_info("oscar",
+						   "Claiming to have a buddy icon\n");
+				args.flags |= AIM_IMFLAGS_HASICON;
+				bi->ico_me_len = args.iconlen;
+				bi->ico_me_csum = args.iconsum;
+				bi->ico_me_time = args.iconstamp;
+				bi->ico_informed = TRUE;
+			}
+
+			purple_imgstore_unref(img);
 		}
-		g_free(iconfile);
 
 		args.destsn = name;
 
@@ -4791,8 +4732,7 @@
 	PurpleBuddy *b;
 	struct aim_ssi_item *curitem;
 	guint32 tmp;
-	const char *icon_path;
-	char *cached_icon_path;
+	PurpleStoredImage *img;
 	va_list ap;
 	guint16 fmtver, numitems;
 	guint32 timestamp;
@@ -5025,10 +4965,9 @@
 	 * the event that the local user set a new icon while this
 	 * account was offline.
 	 */
-	icon_path = purple_account_get_buddy_icon(account);
-	cached_icon_path = purple_buddy_icons_get_full_path(icon_path);
-	oscar_set_icon(gc, cached_icon_path);
-	g_free(cached_icon_path);
+	img = purple_buddy_icons_find_account_icon(account);
+	oscar_set_icon(gc, img);
+	purple_imgstore_unref(img);
 
 	return 1;
 }
@@ -5633,37 +5572,27 @@
 					od->set_icon = TRUE;
 					aim_srv_requestnew(od, SNAC_FAMILY_BART);
 				} else {
-					struct stat st;
-					char *iconfile = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(purple_connection_get_account(gc)));
-					if (iconfile == NULL) {
+					PurpleAccount *account = purple_connection_get_account(gc);
+					PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+					if (img == NULL) {
 						aim_ssi_delicon(od);
-					} else if (!g_stat(iconfile, &st)) {
-						guchar *buf = g_malloc(st.st_size);
-						FILE *file = g_fopen(iconfile, "rb");
-						if (file) {
-							/* XXX - Use g_file_get_contents()? */
-							fread(buf, 1, st.st_size, file);
-							fclose(file);
-							purple_debug_info("oscar",
-											"Uploading icon to icon server\n");
-							aim_bart_upload(od, buf, st.st_size);
-						} else
-							purple_debug_error("oscar",
-											 "Can't open buddy icon file!\n");
-						g_free(buf);
 					} else {
-						purple_debug_error("oscar",
-										 "Can't stat buddy icon file!\n");
+
+						purple_debug_info("oscar",
+										"Uploading icon to icon server\n");
+						aim_bart_upload(od, purple_imgstore_get_data(img),
+						                purple_imgstore_get_size(img));
+						purple_imgstore_unref(img);
 					}
-					g_free(iconfile);
 				}
 			} else if (flags == 0x81) {
-				char *iconfile = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(purple_connection_get_account(gc)));
-				if (iconfile == NULL)
+				PurpleAccount *account = purple_connection_get_account(gc);
+				PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+				if (img == NULL)
 					aim_ssi_delicon(od);
 				else {
 					aim_ssi_seticon(od, md5, length);
-					g_free(iconfile);
+					purple_imgstore_unref(img);
 				}
 			}
 		} break;
@@ -6253,41 +6182,28 @@
 	purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
 }
 
-void oscar_set_icon(PurpleConnection *gc, const char *iconfile)
+void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	OscarData *od = gc->proto_data;
-	FILE *file;
-	struct stat st;
-
-	if (iconfile == NULL) {
+
+	if (img == NULL) {
 		aim_ssi_delicon(od);
-	} else if (!g_stat(iconfile, &st)) {
-		guchar *buf = g_malloc(st.st_size);
-		file = g_fopen(iconfile, "rb");
-		if (file)
-		{
-			PurpleCipher *cipher;
-			PurpleCipherContext *context;
-			guchar md5[16];
-			int len;
-
-			/* XXX - Use g_file_get_contents()? */
-			len = fread(buf, 1, st.st_size, file);
-			fclose(file);
-
-			cipher = purple_ciphers_find_cipher("md5");
-			context = purple_cipher_context_new(cipher, NULL);
-			purple_cipher_context_append(context, buf, len);
-			purple_cipher_context_digest(context, 16, md5, NULL);
-			purple_cipher_context_destroy(context);
-
-			aim_ssi_seticon(od, md5, 16);
-		} else
-			purple_debug_error("oscar",
-				   "Can't open buddy icon file!\n");
-		g_free(buf);
-	} else
-		purple_debug_error("oscar", "Can't stat buddy icon file!\n");
+	} else {
+		PurpleCipher *cipher;
+		PurpleCipherContext *context;
+		guchar md5[16];
+		gconstpointer data = purple_imgstore_get_data(img);
+		size_t len = purple_imgstore_get_size(img);
+
+
+		cipher = purple_ciphers_find_cipher("md5");
+		context = purple_cipher_context_new(cipher, NULL);
+		purple_cipher_context_append(context, data, len);
+		purple_cipher_context_digest(context, 16, md5, NULL);
+		purple_cipher_context_destroy(context);
+
+		aim_ssi_seticon(od, md5, 16);
+	}
 }
 
 /**
--- a/libpurple/protocols/oscar/oscarcommon.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/oscar/oscarcommon.h	Fri Apr 27 04:03:04 2007 +0000
@@ -81,7 +81,7 @@
 void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
 void oscar_convo_closed(PurpleConnection *gc, const char *who);
 const char *oscar_normalize(const PurpleAccount *account, const char *str);
-void oscar_set_icon(PurpleConnection *gc, const char *iconfile);
+void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
 gboolean oscar_can_receive_file(PurpleConnection *gc, const char *who);
 void oscar_send_file(PurpleConnection *gc, const char *who, const char *file);
 PurpleXfer *oscar_new_xfer(PurpleConnection *gc, const char *who);
--- a/libpurple/protocols/qq/buddy_info.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Fri Apr 27 04:03:04 2007 +0000
@@ -532,27 +532,21 @@
 	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);
 	}
 }
 
 /* TODO: custom faces for QQ members and users with level >= 16 */
-void qq_set_my_buddy_icon(PurpleConnection *gc, const gchar *iconfile)
+void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	gchar *icon;
 	gint icon_num;
@@ -601,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 */
--- a/libpurple/protocols/qq/buddy_info.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Fri Apr 27 04:03:04 2007 +0000
@@ -86,8 +86,8 @@
 
 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_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
+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);
--- a/libpurple/protocols/sametime/sametime.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Fri Apr 27 04:03:04 2007 +0000
@@ -2698,8 +2698,7 @@
       cid = make_cid(cid);
 
       /* add image to the purple image store */
-      img = purple_imgstore_add(d_dat, d_len, cid);
-      g_free(d_dat);
+      img = purple_imgstore_add_with_id(d_dat, d_len, cid);
 
       /* map the cid to the image store identifier */
       g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img));
@@ -2772,7 +2771,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);
   }
 }
@@ -3859,7 +3858,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;
@@ -3885,9 +3884,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);
 
--- a/libpurple/protocols/silc/buddy.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/silc/buddy.c	Fri Apr 27 04:03:04 2007 +0000
@@ -987,8 +987,10 @@
 					const unsigned char *data;
 					SilcUInt32 data_len;
 					data = silc_mime_get_data(m, &data_len);
-					if (data)
-						purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), (void *)data, data_len);
+					if (data) {
+						/* TODO: Check if SILC gives us something to use as the checksum instead */
+						purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
+					}
 				}
 				silc_mime_free(m);
 			}
@@ -1681,48 +1683,31 @@
 }
 
 #ifdef SILC_ATTRIBUTE_USER_ICON
-void silcpurple_buddy_set_icon(PurpleConnection *gc, const char *iconfile)
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcMime mime;
-	PurpleBuddyIcon ic;
 	char type[32];
 	unsigned char *icon;
 	const char *t;
-	struct stat st;
-	FILE *fp;
 	SilcAttributeObjMime obj;
 
 	/* Remove */
-	if (!iconfile) {
+	if (!img) {
 		silc_client_attribute_del(client, conn,
 					  SILC_ATTRIBUTE_USER_ICON, NULL);
 		return;
 	}
 
 	/* Add */
-	if (g_stat(iconfile, &st) < 0)
-		return;
-	fp = g_fopen(iconfile, "rb");
-	if (!fp)
-		return;
-	ic.data = g_malloc(st.st_size);
-	if (!ic.data)
+	mime = silc_mime_alloc();
+	if (!mime)
 		return;
-	ic.len = fread(ic.data, 1, st.st_size, fp);
-	fclose(fp);
 
-	mime = silc_mime_alloc();
-	if (!mime) {
-		g_free(ic.data);
-		return;
-	}
-
-	t = purple_buddy_icon_get_type((const PurpleBuddyIcon *)&ic);
-	if (!t) {
-		g_free(ic.data);
+	t = purple_util_get_image_extension(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
+	if (!t || !strcmp(t, "icon")) {
 		silc_mime_free(mime);
 		return;
 	}
@@ -1730,7 +1715,7 @@
 		t = "jpeg";
 	g_snprintf(type, sizeof(type), "image/%s", t);
 	silc_mime_add_field(mime, "Content-Type", type);
-	silc_mime_add_data(mime, ic.data, ic.len);
+	silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 
 	obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
 	if (obj.mime)
@@ -1738,7 +1723,6 @@
 					  SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
 
 	silc_free(icon);
-	g_free(ic.data);
 	silc_mime_free(mime);
 }
 #endif
--- a/libpurple/protocols/silc/ops.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/silc/ops.c	Fri Apr 27 04:03:04 2007 +0000
@@ -161,7 +161,7 @@
 		if (channel && !convo)
 			goto out;
 
-		imgid = purple_imgstore_add(data, data_len, "");
+		imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, "");
 		if (imgid) {
 			cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
 			g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
@@ -177,7 +177,7 @@
 					    sender->nickname : "<unknown>",
 					    tmp, cflags, time(NULL));
 
-			purple_imgstore_unref(imgid);
+			purple_imgstore_unref_by_id(imgid);
 			cflags = 0;
 		}
 		goto out;
--- a/libpurple/protocols/silc/silc.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Fri Apr 27 04:03:04 2007 +0000
@@ -194,7 +194,7 @@
 		SilcUInt32 mask;
 		const char *tmp;
 #ifdef SILC_ATTRIBUTE_USER_ICON
-		char *icon;
+		PurpleStoredImage *img;
 #endif
 #ifdef HAVE_SYS_UTSNAME_H
 		struct utsname u;
@@ -233,9 +233,9 @@
 
 #ifdef SILC_ATTRIBUTE_USER_ICON
 		/* Set our buddy icon */
-		icon = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(account));
-		silcpurple_buddy_set_icon(gc, icon);
-		g_free(icon);
+		img = purple_buddy_icons_find_account_icon(account);
+		silcpurple_buddy_set_icon(gc, img);
+		purple_imgstore_unref(img);
 #endif
 	}
 
--- a/libpurple/protocols/silc/silcpurple.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/silc/silcpurple.h	Fri Apr 27 04:03:04 2007 +0000
@@ -145,7 +145,7 @@
 					 char **contactstr, char **langstr, char **devicestr,
 					 char **tzstr, char **geostr);
 #ifdef SILC_ATTRIBUTE_USER_ICON
-void silcpurple_buddy_set_icon(PurpleConnection *gc, const char *iconfile);
+void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img);
 #endif
 #ifdef HAVE_SILCMIME_H
 char *silcpurple_file2mime(const char *filename);
--- a/libpurple/protocols/yahoo/yahoo.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri Apr 27 04:03:04 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;
@@ -2686,11 +2686,10 @@
 static void yahoo_picture_check(PurpleAccount *account)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
-	char *buddyicon;
-
-	buddyicon = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(account));
-	yahoo_set_buddy_icon(gc, buddyicon);
-	g_free(buddyicon);
+	PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+
+	yahoo_set_buddy_icon(gc, img);
+	purple_imgstore_unref(img);
 }
 
 static int get_yahoo_status_from_purple_status(PurpleStatus *status)
--- a/libpurple/protocols/yahoo/yahoo.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Fri Apr 27 04:03:04 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"
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Fri Apr 27 04:03:04 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, g_memdup(pic_data, len), 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);
@@ -518,15 +514,12 @@
 	}
 }
 
-void yahoo_set_buddy_icon(PurpleConnection *gc, const char *iconfile)
+void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	struct yahoo_data *yd = gc->proto_data;
 	PurpleAccount *account = gc->account;
-	gchar *icondata;
-	gsize len;
-	GError *error = NULL;
 
-	if (iconfile == NULL) {
+	if (img == NULL) {
 		g_free(yd->picture_url);
 		yd->picture_url = NULL;
 
@@ -537,14 +530,19 @@
 			/* Tell everyone we ain't got one no more */
 			yahoo_send_picture_update(gc, 0);
 
-	} else if (g_file_get_contents(iconfile, &icondata, &len, &error)) {
-		GString *s = g_string_new_len(icondata, len);
+	} else {
+		gconstpointer data = purple_imgstore_get_data(img);
+		size_t len = purple_imgstore_get_size(img);
+		GString *s = g_string_new_len(data, len);
 		struct yahoo_buddy_icon_upload_data *d;
 		int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0);
 		int expire = purple_account_get_int(account, YAHOO_PICEXPIRE_SETTING, 0);
 		const char *oldurl = purple_account_get_string(account, YAHOO_PICURL_SETTING, NULL);
+		char *iconfile;
 
-		g_free(icondata);
+		/* TODO: At some point, it'd be nice to fix this for real, or
+		 * TODO: at least change it to be something like:
+		 * TODO: purple_imgstore_get_filename(img); */
 		yd->picture_checksum = g_string_hash(s);
 
 		if ((yd->picture_checksum == oldcksum) &&
@@ -557,11 +555,15 @@
 			return;
 		}
 
+		/* TODO: FIXME: This is completely wrong.  The upload code needs to
+		 * TODO: be modified to work with a PurpleStoredImage. */
+		iconfile = g_build_filename(purple_buddy_icons_get_cache_dir(),
+		                            purple_imgstore_get_filename(img), NULL);
 		d = g_new0(struct yahoo_buddy_icon_upload_data, 1);
 		d->gc = gc;
 		d->str = s;
 		d->fd = -1;
-		d->filename = g_strdup(iconfile);
+		d->filename = iconfile;
 
 		if (!yd->logged_in) {
 			yd->picture_upload_todo = d;
@@ -570,10 +572,5 @@
 
 		yahoo_buddy_icon_upload(gc, d);
 
-	} else {
-		purple_debug_error("yahoo",
-				"Could not read buddy icon file '%s': %s\n",
-				iconfile, error->message);
-		g_error_free(error);
 	}
 }
--- a/libpurple/protocols/yahoo/yahoo_picture.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.h	Fri Apr 27 04:03:04 2007 +0000
@@ -37,7 +37,7 @@
 
 void yahoo_process_avatar_update(PurpleConnection *gc, struct yahoo_packet *pkt);
 
-void yahoo_set_buddy_icon(PurpleConnection *gc, const char *iconfile);
+void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void yahoo_buddy_icon_upload(PurpleConnection *gc, struct yahoo_buddy_icon_upload_data *d);
 void yahoo_buddy_icon_upload_data_free(struct yahoo_buddy_icon_upload_data *d);
 
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Fri Apr 27 04:03:04 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(g_memdup(url_text, len), len, NULL);
 			
 			tmp = g_strdup_printf("<img id=\"%d\"><br>", 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
 }
 
--- a/libpurple/prpl.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/prpl.h	Fri Apr 27 04:03:04 2007 +0000
@@ -59,6 +59,7 @@
 #include "blist.h"
 #include "conversation.h"
 #include "ft.h"
+#include "imgstore.h"
 #include "notify.h"
 #include "proxy.h"
 #include "plugin.h"
@@ -284,7 +285,9 @@
 
 	const char *(*normalize)(const PurpleAccount *, const char *);
 
-	void (*set_buddy_icon)(PurpleConnection *, const char *cached_path);
+	/* The prpl does NOT own a reference to img.  If it needs one, it
+	 * must purple_imgstore_ref(img) itself. */
+	void (*set_buddy_icon)(PurpleConnection *, PurpleStoredImage *img);
 
 	void (*remove_group)(PurpleConnection *gc, PurpleGroup *group);
 
--- a/libpurple/purple-url-handler	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/purple-url-handler	Fri Apr 27 04:03:04 2007 +0000
@@ -92,13 +92,13 @@
 
 def aim(uri):
     protocol = "prpl-aim"
-    match = re.match(r"^(aim):([^?]*)(\?(.*))", uri)
+    match = re.match(r"^aim:([^?]*)(\?(.*))", uri)
     if not match:
         print "Invalid aim URI: %s" % uri
         return
 
-    command = urllib.unquote_plus(match.group(2))
-    paramstring = match.group(4)
+    command = urllib.unquote_plus(match.group(1))
+    paramstring = match.group(3)
     params = {}
     if paramstring:
         for param in paramstring.split("&"):
@@ -129,13 +129,13 @@
 
 def icq(uri):
     protocol = "prpl-icq"
-    match = re.match(r"^(icq):([^?]*)(\?(.*))", uri)
+    match = re.match(r"^icq:([^?]*)(\?(.*))", uri)
     if not match:
-        print "Invalid aim URI: %s" % uri
+        print "Invalid icq URI: %s" % uri
         return
 
-    command = urllib.unquote_plus(match.group(2))
-    paramstring = match.group(4)
+    command = urllib.unquote_plus(match.group(1))
+    paramstring = match.group(3)
     params = {}
     if paramstring:
         for param in paramstring.split("&"):
@@ -225,26 +225,26 @@
 
 def xmpp(uri):
     protocol = "prpl-jabber"
-    match = re.match(r"^xmpp:((//)?([^/?#]*))?(/?([^?#]*))(\?([^;#]*)(;([^#]*))?)?(#(.*))?", uri)
+    match = re.match(r"^xmpp:(//([^/?#]*)/?)?([^?#]*)(\?([^;#]*)(;([^#]*))?)?(#(.*))?", uri)
     if not match:
         print "Invalid xmpp URI: %s" % uri
         return
 
-    tmp = match.group(3)
+    tmp = match.group(2)
     if (tmp):
         accountname = urllib.unquote_plus(tmp)
     else:
         accountname = ""
 
-    screenname = urllib.unquote_plus(match.group(5))
+    screenname = urllib.unquote_plus(match.group(3))
 
-    tmp = match.group(7)
+    tmp = match.group(5)
     if (tmp):
         command = urllib.unquote_plus(tmp)
     else:
         command = ""
 
-    paramstring = match.group(9)
+    paramstring = match.group(7)
     params = {}
     if paramstring:
         for param in paramstring.split(";"):
--- a/libpurple/util.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/util.c	Fri Apr 27 04:03:04 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(gconstpointer 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)
 {
@@ -2654,7 +2667,7 @@
 gboolean
 purple_running_osx(void)
 {
-#if defined(__APPLE__)	
+#if defined(__APPLE__)
 	return TRUE;
 #else
 	return FALSE;
@@ -3072,7 +3085,6 @@
 	char *cmd;
 	GHashTable *params = NULL;
 	int len;
-printf("got handler uri \n");
 	if (!(tmp = strchr(uri, ':')) || tmp == uri) {
 		purple_debug_error("util", "Malformed protocol handler message - missing protocol.\n");
 		return;
@@ -4195,7 +4207,7 @@
 	signal(SIGABRT, SIG_DFL);	/* 6:  abort program */
 
 #ifdef SIGPOLL
-	signal(SIGPOLL,  SIG_DFL);	/* 7:  pollable event (POSIX) */	
+	signal(SIGPOLL,  SIG_DFL);	/* 7:  pollable event (POSIX) */
 #endif /* SIGPOLL */
 
 #ifdef SIGEMT
@@ -4211,7 +4223,7 @@
 	signal(SIGTERM, SIG_DFL);	/* 15: software termination signal */
 	signal(SIGCHLD, SIG_DFL);	/* 20: child status has changed */
 	signal(SIGXCPU, SIG_DFL);	/* 24: exceeded CPU time limit */
-	signal(SIGXFSZ, SIG_DFL);	/* 25: exceeded file size limit */	
+	signal(SIGXFSZ, SIG_DFL);	/* 25: exceeded file size limit */
 #endif /* HAVE_SIGNAL_H */
 #endif /* !_WIN32 */
 }
--- a/libpurple/util.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/util.h	Fri Apr 27 04:03:04 2007 +0000
@@ -469,7 +469,7 @@
  * the first character of the entity. If given an unrecognized entity, the function
  * returns @c NULL.
  *
- * Note that this function, unlike purple_unescape_html(), does not search 
+ * Note that this function, unlike purple_unescape_html(), does not search
  * the string for the entity, does not replace the entity, and does not
  * return a newly allocated string.
  *
@@ -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(gconstpointer 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
--- a/libpurple/value.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/libpurple/value.h	Fri Apr 27 04:03:04 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;
 
 /**
--- a/pidgin.desktop.in	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin.desktop.in	Fri Apr 27 04:03:04 2007 +0000
@@ -4,7 +4,7 @@
 _GenericName=Internet Messenger
 _Comment=Send instant messages over multiple protocols
 Exec=pidgin
-Icon=pidgin.png
+Icon=pidgin
 StartupNotify=true
 Terminal=false
 Type=Application
--- a/pidgin.spec.in	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin.spec.in	Fri Apr 27 04:03:04 2007 +0000
@@ -337,6 +337,7 @@
 
 %{_bindir}/pidgin
 %{_datadir}/pixmaps/pidgin
+%{_datadir}/icons/hicolor/*/apps/pidgin.png
 %dir %{_datadir}/sounds/pidgin
 %{_datadir}/sounds/pidgin/*
 %{_datadir}/applications/*
--- a/pidgin/gtkaccount.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkaccount.c	Fri Apr 27 04:03:04 2007 +0000
@@ -122,8 +122,7 @@
 	GtkWidget *icon_filesel;
 	GtkWidget *icon_preview;
 	GtkWidget *icon_text;
-	char *cached_icon_path;
-	char *icon_path;
+	PurpleStoredImage *icon_img;
 
 	/* Protocol Options */
 	GtkWidget *protocol_frame;
@@ -195,20 +194,25 @@
 }
 
 static void
-set_dialog_icon(AccountPrefsDialog *dialog, gchar *new_cached_icon_path, gchar *new_icon_path)
+set_dialog_icon(AccountPrefsDialog *dialog, gpointer *data, size_t len, gchar *new_icon_path)
 {
-	char *filename;
 	GdkPixbuf *pixbuf = NULL;
 
-	g_free(dialog->cached_icon_path);
-	g_free(dialog->icon_path);
-	dialog->cached_icon_path = new_cached_icon_path;
-	dialog->icon_path = new_icon_path;
-
-	filename = purple_buddy_icons_get_full_path(dialog->cached_icon_path);
-	if (filename != NULL) {
-		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-		g_free(filename);
+	purple_imgstore_unref(dialog->icon_img);
+	if (data != NULL)
+	{
+		if (len > 0)
+			dialog->icon_img = purple_imgstore_add(data, len, new_icon_path);
+		else
+			g_free(data);
+	}
+
+	if (dialog->icon_img != NULL) {
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+		gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(dialog->icon_img),
+		                        purple_imgstore_get_size(dialog->icon_img), NULL);
+		gdk_pixbuf_loader_close(loader, NULL);
+		pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
 	}
 
 	if (pixbuf && dialog->prpl_info &&
@@ -298,12 +302,14 @@
 static void
 icon_filesel_choose_cb(const char *filename, gpointer data)
 {
-	AccountPrefsDialog *dialog;
-
-	dialog = data;
+	AccountPrefsDialog *dialog = data;
 
 	if (filename != NULL)
-		set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, filename), g_strdup(filename));
+	{
+		size_t len;
+		gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
+		set_dialog_icon(dialog, data, len, g_strdup(filename));
+	}
 
 	dialog->icon_filesel = NULL;
 }
@@ -318,7 +324,7 @@
 static void
 icon_reset_cb(GtkWidget *button, AccountPrefsDialog *dialog)
 {
-	set_dialog_icon(dialog, NULL, NULL);
+	set_dialog_icon(dialog, NULL, 0, NULL);
 }
 
 static void
@@ -333,6 +339,9 @@
 		if (!g_ascii_strncasecmp(name, "file://", 7)) {
 			GError *converr = NULL;
 			gchar *tmp, *rtmp;
+			gpointer data;
+			size_t len;
+
 			/* It looks like we're dealing with a local file. */
 			if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
 				purple_debug(PURPLE_DEBUG_ERROR, "buddyicon", "%s\n",
@@ -342,8 +351,10 @@
 			}
 			if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n')))
 				*rtmp = '\0';
-			set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, tmp), g_strdup(tmp));
-			g_free(tmp);
+
+			data = pidgin_convert_buddy_icon(dialog->plugin, tmp, &len);
+			/* This takes ownership of tmp */
+			set_dialog_icon(dialog, data, len, tmp);
 		}
 		gtk_drag_finish(dc, TRUE, FALSE, t);
 	}
@@ -591,8 +602,8 @@
 	gtk_widget_show(dialog->icon_entry);
 	/* TODO: Uh, isn't this next line pretty useless? */
 	pidgin_set_accessible_label (dialog->icon_entry, label);
-	dialog->cached_icon_path = NULL;
-	dialog->icon_path = NULL;
+	purple_imgstore_unref(dialog->icon_img);
+	dialog->icon_img = NULL;
 
 	vbox2 = gtk_vbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
@@ -617,19 +628,27 @@
 	}
 
 	if (dialog->account != NULL) {
+		PurpleStoredImage *img;
+		gpointer data = NULL;
+		size_t len = 0;
+
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->new_mail_check),
 					     purple_account_get_check_mail(dialog->account));
 
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->icon_check),
 					     !purple_account_get_bool(dialog->account, "use-global-buddyicon",
 								       TRUE));
-		set_dialog_icon(dialog,
-				g_strdup(purple_account_get_ui_string(dialog->account,
-						PIDGIN_UI, "non-global-buddyicon-cached-path", NULL)),
-				g_strdup(purple_account_get_ui_string(dialog->account,
-						PIDGIN_UI, "non-global-buddyicon-path", NULL)));
+
+		img = purple_buddy_icons_find_account_icon(dialog->account);
+		if (img)
+		{
+			len = purple_imgstore_get_size(img);
+			data = g_memdup(purple_imgstore_get_data(img), len);
+		}
+		set_dialog_icon(dialog, data, len,
+		                g_strdup(purple_account_get_buddy_icon_path(dialog->account)));
 	} else {
-		set_dialog_icon(dialog, NULL, NULL);
+		set_dialog_icon(dialog, NULL, 0, NULL);
 	}
 
 	if (!dialog->prpl_info ||
@@ -1072,22 +1091,7 @@
 	g_list_free(dialog->protocol_opt_entries);
 	g_free(dialog->protocol_id);
 
-	if (dialog->cached_icon_path != NULL)
-	{
-		const char *icon = purple_account_get_ui_string(dialog->account, PIDGIN_UI, "non-global-buddyicon-cached-path", NULL);
-		if (dialog->cached_icon_path != NULL && (icon == NULL || strcmp(dialog->cached_icon_path, icon)))
-		{
-			/* The user set an icon, which would've been cached by convert_buddy_icon,
-			 * but didn't save the changes. Delete the cache file. */
-			char *filename = g_build_filename(purple_buddy_icons_get_cache_dir(), dialog->cached_icon_path, NULL);
-			g_unlink(filename);
-			g_free(filename);
-		}
-
-		g_free(dialog->cached_icon_path);
-	}
-
-	g_free(dialog->icon_path);
+	purple_imgstore_unref(dialog->icon_img);
 
 	if (dialog->icon_filesel)
 		gtk_widget_destroy(dialog->icon_filesel);
@@ -1143,26 +1147,37 @@
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 	if (prpl_info != NULL && prpl_info->icon_spec.format != NULL)
 	{
+		const char *filename;
+
 		if (new || purple_account_get_bool(account, "use-global-buddyicon", TRUE) ==
 			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)))
 		{
 			icon_change = TRUE;
 		}
 		purple_account_set_bool(account, "use-global-buddyicon", !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
-		purple_account_set_ui_string(account, PIDGIN_UI, "non-global-buddyicon-cached-path", dialog->cached_icon_path);
-		purple_account_set_ui_string(account, PIDGIN_UI, "non-global-buddyicon-path", dialog->icon_path);
+
 		if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)))
 		{
-			purple_account_set_buddy_icon_path(account, dialog->icon_path);
-			purple_account_set_buddy_icon(account, dialog->cached_icon_path);
+			if (dialog->icon_img)
+			{
+				size_t len = purple_imgstore_get_size(dialog->icon_img);
+				purple_buddy_icons_set_account_icon(account,
+				                                    g_memdup(purple_imgstore_get_data(dialog->icon_img), len),
+				                                    len);
+				purple_account_set_buddy_icon_path(account, purple_imgstore_get_filename(dialog->icon_img));
+			}
+			else
+			{
+				purple_buddy_icons_set_account_icon(account, NULL, 0);
+				purple_account_set_buddy_icon_path(account, NULL);
+			}
 		}
-		else if (purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon") && icon_change)
+		else if ((filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) && icon_change)
 		{
-			const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
-			char *icon = pidgin_convert_buddy_icon(dialog->plugin, filename);
+			size_t len;
+			gpointer data = pidgin_convert_buddy_icon(dialog->plugin, filename, &len);
 			purple_account_set_buddy_icon_path(account, filename);
-			purple_account_set_buddy_icon(account, icon);
-			g_free(icon);
+			purple_buddy_icons_set_account_icon(account, data, len);
 		}
 	}
 
@@ -1977,7 +1992,7 @@
 set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account, GdkPixbuf *global_buddyicon)
 {
 	GdkPixbuf *pixbuf, *buddyicon = NULL;
-	const char *path = NULL;
+	PurpleStoredImage *img = NULL;
 
 	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 	if ((pixbuf != NULL) && purple_account_is_disconnected(account))
@@ -1988,12 +2003,22 @@
 			buddyicon = g_object_ref(G_OBJECT(global_buddyicon));
 		/* This is for when set_account() is called for a single account */
 		else
-			path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
-	} else
-		path = purple_account_get_ui_string(account, PIDGIN_UI, "non-global-buddyicon-path", NULL);
-
-	if (path != NULL) {
-		GdkPixbuf *buddyicon_pixbuf = gdk_pixbuf_new_from_file(path, NULL);
+			img = purple_buddy_icons_find_account_icon(account);
+	} else {
+		img = purple_buddy_icons_find_account_icon(account);
+	}
+
+	if (img != NULL) {
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+		GdkPixbuf *buddyicon_pixbuf;
+
+		gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(img),
+		                        purple_imgstore_get_size(img), NULL);
+		gdk_pixbuf_loader_close(loader, NULL);
+		buddyicon_pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+
+		purple_imgstore_unref(img);
+
 		if (buddyicon_pixbuf != NULL) {
 			buddyicon = gdk_pixbuf_scale_simple(buddyicon_pixbuf, 22, 22, GDK_INTERP_HYPER);
 			g_object_unref(G_OBJECT(buddyicon_pixbuf));
--- a/pidgin/gtkblist.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkblist.c	Fri Apr 27 04:03:04 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);
@@ -4894,7 +4889,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;
@@ -5079,7 +5074,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);
 
--- a/pidgin/gtkconv.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkconv.c	Fri Apr 27 04:03:04 2007 +0000
@@ -2484,23 +2484,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)
 {
@@ -2540,9 +2523,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);
 
@@ -2580,7 +2561,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;
@@ -2614,11 +2596,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);
@@ -6181,12 +6170,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;
@@ -6243,17 +6234,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) {
@@ -6263,20 +6255,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;
@@ -6287,9 +6281,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);
--- a/pidgin/gtkimhtmltoolbar.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Fri Apr 27 04:03:04 2007 +0000
@@ -481,8 +481,7 @@
 
 	name = strrchr(filename, G_DIR_SEPARATOR) + 1;
 
-	id = purple_imgstore_add(filedata, size, name);
-	g_free(filedata);
+	id = purple_imgstore_add_with_id(filedata, size, name);
 
 	if (id == 0) {
 		buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
@@ -500,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);
 }
 
 
--- a/pidgin/gtkmain.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkmain.c	Fri Apr 27 04:03:04 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.
  *
@@ -240,11 +240,15 @@
 	GdkPixbuf *icon = NULL;
 	char *icon_path;
 	int i;
-	const char *icon_sizes[] = {
-		"16",
-		"24",
-		"32",
-		"48"
+	struct {
+		const char *dir;
+		const char *filename;
+	} icon_sizes[] = {
+		{"16x16", "pidgin.png"},
+		{"24x24", "pidgin.png"},
+		{"32x32", "pidgin.png"},
+		{"48x48", "pidgin.png"},
+		{"scalable", "pidgin.svg"}
 	};
 
 #endif
@@ -256,7 +260,7 @@
 #ifndef _WIN32
 	/* use the nice PNG icon for all the windows */
 	for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
-		icon_path = g_build_filename(DATADIR, "pixmaps", "pidgin", "icons", icon_sizes[i], "pidgin.png", NULL);
+		icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
 		icon = gdk_pixbuf_new_from_file(icon_path, NULL);
 		g_free(icon_path);
 		if (icon) {
@@ -446,6 +450,7 @@
 	int opt;
 	gboolean gui_check;
 	gboolean debug_enabled;
+	gboolean migration_failed = FALSE;
 
 	struct option long_options[] = {
 		{"config",   required_argument, NULL, 'c'},
@@ -639,6 +644,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 +677,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());
 
--- a/pidgin/gtkstatusbox.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Fri Apr 27 04:03:04 2007 +0000
@@ -42,8 +42,10 @@
 #include <gdk/gdkkeysyms.h>
 
 #include "account.h"
+#include "buddyicon.h"
 #include "core.h"
 #include "internal.h"
+#include "imgstore.h"
 #include "network.h"
 #include "savedstatuses.h"
 #include "status.h"
@@ -383,13 +385,26 @@
 	if (status_box->account &&
 		!purple_account_get_bool(status_box->account, "use-global-buddyicon", TRUE))
 	{
-		char *string = purple_buddy_icons_get_full_path(purple_account_get_buddy_icon(status_box->account));
-		pidgin_status_box_set_buddy_icon(status_box, string);
-		g_free(string);
+		PurpleStoredImage *img = purple_buddy_icons_find_account_icon(status_box->account);
+		pidgin_status_box_set_buddy_icon(status_box, img);
+		purple_imgstore_unref(img);
 	}
 	else
 	{
-		pidgin_status_box_set_buddy_icon(status_box, purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon"));
+		const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
+		PurpleStoredImage *img = NULL;
+
+		if (filename != NULL)
+		{
+			gchar *contents;
+			gsize size;
+			if (g_file_get_contents(filename, &contents, &size, NULL))
+			{
+				img = purple_imgstore_add(contents, size, filename);
+			}
+		}
+
+		pidgin_status_box_set_buddy_icon(status_box, img);
 	}
 
 	status_box->hand_cursor = gdk_cursor_new (GDK_HAND2);
@@ -422,6 +437,8 @@
 	gdk_cursor_unref(statusbox->hand_cursor);
 	gdk_cursor_unref(statusbox->arrow_cursor);
 
+	purple_imgstore_unref(statusbox->buddy_icon_img);
+
 	g_object_unref(G_OBJECT(statusbox->buddy_icon));
 	g_object_unref(G_OBJECT(statusbox->buddy_icon_hover));
 
@@ -431,12 +448,10 @@
 	if (statusbox->icon_box_menu)
 		gtk_widget_destroy(statusbox->icon_box_menu);
 
-	g_free(statusbox->buddy_icon_path);
-
 	statusbox->icon = NULL;
 	statusbox->icon_box = NULL;
 	statusbox->icon_box_menu = NULL;
-	statusbox->buddy_icon_path = NULL;
+	statusbox->buddy_icon_img = NULL;
 	statusbox->buddy_icon = NULL;
 	statusbox->buddy_icon_hover = NULL;
 	statusbox->hand_cursor = NULL;
@@ -489,14 +504,13 @@
 	gdk_cursor_unref(statusbox->hand_cursor);
 	gdk_cursor_unref(statusbox->arrow_cursor);
 
+	purple_imgstore_unref(statusbox->buddy_icon_img);
 	g_object_unref(G_OBJECT(statusbox->buddy_icon));
 	g_object_unref(G_OBJECT(statusbox->buddy_icon_hover));
 
 	if (statusbox->buddy_icon_sel)
 		gtk_widget_destroy(statusbox->buddy_icon_sel);
 
-	g_free(statusbox->buddy_icon_path);
-
 	G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
@@ -1402,20 +1416,21 @@
 static void
 buddy_icon_set_cb(const char *filename, PidginStatusBox *box)
 {
+	PurpleStoredImage *img = NULL;
 
 	if (box->account) {
 		PurplePlugin *plug = purple_find_prpl(purple_account_get_protocol_id(box->account));
 		if (plug) {
 			PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug);
 			if (prplinfo && prplinfo->icon_spec.format) {
-				char *icon = NULL;
+				gpointer data = NULL;
+				size_t len = 0;
 				if (filename)
-					icon = pidgin_convert_buddy_icon(plug, filename);
+					data = pidgin_convert_buddy_icon(plug, filename, &len);
+				img = purple_buddy_icons_set_account_icon(box->account, data, len);
+				purple_account_set_buddy_icon_path(box->account, filename);
+
 				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);
-				purple_account_set_buddy_icon(box->account, icon);
-				g_free(icon);
 			}
 		}
 	} else {
@@ -1428,17 +1443,20 @@
 				if (prplinfo != NULL &&
 				    purple_account_get_bool(account, "use-global-buddyicon", TRUE) &&
 				    prplinfo->icon_spec.format) {
-					char *icon = NULL;
+					gpointer data = NULL;
+					size_t len = 0;
 					if (filename)
-						icon = pidgin_convert_buddy_icon(plug, filename);
-					purple_account_set_buddy_icon_path(account, filename);
-					purple_account_set_buddy_icon(account, icon);
-					g_free(icon);
+						data = pidgin_convert_buddy_icon(plug, filename, &len);
+					img = purple_buddy_icons_set_account_icon(box->account, data, len);
+					purple_account_set_buddy_icon_path(box->account, filename);
+
+					purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL));
 				}
 			}
 		}
 	}
-	pidgin_status_box_set_buddy_icon(box, filename);
+
+	pidgin_status_box_set_buddy_icon(box, img);
 }
 
 static void
@@ -2010,10 +2028,14 @@
 	status_box->buddy_icon = NULL;
 	status_box->buddy_icon_hover = NULL;
 
-	if ((status_box->buddy_icon_path != NULL) &&
-			(*status_box->buddy_icon_path != '\0'))
-		status_box->buddy_icon = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path,
-				status_box->icon_size, status_box->icon_size, FALSE, NULL);
+	if (status_box->buddy_icon_img != NULL)
+	{
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+		gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(status_box->buddy_icon_img),
+		                        purple_imgstore_get_size(status_box->buddy_icon_img), NULL);
+		gdk_pixbuf_loader_close(loader, NULL);
+		status_box->buddy_icon = gdk_pixbuf_loader_get_pixbuf(loader);
+	}
 
 	if (status_box->buddy_icon == NULL)
 	{
@@ -2034,20 +2056,14 @@
 }
 
 void
-pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, const char *filename)
+pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleStoredImage *img)
 {
-	g_free(status_box->buddy_icon_path);
-	status_box->buddy_icon_path = g_strdup(filename);
+	purple_imgstore_unref(status_box->buddy_icon_img);
+	status_box->buddy_icon_img = purple_imgstore_ref(img);
 
 	pidgin_status_box_redisplay_buddy_icon(status_box);
 }
 
-const char*
-pidgin_status_box_get_buddy_icon(PidginStatusBox *box)
-{
-	return box->buddy_icon_path;
-}
-
 void
 pidgin_status_box_pulse_connecting(PidginStatusBox *status_box)
 {
--- a/pidgin/gtkstatusbox.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkstatusbox.h	Fri Apr 27 04:03:04 2007 +0000
@@ -30,6 +30,7 @@
 #include <gtk/gtk.h>
 #include "gtkimhtml.h"
 #include "account.h"
+#include "imgstore.h"
 #include "savedstatuses.h"
 #include "status.h"
 #include <gtk/gtktreemodel.h>
@@ -89,7 +90,7 @@
 	GtkWidget *vbox, *sw;
 	GtkWidget *imhtml;
 
-	char      *buddy_icon_path;
+	PurpleStoredImage *buddy_icon_img;
 	GdkPixbuf *buddy_icon;
 	GdkPixbuf *buddy_icon_hover;
 	GtkWidget *buddy_icon_sel;
@@ -176,10 +177,7 @@
 pidgin_status_box_pulse_connecting(PidginStatusBox *status_box);
 
 void
-pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, const char *filename);
-
-const char *
-pidgin_status_box_get_buddy_icon(PidginStatusBox *status_box);
+pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleStoredImage *img);
 
 char *pidgin_status_box_get_message(PidginStatusBox *status_box);
 
--- a/pidgin/gtkutils.c	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkutils.c	Fri Apr 27 04:03:04 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,12 @@
 
 			return;
 		}
-		id = purple_imgstore_add(filedata, size, data->filename);
-		g_free(filedata);
+		id = purple_imgstore_add_with_id(filedata, size, data->filename);
 
 		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;
 	}
@@ -2418,15 +2417,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)
@@ -2435,28 +2433,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)
@@ -2487,47 +2468,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
@@ -2536,6 +2491,7 @@
 		GError *error = NULL;
 		GdkPixbuf *scale;
 		gboolean success = FALSE;
+		char *filename = NULL;
 
 		g_strfreev(pixbuf_formats);
 
@@ -2543,8 +2499,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;
 		}
@@ -2567,6 +2521,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).  */
@@ -2595,29 +2560,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"),
@@ -2627,16 +2596,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
@@ -2789,10 +2757,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) {
@@ -2800,35 +2768,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)
--- a/pidgin/gtkutils.h	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/gtkutils.h	Fri Apr 27 04:03:04 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)
 /**
--- a/pidgin/pixmaps/icons/16/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/16/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -2,7 +2,7 @@
 
 EXTRA_DIST = pidgin.png 
 
-pidginiconspixdir = $(datadir)/pixmaps/pidgin/icons/16
+pidginiconspixdir = $(datadir)/icons/hicolor/16x16/apps
 
 pidginiconspix_DATA = $(EXTRA_DIST)
 
Binary file pidgin/pixmaps/icons/16/pidgin.png has changed
--- a/pidgin/pixmaps/icons/16/scalable/pidgin.svg	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/16/scalable/pidgin.svg	Fri Apr 27 04:03:04 2007 +0000
@@ -14,8 +14,8 @@
    id="svg4345"
    sodipodi:version="0.32"
    inkscape:version="0.44.1"
-   sodipodi:docbase="/home/hbons/Desktop"
-   sodipodi:docname="pidgin16.svg"
+   sodipodi:docbase="/home/hbons/Desktop/icons/16/scalable"
+   sodipodi:docname="pidgin.svg"
    inkscape:export-filename="/home/hbons/Desktop/pidgin16.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
@@ -24,15 +24,15 @@
      id="defs4347">
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient6829">
+       id="linearGradient5438">
       <stop
          style="stop-color:white;stop-opacity:1;"
          offset="0"
-         id="stop6831" />
+         id="stop5440" />
       <stop
          style="stop-color:white;stop-opacity:0;"
          offset="1"
-         id="stop6833" />
+         id="stop5442" />
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
@@ -48,18 +48,6 @@
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient6563">
-      <stop
-         style="stop-color:white;stop-opacity:1;"
-         offset="0"
-         id="stop6565" />
-      <stop
-         style="stop-color:white;stop-opacity:0;"
-         offset="1"
-         id="stop6567" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
        id="linearGradient6537">
       <stop
          style="stop-color:white;stop-opacity:1;"
@@ -217,15 +205,6 @@
        gradientUnits="userSpaceOnUse" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient6563"
-       id="linearGradient6569"
-       x1="-1.6841649"
-       y1="39.902092"
-       x2="5.5366187"
-       y2="44.272076"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
        xlink:href="#linearGradient6817"
        id="linearGradient6823"
        x1="0.23931108"
@@ -235,22 +214,13 @@
        gradientUnits="userSpaceOnUse" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient6829"
-       id="linearGradient6835"
-       x1="31.171875"
-       y1="4.5053854"
-       x2="31.171875"
-       y2="23.391998"
+       xlink:href="#linearGradient5438"
+       id="linearGradient5444"
+       x1="30.152058"
+       y1="-0.86487341"
+       x2="30.152058"
+       y2="23.011967"
        gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6829"
-       id="linearGradient6841"
-       gradientUnits="userSpaceOnUse"
-       x1="31.171875"
-       y1="4.5053854"
-       x2="31.171875"
-       y2="23.391998" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -259,17 +229,17 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="39.59798"
-     inkscape:cx="12.819877"
-     inkscape:cy="7.4283091"
+     inkscape:zoom="42.440064"
+     inkscape:cx="13.087113"
+     inkscape:cy="8.5298663"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
      inkscape:document-units="px"
      inkscape:window-width="1274"
      inkscape:window-height="966"
-     inkscape:window-x="25"
-     inkscape:window-y="155"
+     inkscape:window-x="3"
+     inkscape:window-y="25"
      showguides="true"
      inkscape:guide-bbox="true"
      inkscape:grid-points="false"
@@ -292,34 +262,110 @@
      inkscape:label="Layer 1"
      inkscape:groupmode="layer">
     <path
-       style="fill:#efefef;fill-opacity:1;stroke:#787878;stroke-width:2.12875605;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="M 20.384686,1.5 C 17.491005,1.5 15.8567,2.9713794 15.8567,5.9241464 L 15.8567,23.784841 C 15.8567,26.90776 17.392438,28.509655 20.286849,28.509655 L 31.191688,28.411202 L 31.197812,31.845969 L 37.774972,28.445938 L 42.130099,28.509655 C 44.974175,28.509655 46.5,26.986029 46.5,24.038295 L 46.5,5.9241464 C 46.5,2.9708383 44.979444,1.5 42.083367,1.5 L 20.384686,1.5 z "
-       id="rect5498"
-       sodipodi:nodetypes="cccccccccccc"
-       transform="matrix(0.45687,0,0,0.483008,-5.744456,-0.224513)" />
+       style="opacity:1;fill:#75507b;fill-opacity:1"
+       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.674297,15.15311 36.86722,15.15311 L 36.878267,14.508325 C 36.288194,13.580567 35.517363,12.502064 35.686987,12.281543 C 37.835781,12.281543 38,11.491782 38,10.027674 C 38,8.7542399 37.228501,8 36.0625,8 z "
+       id="path6521"
+       transform="matrix(1.148904,0,0,0.920169,-34.28919,-3.241212)"
+       sodipodi:nodetypes="csccccc" />
+    <path
+       style="opacity:1;fill:#82508e;fill-opacity:1;stroke:#3b1941;stroke-width:2.30209565;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 15,13 C 8.3759979,13 3,18.600001 3,25.5 C 3,31.153143 2.9750563,38.402618 2.9750563,45.572826 C 4.1625449,45.572826 27.946366,45.600605 30.637365,45.600605 C 32.751492,45.600605 32.586331,43.541005 32.586331,43.541005 C 32.586331,40.875594 27.597902,38.639057 25.813453,36.682531 C 23.985035,34.68151 26,30.884078 26,30.884078 C 26.641306,29.354278 28.01889,26.891006 28.01889,25.115922 C 28.01889,18.215923 21.624002,13 15,13 z "
+       id="path5176"
+       sodipodi:nodetypes="ccccszcsc"
+       transform="matrix(0.43939,0,0,0.42944,-0.819445,-4.082726)" />
+    <rect
+       style="opacity:1;fill:white;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect6331"
+       width="2.6154532"
+       height="1.1545694"
+       x="5.513658"
+       y="6.4806929"
+       rx="0.48532724"
+       ry="0.32910046"
+       transform="matrix(1.147027,0,0,0.866113,-0.324314,2.386999)" />
     <path
-       transform="matrix(0.798987,0,0,0.495994,21.86572,1.158205)"
-       style="fill:url(#linearGradient6569);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
-       d="M -0.99929609,42.004237 C 3.5188333,45.348102 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 6.1724645,45.375511 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
-       id="path6561"
-       sodipodi:nodetypes="czczc" />
+       sodipodi:type="arc"
+       style="opacity:1;fill:url(#radialGradient5286);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5273"
+       sodipodi:cx="15.004828"
+       sodipodi:cy="39.80859"
+       sodipodi:rx="9.7225161"
+       sodipodi:ry="7.119638"
+       d="M 24.727345 39.80859 A 9.7225161 7.119638 0 1 1  5.2823124,39.80859 A 9.7225161 7.119638 0 1 1  24.727345 39.80859 z"
+       transform="matrix(0.434788,0,0,0.500648,-1.130366,-6.547801)" />
+    <path
+       style="opacity:0.5152838;fill:#5c3466;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -42.833794,49.038847 C -42.833794,49.038847 -42.815855,44.709724 -42.815855,37.951005 C -42.815855,37.951005 -39.03087,40.509965 -38.631892,41.678764 C -39.153711,41.596001 -40.372039,41.685633 -40.372039,41.685633 C -40.038351,42.463101 -38.163796,45.952105 -36.121045,48.058218 C -37.780497,47.329365 -39.471111,47.196178 -39.471111,47.196178 C -39.009636,48.111847 -37.250115,48.69173 -36.943887,49.110993 C -36.943887,49.110993 -41.348464,49.038847 -42.833794,49.038847 z "
+       id="rect5312"
+       sodipodi:nodetypes="cccccccc"
+       transform="matrix(0.446908,0,0,0.358425,20.14277,-2.602607)" />
+    <path
+       style="opacity:0.5152838;fill:#5c3466;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -49.441852,47.4154 C -47.112614,42.001764 -42.19329,43.469796 -40.961409,37.951005 C -40.961409,37.951005 -39.025024,40.280009 -38.974466,41.581367 C -39.496285,41.498604 -39.881988,41.516405 -39.881988,41.516405 C -39.5483,42.293873 -38.196948,45.748112 -36.154197,47.854225 C -37.813649,47.125372 -38.925816,46.898039 -38.925816,46.898039 C -38.847603,47.794771 -38.376224,47.369394 -38.122982,48.215235 C -42.942294,48.046898 -46.252916,49.195571 -49.441852,47.4154 z "
+       id="path5317"
+       sodipodi:nodetypes="cccccccc"
+       transform="matrix(-0.376289,0,0,0.381853,-5.604413,-3.491724)" />
+    <path
+       transform="matrix(0.399589,0,0,0.393554,-5.973603e-2,-3.038964)"
+       style="opacity:0.31004363;fill:url(#linearGradient5310);fill-opacity:1;stroke:url(#linearGradient6512);stroke-width:2.5216887;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 15,14.0625 C 8.9877035,14.0625 4.0789961,19.13808 4.0625,25.46875 C 4.0624722,25.479427 4.0617033,25.489349 4.0625,25.5 C 4.0625,32.787473 3.9033639,38.26012 3.9033639,44.499878 C 5.8399452,44.499878 24.86993,44.470084 30.695903,44.470084 C 29.746978,42.549359 26.273076,40.711023 23.972608,38.78763 C 23.362603,37.973536 23.023697,35.459892 22.969233,34.418473 C 23.611113,31.359155 25.129532,28.401757 26.527172,25.440129 C 26.527172,19.094533 21.022436,14.0625 15,14.0625 z "
+       id="path5241"
+       sodipodi:nodetypes="csccccccc" />
     <path
-       transform="matrix(0.432919,0,0,0.460984,-4.994917,0.131452)"
-       style="fill:url(#linearGradient6841);fill-opacity:1;stroke:white;stroke-width:2.2384789;stroke-miterlimit:4;stroke-opacity:1"
-       d="M 20.375,2.96875 C 19.179613,2.96875 18.460539,3.2753711 18.03125,3.6875 C 17.601961,4.0996289 17.3125,4.7329044 17.3125,5.9375 L 17.3125,23.716146 C 17.3125,25.035452 17.612948,25.805734 18.03125,26.247396 C 18.449552,26.689058 19.104245,26.966146 20.28125,26.966146 L 32.531243,26.872396 C 33.342043,26.873285 33.542201,27.065502 33.54309,27.876302 L 33.601424,28.954617 L 37.875,27.184896 C 38.12912,27.000056 38.435773,26.901488 38.75,26.903646 L 42.15625,26.966146 C 43.316196,26.966146 43.940506,26.687615 44.34375,26.278646 C 44.746994,25.869677 45.03125,25.185491 45.03125,23.966146 L 45.03125,5.9375 C 45.03125,4.7114441 44.741003,4.0538566 44.34375,3.65625 C 43.946497,3.2586434 43.290635,2.96875 42.09375,2.96875 L 20.375,2.96875 z "
-       id="path6827"
-       sodipodi:nodetypes="csccscccccccsccscc" />
+       transform="matrix(0.626764,0,0,0.689441,-3.309086,-9.833164)"
+       style="opacity:1;fill:white;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5157"
+       sodipodi:nodetypes="csssc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5162"
+       sodipodi:cx="10.169642"
+       sodipodi:cy="24.3125"
+       sodipodi:rx="1.2410715"
+       sodipodi:ry="1.2946428"
+       d="M 11.410714 24.3125 A 1.2410715 1.2946428 0 1 1  8.928571,24.3125 A 1.2410715 1.2946428 0 1 1  11.410714 24.3125 z"
+       transform="matrix(0.805756,0,0,0.772414,-5.19425,-11.77932)" />
+    <path
+       transform="matrix(0.29055,-0.412361,-0.273947,-0.388798,13.58792,20.45445)"
+       style="opacity:1;fill:white;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5169"
+       sodipodi:nodetypes="csssc" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.7838,15.15311 36.976723,15.15311 L 36.900168,14.125 C 36.310095,13.197242 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6521"
-       transform="matrix(1,0,0,0.838795,-30.01297,-1.71036)"
-       sodipodi:nodetypes="csccssssc" />
+       sodipodi:type="arc"
+       style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5171"
+       sodipodi:cx="10.169642"
+       sodipodi:cy="24.3125"
+       sodipodi:rx="1.2410715"
+       sodipodi:ry="1.2946428"
+       d="M 11.410714 24.3125 A 1.2410715 1.2946428 0 1 1  8.928571,24.3125 A 1.2410715 1.2946428 0 1 1  11.410714 24.3125 z"
+       transform="matrix(0.805755,0,0,0.772417,0.805755,-11.77938)" />
+    <path
+       transform="matrix(0.63556,0,0,0.371992,4.635114,-6.489142)"
+       style="fill:#f9751a;fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -0.99929609,42.004237 C 2.9290204,42.117701 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 7.1161651,42.240122 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
+       id="rect5187"
+       sodipodi:nodetypes="ccczc" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.78381,15.000004 36.976733,15.000004 L 36.976733,14.048449 C 36.38666,13.120691 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6531"
-       transform="matrix(-0.999997,0,0,-0.857142,47.00104,15.85713)"
-       sodipodi:nodetypes="csccssssc" />
+       transform="matrix(0.743268,0,0,0.267357,-7.659906,-1.671861)"
+       style="fill:#3b1941;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 11.651119,11.193815 C 13.577759,8.2509126 16.249277,11.481501 17.614226,12.804399 L 14.761306,13.747613 L 11.651119,11.193815 z "
+       id="path5192"
+       sodipodi:nodetypes="cccc" />
+    <path
+       transform="matrix(0.686065,0,0,0.46633,-5.019573,-4.365629)"
+       style="fill:#3b1941;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 11.689238,9.8218679 C 13.591296,8.0161941 17.174576,11.994261 17.519594,13.650486 L 14.543472,12.891665 L 11.689238,9.8218679 z "
+       id="rect5189"
+       sodipodi:nodetypes="cccc" />
+    <path
+       transform="matrix(0.63556,0,0,0.371995,4.635112,-6.489295)"
+       style="fill:url(#linearGradient6823);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -0.99929609,42.004237 C 2.9290204,42.117701 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 7.1161651,42.240122 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
+       id="path6815"
+       sodipodi:nodetypes="ccczc" />
   </g>
 </svg>
--- a/pidgin/pixmaps/icons/24/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/24/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -2,7 +2,7 @@
 
 EXTRA_DIST = pidgin.png 
 
-pidginiconspixdir = $(datadir)/pixmaps/pidgin/icons/24
+pidginiconspixdir = $(datadir)/icons/hicolor/24x24/apps
 
 pidginiconspix_DATA = $(EXTRA_DIST)
 
Binary file pidgin/pixmaps/icons/24/pidgin.png has changed
--- a/pidgin/pixmaps/icons/24/scalable/pidgin.svg	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/24/scalable/pidgin.svg	Fri Apr 27 04:03:04 2007 +0000
@@ -14,9 +14,9 @@
    id="svg4345"
    sodipodi:version="0.32"
    inkscape:version="0.44.1"
-   sodipodi:docbase="/home/hbons/Desktop"
-   sodipodi:docname="pidgin22.svg"
-   inkscape:export-filename="/home/hbons/Desktop/pidgin32.png"
+   sodipodi:docbase="/home/hbons/Desktop/icons/24/scalable"
+   sodipodi:docname="pidgin.svg"
+   inkscape:export-filename="/home/hbons/Desktop/pidgin24-2.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
    version="1.0">
@@ -24,15 +24,15 @@
      id="defs4347">
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient6829">
+       id="linearGradient5438">
       <stop
          style="stop-color:white;stop-opacity:1;"
          offset="0"
-         id="stop6831" />
+         id="stop5440" />
       <stop
          style="stop-color:white;stop-opacity:0;"
          offset="1"
-         id="stop6833" />
+         id="stop5442" />
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
@@ -48,18 +48,6 @@
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient6563">
-      <stop
-         style="stop-color:white;stop-opacity:1;"
-         offset="0"
-         id="stop6565" />
-      <stop
-         style="stop-color:white;stop-opacity:0;"
-         offset="1"
-         id="stop6567" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
        id="linearGradient6537">
       <stop
          style="stop-color:white;stop-opacity:1;"
@@ -217,15 +205,6 @@
        gradientUnits="userSpaceOnUse" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient6563"
-       id="linearGradient6569"
-       x1="-1.6841649"
-       y1="39.902092"
-       x2="5.5366187"
-       y2="44.272076"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
        xlink:href="#linearGradient6817"
        id="linearGradient6823"
        x1="0.23931108"
@@ -235,12 +214,12 @@
        gradientUnits="userSpaceOnUse" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient6829"
-       id="linearGradient6835"
-       x1="31.171875"
-       y1="4.5053854"
-       x2="31.171875"
-       y2="23.391998"
+       xlink:href="#linearGradient5438"
+       id="linearGradient5444"
+       x1="30.152058"
+       y1="-0.86487341"
+       x2="30.152058"
+       y2="23.011967"
        gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
@@ -250,9 +229,9 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="16.081777"
-     inkscape:cx="24.252631"
-     inkscape:cy="13.684805"
+     inkscape:zoom="26.124924"
+     inkscape:cx="19.621039"
+     inkscape:cy="12.735051"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
@@ -282,55 +261,137 @@
      id="layer1"
      inkscape:label="Layer 1"
      inkscape:groupmode="layer">
-    <rect
-       style="opacity:0.16157205;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect6837"
-       width="13.818222"
-       height="3.7703342"
-       x="3.559819"
-       y="20"
-       rx="1.8851671"
-       ry="1.8851671"
-       transform="matrix(1.592101,0,0,1.060911,-4.667591,-4.218209)" />
+    <path
+       style="fill:#efefef;fill-opacity:1;stroke:#787878;stroke-width:2.68207097;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 17.892492,1.5 C 14.553935,1.5 11.861242,4.611235 11.861242,8.46875 L 11.861242,20.53125 C 11.861242,24.388765 14.790157,28.553306 18.128714,28.553306 L 33.206307,28.454853 L 33.212431,35.359517 L 40.550287,28.59293 C 46.237519,28.59293 46.5,25.478162 46.5,20.728157 L 46.5,8.46875 C 46.5,4.6112353 43.807307,1.5 40.46875,1.5 L 17.892492,1.5 z "
+       id="path4547"
+       sodipodi:nodetypes="ccccccccccc"
+       transform="matrix(0.375302,0,0,0.370406,6.048447,-5.560909e-2)" />
     <path
        sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.0057179"
-       inkscape:original="M 20.53125 1.5 C 17.192693 1.5 14.5 4.611235 14.5 8.46875 L 14.5 20.53125 C 14.5 24.388765 17.192693 27.5 20.53125 27.5 L 34.53125 27.5 L 34.53125 31.09375 L 39.34375 27.5 L 41.5 27.5 C 45.512737 27.5 46.5 24.38319 46.5 20.53125 L 46.5 8.46875 C 46.5 4.6112353 43.807307 1.5 40.46875 1.5 L 20.53125 1.5 z "
-       style="fill:none;fill-opacity:1;stroke:white;stroke-width:1.47727883;stroke-miterlimit:4;stroke-opacity:1"
-       id="path6504"
-       d="M 20.53125,2.5 C 17.817519,2.5 15.5,5.0792547 15.5,8.46875 L 15.5,20.53125 C 15.5,23.920745 17.817519,26.5 20.53125,26.5 L 34.53125,26.5 C 35.082213,26.503178 35.528072,26.949037 35.53125,27.5 L 35.53125,29.09375 L 38.75,26.6875 C 38.923102,26.563771 39.130983,26.498125 39.34375,26.5 L 41.5,26.5 C 43.245978,26.5 44.034662,25.931926 44.625,24.9375 C 45.215338,23.943074 45.5,22.371297 45.5,20.53125 L 45.5,8.46875 C 45.5,5.079255 43.182481,2.5 40.46875,2.5 L 20.53125,2.5 z "
-       transform="matrix(0.67692,0,0,0.67692,-8.838503,0.484647)" />
+       inkscape:radius="-2.6972473"
+       inkscape:original="M 17.90625 1.5 C 14.567693 1.5 11.875 4.611235 11.875 8.46875 L 11.875 20.53125 C 11.875 24.388765 14.786443 28.5625 18.125 28.5625 L 33.21875 28.46875 L 33.21875 35.375 L 40.5625 28.59375 C 46.249734 28.59375 46.5 25.468755 46.5 20.71875 L 46.5 8.46875 C 46.5 4.6112353 43.807307 1.5 40.46875 1.5 L 17.90625 1.5 z "
+       style="fill:url(#linearGradient5444);fill-opacity:1.0;stroke:white;stroke-width:2.68207097;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5436"
+       d="M 17.90625,4.1875 C 16.243421,4.1875 14.5625,5.8664228 14.5625,8.46875 L 14.5625,20.53125 C 14.5625,21.781429 15.113741,23.319209 15.90625,24.375 C 16.698759,25.430791 17.57972,25.875 18.125,25.875 L 33.1875,25.78125 C 33.904865,25.775598 34.594934,26.055928 35.105127,26.560257 C 35.61532,27.064586 35.903608,27.751368 35.90625,28.46875 L 35.90625,29.21875 L 38.71875,26.625 C 39.22009,26.160499 39.879056,25.903614 40.5625,25.90625 C 42.912093,25.90625 43.047945,25.595299 43.3125,25.125 C 43.577055,24.654701 43.8125,23.046382 43.8125,20.71875 L 43.8125,8.46875 C 43.8125,5.8664232 42.131579,4.1875 40.46875,4.1875 L 17.90625,4.1875 z "
+       transform="matrix(0.375302,0,0,0.370406,6.048447,-5.560909e-2)" />
+    <path
+       style="opacity:1;fill:#75507b;fill-opacity:1"
+       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.674297,15.15311 36.86722,15.15311 L 36.878267,14.508325 C 36.288194,13.580567 35.517363,12.502064 35.686987,12.281543 C 37.835781,12.281543 38,11.491782 38,10.027674 C 38,8.7542399 37.228501,8 36.0625,8 z "
+       id="path6521"
+       transform="matrix(1.747787,0,0,1.397993,-52.41719,-5.183942)"
+       sodipodi:nodetypes="csccccc" />
+    <path
+       style="opacity:1;fill:#82508e;fill-opacity:1;stroke:#3b1941;stroke-width:1.51128328;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 15,13 C 8.3759979,13 3,18.600001 3,25.5 C 3,31.153143 2.9750563,38.402618 2.9750563,45.572826 C 4.1625449,45.572826 27.946366,45.600605 30.637365,45.600605 C 32.751492,45.600605 32.586331,43.541005 32.586331,43.541005 C 32.586331,40.875594 27.597902,38.639057 25.813453,36.682531 C 23.985035,34.68151 26,30.5 26,30.5 C 26.641306,28.9702 27,27.275084 27,25.5 C 27,18.600001 21.624002,13 15,13 z "
+       id="path5176"
+       sodipodi:nodetypes="ccccszcsc"
+       transform="matrix(0.67692,0,0,0.646801,-1.52611,-5.949693)" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:url(#radialGradient5286);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5273"
+       sodipodi:cx="15.004828"
+       sodipodi:cy="39.80859"
+       sodipodi:rx="9.7225161"
+       sodipodi:ry="7.119638"
+       d="M 24.727345 39.80859 A 9.7225161 7.119638 0 1 1  5.2823124,39.80859 A 9.7225161 7.119638 0 1 1  24.727345 39.80859 z"
+       transform="matrix(0.661428,0,0,0.760624,-1.973841,-10.20757)" />
     <path
-       style="fill:#efefef;fill-opacity:1;stroke:#787878;stroke-width:1.46586692;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="M 20.384686,1.5 C 17.491005,1.5 15.8567,2.9713794 15.8567,5.9241464 L 15.8567,23.415134 C 15.8567,26.538053 17.392438,28.139948 20.286849,28.139948 L 33.409288,28.041495 L 33.415412,31.845969 L 38.723454,28.076231 L 42.130099,28.139948 C 44.974175,28.139948 46.5,26.616322 46.5,23.668588 L 46.5,5.9241464 C 46.5,2.9708383 44.979444,1.5 42.083367,1.5 L 20.384686,1.5 z "
-       id="rect5498"
-       sodipodi:nodetypes="cccccccccccc"
-       transform="matrix(0.687501,0,0,0.67692,-9.468796,0.48462)" />
+       style="opacity:0.5152838;fill:#5c3466;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -42.833794,49.038847 C -42.833794,49.038847 -42.815855,44.709724 -42.815855,37.951005 C -42.815855,37.951005 -39.03087,40.509965 -38.631892,41.678764 C -39.153711,41.596001 -40.372039,41.685633 -40.372039,41.685633 C -40.038351,42.463101 -38.163796,45.952105 -36.121045,48.058218 C -37.780497,47.329365 -39.471111,47.196178 -39.471111,47.196178 C -39.009636,48.111847 -37.250115,48.69173 -36.943887,49.110993 C -36.943887,49.110993 -41.348464,49.038847 -42.833794,49.038847 z "
+       id="rect5312"
+       sodipodi:nodetypes="cccccccc"
+       transform="matrix(0.799568,0,0,0.627241,35.24855,-7.804428)" />
+    <path
+       style="opacity:0.5152838;fill:#5c3466;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -49.441852,47.4154 C -48.364983,40.273995 -40.690447,44.765623 -40.961409,37.951005 C -40.961409,37.951005 -39.025024,40.280009 -38.974466,41.581367 C -39.496285,41.498604 -39.881988,41.516405 -39.881988,41.516405 C -39.5483,42.293873 -38.196948,45.748112 -36.154197,47.854225 C -37.813649,47.125372 -38.925816,46.898039 -38.925816,46.898039 C -38.847603,47.794771 -38.376224,47.369394 -38.122982,48.215235 C -42.942294,48.046898 -46.252916,49.195571 -49.441852,47.4154 z "
+       id="path5317"
+       sodipodi:nodetypes="cccccccc"
+       transform="matrix(-0.602064,0,0,0.572779,-9.767144,-4.737575)" />
     <path
-       transform="matrix(0.798987,0,0,0.495994,22.79842,2.215302)"
-       style="fill:url(#linearGradient6569);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
-       d="M -0.99929609,42.004237 C 3.5188333,45.348102 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 6.1724645,45.375511 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
-       id="path6561"
-       sodipodi:nodetypes="czczc" />
+       transform="matrix(0.660903,0,0,0.627207,-1.258953,-5.361383)"
+       style="opacity:0.31004363;fill:url(#linearGradient5310);fill-opacity:1;stroke:url(#linearGradient6512);stroke-width:1.55319395;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="M 15,14.0625 C 8.9877035,14.0625 4.0789961,19.13808 4.0625,25.46875 C 4.0624722,25.479427 4.0617033,25.489349 4.0625,25.5 C 4.0625,32.787473 3.9033639,38.26012 3.9033639,44.499878 C 5.8399452,44.499878 22.452275,44.470084 28.278248,44.470084 C 29.445455,44.470084 31.431654,44.974157 31.431654,43.509594 C 31.431654,43.287851 31.231903,42.870917 30.681654,42.353344 C 30.131405,41.835771 29.308414,41.280003 28.400404,40.728344 C 26.665321,39.858723 25.411769,39.090553 24.621247,37.290844 C 24.011242,36.47675 23.731303,35.519763 23.676839,34.478344 C 23.622375,33.436925 24.107721,32.319635 24.224561,31.259594 C 24.458241,29.139511 24.96875,30.28125 24.96875,30.28125 C 24.98374,30.216952 25.004663,30.154183 25.03125,30.09375 C 25.618731,28.692346 25.9375,27.131297 25.9375,25.5 C 25.9375,19.154404 21.022436,14.0625 15,14.0625 z "
+       id="path5241"
+       sodipodi:nodetypes="cscccssccsscssc" />
+    <path
+       transform="matrix(-0.861857,-0.809791,0.812609,-0.76352,-5.667036,37.33378)"
+       style="opacity:1;fill:#a46bb0;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5160"
+       sodipodi:nodetypes="csssc" />
+    <path
+       transform="matrix(1.010846,0,0,1.029732,-5.552971,-15.17001)"
+       style="opacity:1;fill:white;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5157"
+       sodipodi:nodetypes="csssc" />
     <path
-       sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.4702154"
-       inkscape:original="M 20.375 1.5 C 17.481319 1.5 15.84375 2.984733 15.84375 5.9375 L 15.84375 23.40625 C 15.84375 26.529169 17.386839 28.125 20.28125 28.125 L 33.40625 28.03125 L 33.40625 31.84375 L 38.71875 28.0625 L 42.125 28.125 C 44.969075 28.125 46.5 26.603984 46.5 23.65625 L 46.5 5.9375 C 46.5 2.9841921 44.989827 1.5 42.09375 1.5 L 20.375 1.5 z "
-       style="fill:url(#linearGradient6835);fill-opacity:1.0;stroke:white;stroke-width:1.46586692;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path6827"
-       d="M 20.375,2.96875 C 19.179613,2.96875 18.460539,3.2753711 18.03125,3.6875 C 17.601961,4.0996289 17.3125,4.7329044 17.3125,5.9375 L 17.3125,23.40625 C 17.3125,24.725556 17.612948,25.495838 18.03125,25.9375 C 18.449552,26.379162 19.104245,26.65625 20.28125,26.65625 L 33.40625,26.5625 C 34.21705,26.563389 34.874111,27.22045 34.875,28.03125 L 34.875,29 L 37.875,26.875 C 38.12912,26.69016 38.435773,26.591592 38.75,26.59375 L 42.15625,26.65625 C 43.316196,26.65625 43.940506,26.377719 44.34375,25.96875 C 44.746994,25.559781 45.03125,24.875595 45.03125,23.65625 L 45.03125,5.9375 C 45.03125,4.7114441 44.741003,4.0538566 44.34375,3.65625 C 43.946497,3.2586434 43.290635,2.96875 42.09375,2.96875 L 20.375,2.96875 z "
-       transform="matrix(0.687501,0,0,0.67692,-9.468796,0.48462)" />
+       sodipodi:type="arc"
+       style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5162"
+       sodipodi:cx="10.169642"
+       sodipodi:cy="24.3125"
+       sodipodi:rx="1.2410715"
+       sodipodi:ry="1.2946428"
+       d="M 11.410714 24.3125 A 1.2410715 1.2946428 0 1 1  8.928571,24.3125 A 1.2410715 1.2946428 0 1 1  11.410714 24.3125 z"
+       transform="matrix(0.805757,0,0,0.772415,-3.184731,-8.820615)" />
+    <path
+       transform="matrix(0.596326,-0.813274,-0.562251,-0.766804,22.42583,37.43862)"
+       style="opacity:1;fill:#975fa3;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5167"
+       sodipodi:nodetypes="csssc" />
+    <path
+       transform="matrix(0.447398,-0.672135,-0.421833,-0.633728,21.07418,32.19024)"
+       style="opacity:1;fill:white;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 13.257119,24.626053 C 13.257119,26.227351 11.8279,27.316574 10.863875,27.316574 C 9.5428049,27.316574 8.4706318,26.01697 8.4706318,24.415672 C 8.4706318,23.024119 9.2055946,21.514771 10.526665,21.514771 C 11.847736,21.514771 13.257119,23.360971 13.257119,24.626053 z "
+       id="path5169"
+       sodipodi:nodetypes="csssc" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.7838,15.15311 36.976723,15.15311 L 36.900168,14.125 C 36.310095,13.197242 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6521"
-       transform="translate(-26.00114,-0.15311)"
-       sodipodi:nodetypes="csccssssc" />
+       sodipodi:type="arc"
+       style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path5171"
+       sodipodi:cx="10.169642"
+       sodipodi:cy="24.3125"
+       sodipodi:rx="1.2410715"
+       sodipodi:ry="1.2946428"
+       d="M 11.410714 24.3125 A 1.2410715 1.2946428 0 1 1  8.928571,24.3125 A 1.2410715 1.2946428 0 1 1  11.410714 24.3125 z"
+       transform="matrix(0.805756,0,0,0.784291,6.815275,-9.124735)" />
+    <rect
+       style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect5215"
+       width="5.6071429"
+       height="2.1785715"
+       x="16"
+       y="27"
+       transform="matrix(0.724348,0,0,0.459012,-2.641559,0.565398)"
+       rx="0.87366539"
+       ry="1.0892857" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.78381,15.000004 36.976733,15.000004 L 36.976733,14.048449 C 36.38666,13.120691 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6531"
-       transform="matrix(-0.999997,0,0,-0.999999,51.00101,19.84688)"
-       sodipodi:nodetypes="csccssssc" />
+       transform="matrix(0.798987,0,0,0.495994,7.007995,-6.360384)"
+       style="fill:#f9751a;fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -0.99929609,42.004237 C 2.9290204,42.117701 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 7.1161651,42.240122 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
+       id="rect5187"
+       sodipodi:nodetypes="ccczc" />
+    <path
+       transform="matrix(0.670792,0,0,0.486348,-3.805943,-3.90166)"
+       style="fill:#3b1941;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 11.651119,11.193815 C 13.577759,8.2509126 16.249277,11.481501 17.614226,12.804399 L 14.761306,13.747613 L 11.651119,11.193815 z "
+       id="path5192"
+       sodipodi:nodetypes="cccc" />
+    <path
+       transform="matrix(0.851014,0,0,0.554879,-3.938174,-5.194599)"
+       style="fill:#3b1941;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 11.689238,9.8218679 C 13.591296,8.0161941 17.174576,11.994261 17.519594,13.650486 L 14.543472,12.891665 L 11.689238,9.8218679 z "
+       id="rect5189"
+       sodipodi:nodetypes="cccc" />
+    <path
+       transform="matrix(0.798987,0,0,0.495994,7.007995,-6.360384)"
+       style="fill:url(#linearGradient6823);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
+       d="M -0.99929609,42.004237 C 2.9290204,42.117701 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 7.1161651,42.240122 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
+       id="path6815"
+       sodipodi:nodetypes="ccczc" />
   </g>
 </svg>
--- a/pidgin/pixmaps/icons/32/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/32/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -2,7 +2,7 @@
 
 EXTRA_DIST = pidgin.png 
 
-pidginiconspixdir = $(datadir)/pixmaps/pidgin/icons/32
+pidginiconspixdir = $(datadir)/icons/hicolor/32x32/apps
 
 pidginiconspix_DATA = $(EXTRA_DIST)
 
Binary file pidgin/pixmaps/icons/32/pidgin.png has changed
--- a/pidgin/pixmaps/icons/32/scalable/pidgin.svg	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/32/scalable/pidgin.svg	Fri Apr 27 04:03:04 2007 +0000
@@ -14,8 +14,8 @@
    id="svg4345"
    sodipodi:version="0.32"
    inkscape:version="0.44.1"
-   sodipodi:docbase="/home/hbons/Desktop"
-   sodipodi:docname="pidgin32.svg"
+   sodipodi:docbase="/home/hbons/Desktop/icons/32/scalable"
+   sodipodi:docname="pidgin.svg"
    inkscape:export-filename="/home/hbons/Desktop/pidgin32.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
@@ -231,7 +231,7 @@
      inkscape:pageshadow="2"
      inkscape:zoom="21.220032"
      inkscape:cx="25.558911"
-     inkscape:cy="16.962156"
+     inkscape:cy="16.938593"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
@@ -404,18 +404,6 @@
        id="path6561"
        sodipodi:nodetypes="czczc" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.209636,15 36.402559,15 L 36.402559,14.125 C 35.812486,13.197242 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6521"
-       transform="translate(-14.00114,-2)"
-       sodipodi:nodetypes="csccssssc" />
-    <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.209644,15.000004 36.402567,15.000004 L 36.402567,14.125004 C 35.812494,13.197246 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6531"
-       transform="matrix(-0.999997,0,0,-0.999999,63.00101,17.99999)"
-       sodipodi:nodetypes="csccssssc" />
-    <path
        transform="matrix(0.798987,0,0,0.495994,8.99846,-1.319104)"
        style="fill:url(#linearGradient6823);fill-opacity:1.0;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
        d="M -0.99929609,42.004237 C 2.9290204,42.117701 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 7.1161651,42.240122 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
--- a/pidgin/pixmaps/icons/48/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/48/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -2,7 +2,7 @@
 
 EXTRA_DIST = pidgin.png 
 
-pidginiconspixdir = $(datadir)/pixmaps/pidgin/icons/48
+pidginiconspixdir = $(datadir)/icons/hicolor/48x48/apps
 
 pidginiconspix_DATA = $(EXTRA_DIST)
 
Binary file pidgin/pixmaps/icons/48/pidgin.png has changed
--- a/pidgin/pixmaps/icons/48/scalable/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/48/scalable/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,1 +1,6 @@
-EXTRA_DIST = 	pidgin.svg
+EXTRA_DIST = pidgin.svg 
+
+pidginiconspixdir = $(datadir)/icons/hicolor/scalable/apps
+
+pidginiconspix_DATA = $(EXTRA_DIST)
+
--- a/pidgin/pixmaps/icons/48/scalable/pidgin.svg	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/48/scalable/pidgin.svg	Fri Apr 27 04:03:04 2007 +0000
@@ -14,7 +14,7 @@
    id="svg4345"
    sodipodi:version="0.32"
    inkscape:version="0.44.1"
-   sodipodi:docbase="/home/hbons/Desktop"
+   sodipodi:docbase="/home/hbons/Desktop/icons/48/scalable"
    sodipodi:docname="pidgin.svg"
    inkscape:export-filename="/home/hbons/Desktop/pidgin.png"
    inkscape:export-xdpi="90"
@@ -239,7 +239,7 @@
      inkscape:pageshadow="2"
      inkscape:zoom="1.1545694"
      inkscape:cx="31.681369"
-     inkscape:cy="19.034466"
+     inkscape:cy="18.601404"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
@@ -422,18 +422,6 @@
        id="rect5189"
        sodipodi:nodetypes="cccc" />
     <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.209636,15 36.402559,15 L 36.402559,14.125 C 35.812486,13.197242 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6521"
-       transform="matrix(1.249263,0,0,1.142857,-10.47637,-1.142855)"
-       sodipodi:nodetypes="csccssssc" />
-    <path
-       style="opacity:1;fill:#a46aaf;fill-opacity:1"
-       d="M 36.0625,8 C 33.978257,8 33.334928,10.523086 34.78125,12.9375 C 35.250389,13.720651 36.209644,15.000004 36.402567,15.000004 L 36.402567,14.125004 C 35.812494,13.197246 35.955376,12.283021 36.125,12.0625 C 36.202572,11.961654 36.363163,11.875 36.5,11.875 C 36.8422,11.875 37.464321,11.409445 37.78125,10.90625 C 38.00166,10.556303 38.037627,10.361453 38,9.78125 C 37.924562,8.6180502 37.228501,8 36.0625,8 z "
-       id="path6531"
-       transform="matrix(-1.249263,0,0,-1.142857,85.47638,22.14286)"
-       sodipodi:nodetypes="csccssssc" />
-    <path
        transform="matrix(1.180326,0,0,0.867993,13.1795,-6.80859)"
        style="fill:url(#linearGradient6569);fill-opacity:1.0;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
        d="M -0.99929609,42.004237 C 3.5188333,45.348102 4.1278823,38.950282 4.9828709,38.950282 C 5.8260957,38.950282 6.1724645,45.375511 10.014606,42.007796 C 9.2074596,43.955072 6.7265914,47.014858 5.6807127,47.014858 C 4.6230593,47.014858 0.31231911,44.880662 -0.99929609,42.004237 z "
--- a/pidgin/pixmaps/icons/Makefile.mingw	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/icons/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -7,8 +7,11 @@
 include ./Makefile.am
 
 install:
+
+install_real:
 	if test '$(SUBDIRS)'; then \
 	  list='$(SUBDIRS)'; for subdir in $$list; do \
 	    $(MAKE) -C $$subdir -f Makefile.mingw install || exit 1; \
 	  done; \
 	fi;
+
Binary file pidgin/pixmaps/pidgin.ico has changed
--- a/pidgin/pixmaps/status/16/Makefile.mingw	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/16/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -18,3 +18,5 @@
 	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
 	fi;
 
+	$(MAKE) -C rtl -f Makefile.mingw install || exit 1; \
+
--- a/pidgin/pixmaps/status/16/rtl/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/16/rtl/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,4 +1,4 @@
-EXTRA_DIST = 	chat.png extended-away.png 
+EXTRA_DIST = 	extended-away.png 
 
 pidginstatuspixdir = $(datadir)/pixmaps/pidgin/status/16/rtl
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/16/rtl/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -0,0 +1,20 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
+#
+
+PIDGIN_TREE_TOP := ../../../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir = $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+
+.PHONY: install
+
+install:
+	if test '$(pidginstatuspix_DATA)'; then \
+	  mkdir -p $(pidginstatuspixdir); \
+	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
+	fi;
+
Binary file pidgin/pixmaps/status/16/rtl/chat.png has changed
Binary file pidgin/pixmaps/status/16/rtl/extended-away.png has changed
--- a/pidgin/pixmaps/status/22/Makefile.mingw	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/22/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -18,3 +18,5 @@
 	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
 	fi;
 
+	$(MAKE) -C rtl -f Makefile.mingw install || exit 1; \
+
--- a/pidgin/pixmaps/status/22/rtl/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/22/rtl/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,4 +1,4 @@
-EXTRA_DIST = 	chat.png extended-away.png
+EXTRA_DIST = 	extended-away.png
 
 pidginstatuspixdir = $(datadir)/pixmaps/pidgin/status/22/rtl
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/22/rtl/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -0,0 +1,20 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
+#
+
+PIDGIN_TREE_TOP := ../../../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir = $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+
+.PHONY: install
+
+install:
+	if test '$(pidginstatuspix_DATA)'; then \
+	  mkdir -p $(pidginstatuspixdir); \
+	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
+	fi;
+
Binary file pidgin/pixmaps/status/22/rtl/chat.png has changed
--- a/pidgin/pixmaps/status/32/Makefile.mingw	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/32/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -18,3 +18,5 @@
 	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
 	fi;
 
+	$(MAKE) -C rtl -f Makefile.mingw install || exit 1; \
+
--- a/pidgin/pixmaps/status/32/rtl/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/32/rtl/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,4 +1,4 @@
-EXTRA_DIST = 	chat.png extended-away.png
+EXTRA_DIST = 	extended-away.png
 
 pidginstatuspixdir = $(datadir)/pixmaps/pidgin/status/32/rtl
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/32/rtl/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -0,0 +1,20 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
+#
+
+PIDGIN_TREE_TOP := ../../../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir = $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+
+.PHONY: install
+
+install:
+	if test '$(pidginstatuspix_DATA)'; then \
+	  mkdir -p $(pidginstatuspixdir); \
+	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
+	fi;
+
Binary file pidgin/pixmaps/status/32/rtl/chat.png has changed
--- a/pidgin/pixmaps/status/48/Makefile.mingw	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/48/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -18,3 +18,5 @@
 	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
 	fi;
 
+	$(MAKE) -C rtl -f Makefile.mingw install || exit 1; \
+
--- a/pidgin/pixmaps/status/48/rtl/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/48/rtl/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -1,4 +1,4 @@
-EXTRA_DIST = 	chat.png extended-away.png
+EXTRA_DIST = 	extended-away.png
 
 pidginstatuspixdir = $(datadir)/pixmaps/pidgin/status/48/rtl
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/status/48/rtl/Makefile.mingw	Fri Apr 27 04:03:04 2007 +0000
@@ -0,0 +1,20 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of Pidgin pixmaps
+#
+
+PIDGIN_TREE_TOP := ../../../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+datadir = $(PIDGIN_INSTALL_DIR)
+include ./Makefile.am
+
+.PHONY: install
+
+install:
+	if test '$(pidginstatuspix_DATA)'; then \
+	  mkdir -p $(pidginstatuspixdir); \
+	  cp $(pidginstatuspix_DATA) $(pidginstatuspixdir); \
+	fi;
+
Binary file pidgin/pixmaps/status/48/rtl/chat.png has changed
--- a/pidgin/pixmaps/status/Makefile.am	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/pixmaps/status/Makefile.am	Fri Apr 27 04:03:04 2007 +0000
@@ -3,7 +3,11 @@
 EXTRA_DIST = \
 	Makefile.mingw \
 	16/Makefile.mingw \
+	16/rtl/Makefile.mingw \
 	22/Makefile.mingw \
+	22/rtl/Makefile.mingw \
 	32/Makefile.mingw \
-	48/Makefile.mingw
+	32/rtl/Makefile.mingw \
+	48/Makefile.mingw \
+	48/rtl/Makefile.mingw
 
--- a/pidgin/plugins/perl/common/GtkStatusBox.xs	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkStatusBox.xs	Fri Apr 27 04:03:04 2007 +0000
@@ -37,13 +37,9 @@
 	Pidgin::StatusBox status_box
 
 void
-pidgin_status_box_set_buddy_icon(status_box, filename)
+pidgin_status_box_set_buddy_icon(status_box, img)
 	Pidgin::StatusBox status_box
-	const char * filename
-
-const char *
-pidgin_status_box_get_buddy_icon(status_box)
-	Pidgin::StatusBox status_box
+	Purple::StoredImage img
 
 gchar_own *
 pidgin_status_box_get_message(status_box)
--- a/pidgin/plugins/perl/common/GtkUtils.xs	Fri Apr 27 04:02:51 2007 +0000
+++ b/pidgin/plugins/perl/common/GtkUtils.xs	Fri Apr 27 04:03:04 2007 +0000
@@ -11,6 +11,7 @@
 pidgin_load_accels()
 
 gchar_own *
-pidgin_convert_buddy_icon(plugin, path)
+pidgin_convert_buddy_icon(plugin, path, size)
 	Purple::Plugin plugin
 	const char * path
+	size_t *size
Binary file pidgin/win32/nsis/pixmaps/pidgin-header.bmp has changed
Binary file pidgin/win32/nsis/pixmaps/pidgin-install.ico has changed
Binary file pidgin/win32/nsis/pixmaps/pidgin-intro.bmp has changed