changeset 21890:5814ae85e756

propagate from branch 'im.pidgin.pidgin.sadrul.tooltips' (head 2c79eb43e6f1106231cc15098ea6467b7fc1e4d5) to branch 'im.pidgin.pidgin.next.minor' (head 991ec59f7dc223bc484d829a9133c5fdcced6e78)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Tue, 18 Dec 2007 09:20:01 +0000
parents f5d961556972 (current diff) a3f0c30eb0bf (diff)
children 46c7f3362e39
files pidgin/gtkblist.c pidgin/gtkroomlist.c
diffstat 47 files changed, 1291 insertions(+), 790 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Dec 18 09:17:39 2007 +0000
+++ b/ChangeLog	Tue Dec 18 09:20:01 2007 +0000
@@ -8,6 +8,11 @@
 	  Bonjour protocol.  Avahi (or Apple's Bonjour runtime on win32) is
 	  now required to use Bonjour.
 
+	Pidgin:
+	* Added the ability to theme conversation name colors (red and blue)
+	  through your GTK+ theme, and exposed those theme settings to the 
+	  Pidgin GTK+ Theme Control plugin (Dustin Howett)
+
 	Finch:
 	* Color is used in the buddylist to indicate status, and the conversation
 	  window to indicate various message attributes. Look at the sample gntrc
@@ -16,6 +21,27 @@
 	  request dialog. M-d will properly delete-forward-word, and M-f has been
 	  fixed to imitate readline's behavior.
 
+version 2.3.1 (12/7/2007):
+	http://developer.pidgin.im/query?status=closed&milestone=2.3.1
+		NOTE: Due to the way this release was made, it is possible that
+		      bugs marked as fixed in 2.3.1 will not be fixed until the
+		      next release.
+	
+	* Fixed a number of MSN bugs introduced in 2.3.0, resolving problems
+	  connecting to MSN and random local display name changes
+	* Going idle on MySpaceIM will no longer clear your status and message.
+	* Idle MySpaceIM buddies should now appear online at login.
+	* Fixed crashes in XMPP when discovering a client's capabilities
+	* Don't set the current tune title if it's NULL (XMPP/Google Talk)
+	* Don't allow buddies to be manually added to Bonjour
+	* Don't advertise IPv6 on Bonjour because we don't support it
+	* Compile fixes for FreeBSD and Solaris
+	* Update QQ client version so some accounts can connect again
+	* Do not allow ISON requests to stack in IRC, preventing flooding IRC
+	  servers when temporary network outages are restored
+	* Plug several leaks in the perl plugin loader
+	* Prevent autoaccept plugin overwriting existing files
+
 version 2.3.0 (11/24/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
 		NOTE: Some bugs marked fixed in 2.2.1, 2.2.2 or 2.2.3 may not
--- a/ChangeLog.API	Tue Dec 18 09:17:39 2007 +0000
+++ b/ChangeLog.API	Tue Dec 18 09:20:01 2007 +0000
@@ -1,5 +1,23 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.4.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_certificate_add_ca_search_path. (Florian Quèze)
+
+	Pidgin:
+		Added:
+		* pidgin_create_dialog to create a window that closes on escape. Also
+		  added utility functions pidgin_dialog_get_vbox_with_properties,
+		  pidgin_dialog_get_vbox, pidgin_dialog_get_action_area to access the
+		  contents in the created dialog. (Peter 'fmoo' Ruibal)
+		* pidgin_dialog_add_button to add buttons to a dialog created by
+		  pidgin_create_dialog.
+		* GTK_IMHTML_NO_SMILEY for GtkIMHtmlOptions means not to look for
+		  smileys in the text. (Florian 'goutnet' Delizy)
+		* pidgin_auto_parent_window to make a window transient for a suitable
+		  parent window.
+
 version 2.3.2 (??/??/????):
 	Finch:
 		libgnt:
--- a/NEWS	Tue Dec 18 09:17:39 2007 +0000
+++ b/NEWS	Tue Dec 18 09:20:01 2007 +0000
@@ -1,5 +1,19 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+2.3.1 (12/7/2007):
+	Stu: I'm sorry for the MSN problems and the plugin crashes in 2.3.0.
+	Hopefully this will redeem us. This fixes a number of bugs. I'm a
+	bit late but I'd like to welcome John to the team. Enjoy!
+
+	Luke: I've done absolutely nothing in the last 2 weeks, except watch
+	others commit bug and, more, leak fixes.  People should be noticing
+	remarkably fewer memory leaks now than 2 or more releases ago.
+
+	Kevin: I'm not quite sure what happened to our MySpaceIM Summer of
+	Code student, but I fixed a few MySpace bugs with idle and status.
+	I will try to fix some of the other more significant bugs, after I
+	figure out the protocol, especially including grouping issues.
+
 2.3.0 (11/20/2007):
 	Luke: While this does not have the new MSN code, rest assured that
 	we are working on it and that it is nearing release.  This contains
--- a/doc/pidgin.1.in	Tue Dec 18 09:17:39 2007 +0000
+++ b/doc/pidgin.1.in	Tue Dec 18 09:20:01 2007 +0000
@@ -88,8 +88,8 @@
 .TP
 .B Add Buddy Pounce
 A Buddy Pounce is a configurable automated action to be performed when the
-buddy's state changes.  This will open the \fBBuddy Pounce\fR dialog to be
-discussed later.
+buddy's state changes.  This will open the \fBBuddy Pounce\fR dialog, which
+will be discussed later.
 .TP
 .B View Log
 Pidgin is capable of automatically logging messages.  These logs are
@@ -118,7 +118,7 @@
 
 .SH ACCOUNT EDITOR
 The account editor consists of a list of accounts and information about
-them.  It can be accessed by selecting \fBManage\fR from the Tools menu.
+them.  It can be accessed by selecting \fBManage\fR from the Accounts menu.
 Clicking \fIDelete\fR will delete the currently selected account.
 Clicking \fIAdd\fR or \fIModify\fR will invoke a \fBModify Account\fR
 window.  Here, the user  can add or alter account information.  When creating
--- a/libpurple/account.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/account.c	Tue Dec 18 09:20:01 2007 +0000
@@ -331,6 +331,12 @@
 	if(err == NULL)
 		return node;
 
+	/* It doesn't make sense to have transient errors persist across a
+	 * restart.
+	 */
+	if(!purple_connection_error_is_fatal (err->type))
+		return node;
+
 	child = xmlnode_new_child(node, "type");
 	snprintf(type_str, sizeof(type_str), "%u", err->type);
 	xmlnode_insert_data(child, type_str, -1);
--- a/libpurple/buddyicon.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/buddyicon.c	Tue Dec 18 09:20:01 2007 +0000
@@ -98,8 +98,7 @@
 {
 	const char *dirname;
 	char *path;
-	FILE *file = NULL;
-
+	
 	g_return_if_fail(img != NULL);
 
 	if (!purple_buddy_icons_is_caching())
@@ -120,24 +119,12 @@
 		}
 	}
 
-	if ((file = g_fopen(path, "wb")) != NULL)
-	{
-		if (!fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file))
-		{
-			purple_debug_error("buddyicon", "Error writing %s: %s\n",
-			                   path, g_strerror(errno));
-		}
-		else
-			purple_debug_info("buddyicon", "Wrote cache file: %s\n", path);
-
-		fclose(file);
-	}
-	else
-	{
+	if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+		purple_util_write_data_to_file_absolute(path, purple_imgstore_get_data(img),
+							purple_imgstore_get_size(img));	
+	} else 	{
 		purple_debug_error("buddyicon", "Unable to create file %s: %s\n",
 		                   path, g_strerror(errno));
-		g_free(path);
-		return;
 	}
 	g_free(path);
 }
--- a/libpurple/certificate.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/certificate.c	Tue Dec 18 09:20:01 2007 +0000
@@ -627,7 +627,7 @@
 
 /** System directory to probe for CA certificates */
 /* This is set in the lazy_init function */
-static const gchar *x509_ca_syspath = NULL;
+static GList *x509_ca_paths = NULL;
 
 /** A list of loaded CAs, populated from the above path whenever the lazy_init
     happens. Contains pointers to x509_ca_elements */
@@ -674,6 +674,7 @@
 	GDir *certdir;
 	const gchar *entry;
 	GPatternSpec *pempat;
+	GList *iter = NULL;
 	
 	if (x509_ca_initialized) return TRUE;
 
@@ -687,54 +688,48 @@
 		return FALSE;
 	}
 
-	/* Attempt to point at the appropriate system path */
-	if (NULL == x509_ca_syspath) {
-#ifdef _WIN32
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "ca-certs", NULL);
-#else
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "purple", "ca-certs", NULL);
-#endif
-	}
-
-	/* Populate the certificates pool from the system path */
-	certdir = g_dir_open(x509_ca_syspath, 0, NULL);
-	g_return_val_if_fail(certdir, FALSE);
-
 	/* Use a glob to only read .pem files */
 	pempat = g_pattern_spec_new("*.pem");
-	
-	while ( (entry = g_dir_read_name(certdir)) ) {
-		gchar *fullpath;
-		PurpleCertificate *crt;
 
-		if ( !g_pattern_match_string(pempat, entry) ) {
+	/* Populate the certificates pool from the search path(s) */
+	for (iter = x509_ca_paths; iter; iter = iter->next) {
+		certdir = g_dir_open(iter->data, 0, NULL);
+		if (!certdir) {
+			purple_debug_error("certificate/x509/ca", "Couldn't open location '%s'\n", iter->data);
 			continue;
 		}
 
-		fullpath = g_build_filename(x509_ca_syspath, entry, NULL);
-		
-		/* TODO: Respond to a failure in the following? */
-		crt = purple_certificate_import(x509, fullpath);
+		while ( (entry = g_dir_read_name(certdir)) ) {
+			gchar *fullpath;
+			PurpleCertificate *crt;
+
+			if ( !g_pattern_match_string(pempat, entry) ) {
+				continue;
+			}
+
+			fullpath = g_build_filename(iter->data, entry, NULL);
+
+			/* TODO: Respond to a failure in the following? */
+			crt = purple_certificate_import(x509, fullpath);
 
-		if (x509_ca_quiet_put_cert(crt)) {
-			purple_debug_info("certificate/x509/ca",
-					  "Loaded %s\n",
-					  fullpath);
-		} else {
-			purple_debug_error("certificate/x509/ca",
-					  "Failed to load %s\n",
-					  fullpath);
+			if (x509_ca_quiet_put_cert(crt)) {
+				purple_debug_info("certificate/x509/ca",
+						  "Loaded %s\n",
+						  fullpath);
+			} else {
+				purple_debug_error("certificate/x509/ca",
+						  "Failed to load %s\n",
+						  fullpath);
+			}
+
+			purple_certificate_destroy(crt);
+			g_free(fullpath);
 		}
-
-		purple_certificate_destroy(crt);
-		g_free(fullpath);
+		g_dir_close(certdir);
 	}
 
 	g_pattern_spec_free(pempat);
-	g_dir_close(certdir);
-	
+
 	purple_debug_info("certificate/x509/ca",
 			  "Lazy init completed.\n");
 	x509_ca_initialized = TRUE;
@@ -744,6 +739,17 @@
 static gboolean
 x509_ca_init(void)
 {
+	/* Attempt to point at the appropriate system path */
+	if (NULL == x509_ca_paths) {
+#ifdef _WIN32
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "ca-certs", NULL));
+#else
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "purple", "ca-certs", NULL));
+#endif
+	}
+
 	/* Attempt to initialize now, but if it doesn't work, that's OK;
 	   it will get done later */
 	if ( ! x509_ca_lazy_init()) {
@@ -752,7 +758,7 @@
 				  "dependency is not yet registered. "
 				  "It has been deferred to later.\n");
 	}
-	
+
 	return TRUE;
 }
 
@@ -768,6 +774,9 @@
 	g_list_free(x509_ca_certs);
 	x509_ca_certs = NULL;
 	x509_ca_initialized = FALSE;
+	g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL);
+	g_list_free(x509_ca_paths);
+	x509_ca_paths = NULL;
 }
 
 /** Look up a ca_element by dn */
@@ -1901,3 +1910,10 @@
 	g_byte_array_free(sha_bin, TRUE);
 }
 
+void purple_certificate_add_ca_search_path(const char *path)
+{
+	if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
+		return;
+	x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path));
+}
+
--- a/libpurple/certificate.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/certificate.h	Tue Dec 18 09:20:01 2007 +0000
@@ -786,6 +786,12 @@
 void
 purple_certificate_display_x509(PurpleCertificate *crt);
 
+/**
+ * Add a search path for certificates.
+ *
+ * @param path   Path to search for certificates.
+ */
+void purple_certificate_add_ca_search_path(const char *path);
 
 #ifdef __cplusplus
 }
--- a/libpurple/plugins/perl/common/Prpl.xs	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/plugins/perl/common/Prpl.xs	Tue Dec 18 09:20:01 2007 +0000
@@ -54,3 +54,20 @@
 	Purple::Account account
 	const char *name
 	time_t login_time
+
+int
+purple_prpl_send_raw(gc, str)
+	Purple::Connection gc
+	const char *str
+PREINIT:
+	PurplePluginProtocolInfo *prpl_info;
+CODE:
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+	if (prpl_info && prpl_info->send_raw != NULL) {
+		RETVAL = prpl_info->send_raw(gc, str, strlen(str));
+	} else {
+		RETVAL = 0;
+	}
+OUTPUT:
+	RETVAL
+
--- a/libpurple/prefs.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/prefs.c	Tue Dec 18 09:20:01 2007 +0000
@@ -914,7 +914,7 @@
 	if(pref) {
 		if(pref->type != PURPLE_PREF_PATH) {
 			purple_debug_error("prefs",
-					"purple_prefs_set_path: %s not a string pref\n", name);
+					"purple_prefs_set_path: %s not a path pref\n", name);
 			return;
 		}
 
--- a/libpurple/protocols/bonjour/bonjour.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Tue Dec 18 09:20:01 2007 +0000
@@ -109,7 +109,7 @@
 	gc->proto_data = bd = g_new0(BonjourData, 1);
 
 	/* Start waiting for jabber connections (iChat style) */
-	bd->jabber_data = g_new(BonjourJabber, 1);
+	bd->jabber_data = g_new0(BonjourJabber, 1);
 	bd->jabber_data->port = BONJOUR_DEFAULT_PORT_INT;
 	bd->jabber_data->account = account;
 
--- a/libpurple/protocols/bonjour/bonjour.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Tue Dec 18 09:20:01 2007 +0000
@@ -44,7 +44,7 @@
 {
 	BonjourDnsSd *dns_sd_data;
 	BonjourJabber *jabber_data;
-	GList *xfer_lists;
+	GSList *xfer_lists;
 } BonjourData;
 
 #endif /* _BONJOUR_H_ */
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Tue Dec 18 09:20:01 2007 +0000
@@ -149,7 +149,7 @@
 static PurpleXfer*
 bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
 {
-	GList *xfers = NULL;
+	GSList *xfers = NULL;
 	PurpleXfer *xfer = NULL;
 	XepXfer *xf = NULL;
 
@@ -309,7 +309,7 @@
 	if(xf != NULL) {
 		bd = (BonjourData*)xf->data;
 		if(bd != NULL) {
-			bd->xfer_lists = g_list_remove(bd->xfer_lists, xfer);
+			bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
 			purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
 		}
 		if (xf->proxy_connection != NULL)
@@ -359,7 +359,7 @@
 	purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
 	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
 
-	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+	bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
 
 	return xfer;
 }
@@ -605,7 +605,7 @@
 	purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
 	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
 
-	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+	bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
 
 	purple_xfer_request(xfer);
 }
--- a/libpurple/protocols/bonjour/jabber.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Tue Dec 18 09:20:01 2007 +0000
@@ -77,33 +77,8 @@
 static void
 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
 
-#if 0 /* this isn't used anywhere... */
-static const char *
-_font_size_purple_to_ichat(int size)
-{
-	switch (size) {
-		case 1:
-			return "8";
-		case 2:
-			return "10";
-		case 3:
-			return "12";
-		case 4:
-			return "14";
-		case 5:
-			return "17";
-		case 6:
-			return "21";
-		case 7:
-			return "24";
-	}
-
-	return "12";
-}
-#endif
-
 static BonjourJabberConversation *
-bonjour_jabber_conv_new(PurpleBuddy *pb) {
+bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
 
 	BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
 	bconv->socket = -1;
@@ -111,13 +86,14 @@
 	bconv->tx_handler = 0;
 	bconv->rx_handler = 0;
 	bconv->pb = pb;
+	bconv->account = account;
+	bconv->ip = g_strdup(ip);
 
 	bonjour_parser_setup(bconv);
 
 	return bconv;
 }
 
-
 static const char *
 _font_size_ichat_to_purple(int size)
 {
@@ -143,34 +119,48 @@
 {
 	xmlnode *body_node, *html_node, *events_node;
 	PurpleConnection *gc = pb->account->gc;
-	char *body, *html_body = NULL;
-	const char *ichat_balloon_color = NULL;
-	const char *ichat_text_color = NULL;
-	const char *font_face = NULL;
-	const char *font_size = NULL;
-	const char *font_color = NULL;
+	gchar *body = NULL;
 	gboolean composing_event = FALSE;
 
 	body_node = xmlnode_get_child(message_node, "body");
-	if (body_node == NULL)
+	html_node = xmlnode_get_child(message_node, "html");
+
+	if (body_node == NULL && html_node == NULL) {
+		purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
 		return;
-	body = xmlnode_get_data(body_node);
+	}
 
-	html_node = xmlnode_get_child(message_node, "html");
-	if (html_node != NULL)
-	{
+	events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
+	if (events_node != NULL) {
+		if (xmlnode_get_child(events_node, "composing") != NULL)
+			composing_event = TRUE;
+		if (xmlnode_get_child(events_node, "id") != NULL) {
+			/* The user is just typing */
+			/* TODO: Deal with typing notification */
+			return;
+		}
+	}
+
+	if (html_node != NULL) {
 		xmlnode *html_body_node;
 
 		html_body_node = xmlnode_get_child(html_node, "body");
-		if (html_body_node != NULL)
-		{
+		if (html_body_node != NULL) {
 			xmlnode *html_body_font_node;
+			const char *ichat_balloon_color = NULL;
+			const char *ichat_text_color = NULL;
+			const char *font_face = NULL;
+			const char *font_size = NULL;
+			const char *font_color = NULL;
 
 			ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor");
 			ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor");
 			html_body_font_node = xmlnode_get_child(html_body_node, "font");
-			if (html_body_font_node != NULL)
-			{ /* Types of messages sent by iChat */
+
+			/* Types of messages sent by iChat */
+			if (html_body_font_node != NULL) {
+				gchar *html_body;
+
 				font_face = xmlnode_get_attrib(html_body_font_node, "face");
 				/* The absolute iChat font sizes should be converted to 1..7 range */
 				font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ");
@@ -178,84 +168,73 @@
 					font_size = _font_size_ichat_to_purple(atoi(font_size));
 				font_color = xmlnode_get_attrib(html_body_font_node, "color");
 				html_body = xmlnode_get_data(html_body_font_node);
+
 				if (html_body == NULL)
 					/* This is the kind of formated messages that Purple creates */
 					html_body = xmlnode_to_str(html_body_font_node, NULL);
+
+				if (html_body != NULL) {
+					/* Use some sane defaults */
+					if (font_face == NULL) font_face = "Helvetica";
+					if (font_size == NULL) font_size = "3";
+					if (ichat_text_color == NULL) ichat_text_color = "#000000";
+					if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF";
+
+					body = g_strdup_printf("<font face='%s' size='%s' color='%s' back='%s'>%s</font>",
+								   font_face, font_size, ichat_text_color, ichat_balloon_color,
+								   html_body);
+
+					g_free(html_body);
+				}
 			}
 		}
 	}
 
-	events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
-	if (events_node != NULL)
-	{
-		if (xmlnode_get_child(events_node, "composing") != NULL)
-			composing_event = TRUE;
-		if (xmlnode_get_child(events_node, "id") != NULL)
-		{
-			/* The user is just typing */
-			/* TODO: Deal with typing notification */
-			g_free(body);
-			g_free(html_body);
-			return;
-		}
-	}
+	/* Compose the message */
+	if (body == NULL && body_node != NULL)
+		body = xmlnode_get_data(body_node);
 
-	/* Compose the message */
-	if (html_body != NULL)
-	{
-		g_free(body);
-
-		if (font_face == NULL) font_face = "Helvetica";
-		if (font_size == NULL) font_size = "3";
-		if (ichat_text_color == NULL) ichat_text_color = "#000000";
-		if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF";
-		body = g_strdup_printf("<font face='%s' size='%s' color='%s' back='%s'>%s</font>",
-				       font_face, font_size, ichat_text_color, ichat_balloon_color,
-				       html_body);
+	if (body == NULL) {
+		purple_debug_error("bonjour", "No html body or regular body found.\n");
+		return;
 	}
 
-	/* TODO: Should we do something with "composing_event" here? */
-
 	/* Send the message to the UI */
 	serv_got_im(gc, pb->name, body, 0, time(NULL));
 
 	g_free(body);
-	g_free(html_body);
 }
 
-struct _check_buddy_by_address_t {
+struct _match_buddies_by_address_t {
 	const char *address;
-	PurpleBuddy **pb;
-	BonjourJabber *bj;
+	GSList *matched_buddies;
+	BonjourJabber *jdata;
 };
 
 static void
-_check_buddy_by_address(gpointer key, gpointer value, gpointer data)
+_match_buddies_by_address(gpointer key, gpointer value, gpointer data)
 {
 	PurpleBuddy *pb = value;
-	BonjourBuddy *bb;
-	struct _check_buddy_by_address_t *cbba = data;
+	struct _match_buddies_by_address_t *mbba = data;
 
 	/*
 	 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account
 	 * is the same as the account requesting the check then continue to determine
 	 * whether one of the buddies IPs matches the target IP.
 	 */
-	if (cbba->bj->account == pb->account)
+	if (mbba->jdata->account == pb->account && pb->proto_data != NULL)
 	{
-		bb = pb->proto_data;
-		if (bb != NULL) {
-			const char *ip;
-			GSList *tmp = bb->ips;
+		const char *ip;
+		BonjourBuddy *bb = pb->proto_data;
+		GSList *tmp = bb->ips;
 
-			while(tmp) {
-				ip = tmp->data;
-				if (ip != NULL && g_ascii_strcasecmp(ip, cbba->address) == 0) {
-					*(cbba->pb) = pb;
-					break;
-				}
-				tmp = tmp->next;
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
+				mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
+				break;
 			}
+			tmp = tmp->next;
 		}
 	}
 }
@@ -268,8 +247,6 @@
 	BonjourJabberConversation *bconv = bb->conversation;
 	int ret, writelen;
 
-	/* TODO: Make sure that the stream has been established before sending */
-
 	writelen = purple_circ_buffer_get_max_read(bconv->tx_buf);
 
 	if (writelen == 0) {
@@ -345,7 +322,7 @@
 
 	if (ret < len) {
 		/* Don't interfere with the stream starting */
-		if (bconv->tx_handler == 0)
+		if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && 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);
@@ -357,6 +334,7 @@
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
 
 	g_return_if_fail(packet != NULL);
+	g_return_if_fail(pb != NULL);
 
 	if (!strcmp(packet->name, "message"))
 		_jabber_parse_and_write_message_to_ui(packet, pb);
@@ -370,31 +348,31 @@
 static void
 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = data;
+	BonjourJabberConversation *bconv = data;
 	gint len, message_length;
 	static char message[4096];
 
-	/*TODO: use a static buffer */
-
 	/* Read the data from the socket */
 	if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) {
 		/* There have been an error reading from the socket */
 		if (errno != EAGAIN) {
-			BonjourBuddy *bb = pb->proto_data;
 			const char *err = g_strerror(errno);
 
 			purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)");
 
-			bonjour_jabber_close_conversation(bb->conversation);
-			bb->conversation = NULL;
+			bonjour_jabber_close_conversation(bconv);
+			if (bconv->pb != NULL) {
+				BonjourBuddy *bb = bconv->pb->proto_data;
+				bb->conversation = NULL;
+			}
 
 			/* I guess we really don't need to notify the user.
 			 * If they try to send another message it'll reconnect */
 		}
 		return;
 	} else if (len == 0) { /* The other end has closed the socket */
-		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", pb->name ? pb->name : "(null)");
-		bonjour_jabber_stream_ended(pb);
+		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (bconv->pb && bconv->pb->name) ? bconv->pb->name : "(unknown)");
+		bonjour_jabber_stream_ended(bconv);
 		return;
 	} else {
 		message_length = len;
@@ -408,30 +386,34 @@
 
 	purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len);
 
-	bonjour_parser_process(pb, message, message_length);
+	bonjour_parser_process(bconv, message, message_length);
 }
 
-void bonjour_jabber_stream_ended(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
+void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
 
-	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", pb->name);
-
-	g_return_if_fail(bb != NULL);
+	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
 
 	/* Inform the user that the conversation has been closed */
-	if (bb->conversation != NULL) {
+	if (bconv != NULL) {
+		BonjourBuddy *bb = NULL;
+
+		if(bconv->pb != NULL)
+			bb = bconv->pb->proto_data;
 #if 0
-		PurpleConversation *conv;
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, pb->account);
-		if (conv != NULL) {
-			char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name);
-			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
-			g_free(tmp);
+		if(bconv->pb != NULL) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
+			if (conv != NULL) {
+				char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
+				purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+				g_free(tmp);
+			}
 		}
 #endif
 		/* Close the socket, clear the watcher and free memory */
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
+		bonjour_jabber_close_conversation(bconv);
+		if(bb)
+			bb->conversation = NULL;
 	}
 }
 
@@ -444,9 +426,7 @@
 static void
 _start_stream(gpointer data, gint source, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = data;
 	struct _stream_start_data *ss = bconv->stream_data;
 	int len, ret;
 
@@ -460,23 +440,26 @@
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
-		const char *ip = NULL;
+		const char *bname = bconv->buddy_name;
+		BonjourBuddy *bb = NULL;
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		if(bconv->pb) {
+			bb = bconv->pb->proto_data;
+			bname = purple_buddy_get_name(bconv->pb);
+		}
 
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send the message, the conversation couldn't be started."),
 				  PURPLE_MESSAGE_SYSTEM, time(NULL));
 
 		bonjour_jabber_close_conversation(bconv);
-		bb->conversation = NULL;
+		if(bb != NULL)
+			bb->conversation = NULL;
 
 		return;
 	}
@@ -498,20 +481,27 @@
 	bconv->tx_handler = 0;
 	bconv->sent_stream_start = FULLY_SENT;
 
-	bonjour_jabber_stream_started(pb);
+	bonjour_jabber_stream_started(bconv);
 }
 
-static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket)
+static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv, int client_socket)
 {
 	int ret, len;
 	char *stream_start;
-	BonjourBuddy *bb = pb->proto_data;
+	const char *bname = bconv->buddy_name;
+
+	if (bconv->pb != NULL)
+		bname = purple_buddy_get_name(bconv->pb);
 
-	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
-								   purple_buddy_get_name(pb));
+	/* If we have no idea who "to" is, use an empty string.
+	 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
+	if (bname == NULL)
+		bname = "";
+
+	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(bconv->account), bname);
 	len = strlen(stream_start);
 
-	bb->conversation->sent_stream_start = PARTIALLY_SENT;
+	bconv->sent_stream_start = PARTIALLY_SENT;
 
 	/* Start the stream */
 	ret = send(client_socket, stream_start, len, 0);
@@ -520,14 +510,18 @@
 		ret = 0;
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
-		const char *ip = NULL;
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   (*bname) ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
-
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
+					  _("Unable to send the message, the conversation couldn't be started."),
+					  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
 
 		close(client_socket);
 		g_free(stream_start);
@@ -539,63 +533,61 @@
 	if (ret < len) {
 		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
 		ss->msg = g_strdup(stream_start + ret);
-		bb->conversation->stream_data = ss;
+		bconv->stream_data = ss;
 		/* Finish sending the stream start */
-		bb->conversation->tx_handler = purple_input_add(client_socket,
-			PURPLE_INPUT_WRITE, _start_stream, pb);
+		bconv->tx_handler = purple_input_add(client_socket,
+			PURPLE_INPUT_WRITE, _start_stream, bconv);
 	} else
-		bb->conversation->sent_stream_start = FULLY_SENT;
+		bconv->sent_stream_start = FULLY_SENT;
 
 	g_free(stream_start);
 
 	return TRUE;
 }
 
-static gboolean
-_async_bonjour_jabber_close_conversation(gpointer data) {
-	BonjourJabberConversation *bconv = data;
-	bonjour_jabber_close_conversation(bconv);
-	return FALSE;
-}
+/* This gets called when we've successfully sent our <stream:stream />
+ * AND when we've recieved a <stream:stream /> */
+void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
-
-	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
+	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
 		const char *err = g_strerror(errno);
-		PurpleConversation *conv;
-		const char *ip = NULL;
+		const char *bname = bconv->buddy_name;
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		if (bconv->pb)
+			bname = purple_buddy_get_name(bconv->pb);
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
+					  _("Unable to send the message, the conversation couldn't be started."),
+					  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
-		if (conv != NULL)
-			purple_conversation_write(conv, NULL,
-				  _("Unable to send the message, the conversation couldn't be started."),
-				  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		/* We don't want to recieve anything else */
+		close(bconv->socket);
+		bconv->socket = -1;
 
-		close(bconv->socket);
 		/* This must be asynchronous because it destroys the parser and we
 		 * may be in the middle of parsing.
 		 */
-		purple_timeout_add(0, _async_bonjour_jabber_close_conversation, bb->conversation);
-		bb->conversation = NULL;
+		async_bonjour_jabber_close_conversation(bconv);
 		return;
 	}
 
-	/* If the stream has been completely started, we can start doing stuff */
-	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+	/* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
+	/* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
+	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
+			&& bconv->pb && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
 		/* Watch for when we can write the buffered messages */
 		bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
-			_send_data_write_cb, pb);
+			_send_data_write_cb, bconv->pb);
 		/* We can probably write the data right now. */
-		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
+		_send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE);
 	}
 
 }
@@ -603,15 +595,14 @@
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = NULL;
+	BonjourJabber *jdata = data;
 	struct sockaddr_in their_addr; /* connector's address information */
 	socklen_t sin_size = sizeof(struct sockaddr);
 	int client_socket;
 	int flags;
-	BonjourBuddy *bb;
 	char *address_text = NULL;
-	PurpleBuddyList *bl = purple_get_blist();
-	struct _check_buddy_by_address_t *cbba;
+	struct _match_buddies_by_address_t *mbba;
+	BonjourJabberConversation *bconv;
 
 	/* Check that it is a read condition */
 	if (condition != PURPLE_INPUT_READ)
@@ -626,39 +617,35 @@
 	/* Look for the buddy that has opened the conversation and fill information */
 	address_text = inet_ntoa(their_addr.sin_addr);
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
-	cbba = g_new0(struct _check_buddy_by_address_t, 1);
-	cbba->address = address_text;
-	cbba->pb = &pb;
-	cbba->bj = data;
-	g_hash_table_foreach(bl->buddies, _check_buddy_by_address, cbba);
-	g_free(cbba);
-	if (pb == NULL)
-	{
+	mbba = g_new0(struct _match_buddies_by_address_t, 1);
+	mbba->address = address_text;
+	mbba->jdata = jdata;
+	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+
+	if (mbba->matched_buddies == NULL) {
 		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
+		g_slist_free(mbba->matched_buddies);
+		g_free(mbba);
 		close(client_socket);
 		return;
 	}
-	bb = pb->proto_data;
 
-	/* Check if the conversation has been previously started */
-	/* This really shouldn't ever happen unless something weird is going on */
-	if (bb->conversation == NULL)
-	{
-		bb->conversation = bonjour_jabber_conv_new(pb);
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
 
-		/* We wait for the stream start before doing anything else */
-		bb->conversation->socket = client_socket;
-		bb->conversation->rx_handler = purple_input_add(client_socket,
-			PURPLE_INPUT_READ, _client_socket_handler, pb);
+	/* We've established that this *could* be from one of our buddies.
+	 * Wait for the stream open to see if that matches too before assigning it.
+	 */
+	bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
 
-	} else {
-		purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
-		close(client_socket);
-	}
+	/* We wait for the stream start before doing anything else */
+	bconv->socket = client_socket;
+	bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv);
+
 }
 
 gint
-bonjour_jabber_start(BonjourJabber *data)
+bonjour_jabber_start(BonjourJabber *jdata)
 {
 	struct sockaddr_in my_addr;
 	int yes = 1;
@@ -666,20 +653,20 @@
 	gboolean bind_successful;
 
 	/* Open a listening socket for incoming conversations */
-	if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+	if ((jdata->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
 	{
 		purple_debug_error("bonjour", "Cannot open socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Cannot open socket"));
 		return -1;
 	}
 
 	/* Make the socket reusable */
-	if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
+	if (setsockopt(jdata->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
 	{
 		purple_debug_error("bonjour", "Error setting socket options: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Error setting socket options"));
 		return -1;
@@ -692,30 +679,30 @@
 	bind_successful = FALSE;
 	for (i = 0; i < 10; i++)
 	{
-		my_addr.sin_port = htons(data->port);
-		if (bind(data->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
+		my_addr.sin_port = htons(jdata->port);
+		if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
 		{
 			bind_successful = TRUE;
 			break;
 		}
-		data->port++;
+		jdata->port++;
 	}
 
 	/* On no!  We tried 10 ports and could not bind to ANY of them */
 	if (!bind_successful)
 	{
 		purple_debug_error("bonjour", "Cannot bind socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Could not bind socket to port"));
 		return -1;
 	}
 
 	/* Attempt to listen on the bound socket */
-	if (listen(data->socket, 10) != 0)
+	if (listen(jdata->socket, 10) != 0)
 	{
 		purple_debug_error("bonjour", "Cannot listen on socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Could not listen on socket"));
 		return -1;
@@ -723,18 +710,18 @@
 
 #if 0
 	/* TODO: Why isn't this being used? */
-	data->socket = purple_network_listen(data->port, SOCK_STREAM);
+	data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
 
-	if (data->socket == -1)
+	if (jdata->socket == -1)
 	{
 		purple_debug_error("bonjour", "No se ha podido crear el socket\n");
 	}
 #endif
 
 	/* Open a watcher in the socket we have just opened */
-	data->watcher_id = purple_input_add(data->socket, PURPLE_INPUT_READ, _server_socket_handler, data);
+	jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
 
-	return data->port;
+	return jdata->port;
 }
 
 static void
@@ -747,14 +734,9 @@
 
 	if (source < 0) {
 		PurpleConversation *conv;
-		const char *ip = NULL;
-
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, error ? error : "(null)");
+				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -767,17 +749,12 @@
 		return;
 	}
 
-	if (!bonjour_jabber_send_stream_init(pb, source)) {
+	if (!bonjour_jabber_send_stream_init(bb->conversation, source)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
-		const char *ip = NULL;
-
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -794,19 +771,116 @@
 	/* Start listening for the stream acknowledgement */
 	bb->conversation->socket = source;
 	bb->conversation->rx_handler = purple_input_add(source,
-		PURPLE_INPUT_READ, _client_socket_handler, pb);
+		PURPLE_INPUT_READ, _client_socket_handler, bb->conversation);
+}
+
+void
+bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
+	PurpleBuddy *pb;
+
+	g_return_if_fail(bconv->ip != NULL);
+	g_return_if_fail(bconv->pb == NULL);
+
+	pb = purple_find_buddy(bconv->account, bconv->buddy_name);
+	if (pb && pb->proto_data) {
+		BonjourBuddy *bb = pb->proto_data;
+		const char *ip;
+		GSList *tmp = bb->ips;
+
+		purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
+			purple_buddy_get_name(pb));
+
+		/* Check that one of the buddy's IPs matches */
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
+				BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+
+				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
+					purple_buddy_get_name(pb), bconv->ip);
+
+				/* Attach conv. to buddy and remove from pending list */
+				jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+				/* Check if the buddy already has a conversation and, if so, replace it */
+				if(bb->conversation != NULL && bb->conversation != bconv)
+					bonjour_jabber_close_conversation(bb->conversation);
+
+				bconv->pb = pb;
+				bb->conversation = bconv;
+
+				break;
+			}
+			tmp = tmp->next;
+		}
+	}
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_jabber_close_conversation(bconv);
+	}
+}
+
+
+void
+bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
+	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+	struct _match_buddies_by_address_t *mbba;
+
+	mbba = g_new0(struct _match_buddies_by_address_t, 1);
+	mbba->address = bconv->ip;
+	mbba->jdata = jdata;
+	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+
+	/* If there is exactly one match, use it */
+	if(mbba->matched_buddies != NULL) {
+		if(mbba->matched_buddies->next != NULL)
+			purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
+		else {
+			PurpleBuddy *pb = mbba->matched_buddies->data;
+			BonjourBuddy *bb = pb->proto_data;
+
+			purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
+				purple_buddy_get_name(pb), bconv->ip);
+
+			/* Attach conv. to buddy and remove from pending list */
+			jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+			/* Check if the buddy already has a conversation and, if so, replace it */
+			if (bb->conversation != NULL && bb->conversation != bconv)
+				bonjour_jabber_close_conversation(bb->conversation);
+
+			bconv->pb = pb;
+			bb->conversation = bconv;
+		}
+	} else
+		purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_jabber_close_conversation(bconv);
+	}
+
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
 }
 
 static PurpleBuddy *
-_find_or_start_conversation(BonjourJabber *data, const gchar *to)
+_find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
 {
 	PurpleBuddy *pb = NULL;
 	BonjourBuddy *bb = NULL;
 
-	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(jdata != NULL, NULL);
 	g_return_val_if_fail(to != NULL, NULL);
 
-	pb = purple_find_buddy(data->account, to);
+	pb = purple_find_buddy(jdata->account, to);
 	if (pb == NULL || pb->proto_data == NULL)
 		/* You can not send a message to an offline buddy */
 		return NULL;
@@ -818,24 +892,21 @@
 	{
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
-		const char *ip = NULL;
-
 		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		const char *ip = bb->ips->data;
 
 		purple_debug_info("bonjour", "Starting conversation with %s\n", to);
 
 		/* Make sure that the account always has a proxy of "none".
 		 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
-		proxy_info = purple_account_get_proxy_info(data->account);
+		proxy_info = purple_account_get_proxy_info(jdata->account);
 		if (proxy_info == NULL) {
 			proxy_info = purple_proxy_info_new();
-			purple_account_set_proxy_info(data->account, proxy_info);
+			purple_account_set_proxy_info(jdata->account, proxy_info);
 		}
 		purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
 
-		connect_data = purple_proxy_connect(NULL, data->account,
+		connect_data = purple_proxy_connect(NULL, jdata->account,
 						    ip, bb->port_p2pj, _connected_to_buddy, pb);
 
 		if (connect_data == NULL) {
@@ -843,7 +914,7 @@
 			return NULL;
 		}
 
-		bb->conversation = bonjour_jabber_conv_new(pb);
+		bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
 		bb->conversation->connect_data = connect_data;
 		/* We don't want _send_data() to register the tx_handler;
 		 * that neeeds to wait until we're actually connected. */
@@ -853,7 +924,7 @@
 }
 
 int
-bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
 {
 	xmlnode *message_node, *node, *node2;
 	gchar *message;
@@ -861,7 +932,7 @@
 	BonjourBuddy *bb;
 	int ret;
 
-	pb = _find_or_start_conversation(data, to);
+	pb = _find_or_start_conversation(jdata, to);
 	if (pb == NULL) {
 		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
 		/* You can not send a message to an offline buddy */
@@ -872,7 +943,7 @@
 
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
-	xmlnode_set_attrib(message_node, "from", purple_account_get_username(data->account));
+	xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account));
 	xmlnode_set_attrib(message_node, "type", "chat");
 
 	/* Enclose the message from the UI within a "font" node */
@@ -904,26 +975,57 @@
 	return ret;
 }
 
+static gboolean
+_async_bonjour_jabber_close_conversation_cb(gpointer data) {
+	BonjourJabberConversation *bconv = data;
+	bonjour_jabber_close_conversation(bconv);
+	return FALSE;
+}
+
+void
+async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
+	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+
+	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+	/* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
+	if(bconv->pb != NULL) {
+		BonjourBuddy *bb = bconv->pb->proto_data;
+		if (bb->conversation == bconv)
+			bb->conversation = NULL;
+	}
+
+	purple_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
+}
+
 void
 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
 {
 	if (bconv != NULL) {
-		GList *xfers, *tmp_next;
-		BonjourData *bd = bconv->pb->account->gc->proto_data;
+		BonjourData *bd = NULL;
+
+		if(PURPLE_CONNECTION_IS_VALID(bconv->account->gc)) {
+			bd = bconv->account->gc->proto_data;
+			bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
+		}
 
 		/* Cancel any file transfers that are waiting to begin */
-		xfers = bd->xfer_lists;
-		while(xfers != NULL) {
-			PurpleXfer *xfer = xfers->data;
-			tmp_next = xfers->next;
-			/* We only need to cancel this if it hasn't actually started transferring. */
-			/* This will change if we ever support IBB transfers. */
-			if (strcmp(xfer->who, bconv->pb->name) == 0
-					&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
-					    || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
-				purple_xfer_cancel_remote(xfer);
+		/* There wont be any transfers if it hasn't been attached to a buddy */
+		if (bconv->pb != NULL && bd != NULL) {
+			GSList *xfers, *tmp_next;
+			xfers = bd->xfer_lists;
+			while(xfers != NULL) {
+				PurpleXfer *xfer = xfers->data;
+				tmp_next = xfers->next;
+				/* We only need to cancel this if it hasn't actually started transferring. */
+				/* This will change if we ever support IBB transfers. */
+				if (strcmp(xfer->who, bconv->pb->name) == 0
+						&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
+							|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
+					purple_xfer_cancel_remote(xfer);
+				}
+				xfers = tmp_next;
 			}
-			xfers = tmp_next;
 		}
 
 		/* Close the socket and remove the watcher */
@@ -952,25 +1054,26 @@
 		if (bconv->context != NULL)
 			bonjour_parser_setup(bconv);
 
+		g_free(bconv->buddy_name);
+		g_free(bconv->ip);
 		g_free(bconv);
 	}
 }
 
 void
-bonjour_jabber_stop(BonjourJabber *data)
+bonjour_jabber_stop(BonjourJabber *jdata)
 {
 	/* Close the server socket and remove the watcher */
-	if (data->socket >= 0)
-		close(data->socket);
-	if (data->watcher_id > 0)
-		purple_input_remove(data->watcher_id);
+	if (jdata->socket >= 0)
+		close(jdata->socket);
+	if (jdata->watcher_id > 0)
+		purple_input_remove(jdata->watcher_id);
 
 	/* Close all the conversation sockets and remove all the watchers after sending end streams */
-	if (data->account->gc != NULL)
-	{
+	if (jdata->account->gc != NULL) {
 		GSList *buddies, *l;
 
-		buddies = purple_find_buddies(data->account, purple_account_get_username(data->account));
+		buddies = purple_find_buddies(jdata->account, NULL);
 		for (l = buddies; l; l = l->next) {
 			BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data;
 			bonjour_jabber_close_conversation(bb->conversation);
@@ -979,6 +1082,11 @@
 
 		g_slist_free(buddies);
 	}
+
+	while (jdata->pending_conversations != NULL) {
+		bonjour_jabber_close_conversation(jdata->pending_conversations->data);
+		jdata->pending_conversations = g_slist_delete_link(jdata->pending_conversations, jdata->pending_conversations);
+	}
 }
 
 XepIq *
@@ -1075,7 +1183,7 @@
 	PurpleBuddy *pb = NULL;
 
 	/* start the talk, reuse the message socket  */
-	pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to);
+	pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
 	/* Send the message */
 	if (pb != NULL) {
 		/* Convert xml node into stream */
--- a/libpurple/protocols/bonjour/jabber.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Tue Dec 18 09:20:01 2007 +0000
@@ -38,7 +38,8 @@
 	gint port;
 	gint socket;
 	gint watcher_id;
-	PurpleAccount* account;
+	PurpleAccount *account;
+	GSList *pending_conversations;
 } BonjourJabber;
 
 typedef struct _BonjourJabberConversation
@@ -54,6 +55,11 @@
 	xmlParserCtxt *context;
 	xmlnode *current;
 	PurpleBuddy *pb;
+	PurpleAccount *account;
+
+	/* The following are only needed before attaching to a PurpleBuddy */
+	gchar *buddy_name;
+	gchar *ip;
 } BonjourJabberConversation;
 
 /**
@@ -68,14 +74,20 @@
 
 void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb);
+void async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
-void bonjour_jabber_stream_ended(PurpleBuddy *pb);
+void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
+
+void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv);
 
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet);
 
 void bonjour_jabber_stop(BonjourJabber *data);
 
+void bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv);
+
+void bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv);
+
 typedef enum {
 	XEP_IQ_SET,
 	XEP_IQ_GET,
--- a/libpurple/protocols/bonjour/parser.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Tue Dec 18 09:20:01 2007 +0000
@@ -31,26 +31,59 @@
 #include "util.h"
 #include "xmlnode.h"
 
+static gboolean
+parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
+	int i;
+
+	/* If the "from" attribute is specified, attach it to the conversation. */
+	for(i=0; i < nb_attributes * 5; i+=5) {
+		if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
+			int len = attributes[i+4] - attributes[i+3];
+			bconv->buddy_name = g_strndup(attributes[i+3], len);
+			bonjour_jabber_conv_match_by_name(bconv);
+
+			return (bconv->pb != NULL);
+		}
+	}
+
+	return FALSE;
+}
+
 static void
 bonjour_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
 				   int nb_namespaces, const xmlChar **namespaces,
 				   int nb_attributes, int nb_defaulted, const xmlChar **attributes)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	xmlnode *node;
 	int i;
 
-	if(!element_name) {
-		return;
-	} else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
-		bconv->recv_stream_start = TRUE;
-		bonjour_jabber_stream_started(pb);
+	g_return_if_fail(element_name != NULL);
+
+	if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+		if(!bconv->recv_stream_start) {
+			bconv->recv_stream_start = TRUE;
+
+			if (bconv->pb == NULL)
+				parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
+
+			bonjour_jabber_stream_started(bconv);
+		}
 	} else {
 
+		/* If we haven't yet attached a buddy and this isn't "<stream:features />",
+		 * try to get a "from" attribute as a last resort to match our buddy. */
+		if(bconv->pb == NULL
+				&& !(prefix && !xmlStrcmp(prefix, (xmlChar*) "stream")
+					&& !xmlStrcmp(element_name, (xmlChar*) "features"))
+				&& !parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes))
+			/* We've run out of options for finding who the conversation is from
+			   using explicitly specified stuff; see if we can make a good match
+			   by using the IP */
+			bonjour_jabber_conv_match_by_ip(bconv);
+
 		if(bconv->current)
 			node = xmlnode_new_child(bconv->current, (const char*) element_name);
 		else
@@ -82,27 +115,19 @@
 	}
 }
 
-static gboolean _async_bonjour_jabber_stream_ended_cb(gpointer data) {
-	bonjour_jabber_stream_ended((PurpleBuddy *) data);
-	return FALSE;
-}
-
 static void
 bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
 				 const xmlChar *prefix, const xmlChar *namespace)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	if(!bconv->current) {
 		/* We don't keep a reference to the start stream xmlnode,
 		 * so we have to check for it here to close the conversation */
-		if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+		if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
 			/* Asynchronously close the conversation to prevent bonjour_parser_setup()
 			 * being called from within this context */
-			purple_timeout_add(0, _async_bonjour_jabber_stream_ended_cb, pb);
-		}
+			async_bonjour_jabber_close_conversation(bconv);
 		return;
 	}
 
@@ -112,7 +137,7 @@
 	} else {
 		xmlnode *packet = bconv->current;
 		bconv->current = NULL;
-		bonjour_jabber_process_packet(pb, packet);
+		bonjour_jabber_process_packet(bconv->pb, packet);
 		xmlnode_free(packet);
 	}
 }
@@ -120,9 +145,7 @@
 static void
 bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	if(!bconv->current)
 		return;
@@ -184,21 +207,17 @@
 }
 
 
-void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len)
+void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len)
 {
-	BonjourBuddy *bb = pb->proto_data;
 
-	g_return_if_fail(bb != NULL);
-	g_return_if_fail(bb->conversation != NULL);
-
-	if (bb->conversation->context ==  NULL) {
+	if (bconv->context == NULL) {
 		/* libxml inconsistently starts parsing on creating the
 		 * parser, so do a ParseChunk right afterwards to force it. */
-		bb->conversation->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, pb, buf, len, NULL);
-		xmlParseChunk(bb->conversation->context, "", 0, 0);
-	} else if (xmlParseChunk(bb->conversation->context, buf, len, 0) < 0) {
+		bconv->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, bconv, buf, len, NULL);
+		xmlParseChunk(bconv->context, "", 0, 0);
+	} else if (xmlParseChunk(bconv->context, buf, len, 0) < 0)
 		/* TODO: What should we do here - I assume we should display an error or something (maybe just print something to the conv?) */
 		purple_debug_error("bonjour", "Error parsing xml.\n");
-	}
+
 }
 
--- a/libpurple/protocols/bonjour/parser.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.h	Tue Dec 18 09:20:01 2007 +0000
@@ -28,6 +28,6 @@
 #include "jabber.h"
 
 void bonjour_parser_setup(BonjourJabberConversation *bconv);
-void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len);
+void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len);
 
 #endif /* _PURPLE_BONJOUR_PARSER_H_ */
--- a/libpurple/protocols/jabber/auth.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Tue Dec 18 09:20:01 2007 +0000
@@ -330,14 +330,21 @@
 							disallow_plaintext_auth);
 					g_free(msg);
 					return;
-				/* Everything else has failed, so fail the
-				 * connection. Should probably have a better
-				 * error here.
-				 */
+
 				} else {
-					purple_connection_error_reason (js->gc,
-						PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
-						_("Server does not use any supported authentication method"));
+					/* We have no mechs which can work.
+					 * Try falling back on the old jabber:iq:auth method. We get here if the server supports
+					 * one or more sasl mechs, we are compiled with cyrus-sasl support, but we support or can connect with none of
+					 * the offerred mechs. jabberd 2.0 w/ SASL and Apple's iChat Server 10.5 both handle and expect
+					 * jabber:iq:auth in this situation.  iChat Server in particular offers SASL GSSAPI by default, which is often
+					 * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
+					 *
+					 * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
+					 * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms.
+					 * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers
+					 * which would connect without issue otherwise. -evands
+					 */
+					jabber_auth_start_old(js);
 					return;
 				}
 				/* not reached */
@@ -563,6 +570,75 @@
 	}
 }
 
+/*!
+ * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest
+ *
+ * This is the crammd5 response.  Inspired by cyrus-sasl's _sasl_hmac_md5()
+ */
+static void
+auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest)
+{
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+	int i;
+	/* inner padding - key XORd with ipad */
+	unsigned char k_ipad[65];    
+	/* outer padding - key XORd with opad */
+	unsigned char k_opad[65];    
+
+	cipher = purple_ciphers_find_cipher("md5");
+
+	/* if key is longer than 64 bytes reset it to key=MD5(key) */
+	if (strlen(key) > 64) {
+		guchar keydigest[16];
+
+		context = purple_cipher_context_new(cipher, NULL);
+		purple_cipher_context_append(context, (const guchar *)key, strlen(key));
+		purple_cipher_context_digest(context, 16, keydigest, NULL);
+		purple_cipher_context_destroy(context);
+
+		key = (char *)keydigest;
+		key_len = 16;
+	} 
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* start out by storing key in pads */
+	memset(k_ipad, '\0', sizeof k_ipad);
+	memset(k_opad, '\0', sizeof k_opad);
+	memcpy(k_ipad, (void *)key, key_len);
+	memcpy(k_opad, (void *)key, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i=0; i<64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner MD5 */
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */
+	purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */
+	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */
+	purple_cipher_context_destroy(context);
+
+	/* perform outer MD5 */	
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */
+	purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */
+	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */
+	purple_cipher_context_destroy(context);
+}
+
 static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
 {
 	JabberIq *iq;
@@ -608,6 +684,33 @@
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
 
+		} else if(js->stream_id && xmlnode_get_child(query, "crammd5")) {
+			const char *challenge;
+			guchar digest[16];
+			char h[17], *p;
+			int i;
+
+			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
+			query = xmlnode_get_child(iq->node, "query");
+
+			x = xmlnode_new_child(query, "username");
+			xmlnode_insert_data(x, js->user->node, -1);
+			x = xmlnode_new_child(query, "resource");
+			xmlnode_insert_data(x, js->user->resource, -1);
+
+			x = xmlnode_new_child(query, "crammd5");
+			challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
+			auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), &digest);
+
+			/* Translate the digest to a hexadecimal notation */
+			p = h;
+			for(i=0; i<16; i++, p+=2)
+				snprintf(p, 3, "%02x", digest[i]);
+			xmlnode_insert_data(x, h, -1);
+
+			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
+			jabber_iq_send(iq);
+
 		} else if(xmlnode_get_child(query, "password")) {
 			if(js->gsc == NULL && !purple_account_get_bool(js->gc->account,
 						"auth_plain_in_clear", FALSE)) {
--- a/libpurple/protocols/jabber/buddy.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1153,8 +1153,10 @@
 
 void jabber_vcard_fetch_mine(JabberStream *js)
 {
-	JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "vcard-temp");
-
+	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
+	
+	xmlnode *vcard = xmlnode_new_child(iq->node, "vCard");
+	xmlnode_set_namespace(vcard, "vcard-temp");
 	jabber_iq_set_callback(iq, jabber_vcard_save_mine, NULL);
 
 	jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/libxmpp.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Tue Dec 18 09:20:01 2007 +0000
@@ -53,7 +53,7 @@
 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
 	NULL,							/* user_splits */
 	NULL,							/* protocol_options */
-	{"png", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	jabber_list_icon,				/* list_icon */
 	jabber_list_emblem,			/* list_emblems */
 	jabber_status_text,				/* status_text */
--- a/libpurple/protocols/msn/contact.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1289,16 +1289,16 @@
 				purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
 				msn_group_new(session->userlist, guid, state->new_group_name);
 
-				g_free(guid);
-
 				if (state->action & MSN_ADD_BUDDY) {
 					msn_userlist_add_buddy(session->userlist,
 						state->who,
 						state->new_group_name);
 				} else if (state->action & MSN_MOVE_BUDDY) {
 					msn_add_contact_to_group(session->contact, state, state->who, guid); 
+					g_free(guid);
 					return;
 				}
+				g_free(guid);
 			} else {
 				purple_debug_info("MSNCL", "Adding group %s failed\n",
 					state->new_group_name);
--- a/libpurple/protocols/sametime/sametime.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Tue Dec 18 09:20:01 2007 +0000
@@ -3749,7 +3749,7 @@
 
     client = purple_account_get_int(account, MW_KEY_CLIENT, mwLogin_BINARY);
     major = purple_account_get_int(account, MW_KEY_MAJOR, 0x001e);
-    minor = purple_account_get_int(account, MW_KEY_MINOR, 0x001d);
+    minor = purple_account_get_int(account, MW_KEY_MINOR, 0x196f);
 
     DEBUG_INFO("client id: 0x%04x\n", client);
     DEBUG_INFO("client major: 0x%04x\n", major);
--- a/libpurple/protocols/simple/sipmsg.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/simple/sipmsg.c	Tue Dec 18 09:20:01 2007 +0000
@@ -45,7 +45,10 @@
 	line = g_strndup(msg, tmp - msg);
 
 	smsg = sipmsg_parse_header(line);
-	smsg->body = g_strdup(tmp + 4);
+	if(smsg != NULL)
+		smsg->body = g_strdup(tmp + 4);
+	else
+		purple_debug_error("SIMPLE", "No header parsed from line: %s\n", line);
 
 	g_free(line);
 	return smsg;
--- a/libpurple/protocols/yahoo/util.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/protocols/yahoo/util.c	Tue Dec 18 09:20:01 2007 +0000
@@ -715,12 +715,15 @@
 			} else if (((len - i) >= 4) && !strncmp(&src[i], "&gt;", 4)) {
 				g_string_append_c(dest, '>');
 				i += 3;
-			} else if (((len - i) >= 5) && !strncmp(&src[i], "&amp;", 4)) {
+			} else if (((len - i) >= 5) && !strncmp(&src[i], "&amp;", 5)) {
 				g_string_append_c(dest, '&');
 				i += 4;
-			} else if (((len - i) >= 6) && !strncmp(&src[i], "&quot;", 4)) {
+			} else if (((len - i) >= 6) && !strncmp(&src[i], "&quot;", 6)) {
 				g_string_append_c(dest, '"');
 				i += 5;
+			} else if (((len - i) >= 6) && !strncmp(&src[i], "&apos;", 6)) {
+				g_string_append_c(dest, '\'');
+				i += 5;
 			} else {
 				g_string_append_c(dest, src[i]);
 			}
--- a/libpurple/status.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/libpurple/status.c	Tue Dec 18 09:20:01 2007 +0000
@@ -607,13 +607,10 @@
 
 		if (old_status != NULL)
 		{
-			tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias,
+			tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name,
 			                      purple_status_get_name(old_status),
 			                      purple_status_get_name(new_status));
-			logtmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name,
-			                      purple_status_get_name(old_status),
-			                      purple_status_get_name(new_status));
-
+			logtmp = g_markup_escape_text(tmp, -1);
 		}
 		else
 		{
@@ -621,18 +618,15 @@
 
 			if (purple_status_is_active(new_status))
 			{
-				tmp = g_strdup_printf(_("%s is now %s"), buddy_alias,
+				tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name,
 				                      purple_status_get_name(new_status));
-				logtmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name,
-				                      purple_status_get_name(new_status));
-
+				logtmp = g_markup_escape_text(tmp, -1);
 			}
 			else
 			{
-				tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias,
+				tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name,
 				                      purple_status_get_name(new_status));
-				logtmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name,
-				                      purple_status_get_name(new_status));
+				logtmp = g_markup_escape_text(tmp, -1);
 			}
 		}
 
@@ -1244,12 +1238,15 @@
 
 			if (log != NULL)
 			{
-				char *tmp = g_strdup_printf(_("%s became idle"),
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became idle"),
 				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
-				purple_buddy_get_alias(buddy), current_time, tmp);
-				g_free(tmp);
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
 			}
 		}
 	}
@@ -1261,12 +1258,15 @@
 
 			if (log != NULL)
 			{
-				char *tmp = g_strdup_printf(_("%s became unidle"),
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became unidle"),
 				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
-				purple_buddy_get_alias(buddy), current_time, tmp);
-				g_free(tmp);
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
 			}
 		}
 	}
@@ -1321,13 +1321,15 @@
 
 			if (log != NULL)
 			{
-				char *msg;
+				char *msg, *tmp;
 
 				if (idle)
-					msg = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
+					tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
 				else
-					msg = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
+					tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
 
+				msg = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
 				                 purple_account_get_username(account),
 				                 (idle ? idle_time : current_time), msg);
--- a/pidgin/gtkaccount.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkaccount.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1437,7 +1437,6 @@
 	GtkWidget *win;
 	GtkWidget *main_vbox;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *dbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
@@ -1475,16 +1474,14 @@
 	if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL)
 		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 
-	dialog->window = win = pidgin_create_window((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
+	dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
 		PIDGIN_HIG_BORDER, "account", FALSE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(account_win_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	main_vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), main_vbox);
-	gtk_widget_show(main_vbox);
+	main_vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	notebook = gtk_notebook_new();
 	gtk_box_pack_start(GTK_BOX(main_vbox), notebook, FALSE, FALSE, 0);
@@ -1511,8 +1508,6 @@
 	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);
@@ -1524,30 +1519,13 @@
 	add_protocol_options(dialog, dbox);
 	add_proxy_options(dialog, dbox);
 
-	/* Setup 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(main_vbox), bbox, FALSE, TRUE, 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);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(cancel_account_prefs_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE, G_CALLBACK(ok_account_prefs_cb), dialog);
 	if (dialog->account == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	gtk_widget_show(button);
-
 	dialog->ok_button = button;
 
 	/* Set up DND */
@@ -1561,9 +1539,6 @@
 	g_signal_connect(G_OBJECT(dialog->window), "drag_data_received",
 			 G_CALLBACK(account_dnd_recv), dialog);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ok_account_prefs_cb), dialog);
-
 	/* Show the window. */
 	gtk_widget_show(win);
 }
@@ -2313,7 +2288,6 @@
 	AccountsWindow *dialog;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	int width, height;
@@ -2328,7 +2302,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -2337,57 +2311,28 @@
 					 G_CALLBACK(configure_cb), accounts_window);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Setup the scrolled window that will contain the list of accounts. */
 	sw = create_accounts_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
-	/* 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(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_account_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(add_account_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(modify_account_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(modify_account_cb), dialog);
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(ask_delete_account_cb), dialog);
 	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ask_delete_account_cb), dialog);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_accounts_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog);
 
 	purple_signal_connect(pidgin_account_get_handle(), "account-modified",
 	                    accounts_window,
--- a/pidgin/gtkblist.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkblist.c	Tue Dec 18 09:20:01 2007 +0000
@@ -4542,6 +4542,13 @@
 }
 
 static void
+clear_elsewhere_errors(PidginMiniDialog *mini_dialog,
+                       gpointer unused)
+{
+	elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error);
+}
+
+static void
 ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
 {
 	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
@@ -4556,6 +4563,12 @@
 	pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"),
 		reconnect_elsewhere_accounts, NULL);
 
+	/* Make dismissing the dialog clear the errors.  The "destroy" signal
+	 * does not appear to fire at quit, which is fortunate!
+	 */
+	g_signal_connect(G_OBJECT(mini_dialog), "destroy",
+		(GCallback) clear_elsewhere_errors, NULL);
+
 	add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog));
 
 	/* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */
--- a/pidgin/gtkcertmgr.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Tue Dec 18 09:20:01 2007 +0000
@@ -559,7 +559,6 @@
 	CertMgrDialog *dlg;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 
 	/* Enumerate all the certificates on file */
 	{
@@ -599,7 +598,7 @@
 	dlg = certmgr_dialog = g_new0(CertMgrDialog, 1);
 
 	win = dlg->window =
-		pidgin_create_window(_("Certificate Manager"),/* Title */
+		pidgin_create_dialog(_("Certificate Manager"),/* Title */
 				     PIDGIN_HIG_BORDER, /*Window border*/
 				     "certmgr",         /* Role */
 				     TRUE); /* Allow resizing */
@@ -611,9 +610,7 @@
 	gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new( FALSE, PIDGIN_HIG_BORDER );
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Notebook of various certificate managers */
 	dlg->notebook = gtk_notebook_new();
@@ -622,19 +619,9 @@
 			   0);
 	gtk_widget_show(dlg->notebook);
 
-	/* Box for the close button */
-	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(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Close button */
-	dlg->closebutton = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dlg->closebutton, FALSE, FALSE, 0);
-	gtk_widget_show(dlg->closebutton);
-	g_signal_connect(G_OBJECT(dlg->closebutton), "clicked",
-			 G_CALLBACK(certmgr_close_cb), dlg);
+	dlg->closebutton = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(certmgr_close_cb), dlg);
 
 	/* Add the defined certificate managers */
 	/* TODO: Find a way of determining whether each is shown or not */
--- a/pidgin/gtkconn.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkconn.c	Tue Dec 18 09:20:01 2007 +0000
@@ -184,36 +184,39 @@
 
 static void pidgin_connection_network_connected ()
 {
-	GList *list = purple_accounts_get_all_active();
+	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
 
 	if(gtkblist)
 		pidgin_status_box_set_network_available(PIDGIN_STATUS_BOX(gtkblist->statusbox), TRUE);
 
-	while (list) {
-		PurpleAccount *account = (PurpleAccount*)list->data;
+	l = list = purple_accounts_get_all_active();
+	while (l) {
+		PurpleAccount *account = (PurpleAccount*)l->data;
 		g_hash_table_remove(auto_reconns, account);
 		if (purple_account_is_disconnected(account))
 			do_signon(account);
-		list = list->next;
+		l = l->next;
 	}
+	g_list_free(list);
 }
 
 static void pidgin_connection_network_disconnected ()
 {
-	GList *l = purple_accounts_get_all_active();
+	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = NULL;
-	
+
 	if(gtkblist)
 		pidgin_status_box_set_network_available(PIDGIN_STATUS_BOX(gtkblist->statusbox), FALSE);
 
+	l = list = purple_accounts_get_all_active();
 	while (l) {
 		PurpleAccount *a = (PurpleAccount*)l->data;
 		if (!purple_account_is_disconnected(a)) {
 			gc = purple_account_get_connection(a);
-			if (gc && gc->prpl) 
+			if (gc && gc->prpl)
 				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 			if (prpl_info) {
 				if (prpl_info->keepalive)
@@ -224,6 +227,7 @@
 		}
 		l = l->next;
 	}
+	g_list_free(list);
 }
 
 static void pidgin_connection_notice(PurpleConnection *gc, const char *text)
--- a/pidgin/gtkconv.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Dec 18 09:20:01 2007 +0000
@@ -95,9 +95,10 @@
 
 #define	PIDGIN_CONV_ALL	((1 << 7) - 1)
 
-#define SEND_COLOR "#204a87"
-#define RECV_COLOR "#cc0000"
-#define HIGHLIGHT_COLOR "#AF7F00"
+#define DEFAULT_SEND_COLOR "#204a87"
+#define DEFAULT_RECV_COLOR "#cc0000"
+#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
+#define DEFAULT_ACTION_COLOR "#062585"
 
 /* Undef this to turn off "custom-smiley" debug messages */
 #define DEBUG_CUSTOM_SMILEY
@@ -3790,7 +3791,7 @@
 	if (is_me)
 	{
 		GdkColor send_color;
-		gdk_color_parse(SEND_COLOR, &send_color);
+		gdk_color_parse(DEFAULT_SEND_COLOR, &send_color);
 
 #if GTK_CHECK_VERSION(2,6,0)
 		gtk_list_store_insert_with_values(ls, &iter,
@@ -4343,14 +4344,15 @@
 
 	/* Show a maximum of 4 lines */
 	lines = MIN(lines, 4);
-	wrapped_lines = MIN(wrapped_lines, 4);
+	wrapped_lines = MIN(MAX(wrapped_lines, 2), 4);
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(gtkconv->entry));
 
-	height = (oneline.height + pad_top + pad_bottom) * (lines + 1);
-	height += (oneline.height + pad_inside) * (wrapped_lines - lines);
+	height = (oneline.height + pad_top + pad_bottom) * lines;
+	if (wrapped_lines > lines)
+		height += (oneline.height + pad_inside) * (wrapped_lines - lines);
 
 	gtkconv->auto_resize = TRUE;
 	g_idle_add(reset_auto_resize_cb, gtkconv);
@@ -5601,6 +5603,7 @@
 		}
 		else {
 			if (purple_message_meify(new_message, -1)) {
+				GdkColor *col;
 				str = g_malloc(1024);
 
 				if (flags & PURPLE_MESSAGE_AUTO_RESP) {
@@ -5613,9 +5616,20 @@
 				}
 
 				if (flags & PURPLE_MESSAGE_NICK)
-					strcpy(color, HIGHLIGHT_COLOR);
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
 				else
-					strcpy(color, "#062585");
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "action-name-color", &col, NULL);
+
+				if(col) {
+					g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+						col->red >> 8, col->green >> 8, col->blue >> 8);
+				}
+				else {
+					if (flags & PURPLE_MESSAGE_NICK)
+						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
+					else
+						strcpy(color, DEFAULT_ACTION_COLOR);
+				}
 			}
 			else {
 				str = g_malloc(1024);
@@ -5627,19 +5641,46 @@
 					g_snprintf(str, 1024, "%s:", alias_escaped);
 					tag_end_offset = 1;
 				}
-				if (flags & PURPLE_MESSAGE_NICK)
-					strcpy(color, HIGHLIGHT_COLOR);
+				if (flags & PURPLE_MESSAGE_NICK) {
+					GdkColor *col;
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
+					if(col) {
+						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+							col->red >> 8, col->green >> 8, col->blue >> 8);
+					}
+					else {
+						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
+					}
+				}
 				else if (flags & PURPLE_MESSAGE_RECV) {
 					if (type == PURPLE_CONV_TYPE_CHAT) {
 						GdkColor *col = get_nick_color(gtkconv, name);
 
 						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
 							   col->red >> 8, col->green >> 8, col->blue >> 8);
-					} else
-						strcpy(color, RECV_COLOR);
+					} else {
+						GdkColor *col;
+						gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "receive-name-color", &col, NULL);
+						if(col) {
+							g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+								col->red >> 8, col->green >> 8, col->blue >> 8);
+						}
+						else {
+							strcpy(color, DEFAULT_RECV_COLOR);
+						}
+					}
 				}
-				else if (flags & PURPLE_MESSAGE_SEND)
-					strcpy(color, SEND_COLOR);
+				else if (flags & PURPLE_MESSAGE_SEND) {
+					GdkColor *col;
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "send-name-color", &col, NULL);
+					if(col) {
+						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+							col->red >> 8, col->green >> 8, col->blue >> 8);
+					}
+					else {
+						strcpy(color, DEFAULT_SEND_COLOR);
+					}
+				}
 				else {
 					purple_debug_error("gtkconv", "message missing flags\n");
 					strcpy(color, "#000000");
@@ -8277,7 +8318,7 @@
 	}
 	
 	if (e->button == 3) {
-		/* Right click was pressed. Popup the Send To menu. */
+		/* Right click was pressed. Popup the context menu. */
 		GtkWidget *menu = gtk_menu_new(), *sub;
 		gboolean populated = populate_menu_with_options(menu, gtkconv, TRUE);
 		sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
@@ -9922,8 +9963,8 @@
 	GdkColor send_color;
 	time_t breakout_time;
 
-	gdk_color_parse(HIGHLIGHT_COLOR, &nick_highlight);
-	gdk_color_parse(SEND_COLOR, &send_color);
+	gdk_color_parse(DEFAULT_HIGHLIGHT_COLOR, &nick_highlight);
+	gdk_color_parse(DEFAULT_SEND_COLOR, &send_color);
 
 	srand(background.red + background.green + background.blue + 1);
 
--- a/pidgin/gtkft.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkft.c	Tue Dec 18 09:20:01 2007 +0000
@@ -745,7 +745,6 @@
 	PidginXferDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *vbox1, *vbox2;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	GtkWidget *expander;
@@ -759,15 +758,13 @@
 		purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
 
 	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 = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Create the main vbox for top half of the window. */
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -812,71 +809,35 @@
 	gtk_container_add(GTK_CONTAINER(expander), table);
 	gtk_widget_show(table);
 
-	/* Now the button box for the buttons */
-	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, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Open button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->open_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(open_button_cb), dialog);
-
 	/* Pause button */
-	button = gtk_button_new_with_mnemonic(_("_Pause"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Pause"), G_CALLBACK(pause_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->pause_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pause_button_cb), dialog);
-
 	/* Resume button */
-	button = gtk_button_new_with_mnemonic(_("_Resume"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Resume"), G_CALLBACK(resume_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->resume_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(resume_button_cb), dialog);
-
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog);
 	gtk_widget_hide(button);
 	dialog->remove_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_button_cb), dialog);
-
 	/* Stop button */
-	button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
 	dialog->stop_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(stop_button_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog);
 	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_button_cb), dialog);
-
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(dialog->window), "show",
 		G_CALLBACK(winpidgin_ensure_onscreen), dialog->window);
--- a/pidgin/gtkimhtml.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkimhtml.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1012,7 +1012,7 @@
 static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext)
 {
 	GtkTextIter iter;
-	GtkIMHtmlOptions flags = plaintext ? 0 : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
+	GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
 
 	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL))
 		gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
@@ -1397,6 +1397,22 @@
 	                                        _("Hyperlink prelight color"),
 	                                        _("Color to draw hyperlinks when mouse is over them."),
 	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("send-name-color",
+	                                        _("Sent Message Name Color"),
+	                                        _("Color to draw the name of a message you sent."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("receive-name-color",
+	                                        _("Received Message Name Color"),
+	                                        _("Color to draw the name of a message you received."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("highlight-name-color",
+	                                        _("\"Attention\" Name Color"),
+	                                        _("Color to draw the name of a message you received containing your name."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color",
+	                                        _("Action Message Name Color"),
+	                                        _("Color to draw the name of an action message."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
 
 	binding_set = gtk_binding_set_by_class (parent_class);
 	gtk_binding_entry_add_signal (binding_set, GDK_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
@@ -2982,6 +2998,7 @@
 			pos += tlen;
 			g_free(tag); /* This was allocated back in VALID_TAG() */
 		} else if (imhtml->edit.link == NULL &&
+				!(options & GTK_IMHTML_NO_SMILEY) &&
 				gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) {
 			GtkIMHtmlFontDetail *fd;
 			gchar *sml = NULL;
--- a/pidgin/gtkimhtml.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkimhtml.h	Tue Dec 18 09:20:01 2007 +0000
@@ -225,7 +225,8 @@
 	GTK_IMHTML_RETURN_LOG          = 1 << 7,
 	GTK_IMHTML_USE_POINTSIZE       = 1 << 8,
 	GTK_IMHTML_NO_FORMATTING       = 1 << 9,
-	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10
+	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10,
+	GTK_IMHTML_NO_SMILEY           = 1 << 11,
 } GtkIMHtmlOptions;
 
 enum {
--- a/pidgin/gtkimhtmltoolbar.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1096,8 +1096,8 @@
 		{PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, do_small, &toolbar->smaller_size, _("Decrease Font Size")},
 		{"", NULL, NULL, NULL},
 		{PIDGIN_STOCK_TOOLBAR_FONT_FACE, toggle_font, &toolbar->font, _("Font Face")},
-		{PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
-		{PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
+		{PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
+		{PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
 		{"", NULL, NULL, NULL},
 		{PIDGIN_STOCK_CLEAR, clear_formatting_cb, &toolbar->clear, _("Reset Formatting")},
 		{"", NULL, NULL, NULL},
--- a/pidgin/gtknotify.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtknotify.c	Tue Dec 18 09:20:01 2007 +0000
@@ -284,6 +284,8 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return dialog;
@@ -684,6 +686,8 @@
 	g_object_set_data(G_OBJECT(window), "info-widget", imhtml);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 
 	return window;
@@ -894,6 +898,8 @@
 	pidgin_notify_searchresults_new_rows(gc, results, data);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 	return data;
 }
--- a/pidgin/gtkpounce.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkpounce.c	Tue Dec 18 09:20:01 2007 +0000
@@ -1317,7 +1317,6 @@
 pidgin_pounces_manager_show(void)
 {
 	PouncesManager *dialog;
-	GtkWidget *bbox;
 	GtkWidget *button;
 	GtkWidget *list;
 	GtkWidget *vbox;
@@ -1334,7 +1333,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1343,61 +1342,33 @@
 					 G_CALLBACK(pounces_manager_configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved buddy pounces */
 	list = create_pounces_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
-	/* 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(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
+	/* Add button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(pounces_manager_add_cb), dialog);
+	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 
-	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 	purple_signal_connect(purple_connections_get_handle(), "signed-on",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
-	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(pounces_manager_modify_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_modify_cb), dialog);
+	dialog->modify_button = button;
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(pounces_manager_delete_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_delete_cb), dialog);
+	dialog->delete_button = button;
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(pounces_manager_close_cb), dialog);
 
 	gtk_widget_show(win);
 }
--- a/pidgin/gtkprefs.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkprefs.c	Tue Dec 18 09:20:01 2007 +0000
@@ -2150,7 +2150,6 @@
 void pidgin_prefs_show(void)
 {
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
 
@@ -2166,31 +2165,20 @@
 	/* Back to instant-apply! I win!  BU-HAHAHA! */
 
 	/* Create the window */
-	prefs = pidgin_create_window(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
+	prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
 	g_signal_connect(G_OBJECT(prefs), "destroy",
 					 G_CALLBACK(delete_prefs), NULL);
 
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(prefs), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(prefs), FALSE, PIDGIN_HIG_BORDER);
 
 	/* The notebook */
 	prefsnotebook = notebook = gtk_notebook_new ();
 	gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
 	gtk_widget_show(prefsnotebook);
 
-	/* The buttons to press! */
-	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_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show (bbox);
-
-	button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(prefs), GTK_STOCK_CLOSE, NULL, NULL);
 	g_signal_connect_swapped(G_OBJECT(button), "clicked",
 							 G_CALLBACK(gtk_widget_destroy), prefs);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 
 	prefs_notebook_init();
 
--- a/pidgin/gtkprivacy.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkprivacy.c	Tue Dec 18 09:20:01 2007 +0000
@@ -45,6 +45,7 @@
 	GtkWidget *add_button;
 	GtkWidget *remove_button;
 	GtkWidget *clear_button;
+	GtkWidget *close_button;
 
 	GtkWidget *button_box;
 	GtkWidget *allow_widget;
@@ -259,19 +260,22 @@
 
 	gtk_widget_hide(dialog->allow_widget);
 	gtk_widget_hide(dialog->block_widget);
-	gtk_widget_hide(dialog->button_box);
+	gtk_widget_hide_all(dialog->button_box);
 
 	if (new_type == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = TRUE;
 	}
 	else if (new_type == PURPLE_PRIVACY_DENY_USERS) {
 		gtk_widget_show(dialog->block_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
 
+	gtk_widget_show_all(dialog->close_button);
+	gtk_widget_show(dialog->button_box);
+
 	purple_blist_schedule_save();
 	pidgin_blist_refresh(purple_get_blist());
 }
@@ -355,7 +359,6 @@
 privacy_dialog_new(void)
 {
 	PidginPrivacyDialog *dialog;
-	GtkWidget *bbox;
 	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *button;
@@ -367,15 +370,13 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
+	dialog->win = pidgin_create_dialog(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog->win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Description label */
 	label = gtk_label_new(
@@ -433,52 +434,27 @@
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->block_widget, TRUE, TRUE, 0);
 
 	/* Add the button box for Add, Remove, Clear */
-	dialog->button_box = bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+	dialog->button_box = pidgin_dialog_get_action_area(GTK_DIALOG(dialog->win));
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_ADD, G_CALLBACK(add_cb), dialog);
 	dialog->add_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_cb), dialog);
 
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_REMOVE, G_CALLBACK(remove_cb), dialog);
 	dialog->remove_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_cb), dialog);
 
 	/* Clear button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLEAR, G_CALLBACK(clear_cb), dialog);
 	dialog->clear_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(clear_cb), dialog);
-
-	/* Another button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show(bbox);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog);
+	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_cb), dialog);
-
+	type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog);
+#if 0
 	if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
 		gtk_widget_show(dialog->button_box);
@@ -489,7 +465,7 @@
 		gtk_widget_show(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
-
+#endif
 	return dialog;
 }
 
--- a/pidgin/gtkrequest.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkrequest.c	Tue Dec 18 09:20:01 2007 +0000
@@ -439,6 +439,8 @@
 	pidgin_set_accessible_label (entry, label);
 	data->u.input.entry = entry;
 
+	pidgin_auto_parent_window(dialog);
+
 	/* Show everything. */
 	gtk_widget_show(dialog);
 
@@ -546,6 +548,8 @@
 	g_object_set_data(G_OBJECT(dialog), "radio", radio);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -661,6 +665,8 @@
 		gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_action);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -1059,7 +1065,6 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *hbox;
-	GtkWidget *bbox;
 	GtkWidget *frame;
 	GtkWidget *label;
 	GtkWidget *table;
@@ -1089,9 +1094,9 @@
 
 
 #ifdef _WIN32
-	data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #else /* !_WIN32 */
-	data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #endif /* _WIN32 */
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1099,7 +1104,7 @@
 
 	/* Setup the main horizontal box */
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), hbox);
+	gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
 	gtk_widget_show(hbox);
 
 	/* Dialog icon. */
@@ -1382,39 +1387,21 @@
 
 	g_object_unref(sg);
 
-	/* 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(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(text_to_stock(cancel_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_cancel_cb), data);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(text_to_stock(ok_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
 	data->ok_button = button;
-
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 	gtk_window_set_default(GTK_WINDOW(win), button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_ok_cb), data);
-
 	if (!purple_request_fields_all_required_filled(fields))
 		gtk_widget_set_sensitive(button, FALSE);
 
+	pidgin_auto_parent_window(win);
+
 	gtk_widget_show(win);
 
 	return data;
@@ -1622,6 +1609,8 @@
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
 #endif /* FILECHOOSER */
 
+	pidgin_auto_parent_window(filesel);
+
 	data->dialog = filesel;
 	gtk_widget_show(filesel);
 
@@ -1673,6 +1662,8 @@
 #endif
 
 	data->dialog = dirsel;
+	pidgin_auto_parent_window(dirsel);
+
 	gtk_widget_show(dirsel);
 
 	return (void *)data;
--- a/pidgin/gtkroomlist.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkroomlist.c	Tue Dec 18 09:20:01 2007 +0000
@@ -531,15 +531,13 @@
 	dialog->account = account;
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(vbox), vbox2);
@@ -584,19 +582,14 @@
 	gtk_widget_show(dialog->progress);
 
 	/* button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
 	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_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
 
 	/* stop button */
-	dialog->stop_button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->stop_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->stop_button), "clicked",
+	dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP,
 	                 G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(dialog->stop_button, FALSE);
-	gtk_widget_show(dialog->stop_button);
 
 	/* list button */
 	dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH,
@@ -625,11 +618,8 @@
 	gtk_widget_show(dialog->join_button);
 
 	/* close button */
-	dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->close_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->close_button), "clicked",
+	dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE,
 					 G_CALLBACK(close_button_cb), dialog);
-	gtk_widget_show(dialog->close_button);
 
 	/* show the dialog window and return the dialog */
 	gtk_widget_show(dialog->window);
--- a/pidgin/gtksavedstatuses.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Tue Dec 18 09:20:01 2007 +0000
@@ -594,7 +594,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -603,18 +603,14 @@
 					 G_CALLBACK(configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved status states */
 	list = create_saved_status_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
 	/* 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(vbox), bbox, FALSE, TRUE, 0);
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
@@ -627,36 +623,23 @@
 					 G_CALLBACK(status_window_use_cb), dialog);
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_add_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD,
+			G_CALLBACK(status_window_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY,
+			G_CALLBACK(status_window_modify_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+	/* Delete button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE,
+			G_CALLBACK(status_window_delete_cb), dialog);
+	dialog->delete_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_modify_cb), dialog);
-
-	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_delete_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(status_window_close_cb), dialog);
 
 	purple_signal_connect(purple_savedstatuses_get_handle(),
 			"savedstatus-changed", status_window,
@@ -1147,14 +1130,13 @@
 	if (edit)
 		dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
 
-	dialog->window = win = pidgin_create_window(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1257,23 +1239,18 @@
 		(saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status));
 
 	/* Button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 	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(vbox), bbox, FALSE, TRUE, 0);
 
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(status_editor_cancel_cb), dialog);
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
 										   PIDGIN_BUTTON_HORIZONTAL);
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
@@ -1284,19 +1261,15 @@
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	dialog->save_button = GTK_BUTTON(button);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE,
+			G_CALLBACK(status_editor_ok_cb), dialog);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_ok_cb), dialog);
+	dialog->save_button = GTK_BUTTON(button);
 
 	gtk_widget_show_all(win);
 	g_object_unref(sg);
@@ -1447,8 +1420,6 @@
 	char *tmp;
 	SubStatusEditor *dialog;
 	GtkSizeGroup *sg;
-	GtkWidget *bbox;
-	GtkWidget *button;
 	GtkWidget *combo;
 	GtkWidget *hbox;
 	GtkWidget *frame;
@@ -1486,15 +1457,14 @@
 	dialog->account = account;
 
 	tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
-	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
+	dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
 	g_free(tmp);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(substatus_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1543,25 +1513,13 @@
 	dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar);
 	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
 
-	/* 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(vbox), bbox, FALSE, TRUE, 0);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(substatus_editor_cancel_cb), dialog);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OK);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_ok_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_OK,
+			G_CALLBACK(substatus_editor_ok_cb), dialog);
 
 	/* Seed the input widgets with the current values */
 
--- a/pidgin/gtkutils.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkutils.c	Tue Dec 18 09:20:01 2007 +0000
@@ -132,12 +132,9 @@
 	}
 }
 
-GtkWidget *
-pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+static
+void pidgin_window_init(GtkWindow *wnd, const char *title, guint border_width, const char *role, gboolean resizable)
 {
-	GtkWindow *wnd = NULL;
-
-	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
 	if (title)
 		gtk_window_set_title(wnd, title);
 #ifdef _WIN32
@@ -148,11 +145,63 @@
 	if (role)
 		gtk_window_set_role(wnd, role);
 	gtk_window_set_resizable(wnd, resizable);
+}
+
+GtkWidget *
+pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+
+	return GTK_WIDGET(wnd);
+}
+
+GtkWidget *
+pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_dialog_new());
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+	g_object_set(G_OBJECT(wnd), "has-separator", FALSE, NULL);
 
 	return GTK_WIDGET(wnd);
 }
 
 GtkWidget *
+pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing)
+{
+	GtkBox *vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
+	gtk_box_set_homogeneous(vbox, homogeneous);
+	gtk_box_set_spacing(vbox, spacing);
+	return GTK_WIDGET(vbox);
+}
+
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->vbox;
+}
+
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->action_area;
+}
+
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata)
+{
+	GtkWidget *button = gtk_button_new_from_stock(label);
+	GtkWidget *bbox = pidgin_dialog_get_action_area(dialog);
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	if (callback)
+		g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata);
+	gtk_widget_show(button);
+	return button;
+}
+
+GtkWidget *
 pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
 {
 	GtkWidget *frame;
@@ -3269,3 +3318,107 @@
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
 }
 
+gboolean pidgin_auto_parent_window(GtkWidget *widget)
+{
+#if 0
+	/* This looks at the most recent window that received focus, and makes
+	 * that the parent window. */
+#ifndef _WIN32
+	static GdkAtom _WindowTime = GDK_NONE;
+	static GdkAtom _Cardinal = GDK_NONE;
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	time_t window_time = 0;
+
+	windows = gtk_window_list_toplevels();
+
+	if (_WindowTime == GDK_NONE) {
+		_WindowTime = gdk_x11_xatom_to_atom(gdk_x11_get_xatom_by_name("_NET_WM_USER_TIME"));
+	}
+	if (_Cardinal == GDK_NONE) {
+		_Cardinal = gdk_atom_intern("CARDINAL", FALSE);
+	}
+
+	while (windows) {
+		GtkWidget *window = windows->data;
+		guchar *data = NULL;
+		int al = 0;
+		time_t value;
+
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window))
+			continue;
+
+		if (!gdk_property_get(window->window, _WindowTime, _Cardinal, 0, sizeof(time_t), FALSE,
+				NULL, NULL, &al, &data))
+			continue;
+		value = *(time_t *)data;
+		if (window_time < value) {
+			window_time = value;
+			parent = window;
+		}
+		g_free(data);
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		if (!gtk_get_current_event() && gtk_window_has_toplevel_focus(GTK_WINDOW(parent))) {
+			/* The window is in focus, and the new window was not triggered by a keypress/click
+			 * event. So do not set it transient, to avoid focus stealing and all that.
+			 */
+			return FALSE;
+		}
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+#else
+	/* This finds the currently active window and makes that the parent window. */
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	GdkEvent *event = gtk_get_current_event();
+	GdkWindow *menu = NULL;
+
+	if (event == NULL)
+		/* The window was not triggered by a user action. */
+		return FALSE;
+
+	/* We need to special case events from a popup menu. */
+	if (event->type == GDK_BUTTON_RELEASE) {
+		/* XXX: Neither of the following works:
+			menu = event->button.window;
+			menu = gdk_window_get_parent(event->button.window);
+			menu = gdk_window_get_toplevel(event->button.window);
+		*/
+	} else if (event->type == GDK_KEY_PRESS)
+		menu = event->key.window;
+
+	windows = gtk_window_list_toplevels();
+	while (windows) {
+		GtkWidget *window = windows->data;
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window)) {
+			continue;
+		}
+
+		if (gtk_window_has_toplevel_focus(GTK_WINDOW(window)) ||
+				(menu && menu == window->window)) {
+			parent = window;
+			break;
+		}
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+}
+
--- a/pidgin/gtkutils.h	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/gtkutils.h	Tue Dec 18 09:20:01 2007 +0000
@@ -121,6 +121,61 @@
 GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
 
 /**
+ * Creates a new dialog window
+ *
+ * @param title        The window title, or @c NULL
+ * @param border_width The window's desired border width
+ * @param role         A string indicating what the window is responsible for doing, or @c NULL
+ * @param resizable    Whether the window should be resizable (@c TRUE) or not (@c FALSE)
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ * @param homogeneous  TRUE if all children are to be given equal space allotments. 
+ * @param spacing      the number of pixels to place by default between children
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog);
+
+/**
+ * Add a button to a dialog created by #pidgin_create_dialog.
+ *
+ * @param dialog         The dialog window
+ * @param label          The stock-id or the label for the button
+ * @param callback       The callback function for the button
+ * @param callbackdata   The user data for the callback function
+ *
+ * @return The created button.
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata);
+
+/**
+ * Retrieves the action area (button box) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog);
+
+/**
  * Toggles the sensitivity of a widget.
  *
  * @param widget    @c NULL. Used for signal handlers.
@@ -725,5 +780,15 @@
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
+/**
+ * Automatically make a window transient to a suitable parent window.
+ *
+ * @param window    The window to make transient.
+ *
+ * @return  Whether the window was made transient or not.
+ * @since 2.4.0
+ */
+gboolean pidgin_auto_parent_window(GtkWidget *window);
+
 #endif /* _PIDGINUTILS_H_ */
 
--- a/pidgin/plugins/pidginrc.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/plugins/pidginrc.c	Tue Dec 18 09:20:01 2007 +0000
@@ -30,17 +30,29 @@
 static const gchar *color_prefs[] = {
 	"/plugins/gtk/purplerc/color/GtkWidget::cursor-color",
 	"/plugins/gtk/purplerc/color/GtkWidget::secondary-cursor-color",
-	"/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color"
+	"/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::send-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::receive-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::highlight-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::action-name-color"
 };
 static const gchar *color_prefs_set[] = {
 	"/plugins/gtk/purplerc/set/color/GtkWidget::cursor-color",
 	"/plugins/gtk/purplerc/set/color/GtkWidget::secondary-cursor-color",
-	"/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color"
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::send-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::receive-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::highlight-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::action-name-color"
 };
 static const gchar *color_names[] = {
 	N_("Cursor Color"),
 	N_("Secondary Cursor Color"),
-	N_("Hyperlink Color")
+	N_("Hyperlink Color"),
+	N_("Sent Message Name Color"),
+	N_("Received Message Name Color"),
+	N_("Highlighted Message Name Color"),
+	N_("Action Message Name Color")
 };
 static GtkWidget *color_widgets[G_N_ELEMENTS(color_prefs)];
 
--- a/pidgin/win32/gtkwin32dep.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Tue Dec 18 09:20:01 2007 +0000
@@ -40,6 +40,7 @@
 
 #include "debug.h"
 #include "notify.h"
+#include "network.h"
 
 #include "resource.h"
 #include "idletrack.h"
@@ -51,6 +52,7 @@
 #include "gtkwin32dep.h"
 #include "win32dep.h"
 #include "gtkconv.h"
+#include "gtkconn.h"
 #include "util.h"
 #include "wspell.h"
 
@@ -64,6 +66,7 @@
 
 typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO);
 static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL;
+static gboolean pwm_handles_connections = TRUE;
 
 
 /*
@@ -202,6 +205,43 @@
 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
 #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)
 
+static void*
+winpidgin_netconfig_changed_cb(void *data)
+{
+	pwm_handles_connections = FALSE;
+
+	return NULL;
+}
+
+static void*
+winpidgin_get_handle(void)
+{
+	static int handle;
+
+	return &handle;
+}
+
+static gboolean
+winpidgin_pwm_reconnect()
+{
+	purple_signal_disconnect(purple_network_get_handle(), "network-configuration-changed",
+		winpidgin_get_handle(), PURPLE_CALLBACK(winpidgin_netconfig_changed_cb));
+
+	if (pwm_handles_connections == TRUE) {
+		PurpleConnectionUiOps *ui_ops = pidgin_connections_get_ui_ops();
+
+		purple_debug_info("winpidgin", "Resumed from standby, reconnecting accounts.\n");
+
+		if (ui_ops != NULL && ui_ops->network_connected != NULL)
+			ui_ops->network_connected();
+	} else {
+		purple_debug_info("winpidgin", "Resumed from standby, gtkconn will handle reconnecting.\n");
+		pwm_handles_connections = TRUE;
+	}
+
+	return FALSE;
+}
+
 static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 	if (msg == PIDGIN_WM_FOCUS_REQUEST) {
@@ -213,6 +253,28 @@
 		purple_debug_info("winpidgin", "Got protocol handler request: %s\n", proto_msg ? proto_msg : "");
 		purple_got_protocol_handler_uri(proto_msg);
 		return TRUE;
+	} else if (msg == WM_POWERBROADCAST) {
+		if (wparam == PBT_APMQUERYSUSPEND) {
+			purple_debug_info("winpidgin", "Windows requesting permission to suspend.\n");
+			return TRUE;
+		} else if (wparam == PBT_APMSUSPEND) {
+			PurpleConnectionUiOps *ui_ops = pidgin_connections_get_ui_ops();
+
+			purple_debug_info("winpidgin", "Entering system standby, disconnecting accounts.\n");
+
+			if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
+				ui_ops->network_disconnected();
+
+			purple_signal_connect(purple_network_get_handle(), "network-configuration-changed", winpidgin_get_handle(),
+				PURPLE_CALLBACK(winpidgin_netconfig_changed_cb), NULL);
+
+			return TRUE;
+		} else if (wparam == PBT_APMRESUMESUSPEND) {
+			purple_debug_info("winpidgin", "Resuming from system standby.\n");
+			/* TODO: It seems like it'd be wise to use the NLA message, if possible, instead of this. */
+			purple_timeout_add_seconds(1, winpidgin_pwm_reconnect, NULL);
+			return TRUE;
+		}
 	}
 
 	return DefWindowProc(hwnd, msg, wparam, lparam);
@@ -242,7 +304,7 @@
 
 	/* Create the window */
 	if(!(win_hwnd = CreateWindow(wname, TEXT("WinpidginMsgWin"), 0, 0, 0, 0, 0,
-			HWND_MESSAGE, NULL, winpidgin_exe_hinstance(), 0))) {
+			NULL, NULL, winpidgin_exe_hinstance(), 0))) {
 		purple_debug_error("winpidgin",
 			"Unable to create message window.\n");
 		return NULL;
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Tue Dec 18 09:20:01 2007 +0000
@@ -1154,11 +1154,21 @@
 !macro RunCheckMacro UN
 Function ${UN}RunCheck
   Push $R0
-  System::Call 'kernel32::OpenMutex(i 2031617, b 0, t "pidgin_is_running") i .R0'
-  IntCmp $R0 0 done
-    MessageBox MB_OK|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDOK
+  Push $R1
+
+  IntOp $R1 0 + 0
+  retry_runcheck:
+  ; Close the Handle (needed if we're retrying)
+  IntCmp $R1 0 +2
+    System::Call 'kernel32::CloseHandle(i $R1) i .R1'
+  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_is_running") i .R1 ?e'
+  Pop $R0
+  IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume
+    MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck
     Abort
+
   done:
+  Pop $R1
   Pop $R0
 FunctionEnd
 !macroend
@@ -1169,10 +1179,16 @@
   Push $R0
   Push $R1
   Push $R2
-  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .r1 ?e'
+
+  IntOp $R1 0 + 0
+  retry_runcheck:
+  ; Close the Handle (needed if we're retrying)
+  IntCmp $R1 0 +2
+    System::Call 'kernel32::CloseHandle(i $R1) i .R1'
+  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .R1 ?e'
   Pop $R0
-  StrCmp $R0 0 +3
-    MessageBox MB_OK|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDOK
+  IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume
+    MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck
     Abort
   Call RunCheck
   StrCpy $name "Pidgin ${PIDGIN_VERSION}"
--- a/pidgin/win32/winpidgin.c	Tue Dec 18 09:17:39 2007 +0000
+++ b/pidgin/win32/winpidgin.c	Tue Dec 18 09:20:01 2007 +0000
@@ -445,11 +445,12 @@
 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
 #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)
 
-static BOOL winpidgin_set_running() {
+static BOOL winpidgin_set_running(BOOL fail_if_running) {
 	HANDLE h;
 
 	if ((h = CreateMutex(NULL, FALSE, "pidgin_is_running"))) {
-		if (GetLastError() == ERROR_ALREADY_EXISTS) {
+		DWORD err = GetLastError();
+		if (err == ERROR_ALREADY_EXISTS && fail_if_running) {
 			HWND msg_win;
 
 			printf("An instance of Pidgin is already running.\n");
@@ -465,7 +466,8 @@
 				NULL, MB_OK | MB_TOPMOST);
 
 			return FALSE;
-		}
+		} else
+			printf("Error (%d) accessing \"pidgin_is_running\" mutex.\n", err);
 	}
 	return TRUE;
 }
@@ -628,8 +630,8 @@
 
 	winpidgin_set_locale();
 	/* If help, version or multiple flag used, do not check Mutex */
-	if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v") && !strstr(lpszCmdLine, "-m"))
-		if (!getenv("PIDGIN_MULTI_INST") && !winpidgin_set_running())
+	if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v"))
+		if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && strstr(lpszCmdLine, "-m") == NULL))
 			return 0;
 
 	/* Now we are ready for Pidgin .. */