libpurple/buddyicon.c

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

mercurial