changeset 21778:e8e9a53b7694

merge of '93a8de710501e9a772b3fb12ab3db6682a41b3d6' and 'ec67b1544528adb30f7d2edac30b22912b2da19b'
author Etan Reisner <pidgin@unreliablesource.net>
date Wed, 05 Dec 2007 00:03:11 +0000
parents 306ee626481d (current diff) 3303c02a46f5 (diff)
children 7697b246fbcc
files libpurple/account.c pidgin/gtkutils.c
diffstat 43 files changed, 538 insertions(+), 294 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Dec 01 06:46:25 2007 +0000
+++ b/ChangeLog	Wed Dec 05 00:03:11 2007 +0000
@@ -1,9 +1,17 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.1 (??/??/????):
+version 2.3.2 (??/??/????):
+	libpurple:
+	* Fixed various problems with loss of status messages when going
+	  or returning from idle on MySpaceIM.
+
 	Finch:
-	* Color is used in the buddylist to indicate status. Look at the sample
-	  gntrc file in the man-page for details.
+	* Color is used in the buddylist to indicate status, and the conversation
+	  window to indicate various message attributes. Look at the sample gntrc
+	  file in the man-page for details.
+	* The default keybinding for dump-screen is now M-D and uses a file
+	  request dialog. M-d will properly delete-forward-word, and M-f has been
+	  fixed to imitate readline's behavior.
 
 version 2.3.0 (11/24/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
--- a/ChangeLog.API	Sat Dec 01 06:46:25 2007 +0000
+++ b/ChangeLog.API	Wed Dec 05 00:03:11 2007 +0000
@@ -1,6 +1,6 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.1 (??/??/????):
+version 2.3.2 (??/??/????):
 	Finch:
 		libgnt:
 		* Added gnt_tree_set_row_color to set the color for a row in a tree.
@@ -8,6 +8,8 @@
 		* Added gnt_color_add_pair to define a new color.
 		* Added gnt_colors_get_color to get an ncurses color value from a
 		  string.
+		* Added gnt_style_get_color to get a color pair from an entry in
+		  ~/.gntrc
 
 version 2.3.0 (11/24/2007):
 	libpurple:
--- a/Makefile.am	Sat Dec 01 06:46:25 2007 +0000
+++ b/Makefile.am	Wed Dec 05 00:03:11 2007 +0000
@@ -9,6 +9,7 @@
 		README.MTN \
 		README.mingw \
 		config.h.mingw \
+		doxy2devhelp.xsl \
 		gaim.pc.in \
 		gaim-uninstalled.pc.in \
 		intltool-extract.in \
--- a/configure.ac	Sat Dec 01 06:46:25 2007 +0000
+++ b/configure.ac	Wed Dec 05 00:03:11 2007 +0000
@@ -328,7 +328,7 @@
 	enable_gtkui="$enableval", enable_gtkui="yes")
 AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui],
 		[compile without console user interface])],
-	enable_consoleui=$enableval, enable_consoleui=yes)
+	[enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no])
 
 dnl #######################################################################
 dnl # Check for GTK+ 2.0 and other things used by the GTK UI
@@ -622,6 +622,14 @@
 	fi
 fi
 
+if test "x$force_finch" = "xyes" -a "x$enable_consoleui" != "xyes"; then
+	AC_MSG_ERROR([
+
+Finch will not be built. You need to install ncursesw (or ncurses) and its development headers.
+
+])
+fi
+
 AC_SUBST(GNT_LIBS)
 AC_SUBST(GNT_CFLAGS)
 AM_CONDITIONAL(ENABLE_GNT, test "x$enable_consoleui" = "xyes")
@@ -1651,16 +1659,24 @@
 			])
 			mozilla_nspr="mozilla-nspr"
 			mozilla_nss="mozilla-nss"
-		else
-			if `$PKG_CONFIG --exists nss`; then
-				PKG_CHECK_MODULES(NSS, nss, [
-					have_nss="yes"
-				], [
-					AC_MSG_RESULT(no)
-				])
-				mozilla_nspr="nspr"
-				mozilla_nss="nss"
-			fi
+		elif `$PKG_CONFIG --exists nss`; then
+			PKG_CHECK_MODULES(NSS, nss, [
+				have_nss="yes"
+			], [
+				AC_MSG_RESULT(no)
+				have_nss="no"
+			])
+			mozilla_nspr="nspr"
+			mozilla_nss="nss"
+		elif `$PKG_CONFIG --exists microb-engine-nss`; then
+			PKG_CHECK_MODULES(NSS, microb-engine-nss, [
+				have_nss="yes"
+			], [
+				AC_MSG_RESULT(no)
+				have_nss="no"
+			])
+			mozilla_nspr="mozilla-nspr"
+			mozilla_nss="microb-engine-nss"
 		fi
 
 		if test "x$have_nss" = "xyes"; then
--- a/doc/finch.1.in	Sat Dec 01 06:46:25 2007 +0000
+++ b/doc/finch.1.in	Wed Dec 05 00:03:11 2007 +0000
@@ -145,8 +145,19 @@
 .br
 color-offline = red; black
 .br
+color-message-sent = cyan; default
+.br
+color-message-received = red; default
+.br
+color-message-highlight = black; green
+.br
+color-message-action = yellow; default
+.br
+color-timestamp = blue; default
+.br
 #See below for details on color
 .br
+
 [general]
 .br
 shadow = 0
--- a/doc/plugin-i18n.dox	Sat Dec 01 06:46:25 2007 +0000
+++ b/doc/plugin-i18n.dox	Wed Dec 05 00:03:11 2007 +0000
@@ -1,13 +1,19 @@
 /** @page plugin-i18n Third Party Plugin Translation Support
 
  @section Introduction
+  For the purpose of this document we're going to assume that your plugin:
+
+   - Is set up to use autotools.  It may be possible to add translation support
+     without autotools, but we have no idea how.  We may not want to know, either ;)
+   - Has an autogen.sh.  You may have also called this bootstrap.sh or similar.
+   - Resides in a source tree that has @c configure.ac and @c Makefile.am in the
+     top-level directory as well as a @c src directory in which the plugin's source
+     is located.  A @c Makefile.am should also exist in the @c src directory.
+
   For a plugin to have translation support there are a few steps that need to
   followed:
 
-   - The plugin must be setup to use autotools.  It may be possible to add
-     translation support without autotools, but I have no idea how.
-   - In your autogen.sh, bootstrap.sh, or whatever you called it, add the
-     following after your other utility checks:
+   - In your autogen.sh, add the following after your other utility checks:
      @code
 (intltoolize --version) < /dev/null > /dev/null 2>&1 || {
     echo;
@@ -16,12 +22,11 @@
     exit;
 }
      @endcode
-     Then before your call aclocal add:
+     Then before your call to aclocal add:
      @code
 intltoolize --force --copy
      @endcode
-   - Now edit configure.ac, configure.in, or whatever you may have called it
-     and add the following:
+   - Now edit configure.ac and add the following:
      @code
 AC_PROG_INTLTOOL
 
@@ -40,13 +45,13 @@
    - Create/edit the file 'POTFILE.in' in your favorite editor.  Each line
      should be the name of a file that could or does have strings marked for
 	 translating (we're getting to that step).  These file names should be
-	 relative to the top directory of your plugin.
-   - 'cd' back to the top directory of your plugin.
+	 relative to the top directory of your plugin's source tree.
+   - 'cd' back to the top directory of your plugin's source tree.
    - Open 'Makefile.am' and add 'po' to your 'SUBDIRS' variable.
-   - While still in the top directory of your plugin execute
+   - While still in the top directory of your plugin's source tree,  execute
      'intltool-prepare'.  This will setup anything extra that intltool needs.
-   - Fire off an 'autogen.sh' and when it's completed, verify that you have a
-     'po/POTFILES', notice the lack of a .in.  If you do, everything should be
+   - Fire off 'autogen.sh' and when it's completed, verify that you have a
+     'po/POTFILES' (notice the lack of a .in).  If you do, everything should be
 	 set on the autotools side.
    - Take a break, stretch your legs, smoke a cigarette, whatever, because
      we're done with the autotools part.
@@ -60,8 +65,10 @@
 #include <glib/gi18n-lib.h>
      @endcode
 	 Make sure that this include is after you include of your 'config.h',
-	 otherwise you will break your build.
-   - This is where things get a bit goofy.  libpurple is going to try and
+	 otherwise you will break your build.  Also note that if you wish to
+     maintain compatibility with older versions of GLib, you will need to
+     include additional preprocessor directives, which we won't cover here.
+   - This is where things get a bit goofy.  libpurple is going to try to
      translate our strings using the libpurple gettext package.  So we have to
      convert them before libpurple attempts to.
    - To do this, we're going to change the entries for name, summary, and
--- a/finch/gntblist.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/gntblist.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1768,37 +1768,18 @@
 	draw_tooltip(ggblist);
 }
 
-static int
-get_color(char *key)
-{
-#if GLIB_CHECK_VERSION(2,6,0)
-	int fg = 0, bg = 0;
-	gsize n;
-	char **vals;
-	vals = gnt_style_get_string_list(NULL, key, &n);
-	if (vals && n == 2) {
-		fg = gnt_colors_get_color(vals[0]);
-		bg = gnt_colors_get_color(vals[1]);
-		return gnt_color_add_pair(fg, bg);
-	}
-	return 0;
-#else
-	return 0;
-#endif
-}
-
 void finch_blist_init()
 {
-	color_available = get_color("color-available");
+	color_available = gnt_style_get_color(NULL, "color-available");
 	if (!color_available)
 		color_available = gnt_color_add_pair(COLOR_GREEN, -1);
-	color_away = get_color("color-away");
+	color_away = gnt_style_get_color(NULL, "color-away");
 	if (!color_away)
 		color_away = gnt_color_add_pair(COLOR_BLUE, -1);
-	color_idle = get_color("color-idle");
+	color_idle = gnt_style_get_color(NULL, "color-idle");
 	if (!color_idle)
 		color_idle = gnt_color_add_pair(COLOR_CYAN, -1);
-	color_offline = get_color("color-offline");
+	color_offline = gnt_style_get_color(NULL, "color-offline");
 	if (!color_offline)
 		color_offline = gnt_color_add_pair(COLOR_RED, -1);
 
--- a/finch/gntconv.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/gntconv.c	Wed Dec 05 00:03:11 2007 +0000
@@ -49,6 +49,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntstyle.h"
 #include "gnttextview.h"
 #include "gnttree.h"
 #include "gntutils.h"
@@ -64,6 +65,12 @@
 		const char *message, PurpleMessageFlags flags, time_t mtime);
 static void generate_send_to_menu(FinchConv *ggc);
 
+static int color_message_receive;
+static int color_message_send;
+static int color_message_highlight;
+static int color_message_action;
+static int color_timestamp;
+
 static PurpleBlistNode *
 get_conversation_blist_node(PurpleConversation *conv)
 {
@@ -753,7 +760,9 @@
 	/* Unnecessary to print the timestamp for delayed message */
 	if (purple_prefs_get_bool("/finch/conversations/timestamps"))
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
-					purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)), GNT_TEXT_FLAG_DIM);
+					purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), gnt_color_pair(color_timestamp));
+
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), " ", GNT_TEXT_FLAG_NORMAL);
 
 	if (flags & PURPLE_MESSAGE_AUTO_RESP)
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
@@ -763,23 +772,31 @@
 			!(flags & PURPLE_MESSAGE_NOTIFY))
 	{
 		char * name = NULL;
-
-		if (purple_message_meify((char*)message, -1))
-			name = g_strdup_printf("*** %s ", who);
-		else
-			name =  g_strdup_printf("%s: ", who);
+		GntTextFormatFlags msgflags = GNT_TEXT_FLAG_NORMAL;
+		gboolean me = FALSE;
 
+		if (purple_message_meify((char*)message, -1)) {
+			name = g_strdup_printf("*** %s", who);
+			msgflags = gnt_color_pair(color_message_action);
+			me = TRUE;
+		} else {
+			name =  g_strdup_printf("%s", who);
+			if (flags & PURPLE_MESSAGE_SEND)
+				msgflags = gnt_color_pair(color_message_send);
+			else if (flags & PURPLE_MESSAGE_NICK)
+				msgflags = gnt_color_pair(color_message_highlight);
+			else
+				msgflags = gnt_color_pair(color_message_receive);
+		}
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
-				name, GNT_TEXT_FLAG_BOLD);
+				name, msgflags);
+		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), me ? " " : ": ", GNT_TEXT_FLAG_NORMAL);
 		g_free(name);
-	}
-	else
+	} else
 		fl = GNT_TEXT_FLAG_DIM;
 
 	if (flags & PURPLE_MESSAGE_ERROR)
 		fl |= GNT_TEXT_FLAG_BOLD;
-	if (flags & PURPLE_MESSAGE_NICK)
-		fl |= GNT_TEXT_FLAG_UNDERLINE;
 
 	/* XXX: Remove this workaround when textview can parse messages. */
 	newline = purple_strdup_withhtml(message);
@@ -1126,6 +1143,21 @@
 
 void finch_conversation_init()
 {
+	color_message_send = gnt_style_get_color(NULL, "color-message-sent");
+	if (!color_message_send)
+		color_message_send = gnt_color_add_pair(COLOR_CYAN, -1);
+	color_message_receive = gnt_style_get_color(NULL, "color-message-received");
+	if (!color_message_receive)
+		color_message_receive = gnt_color_add_pair(COLOR_RED, -1);
+	color_message_highlight = gnt_style_get_color(NULL, "color-message-highlight");
+	if (!color_message_highlight)
+		color_message_highlight = gnt_color_add_pair(COLOR_GREEN, -1);
+	color_timestamp = gnt_style_get_color(NULL, "color-timestamp");
+	if (!color_timestamp)
+		color_timestamp = gnt_color_add_pair(COLOR_BLUE, -1);
+	color_message_action = gnt_style_get_color(NULL, "color-message-action");
+	if (!color_message_action)
+		color_message_action = gnt_color_add_pair(COLOR_YELLOW, -1);
 	purple_prefs_add_none(PREF_ROOT);
 	purple_prefs_add_none(PREF_ROOT "/size");
 	purple_prefs_add_int(PREF_ROOT "/size/width", 70);
--- a/finch/libgnt/gntmain.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/gntmain.c	Wed Dec 05 00:03:11 2007 +0000
@@ -679,8 +679,9 @@
 	g_free(cp);
 	clean_pid();
 	wm->mode = GNT_KP_MODE_NORMAL;
-	clear();
+	endwin();
 	setup_io();
+	refresh();
 	refresh_screen();
 }
 #endif
--- a/finch/libgnt/gntstyle.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Wed Dec 05 00:03:11 2007 +0000
@@ -59,6 +59,25 @@
 #endif
 }
 
+int
+gnt_style_get_color(char *group, char *key)
+{
+#if GLIB_CHECK_VERSION(2,6,0)
+	int fg = 0, bg = 0;
+	gsize n;
+	char **vals;
+	vals = gnt_style_get_string_list(group, key, &n);
+	if (vals && n == 2) {
+		fg = gnt_colors_get_color(vals[0]);
+		bg = gnt_colors_get_color(vals[1]);
+		return gnt_color_add_pair(fg, bg);
+	}
+	return 0;
+#else
+	return 0;
+#endif
+}
+
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length)
 {
 #if GLIB_CHECK_VERSION(2,6,0)
--- a/finch/libgnt/gntstyle.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Wed Dec 05 00:03:11 2007 +0000
@@ -74,11 +74,24 @@
  *
  * @return        NULL terminated string array. The array should be freed with g_strfreev().
  *
- * @since 2.3.1 (gnt), 2.3.1 (pidgin)
+ * @since 2.3.2
  */
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length);
 
 /**
+ * Get the value of a color pair in ~/.gntrc.
+ *
+ * @param group   The name of the group in the keyfile. If @c NULL, the prgname
+ *                will be used first, if available. Otherwise, "general" will be used.
+ * @param key     The key
+ *
+ * @return  The value of the color as an int, or 0 on error.
+ *
+ * @since 2.3.2
+ */
+int gnt_style_get_color(char *group, char *key);
+
+/**
  * Parse a boolean preference. For example, if 'value' is "false" (ignoring case)
  * or "0", the return value will be @c FALSE, otherwise @c TRUE.
  *
--- a/finch/libgnt/gnttextview.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Wed Dec 05 00:03:11 2007 +0000
@@ -650,8 +650,10 @@
 		fl |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
 	else if (flags & GNT_TEXT_FLAG_HIGHLIGHT)
 		fl |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
+	else if ((flags & A_COLOR) == 0)
+		fl |= gnt_color_pair(GNT_COLOR_NORMAL);
 	else
-		fl |= gnt_color_pair(GNT_COLOR_NORMAL);
+		fl |= (flags & A_COLOR);
 
 	return fl;
 }
--- a/finch/libgnt/gntwm.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/gntwm.c	Wed Dec 05 00:03:11 2007 +0000
@@ -692,6 +692,7 @@
 		{'\0', NULL}
 	};
 
+	gnt_widget_destroy(GNT_WIDGET(fs));
 
 	if ((file = g_fopen(path, "w+")) == NULL) {
 		return;
@@ -803,7 +804,6 @@
 	}
 	fprintf(file, "</pre>\n</body>");
 	fclose(file);
-	gnt_widget_destroy(GNT_WIDGET(fs));
 }
 
 static void
@@ -817,6 +817,11 @@
 {
 	GntWidget *window = gnt_file_sel_new();
 	GntFileSel *sel = GNT_FILE_SEL(window);
+
+	g_object_set(G_OBJECT(window), "vertical", TRUE, NULL);
+	gnt_box_add_widget(GNT_BOX(window), gnt_label_new("Please enter the filename to save the screenshot."));
+	gnt_box_set_title(GNT_BOX(window), "Save Screenshot...");
+
 	gnt_file_sel_set_suggested_filename(sel, "dump.html");
 	g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(dump_file_save), NULL);
 	g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(dump_file_cancel), sel);
--- a/finch/libgnt/test/tv.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/finch/libgnt/test/tv.c	Wed Dec 05 00:03:11 2007 +0000
@@ -112,8 +112,8 @@
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 2nd line\n", GNT_TEXT_FLAG_NORMAL);
 
-	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
-	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL);
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
+	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
 
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD);
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 4th line\n", GNT_TEXT_FLAG_NORMAL);
--- a/libpurple/account.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/account.c	Wed Dec 05 00:03:11 2007 +0000
@@ -741,7 +741,7 @@
 
 	current_error = g_new0(PurpleConnectionErrorInfo, 1);
 	current_error->type = type;
-	current_error->description = description;
+	current_error->description = g_strdup(description);
 
 	set_current_error(account, current_error);
 }
--- a/libpurple/blist.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/blist.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1310,7 +1310,7 @@
 		g = (PurpleGroup *)((PurpleBlistNode *)c)->parent;
 	} else {
 		if (group) {
-			/* Add chat to blist if isn't already on it. Fixes #2752. */
+			/* Add group to blist if isn't already on it. Fixes #2752. */
 			if (!purple_find_group(group->name)) {
 				purple_blist_add_group(group,
 						purple_blist_get_last_sibling(purplebuddylist->root));
--- a/libpurple/connection.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/connection.c	Wed Dec 05 00:03:11 2007 +0000
@@ -38,6 +38,8 @@
 #include "signals.h"
 #include "util.h"
 
+#define KEEPALIVE_INTERVAL 30
+
 static GList *connections = NULL;
 static GList *connections_connecting = NULL;
 static PurpleConnectionUiOps *connection_ui_ops = NULL;
@@ -73,7 +75,7 @@
 	if (on && !gc->keepalive)
 	{
 		purple_debug_info("connection", "Activating keepalive.\n");
-		gc->keepalive = purple_timeout_add_seconds(30, send_keepalive, gc);
+		gc->keepalive = purple_timeout_add_seconds(KEEPALIVE_INTERVAL, send_keepalive, gc);
 	}
 	else if (!on && gc->keepalive > 0)
 	{
@@ -219,9 +221,6 @@
 {
 	PurpleAccount *account;
 	GSList *buddies;
-#if 0
-	GList *wins;
-#endif
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	gboolean remove = FALSE;
 
@@ -269,19 +268,6 @@
 
 	purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
 
-#if 0
-	/* see comment later in file on if 0'd same code */
-	/*
-	 * XXX This is a hack! Remove this and replace it with a better event
-	 *     notification system.
-	 */
-	for (wins = purple_get_windows(); wins != NULL; wins = wins->next) {
-		PurpleConvWindow *win = (PurpleConvWindow *)wins->data;
-		purple_conversation_update(purple_conv_window_get_conversation_at(win, 0),
-								 PURPLE_CONV_ACCOUNT_OFFLINE);
-	}
-#endif
-
 	purple_account_request_close_with_account(account);
 	purple_request_close_with_handle(gc);
 	purple_notify_close_with_handle(gc);
--- a/libpurple/protocols/bonjour/jabber.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Dec 05 00:03:11 2007 +0000
@@ -62,6 +62,12 @@
 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
 		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
 
+enum sent_stream_start_types {
+	NOT_SENT       = 0,
+	PARTIALLY_SENT = 1,
+	FULLY_SENT     = 2
+};
+
 static void
 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
 
@@ -100,6 +106,8 @@
 	bconv->rx_handler = 0;
 	bconv->pb = pb;
 
+	bonjour_parser_setup(bconv);
+
 	return bconv;
 }
 
@@ -289,7 +297,7 @@
 	/* If we're not ready to actually send, append it to the buffer */
 	if (bconv->tx_handler != 0
 			|| bconv->connect_data != NULL
-			|| !bconv->sent_stream_start
+			|| bconv->sent_stream_start != FULLY_SENT
 			|| !bconv->recv_stream_start
 			|| purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
 		ret = -1;
@@ -319,6 +327,7 @@
 	}
 
 	if (ret < len) {
+		/* Don't interfere with the stream starting */
 		if (bconv->tx_handler == 0)
 			bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
 				_send_data_write_cb, pb);
@@ -409,20 +418,6 @@
 	}
 }
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
-
-	/* If the stream has been completely started, we can start doing stuff */
-	if (bconv->sent_stream_start && bconv->recv_stream_start && 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);
-		/* We can probably write the data right now. */
-		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
-	}
-
-}
 
 struct _stream_start_data {
 	char *msg;
@@ -478,14 +473,13 @@
 
 	/* Stream started; process the send buffer if there is one */
 	purple_input_remove(bconv->tx_handler);
-	bconv->tx_handler= 0;
-	bconv->sent_stream_start = TRUE;
+	bconv->tx_handler = 0;
+	bconv->sent_stream_start = FULLY_SENT;
 
 	bonjour_jabber_stream_started(pb);
-
 }
 
-static gboolean bonjour_jabber_stream_init(PurpleBuddy *pb, int client_socket)
+static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket)
 {
 	int ret, len;
 	char *stream_start;
@@ -495,6 +489,8 @@
 								   purple_buddy_get_name(pb));
 	len = strlen(stream_start);
 
+	bb->conversation->sent_stream_start = PARTIALLY_SENT;
+
 	/* Start the stream */
 	ret = send(client_socket, stream_start, len, 0);
 
@@ -521,18 +517,55 @@
 		bb->conversation->tx_handler = purple_input_add(client_socket,
 			PURPLE_INPUT_WRITE, _start_stream, pb);
 	} else
-		bb->conversation->sent_stream_start = TRUE;
+		bb->conversation->sent_stream_start = FULLY_SENT;
 
 	g_free(stream_start);
 
-	/* setup the parser fresh for each stream */
-	bonjour_parser_setup(bb->conversation);
+	return TRUE;
+}
+
+static gboolean
+_async_bonjour_jabber_close_conversation(gpointer data) {
+	BonjourJabberConversation *bconv = data;
+	bonjour_jabber_close_conversation(bconv);
+	return FALSE;
+}
+
+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)) {
+		const char *err = g_strerror(errno);
+		PurpleConversation *conv;
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
 
-	bb->conversation->socket = client_socket;
-	bb->conversation->rx_handler = purple_input_add(client_socket,
-		PURPLE_INPUT_READ, _client_socket_handler, pb);
+		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));
 
-	return TRUE;
+		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;
+		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) {
+		/* 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);
+		/* We can probably write the data right now. */
+		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
+	}
+
 }
 
 static void
@@ -576,14 +609,15 @@
 	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);
 
-		if (!bonjour_jabber_stream_init(pb, client_socket)) {
-			close(client_socket);
-			return;
-		}
+		/* 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);
 
 	} else {
 		purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
@@ -696,7 +730,7 @@
 		return;
 	}
 
-	if (!bonjour_jabber_stream_init(pb, source)) {
+	if (!bonjour_jabber_send_stream_init(pb, source)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
 
@@ -714,6 +748,11 @@
 		bb->conversation = NULL;
 		return;
 	}
+
+	/* 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);
 }
 
 static PurpleBuddy *
@@ -843,7 +882,7 @@
 		/* Close the socket and remove the watcher */
 		if (bconv->socket >= 0) {
 			/* Send the end of the stream to the other end of the conversation */
-			if (bconv->sent_stream_start)
+			if (bconv->sent_stream_start == FULLY_SENT)
 				send(bconv->socket, STREAM_END, strlen(STREAM_END), 0);
 			/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
 			close(bconv->socket);
--- a/libpurple/protocols/bonjour/jabber.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Wed Dec 05 00:03:11 2007 +0000
@@ -47,7 +47,7 @@
 	guint rx_handler;
 	guint tx_handler;
 	PurpleCircBuffer *tx_buf;
-	gboolean sent_stream_start;
+	int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
 	gboolean recv_stream_start;
 	PurpleProxyConnectData *connect_data;
 	gpointer stream_data;
--- a/libpurple/protocols/irc/irc.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/irc/irc.c	Wed Dec 05 00:03:11 2007 +0000
@@ -184,9 +184,14 @@
 /* XXX I don't like messing directly with these buddies */
 gboolean irc_blist_timeout(struct irc_conn *irc)
 {
-	GString *string = g_string_sized_new(512);
+	GString *string;
 	char *list, *buf;
 
+	if (irc->ison_outstanding)
+		return TRUE;
+
+	string = g_string_sized_new(512);
+
 	g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string);
 
 	list = g_string_free(string, FALSE);
@@ -200,6 +205,8 @@
 	irc_send(irc, buf);
 	g_free(buf);
 
+	irc->ison_outstanding = TRUE;
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/irc/irc.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/irc/irc.h	Wed Dec 05 00:03:11 2007 +0000
@@ -56,6 +56,8 @@
 	guint timer;
 	GHashTable *buddies;
 
+	gboolean ison_outstanding;
+
 	char *inbuf;
 	int inbuflen;
 	int inbufused;
--- a/libpurple/protocols/irc/msgs.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/irc/msgs.c	Wed Dec 05 00:03:11 2007 +0000
@@ -707,6 +707,7 @@
 	g_strfreev(nicks);
 
 	g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_status, (gpointer)irc);
+	irc->ison_outstanding = FALSE;
 }
 
 static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc)
--- a/libpurple/protocols/msn/notification.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1777,7 +1777,7 @@
 			passport = msn_user_get_passport(session->user);
 			url = session->passport_info.file;
 
-			purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
+			purple_notify_emails(gc, count, FALSE, NULL, NULL,
 							   &passport, &url, NULL, NULL);
 		}
 	}
@@ -1850,7 +1850,7 @@
 			passport = msn_user_get_passport(session->user);
 			url = session->passport_info.file;
 
-			purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
+			purple_notify_emails(gc, count, FALSE, NULL, NULL,
 							   &passport, &url, NULL, NULL);
 		}
 	}
--- a/libpurple/protocols/msn/oim.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/msn/oim.c	Wed Dec 05 00:03:11 2007 +0000
@@ -488,10 +488,12 @@
 		char *unread = xmlnode_get_data(iu_node);
 		const char *passport = msn_user_get_passport(session->user);
 		const char *url = session->passport_info.file;
+		int count = atoi(unread);
 
 		/* XXX/khc: pretty sure this is wrong */
-		purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL,
-			NULL, &passport, &url, NULL, NULL);
+		if (count > 0)
+			purple_notify_emails(session->account->gc, count, FALSE, NULL,
+				NULL, &passport, &url, NULL, NULL);
 		g_free(unread);
 	}
 
--- a/libpurple/protocols/msnp9/directconn.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/msnp9/directconn.c	Wed Dec 05 00:03:11 2007 +0000
@@ -81,6 +81,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -156,7 +157,8 @@
 		return -1;
 	}
 
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
 	return fd;
 }
@@ -353,7 +355,7 @@
 }
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnDirectConn* directconn;
 	int fd;
@@ -405,6 +407,15 @@
 	}
 }
 
+static void
+directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	if (error_message)
+		purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
+
+	connect_cb(data, source, PURPLE_INPUT_READ);
+}
+
 gboolean
 msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
 {
@@ -424,14 +435,9 @@
 #endif
 
 	directconn->connect_data = purple_proxy_connect(NULL, session->account,
-			host, port, connect_cb, directconn);
+			host, port, directconn_connect_cb, directconn);
 
-	if (directconn->connect_data != NULL)
-	{
-		return TRUE;
-	}
-	else
-		return FALSE;
+	return (directconn->connect_data != NULL);
 }
 
 #if 0
--- a/libpurple/protocols/msnp9/servconn.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/msnp9/servconn.c	Wed Dec 05 00:03:11 2007 +0000
@@ -468,6 +468,7 @@
 create_listener(int port)
 {
 	int fd;
+	int flags;
 	const int on = 1;
 
 #if 0
@@ -543,7 +544,8 @@
 		return -1;
 	}
 
-	fcntl(fd, F_SETFL, O_NONBLOCK);
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
 	return fd;
 }
--- a/libpurple/protocols/myspace/myspace.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1047,15 +1047,18 @@
 msim_set_status(PurpleAccount *account, PurpleStatus *status)
 {
 	PurpleStatusType *type;
+	PurplePresence *pres;
 	MsimSession *session;
 	guint status_code;
-	gchar *statstring;
+	const gchar *message;
+	gchar *stripped;
 
 	session = (MsimSession *)account->gc->proto_data;
 
 	g_return_if_fail(MSIM_SESSION_VALID(session));
 
 	type = purple_status_get_type(status);
+	pres = purple_status_get_presence(status);
 
 	switch (purple_status_type_get_primitive(type)) {
 		case PURPLE_STATUS_AVAILABLE:
@@ -1083,16 +1086,20 @@
 			break;
 	}
 
-	statstring = (gchar *)purple_status_get_attr_string(status, "message");
-
-	if (!statstring) {
-		statstring = "";
-	}
+	message = purple_status_get_attr_string(status, "message");
 
 	/* Status strings are plain text. */
-	statstring = purple_markup_strip_html(statstring);
-
-	msim_set_status_code(session, status_code, statstring);
+	if (message != NULL)
+		stripped = purple_markup_strip_html(message);
+	else
+		stripped = g_strdup("");
+
+	msim_set_status_code(session, status_code, stripped);
+
+	/* If we should be idle, set that status. Time is irrelevant here. */
+	if (purple_presence_is_idle(pres) && status_code != MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN)
+		msim_set_idle(account->gc, 1);
+
 }
 
 /** Go idle. */
@@ -1100,6 +1107,7 @@
 msim_set_idle(PurpleConnection *gc, int time)
 {
 	MsimSession *session;
+	PurpleStatus *status;
 
 	g_return_if_fail(gc != NULL);
 
@@ -1107,16 +1115,30 @@
 
 	g_return_if_fail(MSIM_SESSION_VALID(session));
 
+	status = purple_account_get_active_status(session->account);
+
 	if (time == 0) {
 		/* Going back from idle. In msim, idle is mutually exclusive 
 		 * from the other states (you can only be away or idle, but not
-		 * both, for example), so by going non-idle I go online.
+		 * both, for example), so by going non-idle I go back to what
+		 * libpurple says I should be.
 		 */
-		/* TODO: find out how to keep old status string? */
-		msim_set_status_code(session, MSIM_STATUS_CODE_ONLINE, g_strdup(""));
+		msim_set_status(session->account, status);
 	} else {
+		const gchar *message;
+		gchar *stripped;
+
+		/* Set the idle message to the status message from the real
+		 * current status.
+		 */
+		message = purple_status_get_attr_string(status, "message");
+		if (message != NULL)
+			stripped = purple_markup_strip_html(message);
+		else
+			stripped = g_strdup("");
+
 		/* msim doesn't support idle time, so just go idle */
-		msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, g_strdup(""));
+		msim_set_status_code(session, MSIM_STATUS_CODE_IDLE, stripped);
 	}
 }
 
@@ -1347,7 +1369,9 @@
 	msim_msg_dump("msim_check_inbox_cb: reply=%s\n", reply);
 
 	body = msim_msg_get_dictionary(reply, "body");
-	g_return_if_fail(body != NULL);
+
+	if (body == NULL)
+		return;
 
 	old_inbox_status = session->inbox_status;
 
@@ -1411,6 +1435,11 @@
 
 	session = (MsimSession *)data;
 
+	if (!MSIM_SESSION_VALID(session)) {
+		purple_debug_info("msim", "msim_check_inbox: session invalid, stopping the mail check.\n");
+		return FALSE;
+	}
+
 	purple_debug_info("msim", "msim_check_inbox: checking mail\n");
 	g_return_val_if_fail(msim_send(session, 
 			"persist", MSIM_TYPE_INTEGER, 1,
@@ -1623,7 +1652,7 @@
 
 	/* Check mail if they want to. */
 	if (purple_account_get_check_mail(session->account)) {
-		purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, 
+		session->inbox_handle = purple_timeout_add(MSIM_MAIL_INTERVAL_CHECK, 
 				(GSourceFunc)msim_check_inbox, session);
 		msim_check_inbox(session);
 	}
@@ -1894,7 +1923,7 @@
 		purple_debug_info("msim", "msim_status: found buddy %s\n", username);
 	}
 
-	if (status_headline) {
+	if (status_headline && strcmp(status_headline, "") != 0) {
 		/* The status headline is plaintext, but libpurple treats it as HTML,
 		 * so escape any HTML characters to their entity equivalents. */
 		status_headline_escaped = g_markup_escape_text(status_headline, strlen(status_headline));
@@ -1925,8 +1954,8 @@
 			break;
 
 		case MSIM_STATUS_CODE_IDLE:
-			/* will be handled below */
-			purple_status_code = -1;
+			/* Treat idle as an available status. */
+			purple_status_code = PURPLE_STATUS_AVAILABLE;
 			break;
 
 		default:
--- a/libpurple/protocols/myspace/session.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/myspace/session.c	Wed Dec 05 00:03:11 2007 +0000
@@ -63,6 +63,7 @@
 	session->next_rid = 1;
 	session->last_comm = time(NULL);
 	session->inbox_status = 0;
+	session->inbox_handle = 0;
 	
 	return session;
 }
@@ -90,6 +91,11 @@
 		msim_msg_free(session->server_info);
 	}
 	
+	/* Stop checking the inbox at the end of the session. */
+	if (session->inbox_handle) {
+		purple_timeout_remove(session->inbox_handle);
+	}
+
 	g_free(session);
 }
 
--- a/libpurple/protocols/myspace/session.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/myspace/session.h	Wed Dec 05 00:03:11 2007 +0000
@@ -45,6 +45,7 @@
 	guint next_rid;                     /**< Next request/response ID */
 	time_t last_comm;                   /**< Time received last communication */
 	guint inbox_status;                 /**< Bit field of inbox notifications */
+	guint inbox_handle;                 /**< The handle for the mail check timer */
 } MsimSession;
 
 /* Check if an MsimSession is valid */
--- a/libpurple/protocols/oscar/family_chatnav.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/oscar/family_chatnav.c	Wed Dec 05 00:03:11 2007 +0000
@@ -29,6 +29,49 @@
 
 #include "oscar.h"
 
+static int
+error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
+{
+	int ret = 0;
+	aim_snac_t *snac2;
+	guint16 error, chatnav_error;
+	GSList *tlvlist;
+
+	if (!(snac2 = aim_remsnac(od, snac->id))) {
+		purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08lx)\n", snac->id);
+		return 0;
+	}
+
+	if (snac2->family != 0x000d) {
+		purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
+		return 0;
+	}
+
+	/*
+	 * We now know what the original SNAC subtype was.
+	 */
+	if (snac2->type == 0x0008) /* create room */
+	{
+		error = byte_stream_get16(bs);
+		tlvlist = aim_tlvlist_read(bs);
+		chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1);
+
+		purple_debug_warning("oscar",
+				"Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n",
+				error, chatnav_error);
+		purple_notify_error(od->gc, NULL, _("Could not join chat room"),
+				chatnav_error == 0x0033 ? _("Invalid chat room name") : _("Unknown error"));
+
+		ret = 1;
+	}
+
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	return ret;
+}
+
 /*
  * Subtype 0x0002
  *
@@ -451,7 +494,9 @@
 static int
 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	if (snac->subtype == 0x0009)
+	if (snac->subtype == 0x0001)
+		return error(od, conn, mod, frame, snac, bs);
+	else if (snac->subtype == 0x0009)
 		return parseinfo(od, conn, mod, frame, snac, bs);
 
 	return 0;
--- a/libpurple/protocols/oscar/family_feedbag.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Wed Dec 05 00:03:11 2007 +0000
@@ -115,14 +115,10 @@
 	gboolean exists;
 	struct aim_ssi_item *cur, *new;
 
-	new = (struct aim_ssi_item *)g_malloc(sizeof(struct aim_ssi_item));
+	new = g_new(struct aim_ssi_item, 1);
 
 	/* Set the name */
-	if (name) {
-		new->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-		strcpy(new->name, name);
-	} else
-		new->name = NULL;
+	new->name = g_strdup(name);
 
 	/* Set the group ID# and buddy ID# */
 	new->gid = gid;
@@ -345,13 +341,9 @@
  */
 struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
 {
-	struct aim_ssi_item *cur;
-	if (!list || !sn)
+	if (!sn)
 		return NULL;
-	for (cur=list; cur; cur=cur->next)
-		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
-			return cur;
-	return NULL;
+	return aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY);
 }
 
 /**
@@ -510,7 +502,7 @@
 		for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
 			if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -530,7 +522,7 @@
 		for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
 			if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -551,7 +543,7 @@
 			cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
 			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
 				n++;
-				new = (struct aim_ssi_tmp *)g_malloc(sizeof(struct aim_ssi_tmp));
+				new = g_new(struct aim_ssi_tmp, 1);
 				new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
 				new->ack = 0xffff;
 				new->name = NULL;
@@ -1028,8 +1020,7 @@
 		return -EINVAL;
 
 	g_free(group->name);
-	group->name = (char *)g_malloc((strlen(newgn)+1)*sizeof(char));
-	strcpy(group->name, newgn);
+	group->name = g_strdup(newgn);
 
 	/* Sync our local list with the server list */
 	return aim_ssi_sync(od);
@@ -1461,11 +1452,7 @@
 		if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
 			item->type = type;
 			g_free(item->name);
-			if (name) {
-				item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
+			item->name = g_strdup(name);
 			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
@@ -1473,11 +1460,7 @@
 		if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
 			item->type = type;
 			g_free(item->name);
-			if (name) {
-				item->name = (char *)g_malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
+			item->name = g_strdup(name);
 			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
@@ -1555,10 +1538,7 @@
 				/* Remove the item from the local list */
 				/* Make sure cur->item is still valid memory */
 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
-					if (cur->item->name) {
-						cur->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char));
-						strcpy(cur->name, cur->item->name);
-					}
+					cur->name = g_strdup(cur->item->name);
 					aim_ssi_itemlist_del(&od->ssi.local, cur->item);
 				}
 				cur->item = NULL;
@@ -1569,11 +1549,7 @@
 					struct aim_ssi_item *cur1;
 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 						g_free(cur->item->name);
-						if (cur1->name) {
-							cur->item->name = (char *)g_malloc((strlen(cur1->name)+1)*sizeof(char));
-							strcpy(cur->item->name, cur1->name);
-						} else
-							cur->item->name = NULL;
+						cur->item->name = g_strdup(cur1->name);
 						aim_tlvlist_free(cur->item->data);
 						cur->item->data = aim_tlvlist_copy(cur1->data);
 					}
@@ -1603,11 +1579,7 @@
 					struct aim_ssi_item *cur1;
 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 						g_free(cur1->name);
-						if (cur->item->name) {
-							cur1->name = (char *)g_malloc((strlen(cur->item->name)+1)*sizeof(char));
-							strcpy(cur1->name, cur->item->name);
-						} else
-							cur1->name = NULL;
+						cur1->name = g_strdup(cur->item->name);
 						aim_tlvlist_free(cur1->data);
 						cur1->data = aim_tlvlist_copy(cur->item->data);
 					}
--- a/libpurple/protocols/oscar/family_icbm.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Wed Dec 05 00:03:11 2007 +0000
@@ -209,7 +209,6 @@
  */
 static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	aim_rxcallback_t userfunc;
 	struct aim_icbmparameters params;
 
 	params.maxchan = byte_stream_get16(bs);
@@ -219,8 +218,11 @@
 	params.maxrecverwarn = byte_stream_get16(bs);
 	params.minmsginterval = byte_stream_get32(bs);
 
-	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		return userfunc(od, conn, frame, &params);
+	params.flags = 0x0000000b;
+	params.maxmsglen = 8000;
+	params.minmsginterval = 0;
+
+	aim_im_setparams(od, &params);
 
 	return 0;
 }
@@ -288,7 +290,7 @@
 		if (!args->msg || (args->msglen <= 0))
 			return -EINVAL;
 
-		if (args->msglen >= MAXMSGLEN)
+		if (args->msglen > MAXMSGLEN)
 			return -E2BIG;
 	}
 
--- a/libpurple/protocols/oscar/oscar.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Dec 05 00:03:11 2007 +0000
@@ -176,7 +176,6 @@
 static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_locerr     (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_icbm_param_info  (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_memrequest       (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_selfinfo         (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -1228,7 +1227,6 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0);
-	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, 0x0005, purple_icbm_param_info, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
@@ -1833,9 +1831,6 @@
 		signon = info->onlinesince;
 	else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		signon = time(NULL) - info->sessionlen;
-	if (!aim_sncmp(purple_account_get_username(account), info->sn)) {
-		purple_connection_set_display_name(gc, info->sn);
-	}
 	purple_prpl_got_user_login_time(account, info->sn, signon);
 
 	/* Idle time stuff */
@@ -3437,6 +3432,8 @@
 	info = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
+	purple_connection_set_display_name(od->gc, info->sn);
+
 	/*
 	 * What's with the + 0.5?
 	 * The 0.5 is basically poor-man's rounding.  Normally
@@ -3498,32 +3495,6 @@
 	return 1;
 }
 
-static int purple_icbm_param_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-	struct aim_icbmparameters *params;
-	va_list ap;
-
-	va_start(ap, fr);
-	params = va_arg(ap, struct aim_icbmparameters *);
-	va_end(ap);
-
-	/* XXX - evidently this crashes on solaris. i have no clue why
-	purple_debug_misc("oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, "
-			"max sender evil = %f, max receiver evil = %f, min msg interval = %u\n",
-			params->maxchan, params->flags, params->maxmsglen,
-			((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
-			params->minmsginterval);
-	*/
-
-	/* Maybe senderwarn and recverwarn should be user preferences... */
-	params->flags = 0x0000000b;
-	params->maxmsglen = 8000;
-	params->minmsginterval = 0;
-
-	aim_im_setparams(od, params);
-
-	return 1;
-}
-
 static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
 {
 	PurpleConnection *gc = od->gc;
@@ -5364,6 +5335,7 @@
 
 	if (chat_name != NULL)
 		g_hash_table_insert(defaults, "room", g_strdup(chat_name));
+	g_hash_table_insert(defaults, "exchange", g_strdup("4"));
 
 	return defaults;
 }
@@ -5380,26 +5352,29 @@
 	OscarData *od = (OscarData *)gc->proto_data;
 	FlapConnection *conn;
 	char *name, *exchange;
+	int exchange_int;
 
 	name = g_hash_table_lookup(data, "room");
 	exchange = g_hash_table_lookup(data, "exchange");
 
-	if ((name == NULL) || (*name == '\0')) {
-		purple_notify_error(gc, NULL, _("Invalid chat name specified."), NULL);
-		return;
-	}
+	g_return_if_fail(name != NULL && *name != '\0');
+	g_return_if_fail(exchange != NULL);
+
+	errno = 0;
+	exchange_int = strtol(exchange, NULL, 10);
+	g_return_if_fail(errno == 0);
 
 	purple_debug_info("oscar", "Attempting to join chat room %s.\n", name);
 
 	if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
 	{
 		purple_debug_info("oscar", "chatnav exists, creating room\n");
-		aim_chatnav_createroom(od, conn, name, atoi(exchange));
+		aim_chatnav_createroom(od, conn, name, exchange_int);
 	} else {
 		/* this gets tricky */
 		struct create_room *cr = g_new0(struct create_room, 1);
 		purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
-		cr->exchange = atoi(exchange);
+		cr->exchange = exchange_int;
 		cr->name = g_strdup(name);
 		od->create_rooms = g_slist_prepend(od->create_rooms, cr);
 		aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
@@ -6725,7 +6700,7 @@
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
 	option = purple_account_option_bool_new(
-		_("Always use ICQ proxy server for file transfers\n(slower, but does not reveal your IP address)"), "always_use_rv_proxy",
+		_("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
 		OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
--- a/libpurple/protocols/oscar/oscar.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Wed Dec 05 00:03:11 2007 +0000
@@ -94,11 +94,8 @@
  * for WinAIM clients (up through the latest (4.0.1957)) to
  * send any more than 1kb.  Amaze all your windows friends
  * with utterly oversized instant messages!
- *
- * TODO: the real limit is the total SNAC size at 8192. Fix this.
- *
  */
-#define MAXMSGLEN 7987
+#define MAXMSGLEN 2544
 
 /*
  * Maximum size of a Buddy Icon.
--- a/libpurple/protocols/qq/header_info.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/protocols/qq/header_info.h	Wed Dec 05 00:03:11 2007 +0000
@@ -33,7 +33,7 @@
 #define QQ_PACKET_TAG           0x02	/* all QQ text packets starts with it */
 #define QQ_PACKET_TAIL          0x03	/* all QQ text packets end with it */
 
-#define QQ_CLIENT       0x0E1B
+#define QQ_CLIENT       0x0d55
 
 /* list of known QQ commands */
 enum {
--- a/libpurple/prpl.h	Sat Dec 01 06:46:25 2007 +0000
+++ b/libpurple/prpl.h	Wed Dec 05 00:03:11 2007 +0000
@@ -292,6 +292,13 @@
 	void (*chat_whisper)(PurpleConnection *, int id,
 						 const char *who, const char *message);
 	int  (*chat_send)(PurpleConnection *, int id, const char *message, PurpleMessageFlags flags);
+
+	/** If implemented, this will be called regularly for this prpl's
+	 *  active connections.  You'd want to do this if you need to repeatedly
+	 *  send some kind of keepalive packet to the server to avoid being
+	 *  disconnected.  ("Regularly" is defined by
+	 *  <code>KEEPALIVE_INTERVAL</code> in <tt>libpurple/connection.c</tt>.)
+	 */
 	void (*keepalive)(PurpleConnection *);
 
 	/** new user registration */
--- a/pidgin/gtkblist.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkblist.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1398,7 +1398,7 @@
 	pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, node);
 	pidgin_append_blist_node_extended_menu(menu, node);
 
-	if (!contact_expanded)
+	if (!contact_expanded && contact != NULL)
 		pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact);
 
 	if (node->parent && node->parent->child->next && 
@@ -3170,6 +3170,7 @@
 		GList *cur;
 		struct proto_chat_entry *pce;
 		char *name, *value;
+		PidginBlistNode *bnode = node->ui_data;
 
 		chat = (PurpleChat *)node;
 		prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
@@ -3182,6 +3183,11 @@
 			g_free(tmp);
 		}
 
+		if (bnode && bnode->conv.conv) {
+			const char *topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(bnode->conv.conv));
+			g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)"));
+		}
+
 		if (prpl_info->chat_info != NULL)
 			cur = prpl_info->chat_info(chat->account->gc);
 		else
@@ -3371,6 +3377,34 @@
 	return g_string_free(str, FALSE);
 }
 
+static GHashTable *cached_emblems;
+
+static void _cleanup_cached_emblem(gpointer data, GObject *obj) {
+	g_hash_table_remove(cached_emblems, data);
+}
+
+static GdkPixbuf * _pidgin_blist_get_cached_emblem(gchar *path) {
+	GdkPixbuf *pb = g_hash_table_lookup(cached_emblems, path);
+
+	if (pb != NULL) {
+		/* The caller gets a reference */
+		g_object_ref(pb);
+		g_free(path);
+	} else {
+		pb = gdk_pixbuf_new_from_file(path, NULL);
+		if (pb != NULL) {
+			/* We don't want to own a ref to the pixbuf, but we need to keep clean up. */
+			/* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */
+			g_object_weak_ref(G_OBJECT(pb), _cleanup_cached_emblem, path);
+			g_hash_table_insert(cached_emblems, path, pb);
+		} else
+			g_free(path);
+	}
+
+	return pb;
+}
+
+
 GdkPixbuf *
 pidgin_blist_get_emblem(PurpleBlistNode *node)
 {
@@ -3381,7 +3415,6 @@
 	PurplePluginProtocolInfo *prpl_info;
 	const char *name = NULL;
 	char *filename, *path;
-	GdkPixbuf *ret;
 	PurplePresence *p;
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
@@ -3394,11 +3427,9 @@
 		gtkbuddynode = node->ui_data;
 		p = purple_buddy_get_presence(buddy);
 		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", 
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems",
 						"16", "mobile.png", NULL);
-			ret = gdk_pixbuf_new_from_file(path, NULL);
-			g_free(path);
-			return ret;
+			return _pidgin_blist_get_cached_emblem(path);
 		}
 
 		if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) {
@@ -3410,26 +3441,22 @@
 		return NULL;
 	}
 
+	g_return_val_if_fail(buddy != NULL, NULL);
+
 	if (!purple_privacy_check(buddy->account, purple_buddy_get_name(buddy))) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "blocked.png", NULL);
-		ret = gdk_pixbuf_new_from_file(path, NULL);
-		g_free(path);
-		return ret;
+		return _pidgin_blist_get_cached_emblem(path);
 	}
 
 	p = purple_buddy_get_presence(buddy);
 	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL);
-		ret = gdk_pixbuf_new_from_file(path, NULL);
-		g_free(path);
-		return ret;
+		return _pidgin_blist_get_cached_emblem(path);
 	}
 
 	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
-		ret = gdk_pixbuf_new_from_file(path, NULL);
-		g_free(path);
-		return ret;
+		return _pidgin_blist_get_cached_emblem(path);
 	}
 
 	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
@@ -3446,12 +3473,10 @@
 	filename = g_strdup_printf("%s.png", name);
 
 	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL);
-	ret = gdk_pixbuf_new_from_file(path, NULL);
-
 	g_free(filename);
-	g_free(path);
-
-	return ret;
+
+	/* _pidgin_blist_get_cached_emblem() assumes ownership of path */
+	return _pidgin_blist_get_cached_emblem(path);
 }
 
 
@@ -5691,7 +5716,6 @@
 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded)
 {
 	PurpleGroup *group;
-	GdkColor textcolor;
 	gboolean selected;
 	char group_count[12] = "";
 	char *mark, *esc;
@@ -5699,7 +5723,6 @@
 	GtkTreeIter iter;
 
 	group = (PurpleGroup*)gnode;
-	textcolor = gtkblist->treeview->style->fg[GTK_STATE_ACTIVE];
 
 	if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) {
 		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
@@ -5714,12 +5737,7 @@
 	}
 
 	esc = g_markup_escape_text(group->name, -1);
-	if (selected)
-		mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc, group_count);
-	else
-		mark = g_strdup_printf("<span color='#%02x%02x%02x' weight='bold'>%s</span>%s",
-				       textcolor.red>>8, textcolor.green>>8, textcolor.blue>>8,
-				       esc, group_count);
+	mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc ? esc : "", group_count);
 
 	g_free(esc);
 	return mark;
@@ -6897,6 +6915,8 @@
 {
 	void *gtk_blist_handle = pidgin_blist_get_handle();
 
+	cached_emblems = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
 	purple_signal_connect(purple_connections_get_handle(), "signed-on",
 						gtk_blist_handle, PURPLE_CALLBACK(account_signon_cb),
 						NULL);
@@ -6948,6 +6968,8 @@
 
 void
 pidgin_blist_uninit(void) {
+	g_hash_table_destroy(cached_emblems);
+
 	purple_signals_unregister_by_instance(pidgin_blist_get_handle());
 	purple_signals_disconnect_by_handle(pidgin_blist_get_handle());
 }
--- a/pidgin/gtkconv.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkconv.c	Wed Dec 05 00:03:11 2007 +0000
@@ -4349,7 +4349,7 @@
 	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;
+	height = (oneline.height + pad_top + pad_bottom) * (lines + 1);
 	height += (oneline.height + pad_inside) * (wrapped_lines - lines);
 
 	gtkconv->auto_resize = TRUE;
@@ -6476,8 +6476,29 @@
 				markup = title;
 			}
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
-			const char *topic = gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text));
-			char *esc = topic ? g_markup_escape_text(topic, -1) : NULL;
+			const char *topic = gtkconv->u.chat->topic_text ? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)) : NULL;
+			char *esc = NULL;
+#if GTK_CHECK_VERSION(2,6,0)
+			esc = topic ? g_markup_escape_text(topic, -1) : NULL;
+#else
+			/* GTK < 2.6 doesn't have auto ellipsization, so we do a crude
+			 * trucation to prevent forcing the window to be as wide as the topic */
+			int len = 0;
+			char *c, *tmp = g_strdup(topic);
+			c = tmp;
+			while(*c && len < 72) {
+				c = g_utf8_next_char(c);
+				len++;
+			}
+			if (len == 72) {
+				*c = '\0';
+				c = g_strdup_printf("%s...", tmp);
+				g_free(tmp);
+				tmp = c;
+			}
+			esc = tmp ? g_markup_escape_text(tmp, -1) : NULL;
+			g_free(tmp);
+#endif
 			markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
 						purple_conversation_get_title(conv),
 						esc  && *esc ? "\n" : "",
@@ -6511,7 +6532,7 @@
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT)	{
 			atk_object_set_description(accessibility_obj, _("Unread Messages"));
 			if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT)
-				style = "color=\"#cc0000\"";
+				style = "tab-label-unreadchat";
 			else
 				style = "tab-label-attention";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
@@ -7172,15 +7193,18 @@
 	if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0)
 		when_away = TRUE;
 
-	while ((l = hidden_convwin->gtkconvs) != NULL)
+	for (l = hidden_convwin->gtkconvs; l; )
 	{
 		gtkconv = l->data;
+		l = l->next;
 
 		conv = gtkconv->active_conv;
 
-		if(when_away && !purple_status_is_available(
+		if (conv->type == PURPLE_CONV_TYPE_CHAT ||
+				gtkconv->unseen_count == 0 ||
+				(when_away && !purple_status_is_available(
 							purple_account_get_active_status(
-							purple_conversation_get_account(conv))))
+							purple_conversation_get_account(conv)))))
 			continue;
 
 		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
@@ -7796,7 +7820,8 @@
 		} styles[] = {
 			{"pidgin_tab_label_typing_default", "tab-label-typing", "#4e9a06"},
 			{"pidgin_tab_label_typed_default", "tab-label-typed", "#c4a000"},
-			{"pidgin_tab_label_attention_default", "tab-label-attention", "#204a87"},
+			{"pidgin_tab_label_attention_default", "tab-label-attention", "#006aff"},
+			{"pidgin_tab_label_unreadchat_default", "tab-label-unreadchat", "#cc0000"},
 			{"pidgin_tab_label_event_default", "tab-label-event", "#888a85"},
 			{NULL, NULL, NULL}
 		};
@@ -8652,7 +8677,7 @@
                                                  gtk_entry_get_text(entry));
 		}
 		serv_alias_buddy(buddy);
-	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {	        
+	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		gtk_entry_set_text(GTK_ENTRY(gtkconv->u.chat->topic_text), gtk_entry_get_text(entry));
 		topic_callback(NULL, gtkconv);
 	}
@@ -8663,7 +8688,7 @@
 infopane_entry_activate(PidginConversation *gtkconv)
 {
 	GtkWidget *entry = NULL;
-        PurpleConversation *conv = gtkconv->active_conv;
+	PurpleConversation *conv = gtkconv->active_conv;
 	const char *text = NULL;
 
 	if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
@@ -8679,9 +8704,21 @@
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
 		PurpleBuddy *buddy = purple_find_buddy(gtkconv->active_conv->account, gtkconv->active_conv->name);
 		if (!buddy)
+			/* This buddy isn't in your buddy list, so we can't alias him */
 			return FALSE;
+
 		text = purple_buddy_get_contact_alias(buddy);
 	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+		PurpleConnection *gc;
+		PurplePluginProtocolInfo *prpl_info = NULL;
+
+		gc = purple_conversation_get_gc(conv);
+		if (gc != NULL)
+			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		if (prpl_info && prpl_info->set_chat_topic == NULL)
+			/* This protocol doesn't support setting the chat room topic */
+			return FALSE;
+
 		text = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv));
 	}
 
@@ -8700,10 +8737,9 @@
 	g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(alias_cb), gtkconv);
 	g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(alias_focus_cb), gtkconv);
 	g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(alias_key_press_cb), gtkconv);
-	
-	
-
-	gtk_entry_set_text(GTK_ENTRY(entry), text);
+
+	if (text != NULL)
+		gtk_entry_set_text(GTK_ENTRY(entry), text);
 	gtk_widget_show(entry);
 	gtk_widget_hide(gtkconv->infopane);
 	gtk_widget_grab_focus(entry);
--- a/pidgin/gtkimhtml.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkimhtml.c	Wed Dec 05 00:03:11 2007 +0000
@@ -844,19 +844,19 @@
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Smile!"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("S_mile!"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Insert"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("_Insert"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Font"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("_Font"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
@@ -4290,19 +4290,19 @@
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Font"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("_Font"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Insert"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("_Insert"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
 
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_label(_("_Smile!"));
+	mi = gtk_image_menu_item_new_with_mnemonic(_("S_mile!"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
 	gtk_widget_show(mi);
 	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
--- a/pidgin/gtkimhtmltoolbar.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Wed Dec 05 00:03:11 2007 +0000
@@ -1315,7 +1315,9 @@
 
 	purple_prefs_connect_callback(toolbar, PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
 			imhtmltoolbar_view_pref_changed, toolbar);
-	purple_prefs_trigger_callback(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide");
+	g_signal_connect_data(G_OBJECT(toolbar), "realize",
+			G_CALLBACK(purple_prefs_trigger_callback), PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
+			NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 
 #if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
--- a/pidgin/gtkmain.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkmain.c	Wed Dec 05 00:03:11 2007 +0000
@@ -609,7 +609,7 @@
 #ifndef _WIN32
 				  "c:dhmnl::s:v",
 #else
-				  "c:dhnl::v",
+				  "c:dhmnl::v",
 #endif
 				  long_options, NULL)) != -1) {
 		switch (opt) {
--- a/pidgin/gtkutils.c	Sat Dec 01 06:46:25 2007 +0000
+++ b/pidgin/gtkutils.c	Wed Dec 05 00:03:11 2007 +0000
@@ -2451,7 +2451,13 @@
 	g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy",
 					 G_CALLBACK(icon_filesel_delete_cb), dialog);
 #endif /* FILECHOOSER */
-	return 	dialog->icon_filesel;
+
+#ifdef _WIN32
+	g_signal_connect(G_OBJECT(dialog->icon_filesel), "show",
+		G_CALLBACK(winpidgin_ensure_onscreen), dialog->icon_filesel);
+#endif
+
+	return dialog->icon_filesel;
 }
 
 
--- a/po/POTFILES.in	Sat Dec 01 06:46:25 2007 +0000
+++ b/po/POTFILES.in	Wed Dec 05 00:03:11 2007 +0000
@@ -118,6 +118,7 @@
 libpurple/protocols/myspace/zap.c
 libpurple/protocols/novell/nmuser.c
 libpurple/protocols/novell/novell.c
+libpurple/protocols/oscar/family_chatnav.c
 libpurple/protocols/oscar/flap_connection.c
 libpurple/protocols/oscar/libaim.c
 libpurple/protocols/oscar/libicq.c