changeset 25497:67ad619bd97f

propagate from branch 'im.pidgin.pidgin' (head 3d1e6b8637ff9133c8f17e9aeaeea1284c898d86) to branch 'im.pidgin.pidgin.yaz' (head cf1ed778db33093adccaec9aa0399ac72a0b712f)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 04 Jun 2007 07:22:38 +0000
parents 794abbe3e14e (diff) 295777a9e2a4 (current diff)
children e62ec4b475ff
files libpurple/protocols/msn/msn.c libpurple/protocols/msn/switchboard.c libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo_packet.c libpurple/util.c pidgin/gtkimhtml.c
diffstat 29 files changed, 518 insertions(+), 315 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Jun 02 15:41:36 2007 +0000
+++ b/COPYRIGHT	Mon Jun 04 07:22:38 2007 +0000
@@ -8,6 +8,7 @@
 Dave Ahlswede
 Manuel Amador
 Matt Amato
+Elliott Sales de Andrade
 Geoffrey Antos
 Daniel Atallah
 Paul Aurich
--- a/ChangeLog	Sat Jun 02 15:41:36 2007 +0000
+++ b/ChangeLog	Mon Jun 04 07:22:38 2007 +0000
@@ -3,12 +3,16 @@
 version 2.0.2 (??/??/????):
 	Pidgin:
 	* Added a custom conversation font option to preferences
+	* Fixed smiley ordering in the insert smiley popup to be more intuitive
 
 	libpurple:
 	* Moving an ICQ buddy from one group to another no longer
 	  re-requests authorization from that person (Rene Hausleitner)
 	* Added nullprpl, an example protocol plugin (Ryan Barrett)
 	* Fixed SOCKS5 bug which caused Jabber file receiving to fail
+	* Remove MSN's random "Authorization Failed" dialogs
+	* Fix MSN to correctly detect incorrect passwords and disable the account
+	* Get User Info on MSN is now more reliable & accurate
 
 	Finch:
 	* Auto account reconnecting
--- a/libpurple/idle.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/idle.c	Mon Jun 04 07:22:38 2007 +0000
@@ -121,7 +121,7 @@
 	gboolean report_idle;
 	GList *l;
 	gint away_seconds = 0;
-	gint idle_recheck_interval;
+	gint idle_recheck_interval = 0;
 
 	purple_signal_emit(purple_blist_get_handle(), "update-idle");
 
--- a/libpurple/plugins/joinpart.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/plugins/joinpart.c	Mon Jun 04 07:22:38 2007 +0000
@@ -210,7 +210,7 @@
 	 * we don't have to worry one will be called after this. */
 	g_hash_table_destroy((GHashTable *)data[0]);
 
-	g_source_remove(GPOINTER_TO_UINT(data[1]));
+	purple_timeout_remove(GPOINTER_TO_UINT(data[1]));
 	g_free(data);
 
 	return TRUE;
--- a/libpurple/plugins/log_reader.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Mon Jun 04 07:22:38 2007 +0000
@@ -1482,46 +1482,66 @@
 			 * ">
 			 * Then, replace the next " " (or add this if the end-of-line is reached) with:
 			 * </a>
+			 * 
+			 * As implemented, this isn't perfect, but it should cover common cases.
 			 */
 			link_temp_line = NULL;
-			while ((link = g_strstr_len(line, strlen(line), "(Link: "))) {
-				GString *temp;
+			while ((link = strstr(line, "(Link: ")))
+			{
+				char *tmp = link;
 
-				if (!*link)
-					continue;
+				link += 7;
+				if (*link)
+				{
+					char *end_paren;
+					char *space;
+					GString *temp;
 
-				*link = '\0';
-				link++;
+					if (!(end_paren = strstr(link, ")")))
+					{
+						/* Something is not as we expect.  Bail out. */
+						break;
+					}
 
-				temp = g_string_new(line);
-				g_string_append(temp, "<a href=\"");
+					*tmp = '\0';
+					temp = g_string_new(line);
+
+					/* Start an <a> tag. */
+					g_string_append(temp, "<a href=\"");
+
+					/* Append up to the ) */
+					g_string_append_len(temp, link, end_paren - link);
 
-				if (strlen(link) >= 6) {
-					link += (sizeof("(Link: ") - 1);
+					/* Finish the <a> tag. */
+					g_string_append(temp, "\">");
+
+					/* The \r is a bit of a hack to keep there from being a \r in
+					 * the link text, which may not matter. */
+					if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r")))
+					{
+						g_string_append_len(temp, end_paren + 1, space - end_paren - 1);
 
-					while (*link && *link != ')') {
-						g_string_append_c(temp, *link);
-						link++;
+						/* Close the <a> tag. */
+						g_string_append(temp, "</a>");
+
+						space++;
+						if (*space)
+						{
+							g_string_append_c(temp, ' ');
+							/* Keep the rest of the line. */
+							g_string_append(temp, space);
+						}
 					}
-					if (link) {
-						link++;
-
-						g_string_append(temp, "\">");
-						while (*link && *link != ' ') {
-							g_string_append_c(temp, *link);
-							link++;
-						}
+					else
+					{
+						/* There is no space before the end of the line. */
+						g_string_append(temp, end_paren + 1);
+						/* Close the <a> tag. */
 						g_string_append(temp, "</a>");
 					}
 
-					g_string_append(temp, link);
-
-					/* Free the last round's line. */
-					if (link_temp_line)
-						g_free(line);
-
-					line = temp->str;
-					g_string_free(temp, FALSE);
+					g_free(link_temp_line);
+					line = g_string_free(temp, FALSE);
 
 					/* Save this memory location so we can free it later. */
 					link_temp_line = line;
@@ -1661,8 +1681,7 @@
 
 			g_string_append_c(formatted, '\n');
 
-			if (link_temp_line)
-				g_free(link_temp_line);
+			g_free(link_temp_line);
 
 			c++;
 			line = c;
--- a/libpurple/plugins/perl/perl-handlers.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Mon Jun 04 07:22:38 2007 +0000
@@ -184,7 +184,7 @@
 	timeout_handlers = g_list_remove(timeout_handlers, handler);
 
 	if (handler->iotag > 0)
-		g_source_remove(handler->iotag);
+		purple_timeout_remove(handler->iotag);
 
 	if (handler->callback != NULL)
 		SvREFCNT_dec(handler->callback);
@@ -405,7 +405,7 @@
 
 	timeout_handlers = g_list_append(timeout_handlers, handler);
 
-	handler->iotag = g_timeout_add(seconds * 1000, perl_timeout_cb, handler);
+	handler->iotag = purple_timeout_add(seconds * 1000, perl_timeout_cb, handler);
 }
 
 void
--- a/libpurple/protocols/Makefile.am	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/Makefile.am	Mon Jun 04 07:22:38 2007 +0000
@@ -1,4 +1,4 @@
-EXTRA_DIST = Makefile.mingw null/
+EXTRA_DIST = Makefile.mingw
 
 DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc toc simple yahoo zephyr
 
--- a/libpurple/protocols/msn/msn.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Jun 04 07:22:38 2007 +0000
@@ -1432,6 +1432,22 @@
 	if (found) \
 		sect_info = TRUE;
 
+#define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \
+	found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
+			"\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \
+	if (found) \
+		sect_info = TRUE;
+
+static char *
+msn_info_strip_search_link(const char *field, size_t len)
+{
+	const char *c;
+	if ((c = strstr(field, " (http://spaces.live.com/default.aspx?page=searchresults")) == NULL &&
+		(c = strstr(field, " (http://spaces.msn.com/default.aspx?page=searchresults")) == NULL)
+		return g_strndup(field, len);
+	return g_strndup(field, c - field);
+}
+
 static void
 msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
 		const gchar *url_text, size_t len, const gchar *error_message)
@@ -1547,10 +1563,10 @@
 	
 	/* General */
 	MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname"));
-	MSN_GOT_INFO_GET_FIELD("Age", _("Age"));
-	MSN_GOT_INFO_GET_FIELD("Gender", _("Gender"));
-	MSN_GOT_INFO_GET_FIELD("Occupation", _("Occupation"));
-	MSN_GOT_INFO_GET_FIELD("Location", _("Location"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation"));
+	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location"));
 
 	/* Extract their Interests and put it in */
 	found = purple_markup_extract_info_field(stripped, stripped_len, user_info,
@@ -1811,7 +1827,10 @@
 		/* This doesn't work with the new spaces profiles - Stu 3/2/06
 		char *p = strstr(url_buffer, "Unknown Member </TITLE>");
 		 * This might not work for long either ... */
+		/* Nope, it failed some time before 5/2/07 :(
 		char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\"");
+		 * Let's see how long this one holds out for ... */
+		char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
 		PurpleBuddy *b = purple_find_buddy
 				(purple_connection_get_account(info_data->gc), info_data->name);
 		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
--- a/libpurple/protocols/msn/msn.h	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Mon Jun 04 07:22:38 2007 +0000
@@ -67,7 +67,7 @@
 
 #define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"
 #define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
-#define PROFILE_URL "http://spaces.msn.com/profile.aspx?mem="
+#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
 
 #define USEROPT_HOTMAIL 0
 
--- a/libpurple/protocols/msn/switchboard.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Mon Jun 04 07:22:38 2007 +0000
@@ -419,7 +419,14 @@
 				case MSN_SB_ERROR_TOO_FAST:
 					str_reason = _("Message could not be sent "
 								   "because we are sending too quickly:");
-					break;					
+					break;
+				case MSN_SB_ERROR_AUTHFAILED:
+					str_reason = _("Message could not be sent "
+								   "because we wer unable to establish a "
+								   "session with the server. This is "
+								   "likely a server problem, try again in "
+								   "a few minutes:");
+					break;
 				default:
 					str_reason = _("Message could not be sent "
 								   "because an error with "
@@ -967,9 +974,13 @@
  * Connect stuff
  **************************************************************************/
 static void
+ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error);
+
+static void
 connect_cb(MsnServConn *servconn)
 {
 	MsnSwitchBoard *swboard;
+	MsnTransaction *trans;
 	MsnCmdProc *cmdproc;
 	PurpleAccount *account;
 
@@ -984,16 +995,44 @@
 	{
 		swboard->empty = FALSE;
 
-		msn_cmdproc_send(cmdproc, "ANS", "%s %s %s",
-						 purple_account_get_username(account),
-						 swboard->auth_key, swboard->session_id);
+		trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s",
+									purple_account_get_username(account),
+									swboard->auth_key, swboard->session_id);
 	}
 	else
 	{
-		msn_cmdproc_send(cmdproc, "USR", "%s %s",
-						 purple_account_get_username(account),
-						 swboard->auth_key);
+		trans = msn_transaction_new(cmdproc, "USR", "%s %s",
+									purple_account_get_username(account),
+									swboard->auth_key);
 	}
+
+	msn_transaction_set_error_cb(trans, ans_usr_error);
+	msn_transaction_set_data(trans, swboard);
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static void
+ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	MsnSwitchBoard *swboard;
+	char **params;
+	char *passport;
+	int reason = MSN_SB_ERROR_UNKNOWN;
+
+	if (error == 911)
+	{
+		reason = MSN_SB_ERROR_AUTHFAILED;
+	}
+
+	purple_debug_warning("msn", "ans_usr_error: command %s gave error %i\n", trans->command, error);
+
+	params = g_strsplit(trans->params, " ", 0);
+	passport = params[0];
+	swboard = trans->data;
+
+	swboard_error_helper(swboard, reason, passport);
+
+	g_strfreev(params);
 }
 
 static void
--- a/libpurple/protocols/msn/switchboard.h	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.h	Mon Jun 04 07:22:38 2007 +0000
@@ -46,6 +46,7 @@
 	MSN_SB_ERROR_USER_OFFLINE, /**< The user to call is offline. */
 	MSN_SB_ERROR_CONNECTION, /**< There was a connection error. */
 	MSN_SB_ERROR_TOO_FAST, /**< We are sending too fast */
+	MSN_SB_ERROR_AUTHFAILED, /**< Authentication failed joining the switchboard session */
 	MSN_SB_ERROR_UNKNOWN /**< An unknown error occurred. */
 
 } MsnSBErrorType;
--- a/libpurple/protocols/null/nullprpl.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Mon Jun 04 07:22:38 2007 +0000
@@ -432,7 +432,7 @@
                     from_username, who, message);
 
   /* is the sender blocked by the recipient's privacy settings? */
-  if (!purple_privacy_check(to_acct, gc->account->username)) {
+  if (to_acct && !purple_privacy_check(to_acct, gc->account->username)) {
     char *msg = g_strdup_printf(
       _("Your message was blocked by %s's privacy settings."), who);
     purple_debug_info("nullprpl",
--- a/libpurple/protocols/oscar/flap_connection.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Mon Jun 04 07:22:38 2007 +0000
@@ -303,7 +303,7 @@
 		}
 	}
 
-	if (conn->fd != -1)
+	if (conn->fd >= 0)
 	{
 		if (conn->type == SNAC_FAMILY_LOCATE)
 			flap_connection_send_close(od, conn);
@@ -792,7 +792,7 @@
 			}
 
 			/* If there was an error then close the connection */
-			if (read == -1)
+			if (read < 0)
 			{
 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 					/* No worries */
@@ -853,7 +853,7 @@
 				break;
 			}
 
-			if (read == -1)
+			if (read < 0)
 			{
 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 					/* No worries */
@@ -902,7 +902,7 @@
 	ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 	if (ret <= 0)
 	{
-		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+		if (ret < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 			/* No worries */
 			return;
 
@@ -936,7 +936,7 @@
 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
 
 	/* If we haven't already started writing stuff, then start the cycle */
-	if ((conn->watcher_outgoing == 0) && (conn->fd != -1))
+	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
 	{
 		conn->watcher_outgoing = purple_input_add(conn->fd,
 				PURPLE_INPUT_WRITE, send_cb, conn);
--- a/libpurple/protocols/oscar/odc.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/oscar/odc.c	Mon Jun 04 07:22:38 2007 +0000
@@ -447,7 +447,7 @@
 		return;
 	}
 
-	if (read == -1)
+	if (read < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No worries */
--- a/libpurple/protocols/oscar/oscar.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Jun 04 07:22:38 2007 +0000
@@ -1625,8 +1625,7 @@
 			straight_to_hell, pos) == NULL)
 	{
 		char buf[256];
-		if (pos->modname)
-			g_free(pos->modname);
+		g_free(pos->modname);
 		g_free(pos);
 		g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  "
 			"Check %s for updates."), PURPLE_WEBSITE);
--- a/libpurple/protocols/oscar/peer.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/oscar/peer.c	Mon Jun 04 07:22:38 2007 +0000
@@ -173,12 +173,12 @@
 		purple_input_remove(conn->watcher_outgoing);
 		conn->watcher_outgoing = 0;
 	}
-	if (conn->listenerfd != -1)
+	if (conn->listenerfd >= 0)
 	{
 		close(conn->listenerfd);
 		conn->listenerfd = -1;
 	}
-	if (conn->fd != -1)
+	if (conn->fd >= 0)
 	{
 		close(conn->fd);
 		conn->fd = -1;
@@ -310,7 +310,7 @@
 		}
 
 		/* If there was an error then close the connection */
-		if (read == -1)
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
@@ -360,7 +360,7 @@
 		return;
 	}
 
-	if (read == -1)
+	if (read < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No worries */
@@ -422,7 +422,7 @@
 	wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 	if (wrotelen <= 0)
 	{
-		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+		if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 			/* No worries */
 			return;
 
@@ -462,7 +462,7 @@
 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
 
 	/* If we haven't already started writing stuff, then start the cycle */
-	if ((conn->watcher_outgoing == 0) && (conn->fd != -1))
+	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
 	{
 		conn->watcher_outgoing = purple_input_add(conn->fd,
 				PURPLE_INPUT_WRITE, send_cb, conn);
@@ -596,7 +596,7 @@
 	purple_debug_info("oscar", "Accepting connection on listener socket.\n");
 
 	conn->fd = accept(conn->listenerfd, &addr, &addrlen);
-	if (conn->fd == -1)
+	if (conn->fd < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No connection yet--no worries */
@@ -640,7 +640,7 @@
 	conn = data;
 	conn->listen_data = NULL;
 
-	if (listenerfd == -1)
+	if (listenerfd < 0)
 	{
 		/* Could not open listener socket */
 		peer_connection_trynext(conn);
--- a/libpurple/protocols/oscar/peer_proxy.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/oscar/peer_proxy.c	Mon Jun 04 07:22:38 2007 +0000
@@ -224,7 +224,7 @@
 		}
 
 		/* If there was an error then close the connection */
-		if (read == -1)
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
@@ -285,7 +285,8 @@
 			return;
 		}
 
-		if (read == -1)
+		/* If there was an error then close the connection */
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
--- a/libpurple/protocols/silc/silc.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Mon Jun 04 07:22:38 2007 +0000
@@ -119,12 +119,12 @@
 				NULL, 0);
 }
 
-static int
+static gboolean
 silcpurple_scheduler(gpointer *context)
 {
 	SilcPurple sg = (SilcPurple)context;
 	silc_client_run_one(sg->client);
-	return 1;
+	return TRUE;
 }
 
 static void
@@ -361,11 +361,7 @@
 	}
 
 	/* Schedule SILC using Glib's event loop */
-#ifndef _WIN32
-	sg->scheduler = g_timeout_add(5, (GSourceFunc)silcpurple_scheduler, sg);
-#else
-	sg->scheduler = g_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
-#endif
+	sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
 }
 
 static int
@@ -396,8 +392,8 @@
 	if (sg->conn)
 		silc_client_close_connection(sg->client, sg->conn);
 
-	g_source_remove(sg->scheduler);
-	g_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
+	purple_timeout_remove(sg->scheduler);
+	purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
 }
 
 
--- a/libpurple/protocols/yahoo/yahoo.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Mon Jun 04 07:22:38 2007 +0000
@@ -2310,7 +2310,7 @@
 			 * are you trying to pull? */
 			guchar *start;
 
-			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!");
+			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
 
 			start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
 			if (start) {
@@ -2377,7 +2377,11 @@
 	}
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+				error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2405,7 +2409,11 @@
 	}
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+				error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2507,12 +2515,16 @@
 	if (written < 0 && errno == EAGAIN)
 		written = 0;
 	else if (written <= 0) {
+		gchar *tmp;
 		g_free(yd->auth);
 		yd->auth = NULL;
 		if (gc->inpa)
 			purple_input_remove(gc->inpa);
 		gc->inpa = 0;
-		purple_connection_error(gc, _("Unable to connect."));
+		tmp = g_strdup_printf(_("Lost connection with %s:\n%s"),
+				"login.yahoo.com:80", strerror(errno));
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2533,7 +2545,11 @@
 	PurpleConnection *gc = data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with %s:\n%s"),
+				"login.yahoo.com:80", error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2616,8 +2632,7 @@
 
 	if (error_message != NULL)
 	{
-		/* TODO: Include error_message in the message below */
-		purple_connection_error(gc, _("Unable to connect."));
+		purple_connection_error(gc, error_message);
 		return;
 	}
 
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Mon Jun 04 07:22:38 2007 +0000
@@ -114,6 +114,29 @@
 	return len;
 }
 
+/*
+ * 'len' is the value given to us by the server that is supposed to
+ * be the length of 'data'.  But apparently there's a time when this
+ * length is incorrect.  Christopher Layne thinks it might be a bug
+ * in their server code.
+ *
+ * The following information is from Christopher:
+ *
+ * It sometimes happens when Yahoo! sends a packet continuation within
+ * chat.  Sometimes when joining a large chatroom the initial
+ * SERVICE_CHATJOIN packet will be so large that it will need to be
+ * split into multiple packets.  That's fine, except that the length
+ * of the second packet is wrong.  The packet has the same length as
+ * the first packet, and the length given in the header is the same,
+ * however the actual data in the packet is shorter than this length.
+ * So half of the packet contains good, valid data, and then the rest
+ * of the packet is junk.  Luckily there is a null terminator after
+ * the valid data and before the invalid data.
+ *
+ * What does all this mean?  It means that we parse through the data
+ * pulling out key/value pairs until we've parsed 'len' bytes, or until
+ * we run into a null terminator, whichever comes first.
+ */
 void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len)
 {
 	int pos = 0;
@@ -125,18 +148,8 @@
 
 	while (pos + 1 < len)
 	{
-		/* this is weird, and in one of the chat packets, and causes us to
-		 * think all the values are keys and all the keys are values after
-		 * this point if we don't handle it */
-		if (data[pos] == '\0') {
-			while (pos + 1 < len) {
-				if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
-					break;
-				pos++;
-			}
-			pos += 2;
-			continue;
-		}
+		if (data[pos] == '\0')
+			break;
 
 		pair = g_new0(struct yahoo_pair, 1);
 
--- a/libpurple/purple-remote	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/purple-remote	Mon Jun 04 07:22:38 2007 +0000
@@ -31,7 +31,7 @@
         return result
             
 def show_help():
-    print """This program uses DBus to communicate with purple.
+    print """This program uses D-Bus to communicate with purple.
 
 Usage:
 
@@ -96,8 +96,6 @@
     protocol = match.group(2)
     if protocol == "xmpp":
         protocol = "jabber"
-    if protocol == "aim" or protocol == "icq":
-        protocol = "oscar"
     if protocol is not None:
         protocol = "prpl-" + protocol
     command = match.group(5)
--- a/libpurple/util.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/util.c	Mon Jun 04 07:22:38 2007 +0000
@@ -46,6 +46,7 @@
 	} website;
 
 	char *url;
+	int num_times_redirected;
 	gboolean full;
 	char *user_agent;
 	gboolean http11;
@@ -1261,14 +1262,17 @@
 								pt->dest_tag = y; \
 								tags = g_list_prepend(tags, pt); \
 							} \
-							xhtml = g_string_append(xhtml, "<" y); \
-							c += strlen("<" x ); \
-							xhtml = g_string_append(xhtml, innards->str); \
-							xhtml = g_string_append_c(xhtml, '>'); \
+							if(xhtml) { \
+								xhtml = g_string_append(xhtml, "<" y); \
+								xhtml = g_string_append(xhtml, innards->str); \
+								xhtml = g_string_append_c(xhtml, '>'); \
+							} \
 							c = p + 1; \
 						} else { \
-							xhtml = g_string_append(xhtml, "&lt;"); \
-							plain = g_string_append_c(plain, '<'); \
+							if(xhtml) \
+								xhtml = g_string_append(xhtml, "&lt;"); \
+							if(plain) \
+								plain = g_string_append_c(plain, '<'); \
 							c++; \
 						} \
 						g_string_free(innards, TRUE); \
@@ -1277,16 +1281,19 @@
 						if(!g_ascii_strncasecmp(c, "<" x, strlen("<" x)) && \
 								(*(c+strlen("<" x)) == '>' || \
 								 !g_ascii_strncasecmp(c+strlen("<" x), "/>", 2))) { \
-							xhtml = g_string_append(xhtml, "<" y); \
+							if(xhtml) \
+								xhtml = g_string_append(xhtml, "<" y); \
 							c += strlen("<" x); \
 							if(*c != '/') { \
 								struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); \
 								pt->src_tag = x; \
 								pt->dest_tag = y; \
 								tags = g_list_prepend(tags, pt); \
-								xhtml = g_string_append_c(xhtml, '>'); \
+								if(xhtml) \
+									xhtml = g_string_append_c(xhtml, '>'); \
 							} else { \
-								xhtml = g_string_append(xhtml, "/>");\
+								if(xhtml) \
+									xhtml = g_string_append(xhtml, "/>");\
 							} \
 							c = strchr(c, '>') + 1; \
 							continue; \
@@ -1296,11 +1303,18 @@
 purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
 						  char **plain_out)
 {
-	GString *xhtml = g_string_new("");
-	GString *plain = g_string_new("");
+	GString *xhtml = NULL;
+	GString *plain = NULL;
 	GList *tags = NULL, *tag;
 	const char *c = html;
 
+	g_return_if_fail(xhtml_out != NULL || plain_out != NULL);
+
+	if(xhtml_out)
+		xhtml = g_string_new("");
+	if(plain_out)
+		plain = g_string_new("");
+
 	while(c && *c) {
 		if(*c == '<') {
 			if(*(c+1) == '/') { /* closing tag */
@@ -1316,7 +1330,8 @@
 				if(tag) {
 					while(tags) {
 						struct purple_parse_tag *pt = tags->data;
-						g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
+						if(xhtml)
+							g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
 						if(tags == tag)
 							break;
 						tags = g_list_remove(tags, pt);
@@ -1334,8 +1349,10 @@
 					if(*end == '>') {
 						c = end+1;
 					} else {
-						xhtml = g_string_append(xhtml, "&lt;");
-						plain = g_string_append_c(plain, '<');
+						if(xhtml)
+							xhtml = g_string_append(xhtml, "&lt;");
+						if(plain)
+							plain = g_string_append_c(plain, '<');
 						c++;
 					}
 				}
@@ -1364,7 +1381,8 @@
 				ALLOW_TAG("span");
 				ALLOW_TAG("strong");
 				ALLOW_TAG("ul");
-
+				ALLOW_TAG("img");
+				
 				/* we skip <HR> because it's not legal in XHTML-IM.  However,
 				 * we still want to send something sensible, so we put a
 				 * linebreak in its place. <BR> also needs special handling
@@ -1375,8 +1393,9 @@
 							!g_ascii_strncasecmp(c+3, "/>", 2) ||
 							!g_ascii_strncasecmp(c+3, " />", 3))) {
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<br/>");
-					if(*c != '\n')
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<br/>");
+					if(plain && *c != '\n')
 						plain = g_string_append_c(plain, '\n');
 					continue;
 				}
@@ -1386,7 +1405,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<u>", 3) || !g_ascii_strncasecmp(c, "<underline>", strlen("<underline>"))) {
@@ -1395,7 +1415,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>");
+					if (xhtml)
+						xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<s>", 3) || !g_ascii_strncasecmp(c, "<strike>", strlen("<strike>"))) {
@@ -1404,7 +1425,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<sub>", 5)) {
@@ -1413,7 +1435,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<sup>", 5)) {
@@ -1422,7 +1445,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
@@ -1516,7 +1540,10 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					if(style->len)
-						g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str));
+					{
+						if(xhtml)
+							g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str));
+					}
 					else
 						pt->ignore = TRUE;
 					g_string_free(style, TRUE);
@@ -1536,7 +1563,8 @@
 								color = g_string_append_c(color, *q);
 								q++;
 							}
-							g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
+							if(xhtml)
+								g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
 							g_string_free(color, TRUE);
 							if ((c = strchr(c, '>')) != NULL)
 								c++;
@@ -1557,14 +1585,17 @@
 				if(!g_ascii_strncasecmp(c, "<!--", strlen("<!--"))) {
 					char *p = strstr(c + strlen("<!--"), "-->");
 					if(p) {
-						xhtml = g_string_append(xhtml, "<!--");
+						if(xhtml)
+							xhtml = g_string_append(xhtml, "<!--");
 						c += strlen("<!--");
 						continue;
 					}
 				}
 
-				xhtml = g_string_append(xhtml, "&lt;");
-				plain = g_string_append_c(plain, '<');
+				if(xhtml)
+					xhtml = g_string_append(xhtml, "&lt;");
+				if(plain)
+					plain = g_string_append_c(plain, '<');
 				c++;
 			}
 		} else if(*c == '&') {
@@ -1577,29 +1608,31 @@
 				g_snprintf(buf, sizeof(buf), "%c", *c);
 				pln = buf;
 			}
-			xhtml = g_string_append_len(xhtml, c, len);
-			plain = g_string_append(plain, pln);
+			if(xhtml)
+				xhtml = g_string_append_len(xhtml, c, len);
+			if(plain)
+				plain = g_string_append(plain, pln);
 			c += len;
 		} else {
-			xhtml = g_string_append_c(xhtml, *c);
-			plain = g_string_append_c(plain, *c);
+			if(xhtml)
+				xhtml = g_string_append_c(xhtml, *c);
+			if(plain)
+				plain = g_string_append_c(plain, *c);
 			c++;
 		}
 	}
-	tag = tags;
-	while(tag) {
-		struct purple_parse_tag *pt = tag->data;
-		if(!pt->ignore)
-			g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
-		tag = tag->next;
+	if(xhtml) {
+		for (tag = tags; tag ; tag = tag->next) {
+			struct purple_parse_tag *pt = tag->data;
+			if(!pt->ignore)
+				g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
+		}
 	}
 	g_list_free(tags);
 	if(xhtml_out)
-		*xhtml_out = g_strdup(xhtml->str);
+		*xhtml_out = g_string_free(xhtml, FALSE);
 	if(plain_out)
-		*plain_out = g_strdup(plain->str);
-	g_string_free(xhtml, TRUE);
-	g_string_free(plain, TRUE);
+		*plain_out = g_string_free(plain, FALSE);
 }
 
 /* The following are probably reasonable changes:
@@ -3188,6 +3221,17 @@
 		g_hash_table_destroy(params);
 }
 
+/*
+ * TODO: Should probably add a "gboolean *ret_ishttps" parameter that
+ *       is set to TRUE if this URL is https, otherwise it is set to
+ *       FALSE.  But that change will break the API.
+ *
+ *       This is important for Yahoo! web messenger login.  They now
+ *       force https login, and if you access the web messenger login
+ *       page via http then it redirects you to the https version, but
+ *       purple_util_fetch_url() ignores the "https" and attempts to
+ *       fetch the URL via http again, which gets redirected again.
+ */
 gboolean
 purple_url_parse(const char *url, char **ret_host, int *ret_port,
 			   char **ret_path, char **ret_user, char **ret_passwd)
@@ -3208,12 +3252,16 @@
 
 	g_return_val_if_fail(url != NULL, FALSE);
 
-	if ((turl = strstr(url, "http://")) != NULL ||
-		(turl = strstr(url, "HTTP://")) != NULL)
+	if ((turl = purple_strcasestr(url, "http://")) != NULL)
 	{
 		turl += 7;
 		url = turl;
 	}
+	else if ((turl = purple_strcasestr(url, "https://")) != NULL)
+	{
+		turl += 8;
+		url = turl;
+	}
 
 	/* parse out authentication information if supplied */
 	/* Only care about @ char BEFORE the first / */
@@ -3293,85 +3341,92 @@
 			   PurpleUtilFetchUrlData *gfud)
 {
 	gchar *s;
-
-	if ((s = g_strstr_len(data, data_len, "Location: ")) != NULL)
+	gchar *new_url, *temp_url, *end;
+	gboolean full;
+	int len;
+
+	if ((s = g_strstr_len(data, data_len, "Location: ")) == NULL)
+		/* We're not being redirected */
+		return FALSE;
+
+	s += strlen("Location: ");
+	end = strchr(s, '\r');
+
+	/* Just in case :) */
+	if (end == NULL)
+		end = strchr(s, '\n');
+
+	if (end == NULL)
+		return FALSE;
+
+	len = end - s;
+
+	new_url = g_malloc(len + 1);
+	strncpy(new_url, s, len);
+	new_url[len] = '\0';
+
+	full = gfud->full;
+
+	if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
 	{
-		gchar *new_url, *temp_url, *end;
-		gboolean full;
-		int len;
-
-		s += strlen("Location: ");
-		end = strchr(s, '\r');
-
-		/* Just in case :) */
-		if (end == NULL)
-			end = strchr(s, '\n');
-
-		if (end == NULL)
-			return FALSE;
-
-		len = end - s;
-
-		new_url = g_malloc(len + 1);
-		strncpy(new_url, s, len);
-		new_url[len] = '\0';
-
-		full = gfud->full;
-
-		if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
-		{
-			temp_url = new_url;
-
-			new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
-									  gfud->website.port, temp_url);
-
-			g_free(temp_url);
-
-			full = FALSE;
-		}
-
-		purple_debug_info("util", "Redirecting to %s\n", new_url);
-
-		/*
-		 * Try again, with this new location.  This code is somewhat
-		 * ugly, but we need to reuse the gfud because whoever called
-		 * us is holding a reference to it.
-		 */
-		g_free(gfud->url);
-		gfud->url = new_url;
-		gfud->full = full;
-		g_free(gfud->request);
-		gfud->request = NULL;
-
-		purple_input_remove(gfud->inpa);
-		gfud->inpa = 0;
-		close(gfud->fd);
-		gfud->fd = -1;
-		gfud->request_written = 0;
-		gfud->len = 0;
-		gfud->data_len = 0;
-
-		g_free(gfud->website.user);
-		g_free(gfud->website.passwd);
-		g_free(gfud->website.address);
-		g_free(gfud->website.page);
-		purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
-					   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
-		gfud->connect_data = purple_proxy_connect(NULL, NULL,
-				gfud->website.address, gfud->website.port,
-				url_fetch_connect_cb, gfud);
-
-		if (gfud->connect_data == NULL)
-		{
-			purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
-					gfud->website.address);
-		}
-
+		temp_url = new_url;
+
+		new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
+								  gfud->website.port, temp_url);
+
+		g_free(temp_url);
+
+		full = FALSE;
+	}
+
+	purple_debug_info("util", "Redirecting to %s\n", new_url);
+
+	gfud->num_times_redirected++;
+	if (gfud->num_times_redirected >= 5)
+	{
+		purple_util_fetch_url_error(gfud,
+				_("Could not open %s: Redirected too many times"),
+				gfud->url);
 		return TRUE;
 	}
 
-	return FALSE;
+	/*
+	 * Try again, with this new location.  This code is somewhat
+	 * ugly, but we need to reuse the gfud because whoever called
+	 * us is holding a reference to it.
+	 */
+	g_free(gfud->url);
+	gfud->url = new_url;
+	gfud->full = full;
+	g_free(gfud->request);
+	gfud->request = NULL;
+
+	purple_input_remove(gfud->inpa);
+	gfud->inpa = 0;
+	close(gfud->fd);
+	gfud->fd = -1;
+	gfud->request_written = 0;
+	gfud->len = 0;
+	gfud->data_len = 0;
+
+	g_free(gfud->website.user);
+	g_free(gfud->website.passwd);
+	g_free(gfud->website.address);
+	g_free(gfud->website.page);
+	purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
+				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
+
+	gfud->connect_data = purple_proxy_connect(NULL, NULL,
+			gfud->website.address, gfud->website.port,
+			url_fetch_connect_cb, gfud);
+
+	if (gfud->connect_data == NULL)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
+				gfud->website.address);
+	}
+
+	return TRUE;
 }
 
 static size_t
--- a/libpurple/xmlnode.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/libpurple/xmlnode.c	Mon Jun 04 07:22:38 2007 +0000
@@ -519,38 +519,38 @@
 }
 
 static xmlSAXHandler xmlnode_parser_libxml = {
-	.internalSubset         = NULL,
-	.isStandalone           = NULL,
-	.hasInternalSubset      = NULL,
-	.hasExternalSubset      = NULL,
-	.resolveEntity          = NULL,
-	.getEntity              = NULL,
-	.entityDecl             = NULL,
-	.notationDecl           = NULL,
-	.attributeDecl          = NULL,
-	.elementDecl            = NULL,
-	.unparsedEntityDecl     = NULL,
-	.setDocumentLocator     = NULL,
-	.startDocument          = NULL,
-	.endDocument            = NULL,
-	.startElement           = NULL,
-	.endElement             = NULL,
-	.reference              = NULL,
-	.characters             = xmlnode_parser_element_text_libxml,
-	.ignorableWhitespace    = NULL,
-	.processingInstruction  = NULL,
-	.comment                = NULL,
-	.warning                = NULL,
-	.error                  = xmlnode_parser_error_libxml,
-	.fatalError             = NULL,
-	.getParameterEntity     = NULL,
-	.cdataBlock             = NULL,
-	.externalSubset         = NULL,
-	.initialized            = XML_SAX2_MAGIC,
-	._private               = NULL,
-	.startElementNs         = xmlnode_parser_element_start_libxml,
-	.endElementNs           = xmlnode_parser_element_end_libxml,
-	.serror                 = NULL
+	NULL, /* internalSubset */
+	NULL, /* isStandalone */
+	NULL, /* hasInternalSubset */
+	NULL, /* hasExternalSubset */
+	NULL, /* resolveEntity */
+	NULL, /* getEntity */
+	NULL, /* entityDecl */
+	NULL, /* notationDecl */
+	NULL, /* attributeDecl */
+	NULL, /* elementDecl */
+	NULL, /* unparsedEntityDecl */
+	NULL, /* setDocumentLocator */
+	NULL, /* startDocument */
+	NULL, /* endDocument */
+	NULL, /* startElement */
+	NULL, /* endElement */
+	NULL, /* reference */
+	xmlnode_parser_element_text_libxml, /* characters */
+	NULL, /* ignorableWhitespace */
+	NULL, /* processingInstruction */
+	NULL, /* comment */
+	NULL, /* warning */
+	xmlnode_parser_error_libxml, /* error */
+	NULL, /* fatalError */
+	NULL, /* getParameterEntity */
+	NULL, /* cdataBlock */
+	NULL, /* externalSubset */
+	XML_SAX2_MAGIC, /* initialized */
+	NULL, /* _private */
+	xmlnode_parser_element_start_libxml, /* startElementNs */
+	xmlnode_parser_element_end_libxml,   /* endElementNs   */
+	NULL, /* serror */
 };
 
 xmlnode *
--- a/pidgin/gtkaccount.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/gtkaccount.c	Mon Jun 04 07:22:38 2007 +0000
@@ -184,6 +184,7 @@
 	gtk_size_group_add_widget(dialog->sg, label);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
 	gtk_widget_show(label);
 
 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER);
@@ -426,7 +427,7 @@
 		gtk_widget_ref(dialog->protocol_menu);
 	}
 
-	hbox = add_pref_box(dialog, vbox, _("Protocol:"), dialog->protocol_menu);
+	hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu);
 	g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox);
 
 	gtk_widget_unref(dialog->protocol_menu);
@@ -437,7 +438,7 @@
 	g_object_set(G_OBJECT(dialog->screenname_entry), "truncate-multiline", TRUE, NULL);
 #endif
 
-	add_pref_box(dialog, vbox, _("Screen name:"), dialog->screenname_entry);
+	add_pref_box(dialog, vbox, _("Screen _name:"), dialog->screenname_entry);
 
 	g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed",
 					 G_CALLBACK(screenname_changed_cb), dialog);
@@ -519,16 +520,16 @@
 	gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE);
 	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*')
 		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR);
-	dialog->password_box = add_pref_box(dialog, vbox, _("Password:"),
+	dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
 										  dialog->password_entry);
 
 	/* Alias */
 	dialog->alias_entry = gtk_entry_new();
-	add_pref_box(dialog, vbox, _("Local alias:"), dialog->alias_entry);
+	add_pref_box(dialog, vbox, _("Local _alias:"), dialog->alias_entry);
 
 	/* Remember Password */
 	dialog->remember_pass_check =
-		gtk_check_button_new_with_label(_("Remember password"));
+		gtk_check_button_new_with_mnemonic(_("Remember pass_word"));
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
 								 FALSE);
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->remember_pass_check,
@@ -599,12 +600,12 @@
 
 	/* New mail notifications */
 	dialog->new_mail_check =
-		gtk_check_button_new_with_label(_("New mail notifications"));
+		gtk_check_button_new_with_mnemonic(_("New _mail notifications"));
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->new_mail_check, FALSE, FALSE, 0);
 	gtk_widget_show(dialog->new_mail_check);
 
 	/* Buddy icon */
-	dialog->icon_check = gtk_check_button_new_with_label(_("Use this buddy icon for this account:"));
+	dialog->icon_check = gtk_check_button_new_with_mnemonic(_("Use this buddy _icon for this account:"));
 	g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog);
 	gtk_widget_show(dialog->icon_check);
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0);
@@ -1023,7 +1024,7 @@
 	if (dialog->proxy_frame != NULL)
 		gtk_widget_destroy(dialog->proxy_frame);
 
-	frame = pidgin_make_frame(parent, _("Proxy Options"));
+	frame = pidgin_make_frame(parent, _("Pro_xy Options"));
 	dialog->proxy_frame = gtk_widget_get_parent(gtk_widget_get_parent(frame));
 
 	gtk_box_reorder_child(GTK_BOX(parent), dialog->proxy_frame, 1);
--- a/pidgin/gtkblist.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/gtkblist.c	Mon Jun 04 07:22:38 2007 +0000
@@ -1124,10 +1124,10 @@
 	if (((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) {
 		pidgin_separator(menu);
 		pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy);
-		pidgin_new_item_from_stock(menu, _("Alias..."), PIDGIN_STOCK_ALIAS,
+		pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
 				G_CALLBACK(gtk_blist_menu_alias_cb),
 				contact, 0, 0, NULL);
-		pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE,
+		pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
 				G_CALLBACK(pidgin_blist_remove_cb),
 				contact, 0, 0, NULL);
 	} else if (!sub || contact_expanded) {
@@ -4737,8 +4737,15 @@
 				NODE_COLUMN, &new_selection, -1);
 	}
 
-	/* we set this up as a timeout, otherwise the blist flickers */
-	g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
+	/* we set this up as a timeout, otherwise the blist flickers ...
+	 * but we don't do it for groups, because it causes total bizarness -
+	 * the previously selected buddy node might rendered at half height.
+	 */
+	if ((new_selection != NULL) && PURPLE_BLIST_NODE_IS_GROUP(new_selection)) {
+		do_selection_changed(new_selection);
+	} else {
+		g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
+	}
 }
 
 static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter)
@@ -5399,12 +5406,25 @@
 	gtk_container_set_border_width(GTK_CONTAINER(table), 0);
 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Screen name:"));
+	/* Set up stuff for the account box */
+	label = gtk_label_new_with_mnemonic(_("_Account:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
 
+	data->account_box = pidgin_account_option_menu_new(account, FALSE,
+			G_CALLBACK(add_buddy_select_account_cb), NULL, data);
+
+	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 0, 1);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_box);
+	pidgin_set_accessible_label (data->account_box, label);
+	/* End of account box */
+
+	label = gtk_label_new_with_mnemonic(_("_Screen name:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+
 	data->entry = gtk_entry_new();
-	gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1);
+	gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 1, 2);
 	gtk_widget_grab_focus(data->entry);
 
 	if (username != NULL)
@@ -5414,19 +5434,20 @@
 										  GTK_RESPONSE_OK, FALSE);
 
 	gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry);
 	pidgin_set_accessible_label (data->entry, label);
 
 	g_signal_connect(G_OBJECT(data->entry), "changed",
 					 G_CALLBACK(pidgin_set_sensitive_if_input),
 					 data->window);
 
-	label = gtk_label_new(_("Alias:"));
+	label = gtk_label_new_with_mnemonic(_("A_lias:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
 
 	data->entry_for_alias = gtk_entry_new();
 	gtk_table_attach_defaults(GTK_TABLE(table),
-							  data->entry_for_alias, 1, 2, 1, 2);
+							  data->entry_for_alias, 1, 2, 2, 3);
 
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias);
@@ -5435,29 +5456,19 @@
 		gtk_widget_grab_focus(GTK_WIDGET(data->entry_for_alias));
 
 	gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry_for_alias);
 	pidgin_set_accessible_label (data->entry_for_alias, label);
 
-	label = gtk_label_new(_("Group:"));
+	label = gtk_label_new_with_mnemonic(_("_Group:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
 
 	data->combo = gtk_combo_new();
 	gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree());
-	gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3);
+	gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 3, 4);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->combo)->entry);
 	pidgin_set_accessible_label (data->combo, label);
 
-	/* Set up stuff for the account box */
-	label = gtk_label_new(_("Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
-
-	data->account_box = pidgin_account_option_menu_new(account, FALSE,
-			G_CALLBACK(add_buddy_select_account_cb), NULL, data);
-
-	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4);
-	pidgin_set_accessible_label (data->account_box, label);
-	/* End of account box */
-
 	g_signal_connect(G_OBJECT(data->window), "response",
 					 G_CALLBACK(add_buddy_cb), data);
 
@@ -5763,7 +5774,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Account:"));
+	label = gtk_label_new_with_mnemonic(_("_Account:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5772,6 +5783,7 @@
 			G_CALLBACK(addchat_select_account_cb),
 			chat_account_filter_func, data);
 	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu);
 	pidgin_set_accessible_label (data->account_menu, label);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
@@ -5783,7 +5795,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Alias:"));
+	label = gtk_label_new_with_mnemonic(_("A_lias:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5793,6 +5805,7 @@
 		gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias);
 	gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0);
 	gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry);
 	pidgin_set_accessible_label (data->alias_entry, label);
 	if (name != NULL)
 		gtk_widget_grab_focus(data->alias_entry);
@@ -5800,7 +5813,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Group:"));
+	label = gtk_label_new_with_mnemonic(_("_Group:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5814,6 +5827,7 @@
 		gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry),
 						   group->name);
 	}
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->group_combo)->entry);
 	pidgin_set_accessible_label (data->group_combo, label);
 
 	g_signal_connect(G_OBJECT(data->window), "response",
--- a/pidgin/gtkimhtml.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/gtkimhtml.c	Mon Jun 04 07:22:38 2007 +0000
@@ -144,6 +144,10 @@
 };
 static guint signals [LAST_SIGNAL] = { 0 };
 
+static char *html_clipboard = NULL;
+static char *text_clipboard = NULL;
+GtkClipboard *clipboard_selection = NULL;
+
 static GtkTargetEntry selection_targets[] = {
 #ifndef _WIN32
 	{ "text/html", 0, TARGET_HTML },
@@ -879,14 +883,17 @@
 
 static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) {
 	char *text = NULL;
-	gboolean primary;
+	gboolean primary = (clipboard != clipboard_selection);
 	GtkTextIter start, end;
-	GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
-	GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
-
-	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
-	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
-	primary = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY) == clipboard;
+	GtkTextMark *sel = NULL;
+	GtkTextMark *ins = NULL; 
+
+	if (primary) { 
+		ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
+		sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
+		gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
+	}
 
 	if (info == TARGET_HTML) {
 		char *selection;
@@ -896,7 +903,7 @@
 		if (primary) {
 			text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
 		} else
-			text = imhtml->clipboard_html_string;
+			text = html_clipboard;
 
 		/* Mozilla asks that we start our text/html with the Unicode byte order mark */
 		str = g_string_append_unichar(str, 0xfeff);
@@ -914,7 +921,7 @@
 		if (primary) {
 			text = gtk_imhtml_get_text(imhtml, &start, &end);
 		} else
-			text = imhtml->clipboard_text_string;
+			text = text_clipboard;
 		gtk_selection_data_set_text(selection_data, text, strlen(text));
 	}
 	if (primary) /* This was allocated here */
@@ -937,20 +944,32 @@
 					   &insert);
 }
 
+static void gtk_imhtml_clipboard_clear (GtkClipboard *clipboard, GtkSelectionData *sel_data,
+				 guint info, gpointer user_data_or_owner)
+{
+	clipboard_selection = NULL;
+}
+
 static void copy_clipboard_cb(GtkIMHtml *imhtml, gpointer unused)
 {
 	GtkTextIter start, end;
 	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
-		gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD),
+		if (!clipboard_selection)
+			clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
+		gtk_clipboard_set_with_owner(clipboard_selection,
 						 selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
 						 (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
-						 (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml));
+						 (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, G_OBJECT(imhtml));
 
 		g_free(imhtml->clipboard_html_string);
 		g_free(imhtml->clipboard_text_string);
 
 		imhtml->clipboard_html_string = gtk_imhtml_get_markup_range(imhtml, &start, &end);
 		imhtml->clipboard_text_string = gtk_imhtml_get_text(imhtml, &start, &end);
+
+		text_clipboard = imhtml->clipboard_text_string;
+		html_clipboard = imhtml->clipboard_html_string;
+	
 	}
 
 	g_signal_stop_emission_by_name(imhtml, "copy-clipboard");
@@ -960,10 +979,12 @@
 {
 	GtkTextIter start, end;
 	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
-		gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD),
+		if (!clipboard_selection)
+			clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
+		gtk_clipboard_set_with_owner(clipboard_selection,
 						 selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
 						 (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
-						 (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml));
+						 (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, G_OBJECT(imhtml));
 
 		g_free(imhtml->clipboard_html_string);
 		g_free(imhtml->clipboard_text_string);
@@ -971,6 +992,9 @@
 		imhtml->clipboard_html_string = gtk_imhtml_get_markup_range(imhtml, &start, &end);
 		imhtml->clipboard_text_string = gtk_imhtml_get_text(imhtml, &start, &end);
 
+		text_clipboard = imhtml->clipboard_text_string;
+		html_clipboard = imhtml->clipboard_html_string;
+
 		if (imhtml->editable)
 			gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE);
 	}
@@ -1207,17 +1231,18 @@
 		g_free(img_data);
 	}
 
-	if (imhtml->clipboard_text_string) {
-		g_free(imhtml->clipboard_text_string);
-		g_free(imhtml->clipboard_html_string);
-	}
-
 	g_list_free(imhtml->scalables);
 	g_slist_free(imhtml->im_images);
 	g_queue_free(imhtml->animations);
 	g_free(imhtml->protocol_name);
 	g_free(imhtml->search_string);
 	G_OBJECT_CLASS(parent_class)->finalize (object);
+	if (clipboard_selection)
+		gtk_clipboard_set_with_owner(clipboard_selection,
+        	                             selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
+                	                     (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
+                        	             (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml));
+
 }
 
 /* Boring GTK+ stuff */
--- a/pidgin/gtkthemes.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/gtkthemes.c	Mon Jun 04 07:22:38 2007 +0000
@@ -146,6 +146,9 @@
 				list->next = child;
 			else
 				theme->list = child;
+			/* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
+			if (list != NULL)
+				list->smileys = g_slist_reverse(list->smileys);
 			list = child;
 		} else if (!g_ascii_strncasecmp(i, "Name=", strlen("Name="))) {
 			int len;
@@ -201,14 +204,16 @@
 
 			}
 
-			/* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
-			list->smileys = g_slist_reverse(list->smileys);
 
 			if (!have_used_sfile)
 				g_free(sfile);
 		}
 	}
 
+	/* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
+	if (list != NULL)
+		list->smileys = g_slist_reverse(list->smileys);
+
 	g_free(dirname);
 	fclose(f);
 
--- a/pidgin/plugins/relnot.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/plugins/relnot.c	Mon Jun 04 07:22:38 2007 +0000
@@ -69,20 +69,17 @@
 
 	message = g_string_new("");
 	g_string_append_printf(message, _("You are using %s version %s.  The "
-			"current version is %s.<hr>"),
+			"current version is %s.  You can get it from "
+			"<a href=\"" PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</a><hr>"),
 			PIDGIN_NAME, purple_core_get_version(), cur_ver);
 
 	if(*changelog) {
 		formatted = purple_strdup_withhtml(changelog);
-		g_string_append_printf(message, _("<b>ChangeLog:</b>\n%s<br><br>"),
+		g_string_append_printf(message, _("<b>ChangeLog:</b><br>%s"),
 				formatted);
 		g_free(formatted);
 	}
 
-	g_string_append_printf(message, _("You can get version %s from:<br>"
-			"<a href=\"http://pidgin.im/\">"
-			"http://pidgin.im</a>."), cur_ver);
-
 	purple_notify_formatted(NULL, _("New Version Available"),
 			_("New Version Available"), NULL, message->str,
 			NULL, NULL);
--- a/pidgin/plugins/xmppconsole.c	Sat Jun 02 15:41:36 2007 +0000
+++ b/pidgin/plugins/xmppconsole.c	Mon Jun 04 07:22:38 2007 +0000
@@ -183,16 +183,17 @@
 	char *text;
 
 	gc = console->gc;
-	
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-	
+
+	if (gc)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
 	gtk_text_buffer_get_start_iter(buffer, &start);
 	gtk_text_buffer_get_end_iter(buffer, &end);
-		
+
 	text = gtk_imhtml_get_text(GTK_IMHTML(console->entry), &start, &end);
-			
-	if (gc && prpl_info->convo_closed != NULL)
+
+	if (prpl_info && prpl_info->send_raw != NULL)
 		prpl_info->send_raw(gc, text, strlen(text));
 
 	g_free(text);