changeset 16381:72dc611f3257

Fix the custom icon stuff, and various memory leaks. At this point, custom icons work properly, EXCEPT FOR A SERIOUS LEAK which I haven't fixed yet because I'm seeing weird memory allocation issues. Once I deal with that, it's just the account icon stuff that's left.
author Richard Laager <rlaager@wiktel.com>
date Tue, 24 Apr 2007 17:00:22 +0000
parents 6c97924af83b
children 5f6abf3a5369
files libpurple/buddyicon.c libpurple/imgstore.c pidgin/gtkaccount.c pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkmain.c pidgin/gtkstatusbox.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 9 files changed, 142 insertions(+), 152 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/buddyicon.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/libpurple/buddyicon.c	Tue Apr 24 17:00:22 2007 +0000
@@ -567,6 +567,7 @@
 				icon->ref_count = 0;
 				icon->img = NULL;
 				purple_buddy_icon_set_data(icon, data, len);
+				g_free(data);
 			}
 			g_free(path);
 		}
@@ -614,6 +615,7 @@
 	{
 		g_free(path);
 		img = purple_buddy_icon_data_new(data, len, custom_icon_file);
+		g_free(data);
 		g_hash_table_insert(custom_icon_cache, contact, img);
 		return img;
 	}
@@ -629,6 +631,7 @@
 	PurpleStoredImage *old_img;
 	PurpleStoredImage *img = NULL;
 	char *old_icon;
+	PurpleBlistNode *child;
 
 	old_img = g_hash_table_lookup(custom_icon_cache, contact);
 
@@ -653,8 +656,27 @@
 	unref_filename(old_icon);
 	g_free(old_icon);
 
+	g_hash_table_insert(custom_icon_cache, contact, img);
 
-	g_hash_table_insert(custom_icon_cache, contact, img);
+	for (child = contact->node.child ; child ; child = child->next)
+	{
+		PurpleBuddy *buddy;
+		PurpleConversation *conv;
+
+		if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+			continue;
+
+		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);
+	}
+
 	purple_imgstore_unref(old_img);
 }
 
@@ -787,11 +809,13 @@
 				}
 				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");
 					}
+					g_free(path);
 					ref_filename(filename);
 				}
 			}
@@ -811,11 +835,13 @@
 				}
 				else
 				{
-					if (!g_file_test(filename, G_FILE_TEST_EXISTS))
+					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");
 					}
+					g_free(path);
 					ref_filename(filename);
 				}
 			}
--- a/libpurple/imgstore.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/libpurple/imgstore.c	Tue Apr 24 17:00:22 2007 +0000
@@ -55,7 +55,7 @@
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(size > 0, 0);
 
-	img = g_slice_new(PurpleStoredImage);
+	img = g_new(PurpleStoredImage, 1);
 	img->data = g_memdup(data, size);
 	img->size = size;
 	img->filename = g_strdup(filename);
@@ -146,10 +146,12 @@
 	{
 		purple_signal_emit(purple_imgstore_get_handle(),
 		                   "image-deleting", img);
-
 		if (img->id)
 			g_hash_table_remove(imgstore, GINT_TO_POINTER(img->id));
-		g_slice_free(PurpleStoredImage, img);
+
+		g_free(img->data);
+		g_free(img->filename);
+		g_free(img);
 	}
 
 	return img;
--- a/pidgin/gtkaccount.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkaccount.c	Tue Apr 24 17:00:22 2007 +0000
@@ -302,8 +302,10 @@
 
 	dialog = data;
 
+#if 0
 	if (filename != NULL)
 		set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, filename), g_strdup(filename));
+#endif
 
 	dialog->icon_filesel = NULL;
 }
@@ -342,7 +344,9 @@
 			}
 			if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n')))
 				*rtmp = '\0';
+#if 0
 			set_dialog_icon(dialog, pidgin_convert_buddy_icon(dialog->plugin, tmp), g_strdup(tmp));
+#endif
 			g_free(tmp);
 		}
 		gtk_drag_finish(dc, TRUE, FALSE, t);
@@ -1159,10 +1163,12 @@
 		else if (purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon") && icon_change)
 		{
 			const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
+#if 0
 			char *icon = pidgin_convert_buddy_icon(dialog->plugin, filename);
 			purple_account_set_buddy_icon_path(account, filename);
 			purple_account_set_buddy_icon(account, icon);
 			g_free(icon);
+#endif
 		}
 	}
 
--- a/pidgin/gtkblist.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkblist.c	Tue Apr 24 17:00:22 2007 +0000
@@ -2204,6 +2204,8 @@
 				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;
 		}
 	}
 
@@ -2213,14 +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));
 
+	// TODO: FIX THIS!!!
+	//purple_imgstore_unref(custom_img);
+
 	if (buf) {
 		int orig_width, orig_height;
 		int scale_width, scale_height;
--- a/pidgin/gtkconv.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Apr 24 17:00:22 2007 +0000
@@ -2482,23 +2482,6 @@
 	fclose(fp);
 }
 
-static const char *
-custom_icon_pref_name(PidginConversation *gtkconv)
-{
-	PurpleConversation *conv;
-	PurpleAccount *account;
-	PurpleBuddy *buddy;
-
-	conv = gtkconv->active_conv;
-	account = purple_conversation_get_account(conv);
-	buddy = purple_find_buddy(account, purple_conversation_get_name(conv));
-	if (buddy) {
-		PurpleContact *contact = purple_buddy_get_contact(buddy);
-		return purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon");
-	}
-	return NULL;
-}
-
 static void
 custom_icon_sel_cb(const char *filename, gpointer data)
 {
@@ -2574,7 +2557,8 @@
 icon_menu(GtkObject *obj, GdkEventButton *e, PidginConversation *gtkconv)
 {
 	static GtkWidget *menu = NULL;
-	const char *pref;
+	PurpleConversation *conv;
+	PurpleBuddy *buddy;
 
 	if (e->button != 3 || e->type != GDK_BUTTON_PRESS)
 		return FALSE;
@@ -2608,11 +2592,18 @@
 							 0, 0, NULL);
 
 	/* Is there a custom icon for this person? */
-	pref = custom_icon_pref_name(gtkconv);
-	if (pref && *pref) {
-		pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL,
-							G_CALLBACK(remove_custom_icon_cb), gtkconv,
-							0, 0, NULL);
+	conv = gtkconv->active_conv;
+	buddy = purple_find_buddy(purple_conversation_get_account(conv),
+	                          purple_conversation_get_name(conv));
+	if (buddy)
+	{
+		PurpleContact *contact = purple_buddy_get_contact(buddy);
+		if (contact && purple_buddy_icons_has_custom_icon(contact))
+		{
+			pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL,
+			                           G_CALLBACK(remove_custom_icon_cb), gtkconv,
+			                           0, 0, NULL);
+		}
 	}
 
 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time);
@@ -6175,12 +6166,14 @@
 	PidginConversation *gtkconv;
 	PidginWindow *win;
 
+	PurpleBuddy *buddy;
+
 	GdkPixbufLoader *loader;
 	GdkPixbufAnimation *anim;
 	GError *err = NULL;
 
-	const char *custom = NULL;
-	const void *data = NULL;
+	PurpleStoredImage *custom_img = NULL;
+	gconstpointer data = NULL;
 	size_t len;
 
 	GdkPixbuf *buf;
@@ -6237,17 +6230,18 @@
 	if (purple_conversation_get_gc(conv) == NULL)
 		return;
 
-	custom = custom_icon_pref_name(gtkconv);
-	if (custom) {
-		/* There is a custom icon for this user */
-		char *contents = NULL;
-		if (!g_file_get_contents(custom, &contents, &len, &err)) {
-			purple_debug_warning("custom icon", "could not load custom icon %s for %s\n",
-						custom, purple_conversation_get_name(conv));
-			g_error_free(err);
-			err = NULL;
-		} else
-			data = contents;
+	buddy = purple_find_buddy(account, purple_conversation_get_name(conv));
+	if (buddy)
+	{
+		PurpleContact *contact = purple_buddy_get_contact(buddy);
+		if (contact) {
+			custom_img = purple_buddy_icons_find_custom_icon(contact);
+			if (custom_img) {
+				/* There is a custom icon for this user */
+				data = purple_imgstore_get_data(custom_img);
+				len = purple_imgstore_get_size(custom_img);
+			}
+		}
 	}
 
 	if (data == NULL) {
@@ -6257,7 +6251,9 @@
 			return;
 
 		data = purple_buddy_icon_get_data(icon, &len);
-		custom = NULL;
+
+		if (data == NULL)
+			return;
 	}
 
 	loader = gdk_pixbuf_loader_new();
@@ -6268,8 +6264,8 @@
 		g_object_ref(G_OBJECT(anim));
 	g_object_unref(loader);
 
-	if (custom)
-		g_free((void*)data);
+	// TODO: FIX THIS!!!
+	//purple_imgstore_unref(custom_img);
 
 	if (!anim)
 		return;
@@ -6281,9 +6277,6 @@
 		g_error_free(err);
 	}
 
-	if (!gtkconv->u.im->anim)
-		return;
-
 	if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) {
 		gtkconv->u.im->iter = NULL;
 		buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
--- a/pidgin/gtkmain.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkmain.c	Tue Apr 24 17:00:22 2007 +0000
@@ -678,7 +678,7 @@
 		char *old = g_strconcat(purple_home_dir(),
 		                        G_DIR_SEPARATOR_S ".gaim", NULL);
 		const char *text = _(
-			"Pidgin encountered errors migrating your settings "
+			"%s encountered errors migrating your settings "
 			"from %s to %s. Please investigate and complete the "
 			"migration by hand.");
 		GtkWidget *dialog;
@@ -687,7 +687,8 @@
 		                                0,
 		                                GTK_MESSAGE_ERROR,
 		                                GTK_BUTTONS_CLOSE,
-		                                text, old, purple_user_dir());
+		                                text, PIDGIN_NAME,
+		                                old, purple_user_dir());
 		g_free(old);
 
 		g_signal_connect_swapped(dialog, "response",
--- a/pidgin/gtkstatusbox.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Tue Apr 24 17:00:22 2007 +0000
@@ -1439,8 +1439,10 @@
 			PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plug);
 			if (prplinfo && prplinfo->icon_spec.format) {
 				char *icon = NULL;
+#if 0
 				if (filename)
 					icon = pidgin_convert_buddy_icon(plug, filename);
+#endif
 				purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL));
 				purple_account_set_ui_string(box->account, PIDGIN_UI, "non-global-buddyicon-cached-path", icon);
 				purple_account_set_buddy_icon_path(box->account, filename);
@@ -1459,8 +1461,10 @@
 				    purple_account_get_bool(account, "use-global-buddyicon", TRUE) &&
 				    prplinfo->icon_spec.format) {
 					char *icon = NULL;
+#if 0
 					if (filename)
 						icon = pidgin_convert_buddy_icon(plug, filename);
+#endif
 					purple_account_set_buddy_icon_path(account, filename);
 					purple_account_set_buddy_icon(account, icon);
 					g_free(icon);
--- a/pidgin/gtkutils.c	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkutils.c	Tue Apr 24 17:00:22 2007 +0000
@@ -2409,15 +2409,14 @@
 }
 #endif
 
-char *
-pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path)
+gpointer
+pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len)
 {
 	PurplePluginProtocolInfo *prpl_info;
 #if GTK_CHECK_VERSION(2,2,0)
 	char **prpl_formats;
 	int width, height;
 	char **pixbuf_formats = NULL;
-	struct stat st;
 	GdkPixbufFormat *format;
 	GdkPixbuf *pixbuf;
 #if !GTK_CHECK_VERSION(2,4,0)
@@ -2426,28 +2425,11 @@
 #endif
 	gchar *contents;
 	gsize length;
-	const char *dirname;
-	char *random;
-	char *filename;
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
 
 	g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL);
 
-	dirname = purple_buddy_icons_get_cache_dir();
-	if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
-		purple_debug_info("buddyicon", "Creating icon cache directory.\n");
-
-		if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
-			purple_debug_error("buddyicon",
-							 "Unable to create directory %s: %s\n",
-							 dirname, strerror(errno));
-			return NULL;
-		}
-	}
-
-	random = g_strdup_printf("%x", g_random_int());
-	filename = g_build_filename(dirname, random, NULL);
 
 #if GTK_CHECK_VERSION(2,2,0)
 #if GTK_CHECK_VERSION(2,4,0)
@@ -2478,47 +2460,21 @@
 		   prpl_info->icon_spec.max_height >= height)))                   /* The icon is the correct size */
 #endif
 	{
-		FILE *image;
-
 #if GTK_CHECK_VERSION(2,2,0)
 		g_strfreev(prpl_formats);
 		g_strfreev(pixbuf_formats);
 #endif
-
-		/* We don't need to scale the image, so copy it to the cache folder verbatim */
+		/* We don't need to scale the image. */
 
 		contents = NULL;
-		if (!g_file_get_contents(path, &contents, &length, NULL) ||
-		    (image = g_fopen(filename, "wb")) == NULL)
+		if (!g_file_get_contents(path, &contents, &length, NULL))
 		{
-			g_free(random);
-			g_free(filename);
 			g_free(contents);
 #if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-			g_object_unref(G_OBJECT(pixbuf));
+		g_object_unref(G_OBJECT(pixbuf));
 #endif
 			return NULL;
 		}
-
-		if (fwrite(contents, 1, length, image) != length)
-		{
-			fclose(image);
-			g_unlink(filename);
-
-			g_free(random);
-			g_free(filename);
-			g_free(contents);
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-			g_object_unref(G_OBJECT(pixbuf));
-#endif
-			return NULL;
-		}
-		fclose(image);
-		g_free(contents);
-
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-		g_object_unref(G_OBJECT(pixbuf));
-#endif
 	}
 #if GTK_CHECK_VERSION(2,2,0)
 	else
@@ -2527,6 +2483,7 @@
 		GError *error = NULL;
 		GdkPixbuf *scale;
 		gboolean success = FALSE;
+		char *filename = NULL;
 
 		g_strfreev(pixbuf_formats);
 
@@ -2534,8 +2491,6 @@
 		if (error) {
 			purple_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message);
 			g_error_free(error);
-			g_free(random);
-			g_free(filename);
 			g_strfreev(prpl_formats);
 			return NULL;
 		}
@@ -2558,6 +2513,17 @@
 		}
 
 		for (i = 0; prpl_formats[i]; i++) {
+			FILE *fp;
+
+			g_free(filename);
+			fp = purple_mkstemp(&filename, TRUE);
+			if (!fp)
+			{
+				g_free(filename);
+				return NULL;
+			}
+			fclose(fp);
+
 			purple_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename);
 			/* The "compression" param wasn't supported until gdk-pixbuf 2.8.
 			 * Using it in previous versions causes the save to fail (and an assert message).  */
@@ -2586,29 +2552,33 @@
 		g_object_unref(G_OBJECT(pixbuf));
 		if (!success) {
 			purple_debug_error("buddyicon", "Could not convert icon to usable format.\n");
-			g_free(random);
+			return NULL;
+		}
+
+		contents = NULL;
+		if (!g_file_get_contents(filename, &contents, &length, NULL))
+		{
+			purple_debug_error("buddyicon",
+					"Could not read '%s', which we just wrote to disk.\n",
+					filename);
+
+			g_free(contents);
 			g_free(filename);
 			return NULL;
 		}
+
+		g_unlink(filename);
+		g_free(filename);
 	}
 
-	if (g_stat(filename, &st) != 0) {
-		purple_debug_error("buddyicon",
-				"Could not stat '%s', which we just wrote to disk: %s\n",
-				filename, strerror(errno));
-		g_free(random);
-		g_free(filename);
-		return NULL;
-	}
-
-	/* Check the file size */
+	/* Check the image size */
 	/*
 	 * TODO: If the file is too big, it would be cool if we checked if
 	 *       the prpl supported jpeg, and then we could convert to that
 	 *       and use a lower quality setting.
 	 */
 	if ((prpl_info->icon_spec.max_filesize != 0) &&
-		(st.st_size > prpl_info->icon_spec.max_filesize))
+	    (length > prpl_info->icon_spec.max_filesize))
 	{
 		gchar *tmp;
 		tmp = g_strdup_printf(_("The file '%s' is too large for %s.  Please try a smaller image.\n"),
@@ -2618,16 +2588,15 @@
 		purple_debug_info("buddyicon",
 				"'%s' was converted to an image which is %" G_GSIZE_FORMAT
 				" bytes, but the maximum icon size for %s is %" G_GSIZE_FORMAT
-				" bytes\n", path, st.st_size, plugin->info->name,
+				" bytes\n", path, length, plugin->info->name,
 				prpl_info->icon_spec.max_filesize);
 		g_free(tmp);
-		g_free(random);
-		g_free(filename);
 		return NULL;
 	}
 
-	g_free(filename);
-	return random;
+	if (len)
+		*len = length;
+	return contents;
 #else
 	/*
 	 * The chosen icon wasn't the right size, and we're using
@@ -2780,10 +2749,10 @@
 
 void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename)
 {
-	PurpleConversation *conv;
 	PurpleBuddy *buddy;
-	PurpleBlistNode *node;
-	char *path = NULL;
+	PurpleContact *contact;
+	gpointer data = NULL;
+	size_t len = 0;
 
 	buddy = purple_find_buddy(account, who);
 	if (!buddy) {
@@ -2791,35 +2760,20 @@
 		return;
 	}
 
-	node = (PurpleBlistNode*)purple_buddy_get_contact(buddy);
-	path = (char*)purple_blist_node_get_string(node, "custom_buddy_icon");
-	if (path) {
-		struct stat st;
-		if (g_stat(path, &st) == 0)
-			g_unlink(path);
-		path = NULL;
-	}
+	contact = purple_buddy_get_contact(buddy);
 
 	if (filename) {
-		char *newfile;
-
-		newfile = pidgin_convert_buddy_icon(purple_find_prpl(purple_account_get_protocol_id(account)),
-						filename);
-		path = purple_buddy_icons_get_full_path(newfile);
-		g_free(newfile);
+		const char *prpl_id = purple_account_get_protocol_id(account);
+		PurplePlugin *prpl = purple_find_prpl(prpl_id);
+
+		data = pidgin_convert_buddy_icon(prpl, filename, &len);
+
+		/* We don't want to delete the old icon if the new one didn't load. */
+		if (data == NULL)
+			return;
 	}
 
-	purple_blist_node_set_string(node, "custom_buddy_icon", path);
-	g_free(path);
-
-	/* Update the conversation */
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
-	if (conv)
-		purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON);
-
-	/* Update the buddylist */
-	if (buddy)
-		purple_blist_update_buddy_icon(buddy);
+	purple_buddy_icons_set_custom_icon(contact, data, len);
 }
 
 char *pidgin_make_pretty_arrows(const char *str)
--- a/pidgin/gtkutils.h	Tue Apr 24 16:20:01 2007 +0000
+++ b/pidgin/gtkutils.h	Tue Apr 24 17:00:22 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)
 /**