changeset 19407:41417f94afe9

merge of '8d675a1883a0f1c7f3c08d9b12242cd3fc7bcaf9' and 'b1a0340ea0f3e2adf428695c63bf6f5c526b375f'
author Sean Egan <seanegan@gmail.com>
date Fri, 24 Aug 2007 15:58:46 +0000
parents 215c02a8ff88 (current diff) fbdabdd3839a (diff)
children cc36a5aac908 3650db1f02d3
files
diffstat 52 files changed, 621 insertions(+), 314 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Tue Aug 21 15:10:03 2007 +0000
+++ b/AUTHORS	Fri Aug 24 15:58:46 2007 +0000
@@ -22,7 +22,7 @@
 Ka-Hing Cheung - Developer
 Sadrul Habib Chowdhury - Developer
 Mark 'KingAnt' Doliner - Developer
-Christian 'ChipX86' Hammond - Developer & Webmaster
+Casey Harkins - Developer
 Gary 'grim' Kramlich - Developer
 Richard 'rlaager' Laager - Developer
 Richard 'wabz' Nelson - Developer
@@ -31,21 +31,16 @@
 Etan 'deryni' Reisner - Developer
 Tim 'marv' Ringenbach - Developer
 Luke 'LSchiere' Schierer - Support
-Megan 'Cae' Schneider (support/QA)
+Megan 'Cae' Schneider - support/QA
 Evan Schoenberg - Developer
+Kevin 'SimGuy' Stange - Developer & Webmaster
 Stu 'nosnilmot' Tomlinson - Developer
 Nathan 'faceprint' Walp - Developer
 
 Crazy Patch Writers:
 -------------------
 John 'rekkanoryo' Bailey
-Felipe 'shx' Contreras
-Decklin Foster
-Casey Harkins
 Peter 'Bleeter' Lawler
-Robert 'Robot101' McQueen
-Benjamin Miller
-Kevin 'SimGuy' Stange
 
 Retired Developers:
 ------------------
@@ -53,11 +48,19 @@
 Jim Duchek <jim@linuxpimps.com> - maintainer
 Rob Flynn <gaim@robflynn.com> - maintainer
 Adam Fritzler - libfaim maintainer
+Christian 'ChipX86' Hammond - Developer & Webmaster
 Syd Logan - hacker and designated driver [lazy bum]
 Jim Seymour - XMPP developer
 Mark Spencer <markster@marko.net> - original author
 Eric Warmenhoven <eric@warmenhoven.org> - lead developer
 
+Retired Crazy Patch Writers:
+---------------------------
+Felipe 'shx' Contreras
+Decklin Foster
+Robert 'Robot101' McQueen
+Benjamin Miller
+
 Artists:
 -------
 Hylke Bons - Icons
--- a/COPYRIGHT	Tue Aug 21 15:10:03 2007 +0000
+++ b/COPYRIGHT	Fri Aug 24 15:58:46 2007 +0000
@@ -214,6 +214,7 @@
 Nicolas Lichtmaier
 Wesley Lin
 Artem Litvinovich
+Josh Littlefield
 Syd Logan
 Lokheed
 Norberto Lopes
--- a/ChangeLog.API	Tue Aug 21 15:10:03 2007 +0000
+++ b/ChangeLog.API	Fri Aug 24 15:58:46 2007 +0000
@@ -1,5 +1,11 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+Version 2.2.0 (??/??/????):
+	Pidgin:
+		Added:
+		* pidgin_set_accessible_relations, sets up label-for and labelled-by
+		  ATK relations (broken out from pidgin_set_accessible_label)
+
 Version 2.1.1 (08/20/2007):
 	libpurple:
 		Changed:
--- a/configure.ac	Tue Aug 21 15:10:03 2007 +0000
+++ b/configure.ac	Fri Aug 24 15:58:46 2007 +0000
@@ -43,18 +43,18 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [1])
+m4_define([purple_lt_current], [2])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [1])
-m4_define([purple_micro_version], [2])
+m4_define([purple_minor_version], [2])
+m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
 
-m4_define([gnt_lt_current], [1])
+m4_define([gnt_lt_current], [2])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [1])
+m4_define([gnt_minor_version], [2])
 m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
@@ -2210,6 +2210,7 @@
 		   finch/Makefile
 		   finch/libgnt/Makefile
 		   finch/libgnt/gnt.pc
+		   finch/libgnt/pygnt/Makefile
 		   finch/libgnt/wms/Makefile
 		   finch/plugins/Makefile
 		   po/Makefile.in
--- a/finch/gntaccount.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntaccount.c	Fri Aug 24 15:58:46 2007 +0000
@@ -222,6 +222,11 @@
 
 	/* XXX: Proxy options */
 
+	if (accounts.window && accounts.tree) {
+		gnt_tree_set_selected(GNT_TREE(accounts.tree), account);
+		gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree);
+	}
+
 	gnt_widget_destroy(dialog->window);
 }
 
--- a/finch/gntblist.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntblist.c	Fri Aug 24 15:58:46 2007 +0000
@@ -2139,20 +2139,29 @@
 	}
 }
 
-static void
-account_signed_on_cb(PurpleConnection *pc, gpointer null)
+static gboolean
+auto_join_chats(gpointer data)
 {
 	PurpleBlistNode *node;
+	PurpleConnection *pc = data;
+	PurpleAccount *account = purple_connection_get_account(pc);
 
 	for (node = purple_blist_get_root(); node;
 			node = purple_blist_node_next(node, FALSE)) {
 		if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 			PurpleChat *chat = (PurpleChat*)node;
-			if (chat->account == purple_connection_get_account(pc) &&
+			if (chat->account == account &&
 					purple_blist_node_get_bool(node, "gnt-autojoin"))
 				serv_join_chat(purple_account_get_connection(chat->account), chat->components);
 		}
 	}
+	return FALSE;
+}
+
+static void
+account_signed_on_cb(PurpleConnection *gc, gpointer null)
+{
+	g_idle_add(auto_join_chats, gc);
 }
 
 static void toggle_pref_cb(GntMenuItem *item, gpointer n)
--- a/finch/gntconv.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntconv.c	Fri Aug 24 15:58:46 2007 +0000
@@ -142,6 +142,11 @@
 			}
 			g_free(error);
 		}
+		else if (!purple_account_is_connected(ggconv->active_conv->account))
+		{
+			purple_conversation_write(ggconv->active_conv, "", _("Message was not sent, because you are not signed on."),
+					PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_NO_LOG, time(NULL));
+		}
 		else
 		{
 			char *escape = g_markup_escape_text(text, -1);
--- a/finch/gntft.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntft.c	Fri Aug 24 15:58:46 2007 +0000
@@ -193,6 +193,8 @@
 	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(finch_xfer_dialog_destroy), NULL);
 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
 	gnt_box_set_title(GNT_BOX(window), _("File Transfers"));
+	gnt_box_set_fill(GNT_BOX(window), TRUE);
+	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
 
 	xfer_dialog->tree = tree = gnt_tree_new_with_columns(NUM_COLUMNS);
 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Progress"), _("Filename"), _("Size"), _("Speed"), _("Remaining"), _("Status"));
@@ -219,7 +221,7 @@
 					 G_CALLBACK(toggle_clear_finished_cb), NULL);
 	gnt_box_add_widget(GNT_BOX(window), checkbox);
 
-	bbox = gnt_hbox_new(TRUE);
+	bbox = gnt_hbox_new(FALSE);
 
 	xfer_dialog->remove_button = button = gnt_button_new(_("Remove"));
 	g_signal_connect(G_OBJECT(button), "activate",
@@ -425,8 +427,11 @@
 	g_free(remaining_str);
 	g_free(kbsec);
 	if (purple_xfer_is_completed(xfer)) {
+		char *msg = g_strdup_printf(_("The file was saved as %s."), purple_xfer_get_local_filename(xfer));
 		gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_STATUS, _("Finished"));
 		gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_REMAINING, _("Finished"));
+		purple_xfer_conversation_write(xfer, msg, FALSE);
+		g_free(msg);
 	} else {
 		gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_STATUS, _("Transferring"));
 	}
--- a/finch/gntpounce.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntpounce.c	Fri Aug 24 15:58:46 2007 +0000
@@ -452,7 +452,7 @@
 
 	gnt_box_add_widget(GNT_BOX(window), gnt_line_new(FALSE));
 	/* Now the button box! */
-	bbox = gnt_hbox_new(TRUE);
+	bbox = gnt_hbox_new(FALSE);
 
 	/* Cancel button */
 	button = gnt_button_new(_("Cancel"));
@@ -613,6 +613,12 @@
 static void
 pounces_manager_add_cb(GntButton *button, gpointer user_data)
 {
+	if (purple_accounts_get_all() == NULL) {
+		purple_notify_error(NULL, _("Cannot create pounce"),
+				_("You do not have any accounts."),
+				_("You must create an account first before you can create a pounce."));
+		return;
+	}
 	finch_pounce_editor_show(NULL, NULL, NULL);
 }
 
@@ -622,7 +628,8 @@
 {
 	PouncesManager *dialog = user_data;
 	PurplePounce *pounce = gnt_tree_get_selection_data(GNT_TREE(dialog->tree));
-	finch_pounce_editor_show(NULL, NULL, pounce);
+	if (pounce)
+		finch_pounce_editor_show(NULL, NULL, pounce);
 }
 
 static void
@@ -645,6 +652,9 @@
 	char *buf;
 
 	pounce = (PurplePounce *)gnt_tree_get_selection_data(GNT_TREE(dialog->tree));
+	if (pounce == NULL)
+		return;
+
 	account = purple_pounce_get_pouncer(pounce);
 	pouncer = purple_account_get_username(account);
 	pouncee = purple_pounce_get_pouncee(pounce);
@@ -696,7 +706,7 @@
 	gnt_box_add_widget(GNT_BOX(win), tree);
 
 	/* Button box. */
-	bbox = gnt_hbox_new(TRUE);
+	bbox = gnt_hbox_new(FALSE);
 
 	/* Add button */
 	button = gnt_button_new(_("Add"));
--- a/finch/gntsound.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntsound.c	Fri Aug 24 15:58:46 2007 +0000
@@ -409,14 +409,14 @@
 	GError *err = NULL;
 
 	switch (GST_MESSAGE_TYPE (msg)) {
-	case GST_MESSAGE_EOS:
-		gst_element_set_state(play, GST_STATE_NULL);
-		gst_object_unref(GST_OBJECT(play));
-		break;
 	case GST_MESSAGE_ERROR:
 		gst_message_parse_error(msg, &err, NULL);
 		purple_debug_error("gstreamer", "%s\n", err->message);
 		g_error_free(err);
+		/* fall-through and clean up */
+	case GST_MESSAGE_EOS:
+		gst_element_set_state(play, GST_STATE_NULL);
+		gst_object_unref(GST_OBJECT(play));
 		break;
 	case GST_MESSAGE_WARNING:
 		gst_message_parse_warning(msg, &err, NULL);
@@ -670,28 +670,34 @@
 {
 	PurpleSoundEventID id = GPOINTER_TO_INT(gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events)));
 	FinchSoundEvent * event = &sounds[id];
-	char *enabled, *file, *tmpfile;
+	char *enabled, *file, *tmpfile, *volpref;
 	gboolean temp_value;
+	int volume;
 
 	enabled = g_strdup_printf(FINCH_PREFS_ROOT "/sound/profiles/%s/enabled/%s",
 			finch_sound_get_active_profile(), event->pref);
 	file = g_strdup_printf(FINCH_PREFS_ROOT "/sound/profiles/%s/file/%s",
 			finch_sound_get_active_profile(), event->pref);
+	volpref = g_strdup(make_pref("/volume"));
 
 	temp_value = purple_prefs_get_bool(enabled);
 	tmpfile = g_strdup(purple_prefs_get_string(file));
+	volume = purple_prefs_get_int(volpref);
 
 	purple_prefs_set_string(file, event->file);
 	if (!temp_value) purple_prefs_set_bool(enabled, TRUE);
+	purple_prefs_set_int(volpref, gnt_slider_get_value(GNT_SLIDER(pref_dialog->volume)));
 
 	purple_sound_play_event(id, NULL);
 
 	if (!temp_value) purple_prefs_set_bool(enabled, FALSE);
 	purple_prefs_set_string(file, tmpfile);
+	purple_prefs_set_int(volpref, volume);
 
 	g_free(enabled);
 	g_free(file);
 	g_free(tmpfile);
+	g_free(volpref);
 }
 
 static void
--- a/finch/gntstatus.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/gntstatus.c	Fri Aug 24 15:58:46 2007 +0000
@@ -299,6 +299,7 @@
 	{
 		purple_notify_error(edit, _("Error"), _("Invalid title"),
 				_("Please enter a non-empty title for the status."));
+		gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
 		return;
 	}
 
@@ -307,6 +308,7 @@
 	{
 		purple_notify_error(edit, _("Error"), _("Duplicate title"),
 				_("Please enter a different title for the status."));
+		gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
 		return;
 	}
 	
@@ -447,6 +449,7 @@
 		sub->window = window = gnt_vbox_new(FALSE);
 		gnt_box_set_toplevel(GNT_BOX(window), TRUE);
 		gnt_box_set_title(GNT_BOX(window), _("Substatus"));  /* XXX: a better title */
+		gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
 
 		box = gnt_hbox_new(FALSE);
 		gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Account:")));
@@ -523,7 +526,7 @@
 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
 	gnt_box_set_title(GNT_BOX(window), _("Edit Status"));
 	gnt_box_set_fill(GNT_BOX(window), TRUE);
-	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_LEFT);
+	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
 	gnt_box_set_pad(GNT_BOX(window), 0);
 
 	edits = g_list_append(edits, edit);
--- a/finch/libgnt/configure.ac	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/configure.ac	Fri Aug 24 15:58:46 2007 +0000
@@ -24,9 +24,9 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [1])
+m4_define([gnt_lt_current], [2])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [1])
+m4_define([gnt_minor_version], [2])
 m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
--- a/finch/libgnt/gnt.h	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gnt.h	Fri Aug 24 15:58:46 2007 +0000
@@ -37,6 +37,15 @@
 #include "gntkeys.h"
 
 /**
+ * Get things to compile in Glib < 2.8
+ */
+#if !GLIB_CHECK_VERSION(2,8,0)
+	#define G_PARAM_STATIC_NAME  G_PARAM_PRIVATE
+	#define G_PARAM_STATIC_NICK  G_PARAM_PRIVATE
+	#define G_PARAM_STATIC_BLURB  G_PARAM_PRIVATE
+#endif
+
+/**
  * 
  */
 void gnt_init(void);
--- a/finch/libgnt/gntcolors.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntcolors.c	Fri Aug 24 15:58:46 2007 +0000
@@ -133,6 +133,7 @@
 		restore_colors();
 }
 
+#if GLIB_CHECK_VERSION(2,6,0)
 static int
 get_color(char *key)
 {
@@ -164,7 +165,6 @@
 	return color;
 }
 
-#if GLIB_CHECK_VERSION(2,6,0)
 void gnt_colors_parse(GKeyFile *kfile)
 {
 	GError *error = NULL;
--- a/finch/libgnt/gntfilesel.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntfilesel.c	Fri Aug 24 15:58:46 2007 +0000
@@ -200,7 +200,7 @@
 	const char *tmp;
 	tmp = sel->suggest ? sel->suggest :
 		(const char*)gnt_tree_get_selection_data(sel->dirsonly ? GNT_TREE(sel->dirs) : GNT_TREE(sel->files));
-	old = g_strdup_printf("%s%s%s", sel->current, sel->current[1] ? G_DIR_SEPARATOR_S : "", tmp ? tmp : "");
+	old = g_strdup_printf("%s%s%s", SAFE(sel->current), SAFE(sel->current)[1] ? G_DIR_SEPARATOR_S : "", tmp ? tmp : "");
 	gnt_entry_set_text(GNT_ENTRY(sel->location), old);
 	g_free(old);
 }
--- a/finch/libgnt/gntstyle.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Fri Aug 24 15:58:46 2007 +0000
@@ -55,6 +55,8 @@
 	if (!group)
 		group = "general";
 	return g_key_file_get_value(gkfile, group, key, NULL);
+#else
+	return NULL;
 #endif
 }
 
@@ -93,6 +95,7 @@
 	return def;
 }
 
+#if GLIB_CHECK_VERSION(2,6,0)
 static void
 refine(char *text)
 {
@@ -133,6 +136,7 @@
 {
 	return (char *)gnt_key_translate(key);
 }
+#endif
 
 void gnt_style_read_workspaces(GntWM *wm)
 {
--- a/finch/libgnt/gnttextview.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Fri Aug 24 15:58:46 2007 +0000
@@ -549,7 +549,8 @@
 		if ((end = strchr(start, '\r')) != NULL ||
 			(end = strchr(start, '\n')) != NULL) {
 			len = gnt_util_onscreen_width(start, end - has_scroll);
-			if (len >= widget->priv.width - line->length - has_scroll) {
+			if (widget->priv.width > 0 &&
+					len >= widget->priv.width - line->length - has_scroll) {
 				end = NULL;
 			}
 		}
--- a/finch/libgnt/gnttree.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gnttree.c	Fri Aug 24 15:58:46 2007 +0000
@@ -985,11 +985,7 @@
 			g_param_spec_int("columns", "Columns",
 				"Number of columns in the tree.",
 				1, G_MAXINT, 1,
-#if GLIB_CHECK_VERSION(2,8,0)
 				G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
-#else
-				G_PARAM_READWRITE|G_PARAM_PRIVATE
-#endif
 			)
 		);
 
--- a/finch/libgnt/gntutils.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntutils.c	Fri Aug 24 15:58:46 2007 +0000
@@ -376,6 +376,101 @@
 #endif
 }
 
+#ifndef NO_LIBXML
+static void
+util_parse_html_to_tv(xmlNode *node, GntTextView *tv, GntTextFormatFlags flag)
+{
+	const char *name;
+	char *content;
+	xmlNode *ch;
+	gboolean processed = FALSE;
+	char *url = NULL;
+	gboolean insert_nl_s = FALSE, insert_nl_e = FALSE;
+
+	if (node == NULL || node->name == NULL || node->type != XML_ELEMENT_NODE)
+		return;
+
+	name = (char*)node->name;
+	if (g_ascii_strcasecmp(name, "b") == 0 ||
+		g_ascii_strcasecmp(name, "strong") == 0 ||
+		g_ascii_strcasecmp(name, "i") == 0 ||
+		g_ascii_strcasecmp(name, "blockquote") == 0) {
+		flag |= GNT_TEXT_FLAG_BOLD;
+	} else if (g_ascii_strcasecmp(name, "u") == 0) {
+		flag |= GNT_TEXT_FLAG_UNDERLINE;
+	} else if (g_ascii_strcasecmp(name, "br") == 0) {
+		insert_nl_e = TRUE;
+	} else if (g_ascii_strcasecmp(name, "a") == 0) {
+		flag |= GNT_TEXT_FLAG_UNDERLINE;
+		url = (char *)xmlGetProp(node, (xmlChar*)"href");
+	} else if (g_ascii_strcasecmp(name, "h1") == 0 ||
+			g_ascii_strcasecmp(name, "h2") == 0 ||
+			g_ascii_strcasecmp(name, "h3") == 0 ||
+			g_ascii_strcasecmp(name, "h4") == 0 ||
+			g_ascii_strcasecmp(name, "h5") == 0 ||
+			g_ascii_strcasecmp(name, "h6") == 0) {
+		insert_nl_s = TRUE;
+		insert_nl_e = TRUE;
+	} else if (g_ascii_strcasecmp(name, "title") == 0) {
+		insert_nl_s = TRUE;
+		insert_nl_e = TRUE;
+		flag |= GNT_TEXT_FLAG_BOLD | GNT_TEXT_FLAG_UNDERLINE;
+	} else {
+		/* XXX: Process other possible tags */
+	}
+
+	if (insert_nl_s)
+		gnt_text_view_append_text_with_flags(tv, "\n", flag);
+
+	for (ch = node->children; ch; ch = ch->next) {
+		if (ch->type == XML_ELEMENT_NODE) {
+			processed = TRUE;
+			util_parse_html_to_tv(ch, tv, flag);
+		}
+	}
+
+	if (!processed) {
+		content = (char*)xmlNodeGetContent(node);
+		gnt_text_view_append_text_with_flags(tv, content, flag);
+		xmlFree(content);
+	}
+
+	if (url) {
+		char *href = g_strdup_printf(" (%s)", url);
+		gnt_text_view_append_text_with_flags(tv, href, flag);
+		g_free(href);
+		xmlFree(url);
+	}
+
+	if (insert_nl_e)
+		gnt_text_view_append_text_with_flags(tv, "\n", flag);
+}
+#endif
+
+gboolean gnt_util_parse_xhtml_to_textview(const char *string, GntTextView *tv)
+{
+#ifdef NO_LIBXML
+	return FALSE;
+#else
+	xmlParserCtxtPtr ctxt;
+	xmlDocPtr doc;
+	xmlNodePtr node;
+	GntTextFormatFlags flag = GNT_TEXT_FLAG_NORMAL;
+	gboolean ret = FALSE;
+
+	ctxt = xmlNewParserCtxt();
+	doc = xmlCtxtReadDoc(ctxt, (xmlChar*)string, NULL, NULL, XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
+	if (doc) {
+		node = xmlDocGetRootElement(doc);
+		util_parse_html_to_tv(node, tv, flag);
+		xmlFreeDoc(doc);
+		ret = TRUE;
+	}
+	xmlCleanupParser();
+	return ret;
+#endif
+}
+
 /* Setup trigger widget */
 typedef struct {
 	char *text;
@@ -408,4 +503,3 @@
 	g_signal_connect(G_OBJECT(wid), "key_pressed", G_CALLBACK(key_pressed), tb);
 	g_signal_connect_swapped(G_OBJECT(button), "destroy", G_CALLBACK(free_trigger_button), tb);
 }
-
--- a/finch/libgnt/gntutils.h	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntutils.h	Fri Aug 24 15:58:46 2007 +0000
@@ -27,6 +27,7 @@
 #include <glib.h>
 
 #include "gnt.h"
+#include "gnttextview.h"
 #include "gntwidget.h"
 
 typedef gpointer (*GDupFunc)(gconstpointer data);
@@ -132,6 +133,16 @@
 void gnt_util_parse_widgets(const char *string, int num, ...);
 
 /**
+ * Parse an XHTML string and add it in a GntTextView with
+ * appropriate text flags.
+ *
+ * @param string   The XHTML string
+ * @param tv       The GntTextView
+ * @return  @c TRUE if the string was added to the textview properly, @c FALSE otherwise.
+ */
+gboolean gnt_util_parse_xhtml_to_textview(const char *string, GntTextView *tv);
+
+/**
  * Make some keypress activate a button when some key is pressed with 'wid' in focus.
  *
  * @param widget  The widget
--- a/finch/libgnt/gntwm.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/gntwm.c	Fri Aug 24 15:58:46 2007 +0000
@@ -738,7 +738,7 @@
 			print = ch;
 #ifndef NO_WIDECHAR
 			if (wch.chars[0] > 255) {
-				snprintf(unicode, sizeof(unicode), "&#x%x;", wch.chars[0]);
+				snprintf(unicode, sizeof(unicode), "&#x%x;", (unsigned int)wch.chars[0]);
 				print = unicode;
 			}
 #endif
--- a/finch/libgnt/test/tv.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/finch/libgnt/test/tv.c	Fri Aug 24 15:58:46 2007 +0000
@@ -5,6 +5,7 @@
 #include "gntbox.h"
 #include "gntentry.h"
 #include "gnttextview.h"
+#include "gntutils.h"
 
 static gboolean
 key_pressed(GntWidget *w, const char *key, GntWidget *view)
@@ -117,6 +118,8 @@
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 4th line\n", GNT_TEXT_FLAG_NORMAL);
 
+	gnt_util_parse_xhtml_to_textview("<p><b>Ohoy hoy!!</b><br/><p>I think this is going to</p> <u> WORK!!! </u><a href='www.google.com'>check this out!!</a></p>", GNT_TEXT_VIEW(view));
+
 #ifdef STANDALONE
 	gnt_main();
 
--- a/libpurple/blist.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/blist.c	Fri Aug 24 15:58:46 2007 +0000
@@ -1190,6 +1190,9 @@
 			group = purple_group_new(_("Chats"));
 			purple_blist_add_group(group,
 					purple_blist_get_last_sibling(purplebuddylist->root));
+		} else {
+			/* Fail if tried to add buddy to a group that isn't on the blist. #2752. */
+			g_return_if_fail(purple_find_group(group->name));
 		}
 	} else {
 		group = (PurpleGroup*)node->parent;
@@ -1284,6 +1287,10 @@
 		g = (PurpleGroup *)((PurpleBlistNode *)c)->parent;
 	} else {
 		if (group) {
+			/*  Fail if trying to add buddy to a group that is not on the buddy list. 
+			 *  Fix for #2752. */
+			g_return_if_fail(purple_find_group(group->name));
+
 			g = group;
 		} else {
 			g = purple_group_new(_("Buddies"));
--- a/libpurple/idle.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/idle.c	Fri Aug 24 15:58:46 2007 +0000
@@ -163,8 +163,8 @@
 		{
 			if (!no_away)
 			{
+				no_away = TRUE;
 				purple_savedstatus_set_idleaway(FALSE);
-				no_away = TRUE;
 			}
 			time_until_next_idle_event = 0;
 			return;
--- a/libpurple/protocols/bonjour/jabber.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Fri Aug 24 15:58:46 2007 +0000
@@ -82,8 +82,8 @@
 	BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
 	bconv->socket = -1;
 	bconv->tx_buf = purple_circ_buffer_new(512);
-	bconv->tx_handler = -1;
-	bconv->rx_handler = -1;
+	bconv->tx_handler = 0;
+	bconv->rx_handler = 0;
 
 	return bconv;
 }
@@ -234,7 +234,7 @@
 
 	if (writelen == 0) {
 		purple_input_remove(bconv->tx_handler);
-		bconv->tx_handler = -1;
+		bconv->tx_handler = 0;
 		return;
 	}
 
@@ -272,7 +272,7 @@
 	BonjourJabberConversation *bconv = bb->conversation;
 
 	/* If we're not ready to actually send, append it to the buffer */
-	if (bconv->tx_handler != -1
+	if (bconv->tx_handler != 0
 			|| bconv->connect_data != NULL
 			|| !bconv->sent_stream_start
 			|| !bconv->recv_stream_start
@@ -304,7 +304,7 @@
 	}
 
 	if (ret < len) {
-		if (bconv->tx_handler == -1)
+		if (bconv->tx_handler == 0)
 			bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
 				_send_data_write_cb, pb);
 		purple_circ_buffer_append(bconv->tx_buf, message + ret, len - ret);
@@ -455,7 +455,7 @@
 
 	/* Stream started; process the send buffer if there is one */
 	purple_input_remove(bconv->tx_handler);
-	bconv->tx_handler= -1;
+	bconv->tx_handler= 0;
 	bconv->sent_stream_start = TRUE;
 
 	bonjour_jabber_stream_started(pb);
@@ -779,7 +779,7 @@
 			/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
 			close(bconv->socket);
 		}
-		if (bconv->rx_handler != -1)
+		if (bconv->rx_handler != 0)
 			purple_input_remove(bconv->rx_handler);
 		if (bconv->tx_handler > 0)
 			purple_input_remove(bconv->tx_handler);
--- a/libpurple/protocols/bonjour/mdns_win32.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Fri Aug 24 15:58:46 2007 +0000
@@ -99,7 +99,7 @@
 
 			/* We've got what we need; stop listening */
 			purple_input_remove(idata->null_query_handler);
-			idata->null_query_handler = -1;
+			idata->null_query_handler = 0;
 			DNSServiceRefDeallocate(idata->null_query);
 			idata->null_query = NULL;
 		}
--- a/libpurple/protocols/msn/servconn.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Fri Aug 24 15:58:46 2007 +0000
@@ -51,7 +51,7 @@
 	servconn->num = session->servconns_count++;
 
 	servconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
-	servconn->tx_handler = -1;
+	servconn->tx_handler = 0;
 
 	return servconn;
 }
@@ -303,7 +303,7 @@
 
 	if (writelen == 0) {
 		purple_input_remove(servconn->tx_handler);
-		servconn->tx_handler = -1;
+		servconn->tx_handler = 0;
 		return;
 	}
 
@@ -328,7 +328,7 @@
 
 	if (!servconn->session->http_method)
 	{
-		if (servconn->tx_handler == -1) {
+		if (servconn->tx_handler == 0) {
 			switch (servconn->type)
 			{
 				case MSN_SERVCONN_NS:
@@ -353,7 +353,7 @@
 		if (ret < 0 && errno == EAGAIN)
 			ret = 0;
 		if (ret >= 0 && ret < len) {
-			if (servconn->tx_handler == -1)
+			if (servconn->tx_handler == 0)
 				servconn->tx_handler = purple_input_add(
 					servconn->fd, PURPLE_INPUT_WRITE,
 					servconn_write_cb, servconn);
--- a/libpurple/protocols/myspace/CHANGES	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/myspace/CHANGES	Fri Aug 24 15:58:46 2007 +0000
@@ -1,3 +1,28 @@
+2007-08-23 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.16
+* Add option to add all friends from myspace.com to your buddy list (#2660)
+* If a user doesn't have a picture, don't display an icon (instead of
+  displaying MySpace's "no photo" icon)
+* Fix #2725, a common crash related to buddy icon data
+* Fix #2752, which led to duplicate groups
+* Fix #2720, crash/disconnect when adding a buddy that doesn't exist
+  (You'll now receive an error when looking up invalid usernames).
+
+2007-08-22 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.15
+* Incomplete implementation of adding friends from myspace.com.
+* Change msim_msg_get() to start at the given node instead of the beginning.
+* Add msim_msg_get_*_from_element() to access data in MsimMessagElement *'s.
+* Use MsimMessage dictionaries everywhere in incoming messages, instead of
+  the old GHashTable method. Dictionary type is now fully implemented.
+* Add functions to loop over MsimMessages.
+* Link to myspace.com profile in Get Info.
+* Conditionally use my proposed attention API if defined.
+* Propagate to im.pidgin.pidgin branch for 2.1.2.
+* GSoC ended on 2007-08-20. The code in this release hasn't changed since
+  then. I did, however, bump the version number to 0.15 to distinguish this
+  release from the previous one. But there were no code changes. I updated
+  the text files, too.
+* Note: msimprpl will continue to be developed as time permits.
+
 2007-08-12 Jeff Connelly <jeff2@soc.pidgin.im> - 0.14
 * Full emoticon support (except no difference between nerd and geek emoticons),
   thanks to a number of new icons from Hylke Bons.
--- a/libpurple/protocols/myspace/README	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/myspace/README	Fri Aug 24 15:58:46 2007 +0000
@@ -1,4 +1,4 @@
-MySpaceIM Protocol Plugin      by Jeff Connelly 20070807
+MySpaceIM Protocol Plugin for Libpurple     by Jeff Connelly 20070807
 
 
 Greetings. This package contains a plugin for libpurple (as used in
@@ -6,7 +6,7 @@
 network and send/receive messages. Functionality is only basic as of yet, 
 and this code should be considered alpha quality.
 
-This code is being developed under Google Summer of Code 2007.
+This code was initially developed under Google Summer of Code 2007.
 
 For features and TODO, see http://developer.pidgin.im/wiki/MySpaceIM
 
@@ -28,7 +28,5 @@
 
 Enjoy,
 -Jeff Connelly
-California Polytechnic State University at San Luis Obispo
-myspaceim@xyzzy.cjb.net
-jeff2@soc.pidgin.im
+msimprpl@xyzzy.cjb.net
 
--- a/libpurple/protocols/myspace/myspace.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Fri Aug 24 15:58:46 2007 +0000
@@ -1669,8 +1669,8 @@
 	 * by Alexandr Shutko, who maintains OSCAR protocol documentation). */
 
 	purple_debug_info("msim", "Unrecognized data on account for %s\n", 
-			session->account->username ? session->account->username
-			: "(NULL)");
+			(session && session->account && session->account->username) ? 
+			session->account->username : "(NULL)");
 	if (note) {
 		purple_debug_info("msim", "(Note: %s)\n", note);
 	}
@@ -1784,7 +1784,7 @@
 	return rc;
 }
 
-/* Process an incoming media (buddy icon) message. */
+/* Process an incoming media (message background?) message. */
 static gboolean
 msim_incoming_media(MsimSession *session, MsimMessage *msg)
 {
@@ -2656,11 +2656,18 @@
 	purple_debug_info("msim_downloaded_buddy_icon",
 			"Downloaded %d bytes\n", len);
 
+	if (!url_text) {
+		purple_debug_info("msim_downloaded_buddy_icon",
+				"failed to download icon for %s",
+				user->buddy->name);
+		return;
+	}
+
 	purple_buddy_icons_set_for_user(user->buddy->account,
 			user->buddy->name,
-			(gchar *)url_text, len, 
-			/*  Use URL itself as buddy icon "checksum" */
-			user->image_url);
+			g_memdup((gchar *)url_text, len), len, 
+			/* Use URL itself as buddy icon "checksum" (TODO: ETag) */
+			user->image_url);		/* checksum */
 }
 
 /** Store a field of information about a buddy. */
@@ -2696,6 +2703,15 @@
 		const gchar *previous_url;
 
 		user->image_url = g_strdup(value_str);
+
+		/* Instead of showing 'no photo' picture, show nothing. */
+		if (!strcmp(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
+		{
+			purple_buddy_icons_set_for_user(user->buddy->account,
+				user->buddy->name,
+				NULL, 0, NULL);
+			return;
+		}
 		
 		previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
 
@@ -2703,6 +2719,9 @@
 		if (!previous_url || strcmp(previous_url, user->image_url)) {
 			purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
 		}
+	} else if (!strcmp(key_str, "LastImageUpdated")) {
+		/* TODO: use somewhere */
+		user->last_image_updated = atol(value_str);
 	} else if (!strcmp(key_str, "Headline")) {
 		user->headline = g_strdup(value_str);
 	} else {
@@ -3207,7 +3226,7 @@
 msim_postprocess_outgoing_cb(MsimSession *session, MsimMessage *userinfo, 
 		gpointer data)
 {
-	gchar *uid_field_name, *uid_before;
+	gchar *uid_field_name, *uid_before, *username;
 	guint uid;
 	MsimMessage *msg, *body;
 
@@ -3222,6 +3241,19 @@
 	uid = msim_msg_get_integer(body, "UserID");
 	msim_msg_free(body);
 
+	username = msim_msg_get_string(msg, "_username");
+
+	if (!uid) {
+		gchar *msg;
+
+		msg = g_strdup_printf(_("No such user: %s"), username);
+		purple_notify_error(NULL, NULL, _("User lookup"), msg);
+		g_free(msg);
+		g_free(username);
+		//msim_msg_free(msg);
+		return;
+	}
+
 	uid_field_name = msim_msg_get_string(msg, "_uid_field_name");
 	uid_before = msim_msg_get_string(msg, "_uid_before");
 
@@ -3238,6 +3270,7 @@
 	 */
 	g_free(uid_field_name);
 	g_free(uid_before);
+	g_free(username);
 	//msim_msg_free(msg);
 }
 
@@ -3908,6 +3941,8 @@
 	group_name = msim_msg_get_string(contact_info, "GroupName");
 	if (group_name) {
 		group = purple_group_new(group_name);
+		purple_debug_info("msim_add_contact_from_server_cb",
+				"adding to GroupName: %s\n", group_name);
 		g_free(group_name);
 	} else {
 		group = purple_group_new(_("IM Friends"));
@@ -3916,13 +3951,17 @@
 	/* 2. Get or create buddy */
 	buddy = purple_find_buddy(session->account, username);
 	if (!buddy) {
+		purple_debug_info("msim_add_contact_from_server_cb",
+				"creating new buddy: %s\n", username);
 		buddy = purple_buddy_new(session->account, username, NULL);
 	}
 
+	/* Add group to beginning. See #2752. */
+	purple_blist_add_group(group, NULL);
+
 	/* TODO: use 'Position' in contact_info to take into account where buddy is */
 	purple_blist_add_buddy(buddy, NULL, group, NULL /* insertion point */);
 
-
 	/* 3. Update buddy information */
 	user = msim_get_user_from_buddy(buddy);
 
@@ -3943,14 +3982,14 @@
  *
  * @return TRUE if added.
  * */
-static void 
+static gboolean
 msim_add_contact_from_server(MsimSession *session, MsimMessage *contact_info)
 {
 	guint uid;
 	const gchar *username;
 
 	uid = msim_msg_get_integer(contact_info, "ContactID");
-	g_return_if_fail(uid != 0);
+	g_return_val_if_fail(uid != 0, FALSE);
 
 	/* Lookup the username, since NickName and IMName is unreliable */
 	username = msim_uid2username_from_blist(session, uid);
@@ -3965,6 +4004,10 @@
 	} else {
 		msim_add_contact_from_server_cb(session, NULL, (gpointer)msim_msg_clone(contact_info));
 	}
+
+	/* Say that the contact was added, even if we're still looking up
+	 * their username. */
+	return TRUE;
 }
 
 /** Called when contact list is received from server. */
@@ -3972,12 +4015,16 @@
 msim_got_contact_list(MsimSession *session, MsimMessage *reply, gpointer user_data)
 {
 	MsimMessage *body, *body_node;
+	gchar *msg;
+	guint buddy_count;
 
 	msim_msg_dump("msim_got_contact_list: reply=%s", reply);
 
 	body = msim_msg_get_dictionary(reply, "body");
 	g_return_if_fail(body != NULL);
 
+	buddy_count = 0;
+
 	for (body_node = body;
 		body_node != NULL;
 		body_node = msim_msg_get_next_element_node(body_node))
@@ -3989,10 +4036,18 @@
 		if (!strcmp(elem->name, "ContactID"))
 		{
 			/* Will look for first contact in body_node */
-			msim_add_contact_from_server(session, body_node);
+			if (msim_add_contact_from_server(session, body_node)) {
+				++buddy_count;
+			}
 		}
 	}
 
+	msg = g_strdup_printf(_("%d buddies were added or updated"), buddy_count);
+
+	purple_notify_info(session->account, _("Add contacts from server"), msg, NULL);
+
+	g_free(msg);
+
 	msim_msg_free(body);
 }
 
--- a/libpurple/protocols/myspace/myspace.h	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.h	Fri Aug 24 15:58:46 2007 +0000
@@ -90,7 +90,7 @@
 #define MSIM_LANGUAGE_NAME_ENGLISH  "ENGLISH"
 
 /* msimprpl version string of this plugin */
-#define MSIM_PRPL_VERSION_STRING    "0.14"
+#define MSIM_PRPL_VERSION_STRING    "0.16"
 
 /* Default server */
 #define MSIM_SERVER                 "im.myspace.akadns.net"
@@ -226,6 +226,7 @@
 	gchar *username;
 	gchar *band_name, *song_name;
 	gchar *image_url;
+	guint last_image_updated;
 } MsimUser;
 
 
--- a/libpurple/protocols/myspace/release.sh	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/myspace/release.sh	Fri Aug 24 15:58:46 2007 +0000
@@ -4,7 +4,7 @@
 
 # Package a new msimprpl for release. Must be run with bash.
 
-VERSION=0.14
+VERSION=0.16
 make
 # Include 'myspace' directory in archive, so it can easily be unextracted
 # into ~/pidgin/libpurple/protocols at the correct location.
--- a/libpurple/protocols/oscar/oscar.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Aug 24 15:58:46 2007 +0000
@@ -3529,6 +3529,7 @@
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	PurpleStatus *status;
+	PurplePresence *presence;
 	const char *message, *itmsurl;
 	char *tmp;
 	va_list ap;
@@ -3572,7 +3573,8 @@
 	aim_srv_setextrainfo(od, FALSE, 0, TRUE, tmp, itmsurl);
 	g_free(tmp);
 
-	aim_srv_setidle(od, 0);
+	presence = purple_status_get_presence(status);
+	aim_srv_setidle(od, purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
 
 	if (od->icq) {
 		aim_icq_reqofflinemsgs(od);
--- a/libpurple/protocols/qq/sys_msg.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Fri Aug 24 15:58:46 2007 +0000
@@ -166,7 +166,7 @@
 		message = g_strdup_printf(_("You have been added by %s"), from);
 		_qq_sys_msg_log_write(gc, message, from);
 		purple_request_action(gc, NULL, message,
-				    _("Would like to add him?"), 2,
+				    _("Would you like to add him?"), 2,
 					purple_connection_get_account(gc), name, NULL,
 					g, 3,
 				    _("Cancel"), NULL,
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri Aug 24 15:58:46 2007 +0000
@@ -568,6 +568,11 @@
 				purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
 			}
 			break;
+		case 317: /* Stealth Setting */
+			if (f && (strtol(pair->value, NULL, 10) == 2)) {
+				f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
+			}
+			break;
 		/* case 242: */ /* this seems related to 241 */
 			/* break; */
 		}
@@ -768,7 +773,13 @@
 			if (bud)
 				yahoo_update_status(gc, from, f);
 		}
+	} else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
+                PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, gc->account);
+		char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
+		purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+		g_free(buf);
 	}
+    	
 }
 
 
@@ -964,7 +975,7 @@
 	yahoo_packet_hash(pkt, "ssiii", 1, add_req->id, 5, add_req->who, 241, add_req->protocol,
 	                  13, 1, 334, 0);
 	yahoo_packet_send_and_free(pkt, yd);
-	
+
 	g_free(add_req->id);
 	g_free(add_req->who);
 	g_free(add_req->msg);
@@ -976,19 +987,20 @@
 	struct yahoo_packet *pkt;
 	char *encoded_msg = NULL;
 	struct yahoo_data *yd = add_req->gc->proto_data;
-
-	if (msg)
+	PurpleAccount *account = purple_connection_get_account(add_req->gc);
+
+	if (msg && *msg)
 		encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
 
-	pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT,
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
 			YAHOO_STATUS_AVAILABLE, 0);
 
-	yahoo_packet_hash(pkt, "sss",
-			1, purple_normalize(add_req->gc->account,
-				purple_account_get_username(
-					purple_connection_get_account(
-						add_req->gc))),
-			7, add_req->who,
+	yahoo_packet_hash(pkt, "ssiiis",
+			1, purple_normalize(account, purple_account_get_username(account)),
+			5, add_req->who,
+			13, 2,
+			334, 0,
+			97, 1,
 			14, encoded_msg ? encoded_msg : "");
 
 	yahoo_packet_send_and_free(pkt, yd);
@@ -1018,51 +1030,129 @@
 			add_req);
 }
 
+static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
+{
+	char *notify_msg;
+	struct yahoo_data *yd = gc->proto_data;
+
+	if (who == NULL)
+		return;
+
+	if (reason != NULL) {
+		char *msg2 = yahoo_string_decode(gc, reason, FALSE);
+		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
+		g_free(msg2);
+	} else
+		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
+
+	purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
+	g_free(notify_msg);
+
+	g_hash_table_remove(yd->friends, who);
+	purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
+	/* TODO: Shouldn't we remove the buddy from our local list? */
+}
+
 static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
-	struct yahoo_add_request *add_req;
-	char *msg = NULL;
 	GSList *l = pkt->hash;
-
-	add_req = g_new0(struct yahoo_add_request, 1);
-	add_req->gc = gc;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 5:
-			add_req->id = g_strdup(pair->value);
-			break;
-		case 4:
-			add_req->who = g_strdup(pair->value);
-			break;
-		case 241:
-			add_req->protocol = strtol(pair->value, NULL, 10);
-			break;
-		case 14:
-			msg = pair->value;
-			break;
+	const char *msg = NULL;
+
+	/* Buddy authorized/declined our addition */
+	if (pkt->status == 1) {
+		const char *who = NULL;
+		int response = 0;
+
+		while (l) {
+			struct yahoo_pair *pair = l->data;
+
+			switch (pair->key) {
+			case 4:
+				who = pair->value;
+				break;
+			case 13:
+				response = strtol(pair->value, NULL, 10);
+				break;
+			case 14:
+				msg = pair->value;
+				break;
+			}
+			l = l->next;
 		}
-		l = l->next;
+
+		if (response == 1) /* Authorized */
+			purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
+		else if (response == 2) { /* Declined */
+			purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
+			yahoo_buddy_denied_our_add(gc, who, msg);
+		} else
+			purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
+
 	}
-
-	if (add_req->id) {
-		if (msg)
-			add_req->msg = yahoo_string_decode(gc, msg, FALSE);
-
-		/* DONE! this is almost exactly the same as what MSN does,
-		 * this should probably be moved to the core.
-		 */
-		 purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
-                                                    NULL, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
+	/* Buddy requested authorization to add us. */
+	else if (pkt->status == 3) {
+		struct yahoo_add_request *add_req;
+		const char *firstname = NULL, *lastname = NULL;
+
+		add_req = g_new0(struct yahoo_add_request, 1);
+		add_req->gc = gc;
+
+		while (l) {
+			struct yahoo_pair *pair = l->data;
+
+			switch (pair->key) {
+			case 4:
+				add_req->who = g_strdup(pair->value);
+				break;
+			case 5:
+				add_req->id = g_strdup(pair->value);
+				break;
+			case 14:
+				msg = pair->value;
+				break;
+			case 216:
+				firstname = pair->value;
+				break;
+			case 241:
+				add_req->protocol = strtol(pair->value, NULL, 10);
+				break;
+			case 254:
+				lastname = pair->value;
+				break;
+
+			}
+			l = l->next;
+		}
+
+		if (add_req->id) {
+			char *alias = NULL;
+			if (msg)
+				add_req->msg = yahoo_string_decode(gc, msg, FALSE);
+
+			if (firstname && lastname)
+				alias = g_strdup_printf("%s %s", firstname, lastname);
+			else if (firstname)
+				alias = g_strdup(firstname);
+			else if (lastname)
+				alias = g_strdup(lastname);
+
+
+			/* DONE! this is almost exactly the same as what MSN does,
+			 * this should probably be moved to the core.
+			 */
+			 purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id,
+						    alias, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL,
 						    yahoo_buddy_add_authorize_cb,
 						    yahoo_buddy_add_deny_reason_cb,
-                                                    add_req);
+						    add_req);
+			g_free(alias);
+		} else {
+			g_free(add_req->id);
+			g_free(add_req->who);
+			/*g_free(add_req->msg);*/
+			g_free(add_req);
+		}
 	} else {
-		g_free(add_req->id);
-		g_free(add_req->who);
-		/*g_free(add_req->msg);*/
-		g_free(add_req);
+		purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
 	}
 }
 
@@ -1113,13 +1203,12 @@
 	}
 }
 
-static void yahoo_buddy_denied_our_add(PurpleConnection *gc, struct yahoo_packet *pkt)
+/* I have no idea if this every gets called in version 15 */
+static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	char *who = NULL;
 	char *msg = NULL;
 	GSList *l = pkt->hash;
-	GString *buf = NULL;
-	struct yahoo_data *yd = gc->proto_data;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -1135,22 +1224,7 @@
 		l = l->next;
 	}
 
-	if (who) {
-		char *msg2;
-		buf = g_string_sized_new(0);
-		if (!msg) {
-			g_string_printf(buf, _("%s has (retroactively) denied your request to add them to your list."), who);
-		} else {
-			msg2 = yahoo_string_decode(gc, msg, FALSE);
-			g_string_printf(buf, _("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
-			g_free(msg2);
-		}
-		purple_notify_info(gc, NULL, _("Add buddy rejected"), buf->str);
-		g_string_free(buf, TRUE);
-		g_hash_table_remove(yd->friends, who);
-		purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
-		/* TODO: Shouldn't we remove the buddy from our local list? */
-	}
+	yahoo_buddy_denied_our_add(gc, who, msg);
 }
 
 static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -1163,7 +1237,7 @@
 		yahoo_buddy_added_us(gc, pkt);
 		break;
 	case 7:
-		yahoo_buddy_denied_our_add(gc, pkt);
+		yahoo_buddy_denied_our_add_old(gc, pkt);
 		break;
 	default:
 		break;
@@ -2265,7 +2339,7 @@
 		break;
 	case YAHOO_SERVICE_AUTH_REQ_15:
 		yahoo_buddy_auth_req_15(gc, pkt);
-		break;	       
+		break;
 	case YAHOO_SERVICE_ADDBUDDY:
 		yahoo_process_addbuddy(gc, pkt);
 		break;
@@ -2846,7 +2920,7 @@
 	purple_connection_set_display_name(gc, purple_account_get_username(account));
 
 	yd->fd = -1;
-	yd->txhandler = -1;
+	yd->txhandler = 0;
 	/* TODO: Is there a good grow size for the buffer? */
 	yd->txbuf = purple_circ_buffer_new(0);
 	yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
@@ -3734,7 +3808,7 @@
 		return;
 
 	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
-	
+
 	if (foo)
 		group = foo->name;
 	if (!group) {
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Fri Aug 24 15:58:46 2007 +0000
@@ -294,7 +294,7 @@
 
 	if (writelen == 0) {
 		purple_input_remove(yd->txhandler);
-		yd->txhandler = -1;
+		yd->txhandler = 0;
 		return;
 	}
 
@@ -355,7 +355,7 @@
 	len = yahoo_packet_build(pkt, 0, yd->wm, yd->jp, &data);
 
 	yahoo_packet_dump(data, len);
-	if (yd->txhandler == -1)
+	if (yd->txhandler == 0)
 		ret = write(yd->fd, data, len);
 	else {
 		ret = -1;
@@ -371,7 +371,7 @@
 	}
 
 	if (ret < len) {
-		if (yd->txhandler == -1)
+		if (yd->txhandler == 0)
 			yd->txhandler = purple_input_add(yd->fd, PURPLE_INPUT_WRITE,
 				yahoo_packet_send_can_write, yd);
 		purple_circ_buffer_append(yd->txbuf, data + ret, len - ret);
--- a/libpurple/protocols/yahoo/yahoochat.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Fri Aug 24 15:58:46 2007 +0000
@@ -62,8 +62,9 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0);
-	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
-	                  109, purple_connection_get_display_name(gc), 6, "abcde");
+	yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc),
+	                  109, purple_connection_get_display_name(gc), 6, "abcde",
+	                  135, "ym8.1.0.415");
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
@@ -155,7 +156,7 @@
 	if (members) {
 		g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str));
 	}
-	if (!yahoo_privacy_check(gc, who) || 
+	if (!yahoo_privacy_check(gc, who) ||
 		(purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) {
 		purple_debug_info("yahoo",
 		    "Invite to conference %s from %s has been dropped.\n", room, who);
@@ -640,7 +641,7 @@
 	GList *w;
 
 	purple_debug_misc("yahoo", "leaving conference %s\n", room);
-	
+
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, 0);
 
 	yahoo_packet_hash_str(pkt, 1, dn);
@@ -732,7 +733,7 @@
 			continue;
 		yahoo_packet_hash(pkt, "ss", 52, name, 53, name);
 	}
-	
+
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(msg2);
 }
--- a/libpurple/win32/global.mak	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/win32/global.mak	Fri Aug 24 15:58:46 2007 +0000
@@ -11,7 +11,7 @@
 # Locations of our various dependencies
 WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
 ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.6
+GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11
 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
--- a/libpurple/xmlnode.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/libpurple/xmlnode.c	Fri Aug 24 15:58:46 2007 +0000
@@ -272,6 +272,8 @@
 	if(NULL != node->parent) {
 		if(node->parent->child == node) {
 			node->parent->child = node->next;
+			if (node->parent->lastchild == node)
+				node->parent->lastchild = node->next;
 		} else {
 			xmlnode *prev = node->parent->child;
 			while(prev && prev->next != node) {
@@ -279,6 +281,8 @@
 			}
 			if(prev) {
 				prev->next = node->next;
+				if (node->parent->lastchild == node)
+					node->parent->lastchild = prev;
 			}
 		}
 	}
--- a/pidgin/gtkaccount.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkaccount.c	Fri Aug 24 15:58:46 2007 +0000
@@ -272,7 +272,8 @@
 	add_user_options(dialog,     dialog->top_vbox);
 	add_protocol_options(dialog, dialog->bottom_vbox);
 
-	if (!dialog->prpl_info || !dialog->prpl_info->register_user) {
+	if (!dialog->prpl_info || !dialog->prpl_info->register_user || 
+	    g_object_get_data(G_OBJECT(item), "fake")) {
 		gtk_widget_hide(dialog->register_button);
 	} else {
 		if (dialog->prpl_info != NULL &&
@@ -1394,7 +1395,9 @@
 		purple_signal_emit(pidgin_account_get_handle(), "account-modified", account);
 
 	/* If this is a new account, then sign on! */
-	if (new && !dialog->registering) {
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->register_button))) {
+		purple_account_register(account);
+	} else if (new) {
 		const PurpleSavedStatus *saved_status;
 
 		saved_status = purple_savedstatus_get_current();
@@ -1410,19 +1413,6 @@
 	return account;
 }
 
-static void
-register_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
-{
-	PurpleAccount *account;
-
-	dialog->registering = TRUE;
-
-	account = ok_account_prefs_cb(NULL, dialog);
-
-	purple_account_register(account);
-}
-
-
 static const GtkTargetEntry dnd_targets[] = {
 	{"text/plain", 0, 0},
 	{"text/uri-list", 0, 1},
@@ -1501,6 +1491,18 @@
 	add_login_options(dialog, vbox);
 	add_user_options(dialog, vbox);
 
+	button = gtk_check_button_new_with_label(_("Create this new account on the server"));
+	gtk_box_pack_start(GTK_BOX(main_vbox), button, FALSE, FALSE, 0);
+	gtk_widget_show(button);
+	dialog->register_button = button;
+	if (dialog->account == NULL)
+		gtk_widget_set_sensitive(button, FALSE);
+
+	if (!dialog->prpl_info || !dialog->prpl_info->register_user)
+		gtk_widget_hide(button);
+
+
+
 	/* Setup the page with 'Advanced'. */
 	dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
@@ -1519,22 +1521,6 @@
 	gtk_box_pack_end(GTK_BOX(main_vbox), bbox, FALSE, TRUE, 0);
 	gtk_widget_show(bbox);
 
-	/* Register button */
-	button = gtk_button_new_with_label(_("Register"));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			G_CALLBACK(register_account_prefs_cb), dialog);
-
-	dialog->register_button = button;
-
-	if (dialog->account == NULL)
-		gtk_widget_set_sensitive(button, FALSE);
-
-	if (!dialog->prpl_info || !dialog->prpl_info->register_user)
-		gtk_widget_hide(button);
-
 	/* Cancel button */
 	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
--- a/pidgin/gtkblist.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkblist.c	Fri Aug 24 15:58:46 2007 +0000
@@ -2528,7 +2528,8 @@
 	GValue val;
 	struct _pidgin_blist_node *gtknode;
 
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2), 
+		&path, NULL, NULL, NULL))
 		return FALSE;
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
 	val.g_type = 0;
@@ -2585,7 +2586,8 @@
 	PurpleBlistNode *node;
 	GValue val;
 
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL))
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2), 
+		&path, NULL, NULL, NULL))
 		return FALSE;
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
 	val.g_type = 0;
--- a/pidgin/gtkconv.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkconv.c	Fri Aug 24 15:58:46 2007 +0000
@@ -100,36 +100,6 @@
 
 #define LUMINANCE(c) (float)((0.3*(c.red))+(0.59*(c.green))+(0.11*(c.blue)))
 
-#if 0
-/* These colors come from the default GNOME palette */
-static GdkColor nick_colors[] = {
-	{0, 47616, 46336, 43776},       /* Basic 3D Medium */
-	{0, 32768, 32000, 29696},       /* Basic 3D Dark */
-	{0, 22016, 20992, 18432},       /* 3D Shadow */
-	{0, 33536, 42496, 32512},       /* Green Medium */
-	{0, 23808, 29952, 21760},       /* Green Dark */
-	{0, 17408, 22016, 12800},       /* Green Shadow */
-	{0, 57344, 46592, 44800},       /* Red Hilight */
-	{0, 49408, 26112, 23040},       /* Red Medium */
-	{0, 34816, 17920, 12544},       /* Red Dark */
-	{0, 49408, 14336,  8704},       /* Red Shadow */
-	{0, 34816, 32512, 41728},       /* Purple Medium */
-	{0, 25088, 23296, 33024},       /* Purple Dark */
-	{0, 18688, 16384, 26112},       /* Purple Shadow */
-	{0, 40192, 47104, 53760},       /* Blue Hilight */
-	{0, 29952, 36864, 44544},       /* Blue Medium */
-	{0, 57344, 49920, 40448},       /* Face Skin Medium */
-	{0, 45824, 37120, 26880},       /* Face skin Dark */
-	{0, 33280, 26112, 18176},       /* Face Skin Shadow */
-	{0, 57088, 16896,  7680},       /* Accent Red */
-	{0, 39168,     0,     0},       /* Accent Red Dark */
-	{0, 17920, 40960, 17920},       /* Accent Green */
-	{0,  9728, 50944,  9728}        /* Accent Green Dark */
-};
-
-#define NUM_NICK_COLORS (sizeof(nick_colors) / sizeof(*nick_colors))
-#endif
-
 /* From http://www.w3.org/TR/AERT#color-contrast */
 #define MIN_BRIGHTNESS_CONTRAST 75
 #define MIN_COLOR_CONTRAST 200
@@ -8736,7 +8706,7 @@
 		angle = 270;
 
 #if GTK_CHECK_VERSION(2,6,0)
-	if (!angle && pidgin_conv_window_get_gtkconv_count(win) > 1) {
+	if (!angle) {
 		g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END,  NULL);
 		gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 4);
 	} else {
@@ -8801,14 +8771,15 @@
 	}
 
 	gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, 
-					   !tabs_side && !angle && pidgin_conv_window_get_gtkconv_count(win) > 1, 
+					   !tabs_side && !angle, 
 					   TRUE, GTK_PACK_START);
 
 	if (pidgin_conv_window_get_gtkconv_count(win) == 1) 
 		gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
-                                           !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||  
+					   purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs") &&
+                                           (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||  
                                            purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT ||
-                                           purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT);
+                                           purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT));
 
 	/* show the widgets */
 /*	gtk_widget_show(gtkconv->icon); */
--- a/pidgin/gtkdialogs.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkdialogs.c	Fri Aug 24 15:58:46 2007 +0000
@@ -86,6 +86,7 @@
 	{"Luke 'LSchiere' Schierer",	N_("support"), "lschiere@users.sf.net"},
 	{"Megan 'Cae' Schneider",       N_("support/QA"), NULL},
 	{"Evan Schoenberg",		N_("developer"), NULL},
+	{"Kevin 'SimGuy' Stange",	N_("developer & webmaster"),	NULL},
 	{"Stu 'nosnilmot' Tomlinson",	N_("developer"), NULL},
 	{"Nathan 'faceprint' Walp",		N_("developer"), NULL},
 	{NULL, NULL, NULL}
@@ -95,7 +96,6 @@
 static struct developer patch_writers[] = {
 	{"John 'rekkanoryo' Bailey",	NULL,	NULL},
 	{"Peter 'Bleeter' Lawler",      NULL,   NULL},
-	{"Kevin 'SimGuy' Stange",		NULL,	NULL},
 	{NULL, NULL, NULL}
 };
 
--- a/pidgin/gtknotify.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtknotify.c	Fri Aug 24 15:58:46 2007 +0000
@@ -589,16 +589,16 @@
 	char label_text[2048];
 	char *linked_text, *primary_esc, *secondary_esc;
 
-	window = pidgin_create_window(title, PIDGIN_HIG_BORDER, NULL, TRUE);
-	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+	window = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(window), title);
+	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(formatted_close_cb), NULL);
 
 	/* Setup the main vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-	gtk_widget_show(vbox);
+	vbox = GTK_DIALOG(window)->vbox;
 
 	/* Setup the descriptive label */
 	primary_esc = g_markup_escape_text(primary, -1);
@@ -630,9 +630,7 @@
 	gtk_widget_show(frame);
 
 	/* Add the Close button. */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
 	gtk_widget_grab_focus(button);
 
 	g_signal_connect_swapped(G_OBJECT(button), "clicked",
@@ -708,7 +706,6 @@
 	guint i;
 
 	GtkWidget *vbox;
-	GtkWidget *button_area;
 	GtkWidget *label;
 	GtkWidget *sw;
 	PidginNotifySearchResultsData *data;
@@ -723,16 +720,16 @@
 	data->results = results;
 
 	/* Create the window */
-	window = pidgin_create_window(title ? title :_("Search Results"), PIDGIN_HIG_BORDER, NULL, TRUE);
-	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+	window = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(window), title ? title :_("Search Results"));
+	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 
 	g_signal_connect_swapped(G_OBJECT(window), "delete_event",
 							 G_CALLBACK(searchresults_close_cb), data);
 
 	/* Setup the main vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-	gtk_widget_show(vbox);
+	vbox = GTK_DIALOG(window)->vbox;
 
 	/* Setup the descriptive label */
 	primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
@@ -796,13 +793,6 @@
 				renderer, "text", i, NULL);
 	}
 
-	/* Setup the button area */
-	button_area = gtk_hbutton_box_new();
-	gtk_box_pack_start(GTK_BOX(vbox), button_area, FALSE, FALSE, 0);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(button_area), GTK_BUTTONBOX_END);
-	gtk_box_set_spacing(GTK_BOX(button_area), PIDGIN_HIG_BORDER);
-	gtk_widget_show(button_area);
-
 	for (i = 0; i < g_list_length(results->buttons); i++) {
 		PurpleNotifySearchButton *b = g_list_nth_data(results->buttons, i);
 		GtkWidget *button = NULL;
@@ -815,22 +805,22 @@
 				}
 				break;
 			case PURPLE_NOTIFY_BUTTON_CONTINUE:
-				button = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_GO_FORWARD, GTK_RESPONSE_NONE);
 				break;
 			case PURPLE_NOTIFY_BUTTON_ADD:
-				button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_ADD, GTK_RESPONSE_NONE);
 				break;
 			case PURPLE_NOTIFY_BUTTON_INFO:
-				button = gtk_button_new_from_stock(PIDGIN_STOCK_TOOLBAR_USER_INFO);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_TOOLBAR_USER_INFO, GTK_RESPONSE_NONE);
 				break;
 			case PURPLE_NOTIFY_BUTTON_IM:
-				button = gtk_button_new_from_stock(PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, GTK_RESPONSE_NONE);
 				break;
 			case PURPLE_NOTIFY_BUTTON_JOIN:
-				button = gtk_button_new_from_stock(PIDGIN_STOCK_CHAT);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_CHAT, GTK_RESPONSE_NONE);
 				break;
 			case PURPLE_NOTIFY_BUTTON_INVITE:
-				button = gtk_button_new_from_stock(PIDGIN_STOCK_INVITE);
+				button = gtk_dialog_add_button(GTK_DIALOG(window), PIDGIN_STOCK_INVITE, GTK_RESPONSE_NONE);
 				break;
 			default:
 				purple_debug_warning("gtknotify", "Incorrect button type: %d\n", b->type);
@@ -838,9 +828,6 @@
 		if (button != NULL) {
 			PidginNotifySearchResultsButtonData *bd;
 
-			gtk_box_pack_start(GTK_BOX(button_area), button, FALSE, FALSE, 0);
-			gtk_widget_show(button);
-
 			bd = g_new0(PidginNotifySearchResultsButtonData, 1);
 			bd->button = b;
 			bd->data = data;
@@ -852,9 +839,7 @@
 	}
 
 	/* Add the Close button */
-	close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(button_area), close_button, FALSE, FALSE, 0);
-	gtk_widget_show(close_button);
+	close_button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
 
 	g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
 	                         G_CALLBACK(searchresults_close_cb), data);
--- a/pidgin/gtkpounce.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkpounce.c	Fri Aug 24 15:58:46 2007 +0000
@@ -467,7 +467,6 @@
 	PidginPounceDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *label;
-	GtkWidget *bbox;
 	GtkWidget *vbox1, *vbox2;
 	GtkWidget *hbox;
 	GtkWidget *button;
@@ -513,17 +512,16 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window((cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")),
-		PIDGIN_HIG_BORDER, "buddy_pounce", FALSE) ;
-	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
+	dialog->window = window = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(window), (cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")));
+	gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
+	gtk_container_set_border_width(GTK_CONTAINER(dialog->window), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox1 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox1);
-	gtk_widget_show(vbox1);
+	vbox1 = GTK_DIALOG(window)->vbox;
 
 	/* Create the vbox that will contain all the prefs stuff. */
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -808,26 +806,13 @@
 	gtk_widget_show(dialog->on_away);
 	gtk_widget_show(dialog->save_pounce);
 
-	/* Now the button box! */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, FALSE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
+	button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(cancel_cb), dialog);
 
 	/* Save button */
-	dialog->save_button = button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
+	dialog->save_button = button = gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_SAVE, GTK_RESPONSE_OK);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(save_pounce_cb), dialog);
 
--- a/pidgin/gtkprefs.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkprefs.c	Fri Aug 24 15:58:46 2007 +0000
@@ -240,7 +240,7 @@
 
 	if (label != NULL) {
 		gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown);
-		pidgin_set_accessible_label (dropdown, label);
+		pidgin_set_accessible_relations (dropdown, label);
 	}
 
 	if (type == PURPLE_PREF_INT)
--- a/pidgin/gtksavedstatuses.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Fri Aug 24 15:58:46 2007 +0000
@@ -1450,7 +1450,9 @@
 	GtkWidget *win;
 	GtkTreeIter iter;
 	GtkCellRenderer *rend;
-	const char *status_id = NULL;
+	char *status_id = NULL;
+	char *message = NULL;
+	gboolean parent_dialog_has_substatus = FALSE;
 	GList *list;
 	gboolean select = FALSE;
 
@@ -1553,25 +1555,29 @@
 					 G_CALLBACK(substatus_editor_ok_cb), dialog);
 
 	/* Seed the input widgets with the current values */
-	/* TODO: Get the current values from our parent's list store, not the saved_status! */
-	if (status_editor->original_title != NULL)
-	{
+
+	/* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
+	gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter, 
+		STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &parent_dialog_has_substatus, -1);
+	if (parent_dialog_has_substatus) {
+		gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
+			STATUS_EDITOR_COLUMN_STATUS_ID, &status_id,
+			STATUS_EDITOR_COLUMN_STATUS_MESSAGE, &message, -1);
+	} else if (status_editor->original_title != NULL) {
 		PurpleSavedStatus *saved_status = NULL;
 		PurpleSavedStatusSub *substatus = NULL;
 
-		saved_status = purple_savedstatus_find(status_editor->original_title);
-		if (saved_status != NULL)
-			substatus = purple_savedstatus_get_substatus(saved_status, account);
+		if ((saved_status = purple_savedstatus_find(status_editor->original_title)) != NULL) {
+			if ((substatus = purple_savedstatus_get_substatus(saved_status, account)) != NULL) {
+				message = (char *)purple_savedstatus_substatus_get_message(substatus);
+				status_id = (char *)purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus));
+			}
+		}
+	}
+	/* TODO: Else get the generic status type from our parent */
 
-		if (substatus != NULL)
-		{
-			gtk_imhtml_append_text(dialog->message,
-								   purple_savedstatus_substatus_get_message(substatus),
-								   0);
-			status_id = purple_status_type_get_id(purple_savedstatus_substatus_get_type(substatus));
-		}
-		/* TODO: Else get the generic status type from our parent */
-	}
+	if (message)
+		gtk_imhtml_append_text(dialog->message, message, 0);
 
 	for (list = purple_account_get_status_types(account); list; list = list->next)
 	{
@@ -1607,6 +1613,12 @@
 	if (!select)
 		gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
 
+	if (parent_dialog_has_substatus) {
+		/* These two were gotten from the parent tree model, so they need to be freed */
+		g_free(status_id);
+		g_free(message);
+	}
+
 	gtk_widget_show_all(win);
 }
 
--- a/pidgin/gtksound.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtksound.c	Fri Aug 24 15:58:46 2007 +0000
@@ -364,14 +364,14 @@
 	GError *err = NULL;
 
 	switch (GST_MESSAGE_TYPE (msg)) {
-	case GST_MESSAGE_EOS:
-		gst_element_set_state(play, GST_STATE_NULL);
-		gst_object_unref(GST_OBJECT(play));
-		break;
 	case GST_MESSAGE_ERROR:
 		gst_message_parse_error(msg, &err, NULL);
 		purple_debug_error("gstreamer", "%s\n", err->message);
 		g_error_free(err);
+		/* fall-through and clean up */
+	case GST_MESSAGE_EOS:
+		gst_element_set_state(play, GST_STATE_NULL);
+		gst_object_unref(GST_OBJECT(play));
 		break;
 	case GST_MESSAGE_WARNING:
 		gst_message_parse_warning(msg, &err, NULL);
--- a/pidgin/gtkstatusbox.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Fri Aug 24 15:58:46 2007 +0000
@@ -1415,15 +1415,15 @@
 }
 
 
-static void
-toggled_cb(GtkWidget *widget, PidginStatusBox *box)
+static 
+gboolean
+toggled_cb(GtkWidget *widget, GdkEventButton *event, PidginStatusBox *box)
 {
-	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))  {
 		if (!box->popup_in_progress)
 			pidgin_status_box_popup (box);
-	}  else {
-		pidgin_status_box_popdown(box);
-	}
+		else
+			pidgin_status_box_popdown(box);
+return TRUE;
 }
 
 static void
@@ -1773,7 +1773,7 @@
 	g_signal_connect(G_OBJECT(status_box->toggle_button), "button-release-event",
 			 G_CALLBACK(button_released_cb), status_box);
 #endif
-	g_signal_connect(G_OBJECT(status_box->toggle_button), "toggled",
+	g_signal_connect(G_OBJECT(status_box->toggle_button), "button-press-event",
 	                 G_CALLBACK(toggled_cb), status_box);
 	g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(imhtml_changed_cb), status_box);
 	g_signal_connect(G_OBJECT(status_box->imhtml), "format_function_toggle",
--- a/pidgin/gtkutils.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkutils.c	Fri Aug 24 15:58:46 2007 +0000
@@ -1147,12 +1147,30 @@
 void
 pidgin_set_accessible_label (GtkWidget *w, GtkWidget *l)
 {
+	AtkObject *acc;
+	const gchar *label_text;
+	const gchar *existing_name;
+
+	acc = gtk_widget_get_accessible (w);
+
+	/* If this object has no name, set it's name with the label text */
+	existing_name = atk_object_get_name (acc);
+	if (!existing_name) {
+		label_text = gtk_label_get_text (GTK_LABEL(l));
+		if (label_text)
+			atk_object_set_name (acc, label_text);
+	}
+
+	pidgin_set_accessible_relations(w, l);
+}
+
+void
+pidgin_set_accessible_relations (GtkWidget *w, GtkWidget *l)
+{
 	AtkObject *acc, *label;
 	AtkObject *rel_obj[1];
 	AtkRelationSet *set;
 	AtkRelation *relation;
-	const gchar *label_text;
-	const gchar *existing_name;
 
 	acc = gtk_widget_get_accessible (w);
 	label = gtk_widget_get_accessible (l);
@@ -1160,14 +1178,6 @@
 	/* Make sure mnemonics work */
         gtk_label_set_mnemonic_widget(GTK_LABEL(l), w);
 	
-	/* If this object has no name, set it's name with the label text */
-	existing_name = atk_object_get_name (acc);
-	if (!existing_name) {
-		label_text = gtk_label_get_text (GTK_LABEL(l));
-		if (label_text)
-			atk_object_set_name (acc, label_text);
-	}
-
 	/* Create the labeled-by relation */
 	set = atk_object_ref_relation_set (acc);
 	rel_obj[0] = label;
--- a/pidgin/gtkutils.h	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/gtkutils.h	Fri Aug 24 15:58:46 2007 +0000
@@ -418,6 +418,14 @@
 void pidgin_set_accessible_label(GtkWidget *w, GtkWidget *l);
 
 /**
+ * Sets the labelled-by and label-for ATK relationships.
+ *
+ * @param w The widget that we want to label.
+ * @param l A GtkLabel that we want to use as the label for the widget.
+ */
+void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l);
+
+/**
  * A helper function for GtkMenuPositionFuncs. This ensures the menu will
  * be kept on screen if possible.
  *
--- a/pidgin/plugins/musicmessaging/musicmessaging.c	Tue Aug 21 15:10:03 2007 +0000
+++ b/pidgin/plugins/musicmessaging/musicmessaging.c	Fri Aug 24 15:58:46 2007 +0000
@@ -71,10 +71,10 @@
 
 /* Globals */
 /* List of sessions */
-GList *conversations;
+static GList *conversations;
 
 /* Pointer to this plugin */
-PurplePlugin *plugin_pointer;
+static PurplePlugin *plugin_pointer;
 
 /* Define types needed for DBus */
 DBusGConnection *connection;
@@ -350,7 +350,16 @@
 static gboolean
 intercept_received(PurpleAccount *account, char **sender, char **message, PurpleConversation *conv, int *flags)
 {
-	MMConversation *mmconv = mmconv_from_conv(conv);
+	MMConversation *mmconv;
+	
+	if (conv == NULL) {
+		/* XXX: This is just to avoid a crash (#2726).
+		 *      We may want to create the conversation instead of returning from here
+		 */
+		return FALSE;
+	}
+
+	mmconv = mmconv_from_conv(conv);
 	
 	purple_debug_misc("purple-musicmessaging", "Intercepted: %s\n", *message);
 	if (strstr(*message, MUSICMESSAGING_PREFIX))