changeset 21774:b63b2a5c2d05

merge of '282b4acb46729e4358221cb7cc78ad9373289199' and '3fe64d635336a6d7f6e520f4b2e041c3316c465d'
author Etan Reisner <pidgin@unreliablesource.net>
date Sat, 01 Dec 2007 03:41:37 +0000
parents 14afe5a92fc2 (current diff) 50d1ee737a68 (diff)
children 4dc66862203f
files libpurple/network.c po/ChangeLog
diffstat 75 files changed, 1600 insertions(+), 672 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Mon Nov 19 23:06:16 2007 +0000
+++ b/COPYRIGHT	Sat Dec 01 03:41:37 2007 +0000
@@ -95,6 +95,7 @@
 Chris Davies
 Josh Davis
 Martijn Dekker
+Florian Delizy
 Vinicius Depizzol
 Philip Derrin
 Taso N. Devetzis
--- a/ChangeLog	Mon Nov 19 23:06:16 2007 +0000
+++ b/ChangeLog	Sat Dec 01 03:41:37 2007 +0000
@@ -1,6 +1,11 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.0:
+version 2.3.1 (??/??/????):
+	Finch:
+	* Color is used in the buddylist to indicate status. Look at the sample
+	  gntrc file in the man-page for details.
+
+version 2.3.0 (11/24/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
 		NOTE: Some bugs marked fixed in 2.2.1, 2.2.2 or 2.2.3 may not
 		      have been fixed until this release (2.3.0).
@@ -14,6 +19,13 @@
 	  transfers between libpurple clients and Gajim clients, but will not
 	  work with iChat or Adium as they use a different file transfer
 	  implementation.
+	* XMPP password changes that return errors no longer cause the saved
+	  password to be changed.
+	* XMPP file transfer support has been enhanced to support sending
+	  files through a proxy when the server supports discovering a
+	  a bytestream proxy.  This should make file transfers much more
+	  reliable.  The next release will add support for manually specifying
+	  a proxy when the server doesn't advertise one.
 
 	Pidgin:
 	* If a plugin says it can't be unloaded, we now display an error and
--- a/ChangeLog.API	Mon Nov 19 23:06:16 2007 +0000
+++ b/ChangeLog.API	Sat Dec 01 03:41:37 2007 +0000
@@ -1,6 +1,15 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.0 (??/??/????):
+version 2.3.1 (??/??/????):
+	Finch:
+		libgnt:
+		* Added gnt_tree_set_row_color to set the color for a row in a tree.
+		* Added gnt_style_get_string_list
+		* Added gnt_color_add_pair to define a new color.
+		* Added gnt_colors_get_color to get an ncurses color value from a
+		  string.
+
+version 2.3.0 (11/24/2007):
 	libpurple:
 		Added:
 		* a PurpleConversation field and an alias field in PurpleConvMessage
--- a/ChangeLog.win32	Mon Nov 19 23:06:16 2007 +0000
+++ b/ChangeLog.win32	Sat Dec 01 03:41:37 2007 +0000
@@ -1,4 +1,4 @@
-version 2.3.0:
+version 2.3.0 (11/24/2007):
 	* Updated GTK+ to 2.12.1 (This was actually included in 2.2.2, but 
 	  didn't get into the Changelog.)
 	* Upgrade SILC to use the 1.1.5 toolkit
@@ -135,7 +135,7 @@
 
 version 0.82 (08/26/2004):
 	* Selecting away messages using the system tray icon works
-	  (Thanks François Gagné)
+	  (Thanks Fran?ois Gagn?)
 	* Transparency plugin will save your settings again (Kevin Stange)
 	* Updated gtk-wimp to 0.6.2
 	* Updated libpng to 1.2.6 (major security update)
--- a/NEWS	Mon Nov 19 23:06:16 2007 +0000
+++ b/NEWS	Sat Dec 01 03:41:37 2007 +0000
@@ -1,5 +1,16 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+2.3.0 (11/20/2007):
+	Luke: While this does not have the new MSN code, rest assured that
+	we are working on it and that it is nearing release.  This contains
+	a significant number of fixes, including some that were marked as
+	fixed for earlier releases.  Happy Thanksgiving!
+
+	John: This is my first NEWS entry!  So, this time around we have an
+	updated man page (the old one hadn't been really updated since
+	before the GTK+ 2.0 migration!), lots of bug fixes, and some new
+	features for you all.  Enjoy!
+
 2.2.2 (10/23/2007):
 	Luke: Because the main branch of pidgin development is still not
 	ready for public consumption, I have taken some time to try to
--- a/configure.ac	Mon Nov 19 23:06:16 2007 +0000
+++ b/configure.ac	Sat Dec 01 03:41:37 2007 +0000
@@ -46,17 +46,17 @@
 m4_define([purple_lt_current], [3])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [3])
-m4_define([purple_micro_version], [0])
-m4_define([purple_version_suffix], [])
+m4_define([purple_micro_version], [1])
+m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
 
-m4_define([gnt_lt_current], [2])
+m4_define([gnt_lt_current], [3])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [2])
-m4_define([gnt_micro_version], [3])
-m4_define([gnt_version_suffix], [])
+m4_define([gnt_minor_version], [3])
+m4_define([gnt_micro_version], [1])
+m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
 m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix]))
@@ -303,6 +303,9 @@
 	AC_MSG_ERROR([
 
 You must have the GLib 2.0 development headers installed to build.
+
+If you have these installed already you may need to install pkg-config so
+I can find them.
 ])])
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
@@ -380,14 +383,14 @@
 		AC_MSG_ERROR([
 
 You must have the GTK+ 2.0 development headers installed to compile Pidgin.
-If you only want to build Finch then specify --disable-gtkui when running configure.
+If you want to build only Finch then specify --disable-gtkui when running configure.
 ])])
 
 	AC_SUBST(GTK_CFLAGS)
 	AC_SUBST(GTK_LIBS)
 
 	dnl We only really need Pango >= 1.4 for decent RTL support
-	PKG_CHECK_MODULES(pango, [pango >= 1.4.0],
+	PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0],
 			AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:)
 
 	dnl #######################################################################
--- a/doc/finch.1.in	Mon Nov 19 23:06:16 2007 +0000
+++ b/doc/finch.1.in	Sat Dec 01 03:41:37 2007 +0000
@@ -88,9 +88,8 @@
 Start resizing a window. Press the cursor keys to resize the window. When you
 are done, press \fBEnter\fR or \fBEscape\fR.
 .TP
-.B Alt \+ d
-Dump the contents of the screen in HTML format in a file named "dump.html" in
-working directory.
+.B Alt \+ D
+Dump the contents of the screen in HTML format.
 .TP
 .B Alt \+ .
 Move the position of the current window in the window list one place to the
@@ -107,8 +106,7 @@
 Jump to the 1st, 2nd ... 10th window.
 .TP
 .B Ctrl \+ o
-Bring up the menu (if there is one) for a window. Note that currently only the
-buddylist has a menu.
+Bring up the menu (if there is one) for a window.
 .TP
 .B Alt \+ /
 Show a list of available key-bindings for the current widget in focus.
@@ -137,6 +135,18 @@
 .TP
 A sample file looks like:
 .br
+[Finch]
+.br
+color-available = green; black
+.br
+color-away = blue; black
+.br
+color-idle = gray; black
+.br
+color-offline = red; black
+.br
+#See below for details on color
+.br
 [general]
 .br
 shadow = 0
@@ -494,7 +504,7 @@
 .SH BUGS
 Known bugs are listed at
 .br
-\fIhttp://sourceforge.net/tracker/?group_id=235&atid=100235\fR
+\fIhttp://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR
 
 Before sending a bug report, please verify that you have the latest
 version of \fBfinch\fR and \fBlibpurple\fR.  Many bugs (major and minor) are
--- a/finch/finch.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/finch.c	Sat Dec 01 03:41:37 2007 +0000
@@ -297,7 +297,6 @@
 	if (opt_version) {
 		/* Translators may want to transliterate the name.
 		 It is not to be translated. */
-		gnt_quit();
 		printf("%s %s\n", _("Finch"), DISPLAY_VERSION);
 		return 0;
 	}
@@ -418,8 +417,6 @@
 	g_set_application_name(_("Finch"));
 #endif
 
-	gnt_init();
-
 	if (gnt_start(&argc, &argv)) {
 		gnt_main();
 
--- a/finch/gntblist.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/gntblist.c	Sat Dec 01 03:41:37 2007 +0000
@@ -37,6 +37,7 @@
 #include "debug.h"
 
 #include "gntbox.h"
+#include "gntcolors.h"
 #include "gntcombobox.h"
 #include "gntentry.h"
 #include "gntft.h"
@@ -46,6 +47,7 @@
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
 #include "gntpounce.h"
+#include "gntstyle.h"
 #include "gnttree.h"
 #include "gntutils.h"
 #include "gntwindow.h"
@@ -124,6 +126,37 @@
 static int blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2);
 static int blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2);
 
+static int color_available;
+static int color_away;
+static int color_offline;
+static int color_idle;
+
+static int
+get_display_color(PurpleBlistNode  *node)
+{
+	PurpleBuddy *buddy;
+	int color = 0;
+
+	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
+	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+		return 0;
+
+	buddy = (PurpleBuddy*)node;
+	if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) {
+		color = color_idle;
+	} else if (purple_presence_is_available(purple_buddy_get_presence(buddy))) {
+		color = color_available;
+	} else if (purple_presence_is_online(purple_buddy_get_presence(buddy)) &&
+			!purple_presence_is_available(purple_buddy_get_presence(buddy))) {
+		color = color_away;
+	} else if (!purple_presence_is_online(purple_buddy_get_presence(buddy))) {
+		color = color_offline;
+	}
+
+	return color;
+}
+
 static gboolean
 is_contact_online(PurpleContact *contact)
 {
@@ -228,6 +261,7 @@
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
 		gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
+		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
 	}
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -581,11 +615,11 @@
 
 	if (node->ui_data)
 		return;
-	
+
 	name = get_display_name(node);
 	if (name == NULL)
 		return;
-	
+
 	group = (PurpleGroup*)node->parent;
 	add_node((PurpleBlistNode*)group, ggblist);
 
@@ -601,6 +635,7 @@
 {
 	PurpleContact *contact;
 	PurpleBlistNode *node = (PurpleBlistNode *)buddy;
+	int color = 0;
 	if (node->ui_data)
 		return;
 
@@ -615,13 +650,11 @@
 	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
 				contact, NULL);
-	if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) {
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, GNT_TEXT_FLAG_DIM);
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, GNT_TEXT_FLAG_DIM);
-	} else {
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, 0);
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, 0);
-	}
+
+	color = get_display_color((PurpleBlistNode*)buddy);
+	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
+	if (buddy == purple_contact_get_priority_buddy(contact))
+		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
 }
 
 #if 0
@@ -1542,7 +1575,8 @@
 {
 	PurpleContact *contact;
 	GntTextFormatFlags bflag = 0, cflag = 0;
-	
+	int color = 0;
+
 	contact = purple_buddy_get_contact(buddy);
 
 	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
@@ -1556,19 +1590,17 @@
 	if (ggblist->tnode == (PurpleBlistNode*)buddy)
 		draw_tooltip(ggblist);
 
-	if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) {
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag | GNT_TEXT_FLAG_DIM);
-		if (buddy == purple_contact_get_priority_buddy(contact))
-			gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag | GNT_TEXT_FLAG_DIM);
-		else
+	color = get_display_color((PurpleBlistNode*)buddy);
+	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
+	if (buddy == purple_contact_get_priority_buddy(contact))
+		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
+
+	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag);
+	if (buddy == purple_contact_get_priority_buddy(contact))
+		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag);
+
+	if (buddy != purple_contact_get_priority_buddy(contact))
 			update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist);
-	} else {
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag);
-		if (buddy == purple_contact_get_priority_buddy(contact))
-			gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag);
-		else
-			update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist);
-	}
 }
 
 static void
@@ -1736,8 +1768,40 @@
 	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");
+	if (!color_available)
+		color_available = gnt_color_add_pair(COLOR_GREEN, -1);
+	color_away = get_color("color-away");
+	if (!color_away)
+		color_away = gnt_color_add_pair(COLOR_BLUE, -1);
+	color_idle = get_color("color-idle");
+	if (!color_idle)
+		color_idle = gnt_color_add_pair(COLOR_CYAN, -1);
+	color_offline = get_color("color-offline");
+	if (!color_offline)
+		color_offline = gnt_color_add_pair(COLOR_RED, -1);
+
 	purple_prefs_add_none(PREF_ROOT);
 	purple_prefs_add_none(PREF_ROOT "/size");
 	purple_prefs_add_int(PREF_ROOT "/size/width", 20);
@@ -2550,4 +2614,3 @@
 {
 	gnt_widget_set_size(ggblist->window, width, height);
 }
-
--- a/finch/gntconn.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/gntconn.c	Sat Dec 01 03:41:37 2007 +0000
@@ -152,6 +152,10 @@
 				purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
 			continue;
 		purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
+		purple_conversation_write(conv, NULL, _("The account has disconnected and you are no "
+					"longer in this chat. You will be automatically rejoined in the chat when "
+					"the account reconnects."),
+				PURPLE_MESSAGE_SYSTEM, time(NULL));
 	}
 }
 
--- a/finch/libgnt/configure.ac	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/configure.ac	Sat Dec 01 03:41:37 2007 +0000
@@ -24,10 +24,10 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [2])
+m4_define([gnt_lt_current], [3])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [2])
-m4_define([gnt_micro_version], [0])
+m4_define([gnt_minor_version], [3])
+m4_define([gnt_micro_version], [1])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/finch/libgnt/gnt.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gnt.h	Sat Dec 01 03:41:37 2007 +0000
@@ -30,6 +30,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#ifndef GNT_H
+#define GNT_H
+
 #include <glib.h>
 #include "gntwidget.h"
 #include "gntclipboard.h"
@@ -201,3 +204,4 @@
  */
 gboolean gnt_is_refugee(void);
 
+#endif /* GNT_H */
--- a/finch/libgnt/gntbox.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntbox.c	Sat Dec 01 03:41:37 2007 +0000
@@ -851,8 +851,14 @@
 
 void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget)
 {
-	GList *find = g_list_find(box->focus, widget);
-	gpointer now = box->active;
+	GList *find;
+	gpointer now;
+
+	while (GNT_WIDGET(box)->parent)
+		box = GNT_BOX(GNT_WIDGET(box)->parent);
+
+	find = g_list_find(box->focus, widget);
+	now = box->active;
 	if (find)
 		box->active = widget;
 	if (now && now != box->active)
--- a/finch/libgnt/gntcolors.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntcolors.c	Sat Dec 01 03:41:37 2007 +0000
@@ -33,6 +33,7 @@
 #include <string.h>
 
 static gboolean hascolors;
+static int custom_type = GNT_COLORS;
 static struct
 {
 	short r, g, b;
@@ -137,8 +138,8 @@
 }
 
 #if GLIB_CHECK_VERSION(2,6,0)
-static int
-get_color(char *key)
+int
+gnt_colors_get_color(char *key)
 {
 	int color;
 	gboolean custom = can_use_custom_color();
@@ -163,8 +164,12 @@
 		color = COLOR_MAGENTA;
 	else if (strcmp(key, "cyan") == 0)
 		color = COLOR_CYAN;
-	else
+	else if (strcmp(key, "default") == 0)
 		color = -1;
+	else {
+		g_warning("Invalid color name: %s\n", key);
+		color = -1;
+	}
 	return color;
 }
 
@@ -196,7 +201,7 @@
 				int color = -1;
 
 				key = g_ascii_strdown(key, -1);
-				color = get_color(key);
+				color = gnt_colors_get_color(key);
 				g_free(key);
 				if (color == -1)
 					continue;
@@ -237,8 +242,8 @@
 			GntColorType type = 0;
 			gchar *fgc = g_ascii_strdown(list[0], -1);
 			gchar *bgc = g_ascii_strdown(list[1], -1);
-			int fg = get_color(fgc);
-			int bg = get_color(bgc);
+			int fg = gnt_colors_get_color(fgc);
+			int bg = gnt_colors_get_color(bgc);
 			g_free(fgc);
 			g_free(bgc);
 			if (fg == -1 || bg == -1)
@@ -287,3 +292,8 @@
 		  pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : A_STANDOUT));
 }
 
+int gnt_color_add_pair(int fg, int bg)
+{
+	init_pair(custom_type, fg, bg);
+	return custom_type++;
+}
--- a/finch/libgnt/gntcolors.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntcolors.h	Sat Dec 01 03:41:37 2007 +0000
@@ -86,6 +86,16 @@
  */
 void gnt_color_pairs_parse(GKeyFile *kfile);
 
+/**
+ * Parse a string color
+ *
+ * @param kfile The string value
+ *
+ * @return A color
+ *
+ * @since 2.3.1 (gnt), 2.3.1 (pidgin)
+ */
+int gnt_colors_get_color(char *key);
 #endif
 
 /**
@@ -101,4 +111,15 @@
  */
 int gnt_color_pair(int color);
 
+/**
+ * Adds a color definition
+ *
+ * @param fg   Foreground
+ * @param bg   Background
+ *
+ * @return  A color pair
+ *
+ * @since 2.3.1
+ */
+int gnt_color_add_pair(int fg, int bg);
 #endif
--- a/finch/libgnt/gntentry.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntentry.c	Sat Dec 01 03:41:37 2007 +0000
@@ -140,7 +140,6 @@
 static gboolean
 complete_suggest(GntEntry *entry, const char *text)
 {
-	gboolean changed = FALSE;
 	int offstart = 0, offend = 0;
 
 	if (entry->word) {
@@ -148,27 +147,22 @@
 		const char *iter = text;
 		offstart = g_utf8_pointer_to_offset(entry->start, s);
 		while (*iter && toupper(*s) == toupper(*iter)) {
-			if (*s != *iter)
-				changed = TRUE;
 			*s++ = *iter++;
 		}
 		if (*iter) {
 			gnt_entry_key_pressed(GNT_WIDGET(entry), iter);
-			changed = TRUE;
 		}
 		offend = g_utf8_pointer_to_offset(entry->start, entry->cursor);
 	} else {
 		offstart = 0;
 		gnt_entry_set_text_internal(entry, text);
-		changed = TRUE;
 		offend = g_utf8_strlen(text, -1);
 	}
 
-	if (changed)
-		g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0,
-				entry->start + offstart, entry->start + offend);
+	g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0,
+			entry->start + offstart, entry->start + offend);
 	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
-	return changed;
+	return TRUE;
 }
 
 static int
@@ -575,15 +569,16 @@
 next_begin_word(const char *text, const char *end)
 {
 	gunichar ch = 0;
+
+	while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
+		text = g_utf8_find_next_char(text, end);
+
 	ch = g_utf8_get_char(text);
 	while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
 		gunichar cur = g_utf8_get_char(text);
 		if (!SAME(ch, cur))
 			break;
 	}
-
-	while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
-		text = g_utf8_find_next_char(text, end);
 	return (text ? text : end);
 }
 
--- a/finch/libgnt/gntmain.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntmain.c	Sat Dec 01 03:41:37 2007 +0000
@@ -21,7 +21,7 @@
  */
 
 #define _GNU_SOURCE
-#if defined(__APPLE__) || defined(__unix__)
+#if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
 
--- a/finch/libgnt/gntmenu.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Sat Dec 01 03:41:37 2007 +0000
@@ -47,6 +47,8 @@
 static void (*org_size_request)(GntWidget *wid);
 static gboolean (*org_key_pressed)(GntWidget *w, const char *t);
 
+static void menuitem_activate(GntMenu *menu, GntMenuItem *item);
+
 static void
 menu_hide_all(GntMenu *menu)
 {
@@ -56,6 +58,20 @@
 }
 
 static void
+show_submenu(GntMenu *menu)
+{
+	GntMenuItem *item;
+
+	if (menu->type != GNT_MENU_TOPLEVEL)
+			return;
+
+	item = g_list_nth_data(menu->list, menu->selected);
+	if (!item || !item->submenu)
+		return;
+	menuitem_activate(menu, item);
+}
+
+static void
 gnt_menu_draw(GntWidget *widget)
 {
 	GntMenu *menu = GNT_MENU(widget);
@@ -276,6 +292,8 @@
 			gnt_widget_hide(widget);
 		} else
 			gnt_widget_hide(widget);
+		if (par && par->type == GNT_MENU_TOPLEVEL)
+			gnt_menu_key_pressed(GNT_WIDGET(par), text);
 		return TRUE;
 	}
 
@@ -297,6 +315,7 @@
 			GntMenu *sub = menu->submenu;
 			if (sub)
 				gnt_widget_hide(GNT_WIDGET(sub));
+			show_submenu(menu);
 			gnt_widget_draw(widget);
 			return TRUE;
 		}
--- a/finch/libgnt/gntstyle.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Sat Dec 01 03:41:37 2007 +0000
@@ -59,6 +59,21 @@
 #endif
 }
 
+char **gnt_style_get_string_list(const char *group, const char *key, gsize *length)
+{
+#if GLIB_CHECK_VERSION(2,6,0)
+	const char *prg = g_get_prgname();
+	if ((group == NULL || *group == '\0') && prg &&
+			g_key_file_has_group(gkfile, prg))
+		group = prg;
+	if (!group)
+		group = "general";
+	return g_key_file_get_string_list(gkfile, group, key, length, NULL);
+#else
+	return NULL;
+#endif
+}
+
 gboolean gnt_style_get_bool(GntStyle style, gboolean def)
 {
 	const char * str;
--- a/finch/libgnt/gntstyle.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Sat Dec 01 03:41:37 2007 +0000
@@ -65,6 +65,20 @@
 char *gnt_style_get_from_name(const char *group, const char *key);
 
 /**
+ * Get the value of a preference 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
+ * @param length  Return location for the number of strings returned, or NULL
+ *
+ * @return        NULL terminated string array. The array should be freed with g_strfreev().
+ *
+ * @since 2.3.1 (gnt), 2.3.1 (pidgin)
+ */
+char **gnt_style_get_string_list(const char *group, const char *key, gsize *length);
+
+/**
  * 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/gnttree.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gnttree.c	Sat Dec 01 03:41:37 2007 +0000
@@ -75,6 +75,7 @@
 	                               If choice is true, then child will be NULL */
 	gboolean isselected;
 	GntTextFormatFlags flags;
+	int color;
 
 	GntTreeRow *parent;
 	GntTreeRow *child;
@@ -522,9 +523,14 @@
 		else
 		{
 			if (flags & GNT_TEXT_FLAG_DIM)
-				attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
+				if (row->color)
+					attr |= (A_DIM | gnt_color_pair(row->color));
+				else
+					attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
 			else if (flags & GNT_TEXT_FLAG_HIGHLIGHT)
 				attr |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
+			else if (row->color)
+				attr |= gnt_color_pair(row->color);
 			else
 				attr |= gnt_color_pair(GNT_COLOR_NORMAL);
 		}
@@ -1559,6 +1565,16 @@
 	redraw_tree(tree);	/* XXX: It shouldn't be necessary to redraw the whole darned tree */
 }
 
+void gnt_tree_set_row_color(GntTree *tree, void *key, int color)
+{
+	GntTreeRow *row = g_hash_table_lookup(tree->hash, key);
+	if (!row || row->color == color)
+		return;
+
+	row->color = color;
+	redraw_tree(tree);
+}
+
 void gnt_tree_set_selected(GntTree *tree , void *key)
 {
 	int dist;
--- a/finch/libgnt/gnttree.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gnttree.h	Sat Dec 01 03:41:37 2007 +0000
@@ -325,6 +325,15 @@
 void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags);
 
 /**
+ * Set color for the text in a row in the tree.
+ *
+ * @param tree   The tree
+ * @param key    The key for the row
+ * @param color  The color
+ */
+void gnt_tree_set_row_color(GntTree *, void *, int);
+
+/**
  * Select a row.
  *
  * @param tree  The tree
--- a/finch/libgnt/gntwindow.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntwindow.c	Sat Dec 01 03:41:37 2007 +0000
@@ -62,7 +62,8 @@
 	if (window->menu)
 		gnt_widget_destroy(GNT_WIDGET(window->menu));
 	if (window->priv) {
-		g_hash_table_destroy(window->priv->accels);
+		if (window->priv->accels)
+			g_hash_table_destroy(window->priv->accels);
 		g_free(window->priv);
 	}
 	org_destroy(widget);
--- a/finch/libgnt/gntwm.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/finch/libgnt/gntwm.c	Sat Dec 01 03:41:37 2007 +0000
@@ -21,13 +21,14 @@
  */
 
 #define _GNU_SOURCE
-#if defined(__APPLE__) || defined(__unix__)
+#if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
 
 #include "config.h"
 
 #include <glib.h>
+#include <glib/gstdio.h>
 #include <ctype.h>
 #include <gmodule.h>
 #include <stdlib.h>
@@ -41,6 +42,7 @@
 #include "gntbox.h"
 #include "gntbutton.h"
 #include "gntentry.h"
+#include "gntfilesel.h"
 #include "gntlabel.h"
 #include "gntmenu.h"
 #include "gnttextview.h"
@@ -81,6 +83,7 @@
 static int widestringwidth(wchar_t *wide);
 #endif
 
+static void ensure_normal_mode(GntWM *wm);
 static gboolean write_already(gpointer data);
 static int write_timeout;
 static time_t last_active_time;
@@ -392,6 +395,10 @@
 	if (!wm->cws->ordered || !wm->cws->ordered->next)
 		return;
 
+	if (wm->mode != GNT_KP_MODE_NORMAL) {
+		ensure_normal_mode(wm);
+	}
+
 	w = wm->cws->ordered->data;
 	pos = g_list_index(wm->cws->list, w);
 	pos += direction;
@@ -503,6 +510,7 @@
 
 	if (wm->cws->ordered) {
 		gnt_widget_destroy(wm->cws->ordered->data);
+		ensure_normal_mode(wm);
 	}
 
 	return TRUE;
@@ -522,6 +530,7 @@
 setup__list(GntWM *wm)
 {
 	GntWidget *tree, *win;
+	ensure_normal_mode(wm);
 	win = wm->_list.window = gnt_box_new(FALSE, FALSE);
 	gnt_box_set_toplevel(GNT_BOX(win), TRUE);
 	gnt_box_set_pad(GNT_BOX(win), 0);
@@ -656,12 +665,12 @@
 	return TRUE;
 }
 
-static gboolean
-dump_screen(GntBindable *bindable, GList *null)
+static void
+dump_file_save(GntFileSel *fs, const char *path, const char *f, gpointer n)
 {
+	FILE *file;
 	int x, y;
 	chtype old = 0, now = 0;
-	FILE *file = fopen("dump.html", "w");
 	struct {
 		char ascii;
 		char *unicode;
@@ -683,6 +692,11 @@
 		{'\0', NULL}
 	};
 
+
+	if ((file = g_fopen(path, "w+")) == NULL) {
+		return;
+	}
+
 	fprintf(file, "<head>\n  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n</head>\n<body>\n");
 	fprintf(file, "<pre>");
 	for (y = 0; y < getmaxy(stdscr); y++) {
@@ -789,6 +803,24 @@
 	}
 	fprintf(file, "</pre>\n</body>");
 	fclose(file);
+	gnt_widget_destroy(GNT_WIDGET(fs));
+}
+
+static void
+dump_file_cancel(GntWidget *w, GntFileSel *fs)
+{
+	gnt_widget_destroy(GNT_WIDGET(fs));
+}
+
+static gboolean
+dump_screen(GntBindable *b, GList *null)
+{
+	GntWidget *window = gnt_file_sel_new();
+	GntFileSel *sel = GNT_FILE_SEL(window);
+	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);
+	gnt_widget_show(window);
 	return TRUE;
 }
 
@@ -972,6 +1004,16 @@
 	update_screen(wm);
 }
 
+static void
+ensure_normal_mode(GntWM *wm)
+{
+	if (wm->mode != GNT_KP_MODE_NORMAL) {
+		if (wm->cws->ordered)
+			window_reverse(wm->cws->ordered->data, FALSE, wm);
+		wm->mode = GNT_KP_MODE_NORMAL;
+	}
+}
+
 static gboolean
 start_move(GntBindable *bindable, GList *null)
 {
@@ -1349,7 +1391,7 @@
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list,
 				"\033" "w", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen,
-				"\033" "d", NULL);
+				"\033" "D", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left,
 				"\033" ",", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right,
@@ -1453,6 +1495,7 @@
 	if (wm->_list.window) {
 		gnt_widget_destroy(wm->_list.window);
 	}
+	ensure_normal_mode(wm);
 	gnt_ws_hide(wm->cws, wm->nodes);
 	wm->cws = s;
 	gnt_ws_show(wm->cws, wm->nodes);
--- a/libpurple/Makefile.am	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/Makefile.am	Sat Dec 01 03:41:37 2007 +0000
@@ -156,7 +156,7 @@
 
 dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \
                 connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \
-                savedstatuses.h status.h server.h util.h xmlnode.h
+                savedstatuses.h status.h server.h util.h xmlnode.h prpl.h
 
 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
 		$(purple_builtheaders)
--- a/libpurple/certificate.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/certificate.c	Sat Dec 01 03:41:37 2007 +0000
@@ -1758,7 +1758,6 @@
 gboolean
 purple_certificate_register_pool(PurpleCertificatePool *pool)
 {
-	gboolean success = FALSE;
 	g_return_val_if_fail(pool, FALSE);
 	g_return_val_if_fail(pool->scheme_name, FALSE);
 	g_return_val_if_fail(pool->name, FALSE);
@@ -1771,46 +1770,42 @@
 
 	/* Initialize the pool if needed */
 	if (pool->init) {
+		gboolean success;
+
 		success = pool->init();
-	} else {
-		success = TRUE;
+		if (!success)
+			return FALSE;
 	}
-	
-	if (success) {
-		/* Register the Pool */
-		cert_pools = g_list_prepend(cert_pools, pool);
 
-		/* TODO: Emit a signal that the pool got registered */
+	/* Register the Pool */
+	cert_pools = g_list_prepend(cert_pools, pool);
+
+	/* TODO: Emit a signal that the pool got registered */
 
-		PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool);
-		purple_signal_register(pool, /* Signals emitted from pool */
-				       "certificate-stored",
-				       purple_marshal_VOID__POINTER_POINTER,
-				       NULL, /* No callback return value */
-				       2,    /* Two non-data arguments */
-				       purple_value_new(PURPLE_TYPE_SUBTYPE,
-							PURPLE_SUBTYPE_CERTIFICATEPOOL),
-				       purple_value_new(PURPLE_TYPE_STRING));
+	PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool);
+	purple_signal_register(pool, /* Signals emitted from pool */
+			       "certificate-stored",
+			       purple_marshal_VOID__POINTER_POINTER,
+			       NULL, /* No callback return value */
+			       2,    /* Two non-data arguments */
+			       purple_value_new(PURPLE_TYPE_SUBTYPE,
+						PURPLE_SUBTYPE_CERTIFICATEPOOL),
+			       purple_value_new(PURPLE_TYPE_STRING));
 
-		purple_signal_register(pool, /* Signals emitted from pool */
-				       "certificate-deleted",
-				       purple_marshal_VOID__POINTER_POINTER,
-				       NULL, /* No callback return value */
-				       2,    /* Two non-data arguments */
-				       purple_value_new(PURPLE_TYPE_SUBTYPE,
-							PURPLE_SUBTYPE_CERTIFICATEPOOL),
-				       purple_value_new(PURPLE_TYPE_STRING));
-
+	purple_signal_register(pool, /* Signals emitted from pool */
+			       "certificate-deleted",
+			       purple_marshal_VOID__POINTER_POINTER,
+			       NULL, /* No callback return value */
+			       2,    /* Two non-data arguments */
+			       purple_value_new(PURPLE_TYPE_SUBTYPE,
+						PURPLE_SUBTYPE_CERTIFICATEPOOL),
+			       purple_value_new(PURPLE_TYPE_STRING));
 
-		purple_debug_info("certificate",
-			  "CertificatePool %s registered\n",
-			  pool->name);
-		return TRUE;
-	} else {
-		return FALSE;
-	}
-	
-	/* Control does not reach this point */
+	purple_debug_info("certificate",
+		  "CertificatePool %s registered\n",
+		  pool->name);
+
+	return TRUE;
 }
 
 gboolean
--- a/libpurple/network.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/network.c	Sat Dec 01 03:41:37 2007 +0000
@@ -182,22 +182,22 @@
 		/* Make sure the IP address entered by the user is valid */
 		if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL))
 			return ip;
-	}
-
-	/* Check if STUN discovery was already done */
-	stun = purple_stun_discover(NULL);
-	if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
-		return stun->publicip;
+	} else {
+		/* Check if STUN discovery was already done */
+		stun = purple_stun_discover(NULL);
+		if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
+			return stun->publicip;
 
-	/* Attempt to get the IP from a NAT device using UPnP */
-	ip = purple_upnp_get_public_ip();
-	if (ip != NULL)
-	  return ip;
+		/* Attempt to get the IP from a NAT device using UPnP */
+		ip = purple_upnp_get_public_ip();
+		if (ip != NULL)
+			return ip;
 
-	/* Attempt to get the IP from a NAT device using NAT-PMP */
-	ip = purple_pmp_get_public_ip();
-	if (ip != NULL)
-		return ip;
+		/* Attempt to get the IP from a NAT device using NAT-PMP */
+		ip = purple_pmp_get_public_ip();
+		if (ip != NULL)
+			return ip;
+	}
 
 	/* Just fetch the IP of the local system */
 	return purple_network_get_local_system_ip(fd);
@@ -362,7 +362,7 @@
 	listen_data->cb_data = cb_data;
 	listen_data->socket_type = socket_type;
 
-	if (!listen_map_external)
+	if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports"))
 	{
 		purple_debug_info("network", "Skipping external port mapping.\n");
 		/* The pmp_map_cb does what we want to do */
@@ -677,11 +677,13 @@
 	purple_prefs_add_none  ("/purple/network");
 	purple_prefs_add_bool  ("/purple/network/auto_ip", TRUE);
 	purple_prefs_add_string("/purple/network/public_ip", "");
+	purple_prefs_add_bool  ("/purple/network/map_ports", TRUE);
 	purple_prefs_add_bool  ("/purple/network/ports_range_use", FALSE);
 	purple_prefs_add_int   ("/purple/network/ports_range_start", 1024);
 	purple_prefs_add_int   ("/purple/network/ports_range_end", 2048);
 
-	purple_upnp_discover(NULL, NULL);
+	if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
+		purple_upnp_discover(NULL, NULL);
 
 #ifdef HAVE_LIBNM
 	nm_context = libnm_glib_init();
--- a/libpurple/ntlm.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/ntlm.c	Sat Dec 01 03:41:37 2007 +0000
@@ -187,7 +187,7 @@
 {
 	PurpleCipher *cipher;
 	PurpleCipherContext *context;
-	gsize outlen;
+	size_t outlen;
 
 	cipher = purple_ciphers_find_cipher("des");
 	context = purple_cipher_context_new(cipher, NULL);
--- a/libpurple/plugins/newline.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/plugins/newline.c	Sat Dec 01 03:41:37 2007 +0000
@@ -31,6 +31,12 @@
 addnewline_msg_cb(PurpleAccount *account, char *sender, char **message,
 					 PurpleConversation *conv, int *flags, void *data)
 {
+	if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
+		 !purple_prefs_get_bool("/plugins/core/newline/im")) ||
+		((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
+		 !purple_prefs_get_bool("/plugins/core/newline/chat")))
+		return FALSE;
+
 	if (g_ascii_strncasecmp(*message, "/me ", strlen("/me "))) {
 		char *tmp = g_strdup_printf("<br/>%s", *message);
 		g_free(*message);
@@ -40,6 +46,25 @@
 	return FALSE;
 }
 
+static PurplePluginPrefFrame *
+get_plugin_pref_frame(PurplePlugin *plugin) {
+	PurplePluginPrefFrame *frame;
+	PurplePluginPref *ppref;
+
+	frame = purple_plugin_pref_frame_new();
+
+	ppref = purple_plugin_pref_new_with_name_and_label(
+			"/plugins/core/newline/im", _("Add new line in IMs"));
+	purple_plugin_pref_frame_add(frame, ppref);
+
+	ppref = purple_plugin_pref_new_with_name_and_label(
+			"/plugins/core/newline/chat", _("Add new line in Chats"));
+	purple_plugin_pref_frame_add(frame, ppref);
+
+	return frame;
+}
+
+
 static gboolean
 plugin_load(PurplePlugin *plugin)
 {
@@ -53,6 +78,17 @@
 	return TRUE;
 }
 
+static PurplePluginUiInfo prefs_info = {
+	get_plugin_pref_frame,
+	0,   /* page_num (Reserved) */
+	NULL, /* frame (Reserved) */
+	/* Padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
 static PurplePluginInfo info =
 {
 	PURPLE_PLUGIN_MAGIC,							/**< magic			*/
@@ -80,7 +116,7 @@
 
 	NULL,											/**< ui_info		*/
 	NULL,											/**< extra_info		*/
-	NULL,											/**< prefs_info		*/
+	&prefs_info,									/**< prefs_info		*/
 	NULL,											/**< actions		*/
 
 	/* padding */
@@ -92,6 +128,9 @@
 
 static void
 init_plugin(PurplePlugin *plugin) {
+	purple_prefs_add_none("/plugins/core/newline");
+	purple_prefs_add_bool("/plugins/core/newline/im", TRUE);
+	purple_prefs_add_bool("/plugins/core/newline/chat", TRUE);
 }
 
 PURPLE_INIT_PLUGIN(lastseen, init_plugin, info)
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Sat Dec 01 03:41:37 2007 +0000
@@ -512,9 +512,8 @@
 	dt.data = (unsigned char *) buf;
 	dt.size = buf_sz;
 
-	/* Perform the conversion */
-	crt = x509_import_from_datum(dt,
-				     GNUTLS_X509_FMT_PEM); // files should be in PEM format
+	/* Perform the conversion; files should be in PEM format */
+	crt = x509_import_from_datum(dt, GNUTLS_X509_FMT_PEM);
 
 	/* Cleanup */
 	g_free(buf);
--- a/libpurple/protocols/bonjour/bonjour.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Dec 01 03:41:37 2007 +0000
@@ -43,14 +43,6 @@
 #include "buddy.h"
 #include "bonjour_ft.h"
 
-/*
- * TODO: Should implement an add_buddy callback that removes the buddy
- *       from the local list.  Bonjour manages buddies for you, and
- *       adding someone locally by hand is stupid.  Or, maybe even better,
- *       if a PRPL does not have an add_buddy callback then do not allow
- *       users to add buddies.
- */
-
 static char *default_firstname;
 static char *default_lastname;
 static char *default_hostname;
@@ -260,6 +252,25 @@
 	g_free(stripped);
 }
 
+/*
+ * The add_buddy callback removes the buddy from the local list.
+ * Bonjour manages buddies for you, and adding someone locally by
+ * hand is stupid.  Perhaps we should change libpurple not to allow adding
+ * if there is no add_buddy callback.
+ */
+static void
+bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
+	purple_debug_error("bonjour", "Buddy '%s' manually added; removing.  "
+				      "Bonjour buddies must be discovered and not manually added.\n",
+			   purple_buddy_get_name(buddy));
+
+	/* I suppose we could alert the user here, but it seems unnecessary. */
+
+	/* If this causes problems, it can be moved to an idle callback */
+	purple_blist_remove_buddy(buddy);
+}
+
+
 static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
 	if (buddy->proto_data) {
 		bonjour_buddy_delete(buddy->proto_data);
@@ -303,7 +314,7 @@
 	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 	BonjourBuddy *bb;
 
-	if (buddy == NULL)
+	if (buddy == NULL || buddy->proto_data == NULL)
 	{
 		/*
 		 * This buddy is not in our buddy list, and therefore does not really
@@ -370,6 +381,11 @@
 	if (message != NULL)
 		purple_notify_user_info_add_pair(user_info, _("Message"), message);
 
+	if (bb == NULL) {
+		purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
+		return;
+	}
+
 	/* Only show first/last name if there is a nickname set (to avoid duplication) */
 	if (bb->nick != NULL) {
 		if (bb->first != NULL)
@@ -425,7 +441,7 @@
 	bonjour_set_status,                                      /* set_status */
 	NULL,                                                    /* set_idle */
 	NULL,                                                    /* change_passwd */
-	NULL,                                                    /* add_buddy */
+	bonjour_fake_add_buddy,                                  /* add_buddy */
 	NULL,                                                    /* add_buddies */
 	bonjour_remove_buddy,                                    /* remove_buddy */
 	NULL,                                                    /* remove_buddies */
--- a/libpurple/protocols/bonjour/issues.txt	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/bonjour/issues.txt	Sat Dec 01 03:41:37 2007 +0000
@@ -2,5 +2,4 @@
 ============= Known issues ===============
 ==========================================
 
-* File transfers
 * Typing notifications
--- a/libpurple/protocols/bonjour/jabber.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Sat Dec 01 03:41:37 2007 +0000
@@ -19,6 +19,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+
+#include "internal.h"
+
 #ifndef _WIN32
 #include <net/if.h>
 #include <sys/ioctl.h>
@@ -33,7 +36,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include "internal.h"
 #include "network.h"
 #include "eventloop.h"
 #include "connection.h"
@@ -618,7 +620,7 @@
 	}
 
 	memset(&my_addr, 0, sizeof(struct sockaddr_in));
-	my_addr.sin_family = PF_INET;
+	my_addr.sin_family = AF_INET;
 
 	/* Attempt to find a free port */
 	bind_successful = FALSE;
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Sat Dec 01 03:41:37 2007 +0000
@@ -155,7 +155,7 @@
 			/* Make sure it isn't us */
 			if (purple_utf8_strcasecmp(name, account->username) != 0) {
 				if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
-						interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC,
+						interface, protocol, name, type, domain, AVAHI_PROTO_INET,
 						0, _resolver_callback, account)) {
 					purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
 						avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
@@ -315,14 +315,14 @@
 		case PUBLISH_START:
 			publish_result = avahi_entry_group_add_service_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_UNSPEC, 0,
+				AVAHI_PROTO_INET, 0,
 				purple_account_get_username(data->account),
 				ICHAT_SERVICE, NULL, NULL, data->port_p2pj, lst);
 			break;
 		case PUBLISH_UPDATE:
 			publish_result = avahi_entry_group_update_service_txt_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_UNSPEC, 0,
+				AVAHI_PROTO_INET, 0,
 				purple_account_get_username(data->account),
 				ICHAT_SERVICE, NULL, lst);
 			break;
@@ -354,7 +354,7 @@
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account);
+	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account);
 	if (!idata->sb) {
 
 		purple_debug_error("bonjour",
@@ -400,7 +400,7 @@
 				purple_account_get_username(data->account));
 
 		ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
-			AVAHI_PROTO_UNSPEC, flags, svc_name,
+			AVAHI_PROTO_INET, flags, svc_name,
 			AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
 
 		g_free(svc_name);
@@ -486,7 +486,7 @@
 
 	name = g_strdup_printf("%s." ICHAT_SERVICE "local", buddy->name);
 	idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
-		AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
+		AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
 		_buddy_icon_record_cb, buddy);
 	g_free(name);
 
--- a/libpurple/protocols/irc/cmds.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/irc/cmds.c	Sat Dec 01 03:41:37 2007 +0000
@@ -294,14 +294,17 @@
 	ops = g_new0(char *, i * 2 + 1);
 
 	for (i = 0; nicks[i]; i++) {
-		if (!*nicks[i])
+		if (!*nicks[i]) {
+			g_free(nicks[i]);
 			continue;
+		}
 		ops[used++] = mode;
 		ops[used++] = nicks[i];
 	}
 
 	irc_do_mode(irc, target, sign, ops);
 	g_free(ops);
+	g_free(nicks);  /* No, not g_strfreev */
 
 	return 0;
 }
--- a/libpurple/protocols/irc/msgs.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/irc/msgs.c	Sat Dec 01 03:41:37 2007 +0000
@@ -138,6 +138,8 @@
 				irc->mode_chars = g_strdup(val + 1);
 		}
 	}
+
+	g_strfreev(features);
 }
 
 void irc_msg_luser(struct irc_conn *irc, const char *name, const char *from, char **args)
--- a/libpurple/protocols/irc/parse.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Sat Dec 01 03:41:37 2007 +0000
@@ -64,7 +64,7 @@
 	{ "318", "nt:", irc_msg_endwhois },	/* End of WHOIS			*/
 	{ "319", "nn:", irc_msg_whois },	/* Whois channels		*/
 	{ "320", "nn:", irc_msg_whois },	/* Whois (fn ident)		*/
-	{ "314", "nnvvv:", irc_msg_whois },	/* Whowas user			*/
+	{ "314", "nnnvv:", irc_msg_whois },	/* Whowas user			*/
 	{ "369", "nt:", irc_msg_endwhois },	/* End of WHOWAS		*/
 	{ "321", "*", irc_msg_list },		/* Start of list		*/
 	{ "322", "ncv:", irc_msg_list },	/* List.			*/
--- a/libpurple/protocols/jabber/caps.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/caps.c	Sat Dec 01 03:41:37 2007 +0000
@@ -257,18 +257,23 @@
 
 /* this function assumes that all information is available locally */
 static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const char *ver, GList *ext) {
-	JabberCapsClientInfo *result = g_new0(JabberCapsClientInfo, 1);
+	JabberCapsClientInfo *result;
 	JabberCapsKey *key = g_new0(JabberCapsKey, 1);
 	JabberCapsValue *caps;
 	GList *iter;
-	
+
 	key->node = (char *)node;
 	key->ver = (char *)ver;
-	
+
 	caps = g_hash_table_lookup(capstable,key);
-	
+
 	g_free(key);
-	
+
+	if (caps == NULL)
+		return NULL;
+
+	result = g_new0(JabberCapsClientInfo, 1);
+
 	/* join all information */
 	for(iter = caps->identities; iter; iter = g_list_next(iter)) {
 		JabberCapsIdentity *id = iter->data;
--- a/libpurple/protocols/jabber/disco.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sat Dec 01 03:41:37 2007 +0000
@@ -44,6 +44,37 @@
 	xmlnode_set_attrib(feature, "var", x); \
 }
 
+static void
+jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer data) {
+	JabberBytestreamsStreamhost *sh = data;
+	const char *from = xmlnode_get_attrib(packet, "from");
+	xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
+		"http://jabber.org/protocol/bytestreams");
+
+	if (from && !strcmp(from, sh->jid) && query != NULL) {
+		xmlnode *sh_node = xmlnode_get_child(query, "streamhost");
+		if (sh_node) {
+			const char *jid = xmlnode_get_attrib(sh_node, "jid");
+			const char *port = xmlnode_get_attrib(sh_node, "port");
+
+
+			if (jid == NULL || strcmp(jid, from) != 0)
+				purple_debug_error("jabber", "Invalid jid(%s) for bytestream.\n",
+						   jid ? jid : "(null)");
+
+			sh->host = g_strdup(xmlnode_get_attrib(sh_node, "host"));
+			sh->zeroconf = g_strdup(xmlnode_get_attrib(sh_node, "zeroconf"));
+			if (port != NULL)
+				sh->port = atoi(port);
+		}
+	}
+
+	purple_debug_info("jabber", "Discovered bytestream proxy server: "
+			  "jid='%s' host='%s' port='%d' zeroconf='%s'\n",
+			   from ? from : "", sh->host ? sh->host : "",
+			   sh->port, sh->zeroconf ? sh->zeroconf : "");
+}
+
 
 void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
 	const char *from = xmlnode_get_attrib(packet, "from");
@@ -191,10 +222,26 @@
 				if(!strcmp(category, "conference") && !strcmp(type, "text")) {
 					/* we found a groupchat or MUC server, add it to the list */
 					/* XXX: actually check for protocol/muc or gc-1.0 support */
-					js->chat_servers = g_list_append(js->chat_servers, g_strdup(from));
+					js->chat_servers = g_list_prepend(js->chat_servers, g_strdup(from));
 				} else if(!strcmp(category, "directory") && !strcmp(type, "user")) {
 					/* we found a JUD */
-					js->user_directories = g_list_append(js->user_directories, g_strdup(from));
+					js->user_directories = g_list_prepend(js->user_directories, g_strdup(from));
+				} else if(!strcmp(category, "proxy") && !strcmp(type, "bytestreams")) {
+					/* This is a bytestream proxy */
+					JabberIq *iq;
+					JabberBytestreamsStreamhost *sh;
+
+					purple_debug_info("jabber", "Found bytestream proxy server: %s\n", from);
+
+					sh = g_new0(JabberBytestreamsStreamhost, 1);
+					sh->jid = g_strdup(from);
+					js->bs_proxies = g_list_prepend(js->bs_proxies, sh);
+
+					iq = jabber_iq_new_query(js, JABBER_IQ_GET,
+								 "http://jabber.org/protocol/bytestreams");
+					xmlnode_set_attrib(iq->node, "to", sh->jid);
+					jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh);
+					jabber_iq_send(iq);
 				}
 
 			} else if(!strcmp(child->name, "feature")) {
@@ -344,8 +391,8 @@
 		g_free(js->server_name);
 		js->server_name = g_strdup(name);
 		if (!strcmp(name, "Google Talk")) {
-		  purple_debug_info("jabber", "Google Talk!\n");
-		  js->googletalk = TRUE;
+			purple_debug_info("jabber", "Google Talk!\n");
+			js->googletalk = TRUE;
 		}
 	}
 
@@ -422,8 +469,7 @@
 	jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL);
 	jabber_iq_send(iq);
 
-	iq = jabber_iq_new_query(js, JABBER_IQ_GET,
-		                 "http://jabber.org/protocol/disco#info");
+	iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
 	xmlnode_set_attrib(iq->node, "to", js->user->domain);
 	jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL);
 	jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/google.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/google.c	Sat Dec 01 03:41:37 2007 +0000
@@ -532,6 +532,6 @@
 
 char *jabber_google_presence_outgoing(PurpleStatus *tune)
 {
-	char *ret = g_strdup_printf("♫ %s", purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE));
-	return ret;
+	const char *attr = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
+	return attr ? g_strdup_printf("♫ %s", attr) : g_strdup("");
 }
--- a/libpurple/protocols/jabber/iq.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/iq.c	Sat Dec 01 03:41:37 2007 +0000
@@ -399,5 +399,6 @@
 void jabber_iq_uninit(void)
 {
 	g_hash_table_destroy(iq_handlers);
+	iq_handlers = NULL;
 }
 
--- a/libpurple/protocols/jabber/jabber.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Dec 01 03:41:37 2007 +0000
@@ -1234,20 +1234,31 @@
 		g_hash_table_destroy(js->buddies);
 	if(js->chats)
 		g_hash_table_destroy(js->chats);
+
 	while(js->chat_servers) {
 		g_free(js->chat_servers->data);
 		js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers);
 	}
+
 	while(js->user_directories) {
 		g_free(js->user_directories->data);
 		js->user_directories = g_list_delete_link(js->user_directories, js->user_directories);
 	}
-	if(js->stream_id)
-		g_free(js->stream_id);
+
+	while(js->bs_proxies) {
+		JabberBytestreamsStreamhost *sh = js->bs_proxies->data;
+		g_free(sh->jid);
+		g_free(sh->host);
+		g_free(sh->zeroconf);
+		g_free(sh);
+		js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies);
+	}
+
+	g_free(js->stream_id);
 	if(js->user)
 		jabber_id_free(js->user);
-	if(js->avatar_hash)
-		g_free(js->avatar_hash);
+	g_free(js->avatar_hash);
+
 	purple_circ_buffer_destroy(js->write_buffer);
 	if(js->writeh)
 		purple_input_remove(js->writeh);
@@ -1256,11 +1267,9 @@
 		sasl_dispose(&js->sasl);
 	if(js->sasl_mechs)
 		g_string_free(js->sasl_mechs, TRUE);
-	if(js->sasl_cb)
-		g_free(js->sasl_cb);
+	g_free(js->sasl_cb);
 #endif
-	if(js->serverFQDN)
-		g_free(js->serverFQDN);
+	g_free(js->serverFQDN);
 	while(js->commands) {
 		JabberAdHocCommands *cmd = js->commands->data;
 		g_free(cmd->jid);
@@ -1272,21 +1281,14 @@
 	g_free(js->server_name);
 	g_free(js->gmail_last_time);
 	g_free(js->gmail_last_tid);
-	if(js->old_msg)
-		g_free(js->old_msg);
-	if(js->old_avatarhash)
-		g_free(js->old_avatarhash);
-	if(js->old_artist)
-		g_free(js->old_artist);
-	if(js->old_title)
-		g_free(js->old_title);
-	if(js->old_source)
-		g_free(js->old_source);
-	if(js->old_uri)
-		g_free(js->old_uri);
-	if(js->old_track)
-		g_free(js->old_track);
-	
+	g_free(js->old_msg);
+	g_free(js->old_avatarhash);
+	g_free(js->old_artist);
+	g_free(js->old_title);
+	g_free(js->old_source);
+	g_free(js->old_uri);
+	g_free(js->old_track);
+
 	g_free(js);
 
 	gc->proto_data = NULL;
--- a/libpurple/protocols/jabber/jabber.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Sat Dec 01 03:41:37 2007 +0000
@@ -117,7 +117,7 @@
 	GHashTable *disco_callbacks;
 	int next_id;
 
-
+	GList *bs_proxies;
 	GList *oob_file_transfers;
 	GList *file_transfers;
 
@@ -146,7 +146,7 @@
 	char *gmail_last_time;
 	char *gmail_last_tid;
 
-    char *serverFQDN;
+	char *serverFQDN;
 
 	/* OK, this stays at the end of the struct, so plugins can depend
 	 * on the rest of the stuff being in the right place
@@ -202,6 +202,13 @@
 	JabberFeatureEnabled *is_enabled;
 } JabberFeature;
 
+typedef struct _JabberBytestreamsStreamhost {
+	char *jid;
+	char *host;
+	int port;
+	char *zeroconf;
+} JabberBytestreamsStreamhost;
+
 /* what kind of additional features as returned from disco#info are supported? */
 extern GList *jabber_features;
 
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sat Dec 01 03:41:37 2007 +0000
@@ -219,25 +219,31 @@
 											   option);
 	
 	option = purple_account_option_bool_new(
-											_("Allow plaintext auth over unencrypted streams"),
-											"auth_plain_in_clear", FALSE);
+						_("Allow plaintext auth over unencrypted streams"),
+						"auth_plain_in_clear", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-	
+						   option);
+
 	option = purple_account_option_int_new(_("Connect port"), "port", 5222);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-	
+						   option);
+
 	option = purple_account_option_string_new(_("Connect server"),
-											  "connect_server", NULL);
+						  "connect_server", NULL);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-	
-	
+						  option);
+
+	option = purple_account_option_string_new(_("File transfer proxies"),
+						  "ft_proxies",
+						/* TODO: Is this an acceptable default? */
+						  "proxy.jabber.org:7777");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
+						  option);
+
 	jabber_init_plugin(plugin);
-	
+
 	purple_prefs_remove("/plugins/prpl/jabber");
-	
+
 	/* XXX - If any other plugin wants SASL this won't be good ... */
 #ifdef HAVE_CYRUS_SASL
 #ifdef _WIN32
--- a/libpurple/protocols/jabber/presence.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sat Dec 01 03:41:37 2007 +0000
@@ -99,8 +99,6 @@
 {
 	PurpleConnection *gc = NULL;
 	JabberStream *js = NULL;
-	gboolean disconnected;
-	int primitive;
 	xmlnode *presence, *x, *photo;
 	char *stripped = NULL;
 	JabberBuddyState state;
@@ -108,22 +106,26 @@
 	const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL;
 	int length = -1;
 	gboolean allowBuzz;
-	PurplePresence *p = purple_account_get_presence(account);
+	PurplePresence *p;
 	PurpleStatus *tune;
 
+	if (purple_account_is_disconnected(account))
+		return;
+
+	p = purple_account_get_presence(account);
 	if (NULL == status) {
 		status = purple_presence_get_active_status(p);
 	}
 
-	if(!purple_status_is_active(status))
-		return;
-
-	disconnected = purple_account_is_disconnected(account);
-
-	if(disconnected)
-		return;
-
-	primitive = purple_status_type_get_primitive(purple_status_get_type(status));
+	if (purple_status_is_exclusive(status)) {
+		/* An exclusive status can't be deactivated. You should just
+		 * activate some other exclusive status. */
+		if (!purple_status_is_active(status))
+			return;
+	} else {
+		/* Work with the exclusive status. */
+		status = purple_presence_get_active_status(p);
+	}
 
 	gc = purple_account_get_connection(account);
 	js = gc->proto_data;
@@ -308,8 +310,9 @@
 static void authorize_add_cb(gpointer data)
 {
 	struct _jabber_add_permit *jap = data;
-	jabber_presence_subscription_set(jap->gc->proto_data, jap->who,
-			"subscribed");
+	if(PURPLE_CONNECTION_IS_VALID(jap->gc))
+		jabber_presence_subscription_set(jap->gc->proto_data,
+			jap->who, "subscribed");
 	g_free(jap->who);
 	g_free(jap);
 }
@@ -317,9 +320,9 @@
 static void deny_add_cb(gpointer data)
 {
 	struct _jabber_add_permit *jap = data;
-	jabber_presence_subscription_set(jap->gc->proto_data, jap->who,
-			"unsubscribed");
-
+	if(PURPLE_CONNECTION_IS_VALID(jap->gc))
+		jabber_presence_subscription_set(jap->gc->proto_data,
+			jap->who, "unsubscribed");
 	g_free(jap->who);
 	g_free(jap);
 }
@@ -373,23 +376,26 @@
 static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
 	JabberPresenceCapabilities *userdata = user_data;
 	GList *iter;
-	
+
 	if(userdata->jbr->caps)
 		jabber_caps_free_clientinfo(userdata->jbr->caps);
 	userdata->jbr->caps = info;
-	
-	for(iter = info->features; iter; iter = g_list_next(iter)) {
-		if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) {
-			JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
-			xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
-			xmlnode_set_attrib(iq->node, "to", userdata->from);
-			xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
-			
-			jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
-			jabber_iq_send(iq);
-			break;
+
+	if (info) {
+		for(iter = info->features; iter; iter = g_list_next(iter)) {
+			if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) {
+				JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
+				xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
+				xmlnode_set_attrib(iq->node, "to", userdata->from);
+				xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
+
+				jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
+				jabber_iq_send(iq);
+				break;
+			}
 		}
 	}
+
 	g_free(userdata->from);
 	g_free(userdata);
 }
--- a/libpurple/protocols/jabber/si.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/jabber/si.c	Sat Dec 01 03:41:37 2007 +0000
@@ -36,19 +36,14 @@
 #include "iq.h"
 #include "si.h"
 
-#include "si.h"
-
-struct bytestreams_streamhost {
-	char *jid;
-	char *host;
-	int port;
-};
+#define STREAMHOST_CONNECT_TIMEOUT 15
 
 typedef struct _JabberSIXfer {
 	JabberStream *js;
 
 	PurpleProxyConnectData *connect_data;
 	PurpleNetworkListenData *listen_data;
+	guint connect_timeout;
 
 	gboolean accepted;
 
@@ -99,39 +94,82 @@
 	JabberSIXfer *jsx = xfer->data;
 	JabberIq *iq;
 	xmlnode *query, *su;
-	struct bytestreams_streamhost *streamhost = jsx->streamhosts->data;
+	JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
 
 	purple_proxy_info_destroy(jsx->gpi);
+	jsx->gpi = NULL;
 	jsx->connect_data = NULL;
 
+	if (jsx->connect_timeout > 0)
+		purple_timeout_remove(jsx->connect_timeout);
+	jsx->connect_timeout = 0;
+
 	if(source < 0) {
 		purple_debug_warning("jabber",
 				"si connection failed, jid was %s, host was %s, error was %s\n",
-				streamhost->jid, streamhost->host, error_message);
+				streamhost->jid, streamhost->host,
+				error_message ? error_message : "(null)");
 		jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
 		g_free(streamhost->jid);
 		g_free(streamhost->host);
+		g_free(streamhost->zeroconf);
 		g_free(streamhost);
 		jabber_si_bytestreams_attempt_connect(xfer);
 		return;
 	}
 
-	iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
-	xmlnode_set_attrib(iq->node, "to", xfer->who);
-	jabber_iq_set_id(iq, jsx->iq_id);
-	query = xmlnode_get_child(iq->node, "query");
-	su = xmlnode_new_child(query, "streamhost-used");
-	xmlnode_set_attrib(su, "jid", streamhost->jid);
+	/* unknown file transfer type is assumed to be RECEIVE */
+	if(xfer->type == PURPLE_XFER_SEND)
+	{
+		xmlnode *activate;
+		iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, "http://jabber.org/protocol/bytestreams");
+		xmlnode_set_attrib(iq->node, "to", streamhost->jid);
+		query = xmlnode_get_child(iq->node, "query");
+		xmlnode_set_attrib(query, "sid", jsx->stream_id);
+		activate = xmlnode_new_child(query, "activate");
+		xmlnode_insert_data(activate, xfer->who, -1);
+
+		/* TODO: We need to wait for an activation result before starting */
+	}
+	else
+	{
+		iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
+		xmlnode_set_attrib(iq->node, "to", xfer->who);
+		jabber_iq_set_id(iq, jsx->iq_id);
+		query = xmlnode_get_child(iq->node, "query");
+		su = xmlnode_new_child(query, "streamhost-used");
+		xmlnode_set_attrib(su, "jid", streamhost->jid);
+	}
 
 	jabber_iq_send(iq);
 
 	purple_xfer_start(xfer, source, NULL, -1);
 }
 
+static gboolean
+connect_timeout_cb(gpointer data)
+{
+	PurpleXfer *xfer = data;
+	JabberSIXfer *jsx = xfer->data;
+
+	purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
+
+	jsx->connect_timeout = 0;
+
+	if (jsx->connect_data != NULL)
+		purple_proxy_connect_cancel(jsx->connect_data);
+	jsx->connect_data = NULL;
+
+	/* Trigger the connect error manually */
+	jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded.");
+
+	return FALSE;
+}
+
 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
 {
 	JabberSIXfer *jsx = xfer->data;
-	struct bytestreams_streamhost *streamhost;
+	JabberBytestreamsStreamhost *streamhost;
 	char *dstaddr, *p;
 	int i;
 	unsigned char hashval[20];
@@ -160,18 +198,28 @@
 
 	streamhost = jsx->streamhosts->data;
 
+	jsx->connect_data = NULL;
+	if (jsx->gpi != NULL)
+		purple_proxy_info_destroy(jsx->gpi);
+	jsx->gpi = NULL;
+
 	dstjid = jabber_id_new(xfer->who);
 
-	if(dstjid != NULL) {
+	/* TODO: Deal with zeroconf */
+
+	if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
 		jsx->gpi = purple_proxy_info_new();
 		purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
 		purple_proxy_info_set_host(jsx->gpi, streamhost->host);
 		purple_proxy_info_set_port(jsx->gpi, streamhost->port);
 
-
-
-		dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, jsx->js->user->node,
-				jsx->js->user->domain, jsx->js->user->resource);
+		/* unknown file transfer type is assumed to be RECEIVE */
+		if(xfer->type == PURPLE_XFER_SEND)
+			dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
+				jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
+		else
+			dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
+				jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
 
 		purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
 				sizeof(hashval), hashval, NULL);
@@ -186,6 +234,11 @@
 				jabber_si_bytestreams_connect_cb, xfer);
 		g_free(dstaddr);
 
+		/* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
+		if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
+			jsx->connect_timeout = purple_timeout_add_seconds(
+				STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
+
 		jabber_id_free(dstjid);
 	}
 
@@ -194,6 +247,7 @@
 		jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
 		g_free(streamhost->jid);
 		g_free(streamhost->host);
+		g_free(streamhost->zeroconf);
 		g_free(streamhost);
 		jabber_si_bytestreams_attempt_connect(xfer);
 	}
@@ -232,17 +286,19 @@
 
 	for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
 			streamhost = xmlnode_get_next_twin(streamhost)) {
-		const char *jid, *host, *port;
-		int portnum;
+		const char *jid, *host = NULL, *port, *zeroconf;
+		int portnum = 0;
 
 		if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
-				(host = xmlnode_get_attrib(streamhost, "host")) &&
+				((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) ||
+				((host = xmlnode_get_attrib(streamhost, "host")) &&
 				(port = xmlnode_get_attrib(streamhost, "port")) &&
-				(portnum = atoi(port))) {
-			struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1);
+				(portnum = atoi(port))))) {
+			JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
 			sh->jid = g_strdup(jid);
 			sh->host = g_strdup(host);
 			sh->port = portnum;
+			sh->zeroconf = g_strdup(zeroconf);
 			jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
 		}
 	}
@@ -351,7 +407,7 @@
 			jsx->js->user->resource, xfer->who);
 
 	purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
-							  sizeof(hashval), hashval, NULL);
+				    sizeof(hashval), hashval, NULL);
 	g_free(dstaddr);
 	dstaddr = g_malloc(41);
 	p = dstaddr;
@@ -363,9 +419,12 @@
 		purple_debug_error("jabber", "someone connected with the wrong info!\n");
 		close(source);
 		purple_xfer_cancel_remote(xfer);
+		g_free(dstaddr);
 		return;
 	}
 
+	g_free(dstaddr);
+
 	g_free(jsx->rxqueue);
 	host = purple_network_get_my_ip(jsx->js->fd);
 
@@ -523,6 +582,32 @@
 		source, PURPLE_INPUT_WRITE);
 }
 
+static gint
+jabber_si_compare_jid(gconstpointer a, gconstpointer b)
+{
+	const JabberBytestreamsStreamhost *sh = a;
+
+	if(!a)
+		return -1;
+
+	return strcmp(sh->jid, (char *)b);
+}
+
+
+static void
+jabber_si_free_streamhost(gpointer data, gpointer user_data)
+{
+	JabberBytestreamsStreamhost *sh = data;
+
+	if(!data)
+		return;
+
+	g_free(sh->jid);
+	g_free(sh->host);
+	g_free(sh->zeroconf);
+	g_free(sh);
+}
+
 static void
 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
 		PurpleInputCondition cond)
@@ -537,6 +622,7 @@
 		return;
 	else if(acceptfd == -1) {
 		purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
+		/* TODO: This should cancel the ft */
 		return;
 	}
 
@@ -544,7 +630,61 @@
 	close(source);
 
 	xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
-			jabber_si_xfer_bytestreams_send_read_cb, xfer);
+					 jabber_si_xfer_bytestreams_send_read_cb, xfer);
+}
+
+static void
+jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet,
+		gpointer data)
+{
+	PurpleXfer *xfer = data;
+	JabberSIXfer *jsx = xfer->data;
+	xmlnode *query, *streamhost_used;
+	const char *from, *type, *jid;
+	GList *matched;
+
+	/* TODO: This need to send errors if we don't see what we're looking for */
+
+	/* In the case of a direct file transfer, this is expected to return */
+	if(!jsx)
+		return;
+
+	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+		return;
+
+	if(!(from = xmlnode_get_attrib(packet, "from")))
+		return;
+
+	if(!(query = xmlnode_get_child(packet, "query")))
+		return;
+
+	if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used")))
+		return;
+
+	if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
+		return;
+
+	if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
+	{
+		gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
+			jsx->js->user->domain, jsx->js->user->resource);
+		if (!strcmp(jid, my_jid))
+			purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
+		else
+			purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
+		g_free(my_jid);
+		return;
+	}
+
+	/* TODO: Clean up the local SOCKS5 proxy - it isn't going to be used.*/
+
+	jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
+	g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+	g_list_free(jsx->streamhosts);
+
+	jsx->streamhosts = matched;
+
+	jabber_si_bytestreams_attempt_connect(xfer);
 }
 
 static void
@@ -554,7 +694,10 @@
 	JabberSIXfer *jsx;
 	JabberIq *iq;
 	xmlnode *query, *streamhost;
-	char *jid, *port;
+	char *jid, port[6];
+	const char *local_ip, *public_ip, *ft_proxies;
+	GList *tmp;
+	JabberBytestreamsStreamhost *sh, *sh2;
 
 	jsx = xfer->data;
 	jsx->listen_data = NULL;
@@ -578,27 +721,112 @@
 
 	xmlnode_set_attrib(query, "sid", jsx->stream_id);
 
-	streamhost = xmlnode_new_child(query, "streamhost");
 	jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
 			jsx->js->user->domain, jsx->js->user->resource);
-	xmlnode_set_attrib(streamhost, "jid", jid);
+	xfer->local_port = purple_network_get_port_from_fd(sock);
+	g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
+
+	/* TODO: Should there be an option to not use the local host as a ft proxy?
+	 *       (to prevent revealing IP address, etc.) */
+
+	/* Include the localhost's IP (for in-network transfers) */
+	local_ip = purple_network_get_local_system_ip(jsx->js->fd);
+	if (strcmp(local_ip, "0.0.0.0") != 0)
+	{
+		streamhost = xmlnode_new_child(query, "streamhost");
+		xmlnode_set_attrib(streamhost, "jid", jid);
+		xmlnode_set_attrib(streamhost, "host", local_ip);
+		xmlnode_set_attrib(streamhost, "port", port);
+	}
+
+	/* Include the public IP (assuming that there is a port mapped somehow) */
+	/* TODO: Check that it isn't the same as above and is a valid IP */
+	public_ip = purple_network_get_my_ip(jsx->js->fd);
+	if (strcmp(public_ip, local_ip) != 0)
+	{
+		streamhost = xmlnode_new_child(query, "streamhost");
+		xmlnode_set_attrib(streamhost, "jid", jid);
+		xmlnode_set_attrib(streamhost, "host", public_ip);
+		xmlnode_set_attrib(streamhost, "port", port);
+	}
+
 	g_free(jid);
 
-	/* XXX: shouldn't we use the public IP or something? here */
-	xmlnode_set_attrib(streamhost, "host",
-			purple_network_get_my_ip(jsx->js->fd));
-	xfer->local_port = purple_network_get_port_from_fd(sock);
-	port = g_strdup_printf("%hu", xfer->local_port);
-	xmlnode_set_attrib(streamhost, "port", port);
-	g_free(port);
-
+	/* The listener for the local proxy */
 	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
 			jabber_si_xfer_bytestreams_send_connected_cb, xfer);
 
-	/* XXX: insert proxies here */
+	/* insert proxies here */
+	ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL);
+	if (ft_proxies) {
+		int i, portnum;
+		char *tmp;
+		gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0);
+
+		g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+		g_list_free(jsx->streamhosts);
+		jsx->streamhosts = NULL;
+
+		for(i = 0; ft_proxy_list[i]; i++) {
+			g_strstrip(ft_proxy_list[i]);
+			if(!(*ft_proxy_list[i]))
+				continue;
+
+			if((tmp = strchr(ft_proxy_list[i], ':'))) {
+				portnum = atoi(tmp + 1);
+				*tmp = '\0';
+			} else
+				portnum = 7777;
+
+			g_snprintf(port, sizeof(port), "%hu", portnum);
+
+			if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL)
+				continue;
+
+			streamhost = xmlnode_new_child(query, "streamhost");
+			xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
+			xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
+			xmlnode_set_attrib(streamhost, "port", port);
 
-	/* XXX: callback to find out which streamhost they used, or see if they
-	 * screwed it up */
+			sh = g_new0(JabberBytestreamsStreamhost, 1);
+			sh->jid = g_strdup(ft_proxy_list[i]);
+			sh->host = g_strdup(ft_proxy_list[i]);
+			sh->port = portnum;
+
+			jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh);
+		}
+
+		g_strfreev(ft_proxy_list);
+	}
+
+	for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
+		sh = tmp->data;
+
+		/* TODO: deal with zeroconf proxies */
+
+		if (!(sh->jid && sh->host && sh->port > 0))
+			continue;
+
+		if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
+			continue;
+
+		streamhost = xmlnode_new_child(query, "streamhost");
+		xmlnode_set_attrib(streamhost, "jid", sh->jid);
+		xmlnode_set_attrib(streamhost, "host", sh->host);
+		g_snprintf(port, sizeof(port), "%hu", sh->port);
+		xmlnode_set_attrib(streamhost, "port", port);
+
+		sh2 = g_new0(JabberBytestreamsStreamhost, 1);
+		sh2->jid = g_strdup(sh->jid);
+		sh2->host = g_strdup(sh->host);
+		sh2->zeroconf = g_strdup(sh->zeroconf);
+		sh2->port = sh->port;
+
+		jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
+	}
+
+	jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
+
 	jabber_iq_send(iq);
 
 }
@@ -688,8 +916,7 @@
 	/* maybe later we'll do hash and date attribs */
 
 	feature = xmlnode_new_child(si, "feature");
-	xmlnode_set_namespace(feature,
-			"http://jabber.org/protocol/feature-neg");
+	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
 	x = xmlnode_new_child(feature, "x");
 	xmlnode_set_namespace(x, "jabber:x:data");
 	xmlnode_set_attrib(x, "type", "form");
@@ -698,8 +925,7 @@
 	xmlnode_set_attrib(field, "type", "list-single");
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
-	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams",
-			-1);
+	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
 	/*
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
@@ -729,6 +955,14 @@
 	if (jsx->iq_id != NULL)
 		jabber_iq_remove_callback_by_id(js, jsx->iq_id);
 
+	if (jsx->connect_timeout > 0)
+		purple_timeout_remove(jsx->connect_timeout);
+
+	if (jsx->streamhosts) {
+		g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+		g_list_free(jsx->streamhosts);
+	}
+
 	g_free(jsx->stream_id);
 	g_free(jsx->iq_id);
 	/* XXX: free other stuff */
--- a/libpurple/protocols/msn/command.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Sat Dec 01 03:41:37 2007 +0000
@@ -117,6 +117,7 @@
 		cmd->trId = 0;
 	}
 
+	/* khc: Huh! */
 	/*add payload Length checking*/
 	msn_set_payload_len(cmd);
 	purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len);
--- a/libpurple/protocols/msn/notification.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Dec 01 03:41:37 2007 +0000
@@ -1181,14 +1181,27 @@
 static void
 rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	/* TODO: This might be for us too */
-
 	MsnSession *session;
+	PurpleAccount *account;
 	PurpleConnection *gc;
 	const char *friendly;
+	char *username;
 
 	session = cmdproc->session;
-	gc = session->account->gc;
+	account = session->account;
+	username = g_strdup(purple_normalize(account,
+						purple_account_get_username(account)));
+
+	/* Only set display name if our *own* friendly name changed! */
+	if (strcmp(username, purple_normalize(account, cmd->params[2])))
+	{
+		g_free(username);
+		return;
+	}
+
+	g_free(username);
+
+	gc = account->gc;
 	friendly = purple_url_decode(cmd->params[3]);
 
 	purple_connection_set_display_name(gc, friendly);
--- a/libpurple/protocols/msnp9/msn.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msnp9/msn.c	Sat Dec 01 03:41:37 2007 +0000
@@ -1920,7 +1920,7 @@
 		purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
 		g_free(stripped);
 		g_free(url_buffer);
-		g_free(user_info);
+		purple_notify_user_info_destroy(user_info);
 		g_free(info_data->name);
 		g_free(info_data);
 		g_free(photo_url_text);
--- a/libpurple/protocols/msnp9/nexus.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msnp9/nexus.c	Sat Dec 01 03:41:37 2007 +0000
@@ -338,7 +338,7 @@
 		g_strdup(purple_url_encode(purple_account_get_username(session->account)));
 
 	password =
-		g_strndup(purple_url_encode(purple_connection_get_password(session->account->gc)), 16);
+		g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc)));
 
 	ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
 
--- a/libpurple/protocols/msnp9/notification.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msnp9/notification.c	Sat Dec 01 03:41:37 2007 +0000
@@ -235,6 +235,8 @@
 		/* OK */
 		const char *friendly = purple_url_decode(cmd->params[3]);
 
+		session->passport_info.verified = atoi(cmd->params[4]);
+
 		purple_connection_set_display_name(gc, friendly);
 
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
@@ -780,14 +782,27 @@
 static void
 rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	/* TODO: This might be for us too */
-
 	MsnSession *session;
+	PurpleAccount *account;
 	PurpleConnection *gc;
 	const char *friendly;
+	char *username;
 
 	session = cmdproc->session;
-	gc = session->account->gc;
+	account = session->account;
+	username = g_strdup(purple_normalize(account,
+						purple_account_get_username(account)));
+
+	/* Only set display name if our *own* friendly name changed! */
+	if (strcmp(username, purple_normalize(account, cmd->params[2])))
+	{
+		g_free(username);
+		return;
+	}
+
+	g_free(username);
+
+	gc = account->gc;
 	friendly = purple_url_decode(cmd->params[3]);
 
 	purple_connection_set_display_name(gc, friendly);
--- a/libpurple/protocols/msnp9/session.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msnp9/session.h	Sat Dec 01 03:41:37 2007 +0000
@@ -114,7 +114,7 @@
 		char *file;
 		char *client_ip;
 		int client_port;
-
+		int verified;
 	} passport_info;
 };
 
--- a/libpurple/protocols/msnp9/user.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/msnp9/user.c	Sat Dec 01 03:41:37 2007 +0000
@@ -126,16 +126,23 @@
 msn_user_set_friendly_name(MsnUser *user, const char *name)
 {
 	MsnCmdProc *cmdproc;
+	MsnSession *session;
+	const char *encoded;
 
 	g_return_if_fail(user != NULL);
 
-	if (user->friendly_name && strcmp(user->friendly_name, name)) {
+	encoded = purple_url_encode(name);
+	session = user->userlist->session;
+
+	if (user->friendly_name && strcmp(user->friendly_name, name)
+		&& (strlen(encoded) < 387) && session->passport_info.verified &&
+		(user->list_op & MSN_LIST_FL_OP)) {
 		/* copy the new name to the server list, but only when new */
 		/* should we check this more thoroughly? */
-		cmdproc = user->userlist->session->notification->cmdproc;
+		cmdproc = session->notification->cmdproc;
 		msn_cmdproc_send(cmdproc, "REA", "%s %s",
 						 user->passport,
-						 purple_url_encode(name));
+						 encoded);
 	}
 
 	g_free(user->friendly_name);
--- a/libpurple/protocols/qq/packet_parse.h	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/qq/packet_parse.h	Sat Dec 01 03:41:37 2007 +0000
@@ -23,7 +23,7 @@
  */
 
 #ifndef _QQ_PACKET_PARSE_H_
-#define _QQ_PACKED_PARSE_H_
+#define _QQ_PACKET_PARSE_H_
 
 #include <glib.h>
 #include <time.h>
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Sat Dec 01 03:41:37 2007 +0000
@@ -219,13 +219,13 @@
 
 void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
 {
-	GSList *l = pkt->hash;
+	GSList *l;
 	int pos = 0;
 
 	/* This is only called from one place, and the list is
 	 * always backwards */
 
-	l = g_slist_reverse(l);
+	l = pkt->hash = g_slist_reverse(pkt->hash);
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
--- a/libpurple/prpl.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/prpl.c	Sat Dec 01 03:41:37 2007 +0000
@@ -258,8 +258,8 @@
 								PurpleStatus *old_status, PurpleStatus *new_status)
 {
 	g_return_if_fail(account    != NULL);
-	g_return_if_fail(old_status != NULL);
 	g_return_if_fail(new_status != NULL);
+	g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL);
 
 	do_prpl_change_account_status(account, old_status, new_status);
 
--- a/libpurple/tests/Makefile.am	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/tests/Makefile.am	Sat Dec 01 03:41:37 2007 +0000
@@ -1,6 +1,9 @@
 if HAVE_CHECK
 TESTS=check_libpurple
 
+clean-local:
+	-rm -rf libpurple..
+
 check_PROGRAMS=check_libpurple
 
 check_libpurple_SOURCES=\
--- a/libpurple/tests/test_util.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/tests/test_util.c	Sat Dec 01 03:41:37 2007 +0000
@@ -76,7 +76,7 @@
 
 START_TEST(test_util_str_to_time)
 {
-	fail_unless(377185800 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL));
+	fail_unless(377182200 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL));
 	fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21", TRUE, NULL, NULL, NULL));
 }
 END_TEST
--- a/libpurple/util.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/util.c	Sat Dec 01 03:41:37 2007 +0000
@@ -753,13 +753,13 @@
                  struct tm *tm, long *tz_off, const char **rest)
 {
 	time_t retval = 0;
-	struct tm *t;
+	static struct tm t;
 	const char *c = timestamp;
 	int year = 0;
 	long tzoff = PURPLE_NO_TZ_OFF;
 
 	time(&retval);
-	t = localtime(&retval);
+	localtime_r(&retval, &t);
 
 	/* 4 digit year */
 	if (sscanf(c, "%04d", &year) && year > 1900)
@@ -767,11 +767,11 @@
 		c += 4;
 		if (*c == '-')
 			c++;
-		t->tm_year = year - 1900;
+		t.tm_year = year - 1900;
 	}
 
 	/* 2 digit month */
-	if (!sscanf(c, "%02d", &t->tm_mon))
+	if (!sscanf(c, "%02d", &t.tm_mon))
 	{
 		if (rest != NULL && *c != '\0')
 			*rest = c;
@@ -780,10 +780,10 @@
 	c += 2;
 	if (*c == '-' || *c == '/')
 		c++;
-	t->tm_mon -= 1;
+	t.tm_mon -= 1;
 
 	/* 2 digit day */
-	if (!sscanf(c, "%02d", &t->tm_mday))
+	if (!sscanf(c, "%02d", &t.tm_mday))
 	{
 		if (rest != NULL && *c != '\0')
 			*rest = c;
@@ -794,13 +794,13 @@
 	{
 		c++;
 
-		if (!sscanf(c, "%04d", &t->tm_year))
+		if (!sscanf(c, "%04d", &t.tm_year))
 		{
 			if (rest != NULL && *c != '\0')
 				*rest = c;
 			return 0;
 		}
-		t->tm_year -= 1900;
+		t.tm_year -= 1900;
 	}
 	else if (*c == 'T' || *c == '.')
 	{
@@ -808,17 +808,20 @@
 		/* we have more than a date, keep going */
 
 		/* 2 digit hour */
-		if ((sscanf(c, "%02d:%02d:%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 8)) ||
-		    (sscanf(c, "%02d%02d%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 6)))
+		if ((sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 8)) ||
+		    (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 6)))
 		{
 			gboolean offset_positive = FALSE;
 			int tzhrs;
 			int tzmins;
 
-			t->tm_isdst = -1;
-
-			if (*c == '.' && *(c+1) >= '0' && *(c+1) <= '9') /* dealing with precision we don't care about */
-				c += 4;
+			t.tm_isdst = -1;
+
+			if (*c == '.') {
+				do {
+					c++;
+				} while (*c >= '0' && *c <= '9'); /* dealing with precision we don't care about */
+			}
 			if (*c == '+')
 				offset_positive = TRUE;
 			if (((*c == '+' || *c == '-') && (c = c + 1)) &&
@@ -830,11 +833,23 @@
 					tzoff *= -1;
 				/* We don't want the C library doing DST calculations
 				 * if we know the UTC offset already. */
-				t->tm_isdst = 0;
+				t.tm_isdst = 0;
 			}
 			else if (utc)
 			{
-				t->tm_isdst = -1;
+				static struct tm tmptm;
+				time_t tmp;
+				tmp = mktime(&t);
+				/* we care about whether it *was* dst, and the offset, here on this
+				 * date, not whether we are currently observing dst locally *now*.
+				 * This isn't perfect, because we would need to know in advance the
+				 * offset we are trying to work out in advance to be sure this
+				 * works for times around dst transitions but it'll have to do. */
+				localtime_r(&tmp, &tmptm);
+				t.tm_isdst = tmptm.tm_isdst;
+#ifdef HAVE_TM_GMTOFF
+				t.tm_gmtoff = tmptm.tm_gmtoff;
+#endif
 			}
 
 			if (rest != NULL && *c != '\0')
@@ -863,7 +878,7 @@
 					tzoff += sys_tzoff;
 #else
 #ifdef HAVE_TM_GMTOFF
-				tzoff += t->tm_gmtoff;
+				tzoff += t.tm_gmtoff;
 #else
 #	ifdef HAVE_TIMEZONE
 				tzset();    /* making sure */
@@ -882,12 +897,12 @@
 
 	if (tm != NULL)
 	{
-		*tm = *t;
+		*tm = t;
 		tm->tm_isdst = -1;
 		mktime(tm);
 	}
 
-	retval = mktime(t);
+	retval = mktime(&t);
 	if (tzoff != PURPLE_NO_TZ_OFF)
 		retval += tzoff;
 
--- a/libpurple/win32/libc_interface.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/win32/libc_interface.c	Sat Dec 01 03:41:37 2007 +0000
@@ -28,6 +28,7 @@
 #include <sys/stat.h>
 #include <time.h>
 #include <glib.h>
+#include "config.h"
 #include "debug.h"
 #include "libc_internal.h"
 #if GLIB_CHECK_VERSION(2,6,0)
@@ -38,6 +39,26 @@
 #define g_stat stat
 #endif
 
+#ifdef ENABLE_NLS
+#  include <locale.h>
+#  include <libintl.h>
+#  define _(String) ((const char *)dgettext(PACKAGE, String))
+#  ifdef gettext_noop
+#    define N_(String) gettext_noop (String)
+#  else
+#    define N_(String) (String)
+#  endif
+#else
+#  include <locale.h>
+#  define N_(String) (String)
+#  ifndef _
+#    define _(String) ((const char *)String)
+#  endif
+#  define ngettext(Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural))
+#  define dngettext(Domain, Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural))
+#endif
+
+
 static char errbuf[1024];
 
 /* helpers */
@@ -294,15 +315,25 @@
 }
 
 /* string.h */
-char* wpurple_strerror( int errornum ) {
-	if( errornum > WSABASEERR ) {
-		sprintf( errbuf, "Windows socket error #%d", errornum );
-		return errbuf;
+char* wpurple_strerror(int errornum) {
+	if (errornum > WSABASEERR) {
+		switch(errornum) {
+			case WSAECONNABORTED: /* 10053 */
+				snprintf(errbuf, sizeof(errbuf), _("Connection interrupted by other software on your computer."));
+			case WSAECONNRESET: /* 10054 */
+				snprintf(errbuf, sizeof(errbuf), _("Remote host closed connection."));
+			case WSAETIMEDOUT: /* 10060 */
+				snprintf(errbuf, sizeof(errbuf), _("Connection timed out."));
+			case WSAECONNREFUSED: /*10061 */
+				snprintf(errbuf, sizeof(errbuf), _("Connection refused."));
+			default:
+				snprintf(errbuf, sizeof(errbuf), "Windows socket error #%d", errornum);
+		}
+	} else {
+		const char *tmp = g_strerror(errornum);
+		snprintf(errbuf, sizeof(errbuf), tmp);
 	}
-	else
-		/* Nothing is supposed to modify what is returned by strerror,
-		   but it isn't const for some reason */
-		return (char *)g_strerror( errornum );
+	return errbuf;
 }
 
 /* unistd.h */
@@ -409,7 +440,7 @@
 
 	if (p != 0) {
 		_ftime(&timebuffer);
-	   	p->tv_sec = timebuffer.time;			/* seconds since 1-1-1970 */
+		p->tv_sec = timebuffer.time;			/* seconds since 1-1-1970 */
 		p->tv_usec = timebuffer.millitm*1000; 	/* microseconds */
 	}
 
--- a/libpurple/xmlnode.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/libpurple/xmlnode.c	Sat Dec 01 03:41:37 2007 +0000
@@ -303,7 +303,7 @@
 
 const char *xmlnode_get_prefix(xmlnode *node)
 {
-	g_return_val_if_fail(node != NULL, NULL);                                   
+	g_return_val_if_fail(node != NULL, NULL);
 	return node->prefix;
 }
 
@@ -346,6 +346,9 @@
 	g_free(node->data);
 	g_free(node->xmlns);
 
+	if(node->namespace_map)
+		g_hash_table_destroy(node->namespace_map);
+
 	PURPLE_DBUS_UNREGISTER_POINTER(node);
 	g_free(node);
 }
--- a/pidgin/gtkblist.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkblist.c	Sat Dec 01 03:41:37 2007 +0000
@@ -753,6 +753,11 @@
 	{
 		case GTK_RESPONSE_OK:
 			do_join_chat(info);
+			break;
+
+		case 1:
+			pidgin_roomlist_dialog_show_with_account(info->account);
+			return;
 
 		break;
 	}
@@ -769,6 +774,8 @@
 static void
 joinchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
 {
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleConnection *gc;
 	PidginJoinChatData *data;
 	GList *tmp;
 	const char *text;
@@ -789,6 +796,12 @@
 	}
 
 	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive);
+
+	gc = purple_account_get_connection(data->account);
+	prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
+	sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL);
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive);
 }
 
 static void
@@ -944,6 +957,7 @@
 
 	data->window = gtk_dialog_new_with_buttons(_("Join a Chat"),
 		NULL, GTK_DIALOG_NO_SEPARATOR,
+		_("Room _List"), 1,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		PIDGIN_STOCK_CHAT, GTK_RESPONSE_OK, NULL);
 	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
@@ -2537,7 +2551,7 @@
  *
  *
  */
-#define STATUS_SIZE 22
+#define STATUS_SIZE 16 
 #define TOOLTIP_BORDER 12
 #define SMALL_SPACE 6
 #define LARGE_SPACE 12
@@ -3451,8 +3465,8 @@
 	struct _pidgin_blist_node *gtkbuddynode = NULL;
 	PurpleBuddy *buddy = NULL;
 	PurpleChat *chat = NULL;
-	GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_SMALL :
-											 PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
+	GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL :
+											 PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC);
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
@@ -4230,7 +4244,7 @@
 {
 	PidginBuddyList *gtkblist = (PidginBuddyList *)user_data;
 	int errors = 0;
-	GList *list;
+	GList *list = NULL;
 	PidginBuddyListPrivate *priv;
 
 	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
@@ -4243,8 +4257,7 @@
 		errors = g_list_length(GTK_NOTEBOOK(priv->error_scrollbook->notebook)->children);
 #endif
 	}
-	if ((list = purple_accounts_get_all_active()) != NULL || errors ||
-	    (list = gtk_container_get_children(GTK_CONTAINER(priv->error_scrollbook)))) {
+	if ((list = purple_accounts_get_all_active()) != NULL || errors) {
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1);
 		g_list_free(list);
 	} else
@@ -4441,8 +4454,12 @@
                              PurpleAccount *account)
 {
 	GList *l = NULL;
-	GList *children = gtk_container_get_children(container);
+	GList *children = NULL;
 	GtkWidget *ret = NULL;
+	/* XXX: Workaround for the currently incomplete implementation of PidginScrollBook */
+	if (PIDGIN_IS_SCROLL_BOOK(container))
+		container = GTK_CONTAINER(PIDGIN_SCROLL_BOOK(container)->notebook);
+	children = gtk_container_get_children(container);
 	l = g_list_find_custom(children, account, (GCompareFunc) find_account_widget);
 	if (l)
 		ret = GTK_WIDGET(l->data);
@@ -4456,7 +4473,6 @@
 {
 	GtkWidget *widget = find_child_widget_by_account(container, account);
 	if(widget) {
-		gtk_container_remove(container, widget);
 		gtk_widget_destroy(widget);
 	}
 }
@@ -4478,16 +4494,11 @@
 }
 
 static void
-generic_error_ignore_cb(PurpleAccount *account)
-{
-	purple_account_clear_current_error(account);
-}
-
-static void
 generic_error_destroy_cb(GtkObject *dialog,
                          PurpleAccount *account)
 {
 	g_hash_table_remove(gtkblist->connection_errors, account);
+	purple_account_clear_current_error(account);
 }
 
 #define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl"
@@ -4508,7 +4519,7 @@
 	gboolean enabled =
 		purple_account_get_enabled(account, purple_core_get_ui());
 	char *primary;
-	
+
 	if (enabled)
 		primary = g_strdup_printf(_("%s disconnected"), username);
 	else
@@ -4520,7 +4531,6 @@
 		(enabled ? PURPLE_CALLBACK(purple_account_connect)
 		         : PURPLE_CALLBACK(generic_error_enable_cb)),
 		_("Modify Account"), PURPLE_CALLBACK(generic_error_modify_cb),
-		_("Ignore"), PURPLE_CALLBACK(generic_error_ignore_cb),
 		NULL);
 
 	g_free(primary);
@@ -4605,14 +4615,6 @@
 }
 
 static void
-ignore_elsewhere_accounts(PidginMiniDialog *mini_dialog,
-                          GtkButton *button,
-                          gpointer unused)
-{
-	elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error);
-}
-
-static void
 ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
 {
 	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
@@ -4620,16 +4622,13 @@
 
 	if(priv->signed_on_elsewhere)
 		return;
-	
+
 	mini_dialog = priv->signed_on_elsewhere =
-		pidgin_mini_dialog_new(NULL, NULL, PIDGIN_STOCK_DISCONNECT);
+		pidgin_mini_dialog_new(_("Welcome back!"), NULL, PIDGIN_STOCK_DISCONNECT);
 
 	pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"),
 		reconnect_elsewhere_accounts, NULL);
 
-	pidgin_mini_dialog_add_button(mini_dialog, _("Ignore"),
-		ignore_elsewhere_accounts, NULL);
-
 	add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog));
 
 	/* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */
@@ -4649,13 +4648,17 @@
 		return;
 
 	accounts = pidgin_mini_dialog_get_num_children(mini_dialog);
+	if (accounts == 0) {
+		gtk_widget_destroy(GTK_WIDGET(mini_dialog));
+		return;
+	}
 
 	title = g_strdup_printf(
-		ngettext("%d account was disabled because you signed on from another location.",
-			 "%d accounts were disabled because you signed on from another location.",
+		ngettext("%d account was disabled because you signed on from another location:",
+			 "%d accounts were disabled because you signed on from another location:",
 			 accounts),
 		accounts);
-	pidgin_mini_dialog_set_title(mini_dialog, title);
+	pidgin_mini_dialog_set_description(mini_dialog, title);
 	g_free(title);
 }
 
@@ -5737,7 +5740,7 @@
 		return;
 
 	status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy,
-						PIDGIN_STATUS_ICON_SMALL);
+						biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
 
 	/* Speed it up if we don't want buddy icons. */
 	if(biglist)
@@ -5821,6 +5824,7 @@
 	PurpleBlistNode *cnode;
 	PurpleContact *contact;
 	PurpleBuddy *buddy;
+	gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 	struct _pidgin_blist_node *gtknode;
 
 	if (editing_blist)
@@ -5856,7 +5860,7 @@
 			char *mark;
 
 			status = pidgin_blist_get_status_icon(cnode,
-					 PIDGIN_STATUS_ICON_SMALL);
+					 biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
 
 			mark = g_markup_escape_text(purple_contact_get_alias(contact), -1);
 			gtk_tree_store_set(gtkblist->treemodel, &iter,
@@ -5935,6 +5939,7 @@
 		GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
+		gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		PidginBlistNode *ui;
 		PurpleConversation *conv;
 		gboolean hidden;
@@ -5947,7 +5952,7 @@
 		hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE));
 
 		status = pidgin_blist_get_status_icon(node,
-				 PIDGIN_STATUS_ICON_SMALL);
+				 biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
 		emblem = pidgin_blist_get_emblem(node);
 
 		/* Speed it up if we don't want buddy icons. */
@@ -6401,6 +6406,10 @@
 	{
 		add_chat_cb(NULL, data);
 	}
+	else if (resp == 1)
+	{
+		pidgin_roomlist_dialog_show_with_account(data->account);
+	}
 	else
 	{
 		gtk_widget_destroy(data->window);
@@ -6417,6 +6426,8 @@
 static void
 addchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
 {
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleConnection *gc;
 	PidginAddChatData *data;
 	GList *tmp;
 	const char *text;
@@ -6437,6 +6448,12 @@
 	}
 
 	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive);
+
+	gc = purple_account_get_connection(data->account);
+	prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
+	sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL);
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive);
 }
 
 static void
@@ -6602,6 +6619,7 @@
 
 	data->window = gtk_dialog_new_with_buttons(_("Add Chat"),
 		gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR,
+		_("Room _List"), 1,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		GTK_STOCK_ADD, GTK_RESPONSE_OK,
 		NULL);
@@ -6685,8 +6703,8 @@
 	pidgin_set_accessible_label (data->group_combo, label);
 	gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0);
 	
-	data->autojoin = gtk_check_button_new_with_mnemonic(_("Autojoin when account becomes online."));
-	data->persistent = gtk_check_button_new_with_mnemonic(_("Hide chat when the window is closed."));
+	data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online."));
+	data->persistent = gtk_check_button_new_with_mnemonic(_("_Hide chat when the window is closed."));
 	gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0);
 
--- a/pidgin/gtkconv.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkconv.c	Sat Dec 01 03:41:37 2007 +0000
@@ -125,6 +125,7 @@
 static GtkWidget *invite_dialog = NULL;
 static GtkWidget *warn_close_dialog = NULL;
 
+static PidginWindow *hidden_convwin = NULL;
 static GList *window_list = NULL;
 
 /* Lists of status icons at all available sizes for use as window icons */
@@ -235,10 +236,7 @@
 	switch (purple_conversation_get_type(conv)) {
 		case PURPLE_CONV_TYPE_IM:
 		{
-			if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
-				close_this_sucker(gtkconv);
-			else
-				hide_conv(gtkconv, TRUE);
+			hide_conv(gtkconv, TRUE);
 			break;
 		}
 		case PURPLE_CONV_TYPE_CHAT:
@@ -266,43 +264,6 @@
 	return FALSE;
 }
 
-static gboolean
-size_allocate_cb(GtkWidget *w, GtkAllocation *allocation, PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-
-	if (!GTK_WIDGET_VISIBLE(w))
-		return FALSE;
-
-	if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
-		return FALSE;
-	if (gtkconv->auto_resize) {
-		return FALSE;
-	}
-
-	if (gdk_window_get_state(gtkconv->win->window->window) & GDK_WINDOW_STATE_MAXIMIZED) {
-		return FALSE;
-	}
-
-	/* I find that I resize the window when it has a bunch of conversations in it, mostly so that the
-	 * tab bar will fit, but then I don't want new windows taking up the entire screen.  I check to see
-	 * if there is only one conversation in the window.  This way we'll be setting new windows to the
-	 * size of the last resized new window. */
-	/* I think that the above justification is not the majority, and that the new tab resizing should
-	 * negate it anyway.  --luke */
-	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
-	{
-		if (w == gtkconv->lower_hbox)
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height);
-	}
-	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
-	{
-		if (w == gtkconv->lower_hbox)
-			purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height);
-	}
-	return FALSE;
-}
-
 static void
 default_formatize(PidginConversation *c)
 {
@@ -452,18 +413,6 @@
 	gtkconv->send_history = g_list_prepend(first, NULL);
 }
 
-static void
-reset_default_size(PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
-		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-					    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height"));
-	else
-		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-					    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
-}
-
 static gboolean
 check_for_and_do_command(PurpleConversation *conv)
 {
@@ -573,7 +522,6 @@
 
 	if (check_for_and_do_command(conv)) {
 		if (gtkconv->entry_growing) {
-			reset_default_size(gtkconv);
 			gtkconv->entry_growing = FALSE;
 		}
 		gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
@@ -633,7 +581,6 @@
 
 	gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
 	if (gtkconv->entry_growing) {
-		reset_default_size(gtkconv);
 		gtkconv->entry_growing = FALSE;
 	}
 	gtkconv_set_unseen(gtkconv, PIDGIN_UNSEEN_NONE);
@@ -1378,7 +1325,13 @@
 			timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv);
 			purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer));
 		}
+#if 0
+		/* I will miss you */
 		purple_conversation_set_ui_ops(conv, NULL);
+#else
+		pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
+		pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
+#endif
 	}
 }
 
@@ -2819,6 +2772,9 @@
 	if (gtkconv == NULL) {
 		pidgin_conv_attach_to_conversation(conv);
 		gtkconv = PIDGIN_CONVERSATION(conv);
+	} else if (gtkconv->win == hidden_convwin) {
+		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
+		pidgin_conv_placement_place(gtkconv);
 	}
 
 	pidgin_conv_switch_active_conversation(conv);
@@ -2851,20 +2807,15 @@
 		PurpleConversation *conv = (PurpleConversation*)l->data;
 		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-		if (gtkconv != NULL && gtkconv->active_conv != conv)
+		if(gtkconv == NULL || gtkconv->active_conv != conv)
 			continue;
-		if (gtkconv == NULL) {
-			if (!purple_conversation_get_data(conv, "unseen-count") ||
-				!purple_conversation_get_data(conv, "unseen-state") ||
-				GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"))<min_state)
-				continue;
+
+		if (gtkconv->unseen_state >= min_state
+			&& (!hidden_only ||
+				(hidden_only && gtkconv->win == hidden_convwin))) {
+
 			r = g_list_prepend(r, conv);
 			c++;
-		} else {
-			if (gtkconv->unseen_state >= min_state && !hidden_only) {
-				r = g_list_prepend(r, conv);
-				c++;
-			}
 		}
 	}
 
@@ -2907,8 +2858,8 @@
 		GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC);
 		GtkWidget *item;
 		gchar *text = g_strdup_printf("%s (%d)",
-				gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv),
-				gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")));
+				gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)),
+				gtkconv->unseen_count);
 
 		gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf);
 		g_object_unref(pbuf);
@@ -3169,7 +3120,7 @@
 	PurpleConversation *conv;
 	GtkWidget *item;
 
-	if (win->window == NULL)
+	if (win->window == NULL || win == hidden_convwin)
 		return;
 
 	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
@@ -4369,7 +4320,7 @@
 	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1);
 }
 
-static void resize_imhtml_cb(PidginConversation *gtkconv)
+static gboolean resize_imhtml_cb(PidginConversation *gtkconv)
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
@@ -4406,18 +4357,12 @@
 
 	diff = height - gtkconv->entry->allocation.height;
 
-	if (diff > 0) {
-		gtk_widget_size_request(gtkconv->lower_hbox, &sr);
-		gtkconv->entry_growing = TRUE;
-
-		/* uncomment this to auto resize even after the user manually
-		   resizes
-		gtk_paned_set_position(GTK_PANED(gtkconv->lower_hbox->parent->parent),
-			-1);
-		*/
-		gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-			diff + gtkconv->lower_hbox->allocation.height);
-	}
+	gtk_widget_size_request(gtkconv->lower_hbox, &sr);
+	gtkconv->entry_growing = TRUE;
+
+	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
+		diff + gtkconv->lower_hbox->allocation.height);
+	return FALSE;
 }
 
 static void
@@ -4687,19 +4632,15 @@
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
-	GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box;
+	GtkWidget *vbox, *frame, *imhtml_sw, *event_box;
 	GtkCellRenderer *rend;
 	GtkTreePath *path;
 	PurpleConversation *conv = gtkconv->active_conv;
 	gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
 	GtkPolicyType imhtml_sw_hscroll;
 
-	paned = gtk_vpaned_new();
-	gtk_widget_show(paned);
-
 	/* Setup the top part of the pane */
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
 	gtk_widget_show(vbox);
 
 	/* Setup the info pane */
@@ -4794,30 +4735,18 @@
 	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
 	                 G_CALLBACK(refocus_entry_cb), gtkconv);
 
-	/* Setup the bottom half of the conversation window */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE);
-	gtk_widget_show(vbox);
-
 	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0);
 	gtk_widget_show(gtkconv->lower_hbox);
 
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox, TRUE, TRUE, 0);
-	gtk_widget_show(vbox);
-
 	/* Setup the toolbar, entry widget and all signals */
 	frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
-	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
 	gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
 			purple_account_get_protocol_name(conv->account));
-	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height") :
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
 
 	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
 	                 G_CALLBACK(entry_popup_menu_cb), gtkconv);
@@ -4827,8 +4756,6 @@
 	                       G_CALLBACK(send_cb), gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
-	                 G_CALLBACK(size_allocate_cb), gtkconv);
 
 	gtkconv->entry_buffer =
 		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4851,7 +4778,7 @@
 	default_formatize(gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
 	                       G_CALLBACK(clear_formatting_cb), gtkconv);
-	return paned;
+	return vbox;
 }
 
 static void
@@ -5025,9 +4952,6 @@
 	GtkWidget *tab_cont;
 	PurpleBlistNode *convnode;
 
-	if (hidden)
-		return;
-
 	if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
 		conv->ui_data = gtkconv;
 		if (!g_list_find(gtkconv->convs, conv))
@@ -5111,6 +5035,7 @@
 		gtk_widget_show(gtkconv->toolbar);
 	else
 		gtk_widget_hide(gtkconv->toolbar);
+	g_idle_add((GSourceFunc)resize_imhtml_cb, gtkconv);
 
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
 		gtk_widget_show(gtkconv->infopane_hbox);
@@ -5126,7 +5051,10 @@
 	                         G_CALLBACK(gtk_widget_grab_focus),
 	                         gtkconv->entry);
 
-	pidgin_conv_placement_place(gtkconv);
+	if (hidden)
+		pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
+	else
+		pidgin_conv_placement_place(gtkconv);
 
 	if (nick_colors == NULL) {
 		nbr_nick_colors = NUM_NICK_COLORS;
@@ -5134,13 +5062,11 @@
 	}
 }
 
-#if 0
 static void
 pidgin_conv_new_hidden(PurpleConversation *conv)
 {
 	private_gtkconv_new(conv, TRUE);
 }
-#endif
 
 void
 pidgin_conv_new(PurpleConversation *conv)
@@ -5156,24 +5082,30 @@
 				   PurpleConversation *conv, PurpleMessageFlags flags)
 {
 	PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops();
+	gboolean hide = FALSE;
 
 	/* create hidden conv if hide_new pref is always */
-	/* or if hide_new pref is away and account is away */
-	if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) ||
-		(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
-		 !purple_status_is_available(purple_account_get_active_status(account)))) {
-		if (!conv) {
-			ui_ops->create_conversation = NULL;
-			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
-			purple_conversation_set_ui_ops(conv, NULL);
-			ui_ops->create_conversation = pidgin_conv_new;
-		} else {
-			/* TODO: update the unseen_state data on the conv here */
-		}
-	} else {
-		/* new message for an IM */
-		if (conv && conv->type == PURPLE_CONV_TYPE_IM)
-			pidgin_conv_attach_to_conversation(conv);
+	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0)
+		hide = TRUE;
+
+	/* create hidden conv if hide_new pref is away and account is away */
+	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
+	    !purple_status_is_available(purple_account_get_active_status(account)))
+		hide = TRUE;
+
+	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv) && !hide) {
+		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+		if (gtkconv->win == hidden_convwin) {
+			pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
+			pidgin_conv_placement_place(gtkconv);
+		}
+		return;
+	}
+
+	if (hide) {
+		ui_ops->create_conversation = pidgin_conv_new_hidden;
+		purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
+		ui_ops->create_conversation = pidgin_conv_new;
 	}
 }
 
@@ -5182,9 +5114,6 @@
 {
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-	if (!gtkconv)
-		return;
-
 	gtkconv->convs = g_list_remove(gtkconv->convs, conv);
 	/* Don't destroy ourselves until all our convos are gone */
 	if (gtkconv->convs) {
@@ -6526,7 +6455,6 @@
 		AtkObject *accessibility_obj;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
 		char *style;
-		gboolean bold = FALSE;
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 			im = PURPLE_CONV_IM(conv);
@@ -6561,7 +6489,7 @@
 				CONV_TEXT_COLUMN, markup, -1);
 	        /* XXX seanegan Why do I have to do this? */
 		gtk_widget_queue_draw(gtkconv->infopane);
-	
+
 		if (title != markup)
 			g_free(markup);
 
@@ -6572,47 +6500,43 @@
 		if (im != NULL &&
 		    purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
 			atk_object_set_description(accessibility_obj, _("Typing"));
-			style = "color=\"#4e9a06\"";
+			style = "tab-label-typing";
 		} else if (im != NULL &&
 		         purple_conv_im_get_typing_state(im) == PURPLE_TYPED) {
 			atk_object_set_description(accessibility_obj, _("Stopped Typing"));
-			style = "color=\"#c4a000\"";
+			style = "tab-label-typed";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK)	{
 			atk_object_set_description(accessibility_obj, _("Nick Said"));
-			style = "color=\"#204a87\"";
+			style = "tab-label-attention";
 		} 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\"";
 			else
-				style = "color=\"#204a87\"";
+				style = "tab-label-attention";
 		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
 			atk_object_set_description(accessibility_obj, _("New Event"));
-			style = "color=\"#888a85\"";
+			style = "tab-label-event";
 		} else {
 			style = NULL;
 		}
 
+		gtk_widget_set_name(gtkconv->tab_label, style);
+		gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
+		gtk_widget_set_state(gtkconv->tab_label, GTK_STATE_ACTIVE);
+
 		if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT ||
 				gtkconv->unseen_state == PIDGIN_UNSEEN_NICK ||
-				gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT)
-			bold = TRUE;
-
-		if (style || bold)
-		{
-			char *html_title,*label;
-
-			html_title = g_markup_escape_text(title, -1);
-			label = g_strdup_printf("<span %s %s>%s</span>",
-			                        style ? style : "",
-			                        bold ? "weight=\"bold\"" : "",
-			                        html_title);
-			g_free(html_title);
-			gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label);
-			g_free(label);
-		}
-		else
-			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
+				gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
+			PangoAttrList *list = pango_attr_list_new();
+			PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+			attr->start_index = 0;
+			attr->end_index = -1;
+			pango_attr_list_insert(list, attr);
+			gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), list);
+			pango_attr_list_unref(list);
+		} else
+			gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), NULL);
 
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
@@ -7121,6 +7045,8 @@
 			gtk_widget_show(gtkconv->toolbar);
 		else
 			gtk_widget_hide(gtkconv->toolbar);
+
+		g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv);
 	}
 }
 
@@ -7196,7 +7122,6 @@
 account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus,
                           PurpleStatus *newstatus)
 {
-#if 0
 	GList *l;
 	PurpleConversation *conv = NULL;
 	PidginConversation *gtkconv;
@@ -7206,7 +7131,27 @@
 
 	if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus))
 		return;
-#endif
+
+	while ((l = hidden_convwin->gtkconvs) != NULL)
+	{
+		gtkconv = l->data;
+
+		conv = gtkconv->active_conv;
+
+		while(l && !purple_status_is_available(
+					purple_account_get_active_status(
+					purple_conversation_get_account(conv))))
+			l = l->next;
+		if (!l)
+			break;
+
+		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
+		pidgin_conv_placement_place(gtkconv);
+
+		/* TODO: do we need to do anything for any other conversations that are in the same gtkconv here?
+		 * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/
+		purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+	}
 }
 
 static void
@@ -7214,25 +7159,32 @@
 				 gconstpointer value, gpointer data)
 {
 	GList *l;
+	PurpleConversation *conv = NULL;
+	PidginConversation *gtkconv;
 	gboolean when_away = FALSE;
 
+	if(!hidden_convwin)
+		return;
+
 	if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0)
 		return;
 
 	if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0)
 		when_away = TRUE;
 
-	for (l = purple_get_conversations(); l; l = l->next)
+	while ((l = hidden_convwin->gtkconvs) != NULL)
 	{
-		PurpleConversation *conv = l->data;
-		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-		if (gtkconv)
-			continue;
+		gtkconv = l->data;
+
+		conv = gtkconv->active_conv;
+
 		if(when_away && !purple_status_is_available(
 							purple_account_get_active_status(
 							purple_conversation_get_account(conv))))
 			continue;
-		pidgin_conv_attach_to_conversation(conv);
+
+		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
+		pidgin_conv_placement_place(gtkconv);
 	}
 }
 
@@ -7513,8 +7465,14 @@
 	GList *list;
 	PidginConversation *gtkconv;
 
-	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
-		return FALSE;
+	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
+		gtkconv = PIDGIN_CONVERSATION(conv);
+		if (gtkconv->win != hidden_convwin)
+			return FALSE;
+		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
+		pidgin_conv_placement_place(gtkconv);
+		return TRUE;
+	}
 
 	pidgin_conv_attach(conv);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -7621,7 +7579,6 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE);
 
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
-	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", TRUE);
 
 #ifdef _WIN32
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE);
@@ -7785,6 +7742,9 @@
 
 	purple_conversations_set_ui_ops(&conversation_ui_ops);
 
+	hidden_convwin = pidgin_conv_window_new();
+	window_list = g_list_remove(window_list, hidden_convwin);
+
 	purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
                         handle, PURPLE_CALLBACK(account_status_changed_cb), NULL);
 
@@ -7824,6 +7784,40 @@
 			PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
 	purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle,
 			PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
+
+	{
+		/* Set default tab colors */
+		GString *str = g_string_new(NULL);
+		GtkSettings *settings = gtk_settings_get_default();
+		struct {
+			const char *stylename;
+			const char *labelname;
+			const char *color;
+		} 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_event_default", "tab-label-event", "#888a85"},
+			{NULL, NULL, NULL}
+		};
+		int iter;
+		for (iter = 0; styles[iter].stylename; iter++) {
+			if (!gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE))
+				/* Apparently both ACTIVE and NORMAL are required */
+				g_string_append_printf(str, "style \"%s\" {\n"
+						"fg[ACTIVE] = \"%s\"\n"
+						"}\n"
+						"widget \"*%s\" style \"%s\"\n",
+						styles[iter].stylename,
+						styles[iter].color,
+						styles[iter].labelname, styles[iter].stylename);
+		}
+		gtk_rc_parse_string(str->str);
+		g_string_free(str, TRUE);
+#if GTK_CHECK_VERSION(2,4,0)
+		gtk_rc_reset_styles(settings);
+#endif
+	}
 }
 
 void
@@ -9126,6 +9120,7 @@
 
 	/* Tab label. */
 	gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv));
+	gtk_widget_set_name(gtkconv->tab_label, "tab-label");
 
 	gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	gtkconv->menu_label = gtk_label_new(tmp_lab);
@@ -9285,7 +9280,7 @@
 	if (win->gtkconvs && win->gtkconvs->next == NULL)
 		pidgin_conv_tab_pack(win, win->gtkconvs->data);
 
-	if (!win->gtkconvs)
+	if (!win->gtkconvs && win != hidden_convwin)
 		pidgin_conv_window_destroy(win);
 }
 
@@ -9824,7 +9819,9 @@
 gboolean
 pidgin_conv_is_hidden(PidginConversation *gtkconv)
 {
-	return (gtkconv == NULL);
+	g_return_val_if_fail(gtkconv != NULL, FALSE);
+
+	return (gtkconv->win == hidden_convwin);
 }
 
 
@@ -9925,5 +9922,3 @@
 
 	return colors;
 }
-
-
--- a/pidgin/gtkdialogs.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkdialogs.c	Sat Dec 01 03:41:37 2007 +0000
@@ -349,6 +349,7 @@
 	AtkObject *obj;
 	char* filename, *tmp;
 	GdkPixbuf *pixbuf;
+	PidginBuddyList *buddylist;
 
 	if (about != NULL) {
 		gtk_window_present(GTK_WINDOW(about));
@@ -724,6 +725,11 @@
 	gtk_widget_grab_default(button);
 
 	/* Let's give'em something to talk about -- woah woah woah */
+	buddylist = pidgin_blist_get_default_gtk_blist();
+	if (buddylist)
+		gtk_window_set_transient_for(GTK_WINDOW(about),
+				GTK_WINDOW(buddylist->window));
+
 	gtk_widget_show_all(about);
 	gtk_window_present(GTK_WINDOW(about));
 }
--- a/pidgin/gtkimhtml.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkimhtml.c	Sat Dec 01 03:41:37 2007 +0000
@@ -31,6 +31,7 @@
 
 #include "internal.h"
 #include "pidgin.h"
+#include "pidginstock.h"
 
 #include "debug.h"
 #include "util.h"
@@ -811,6 +812,7 @@
 static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
 {
 	GtkWidget *menuitem;
+	GtkWidget *mi, *img;
 
 	menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text"));
 	gtk_widget_show(menuitem);
@@ -836,6 +838,28 @@
 	gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 5);
 
 	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(clear_formatting_cb), imhtml);
+	
+	mi = gtk_menu_item_new();
+	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!"));
+	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"));
+	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"));
+	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);
 }
 
 static char *
@@ -1097,6 +1121,38 @@
 	g_free(text);
 }
 
+
+static void smart_backspace_cb(GtkIMHtml *imhtml, gpointer blah)
+{
+	GtkTextIter iter;
+	GtkTextChildAnchor* anchor;
+	char * text;
+	gint offset;
+
+	if (!imhtml->editable)
+		return;
+
+	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
+
+	/* Get the character before the insertion point */
+	offset = gtk_text_iter_get_offset(&iter);
+	if (offset <= 0)
+		return;
+
+	gtk_text_iter_backward_char(&iter);
+	anchor = gtk_text_iter_get_child_anchor(&iter);
+
+	if (!anchor)
+		return; /* No smiley here */
+
+	text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
+	if (!text)
+		return;
+
+	/* ok, then we need to insert the image buffer text before the anchor */
+	gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1);
+}
+
 static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah)
 {
 #ifdef _WIN32
@@ -4052,8 +4108,11 @@
 	imhtml->format_functions = GTK_IMHTML_ALL;
 
 	if (editable)
+	{
 		g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
-		                       G_CALLBACK(mark_set_cb), imhtml);
+				G_CALLBACK(mark_set_cb), imhtml);
+		g_signal_connect(G_OBJECT(imhtml), "backspace", G_CALLBACK(smart_backspace_cb), NULL);
+	}
 }
 
 void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo)
@@ -4222,6 +4281,33 @@
 	g_object_unref(object);
 }
 
+static void populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer nul)
+{
+	GtkWidget *mi, *img;
+	
+	mi = gtk_menu_item_new();
+	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"));
+	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"));
+	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!"));
+	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);
+}
+
 static void imhtml_toggle_bold(GtkIMHtml *imhtml)
 {
 	GtkTextIter start, end;
--- a/pidgin/gtkimhtmltoolbar.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sat Dec 01 03:41:37 2007 +0000
@@ -777,6 +777,8 @@
 	/* show everything */
 	gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!"));
 	gtk_widget_show_all(dialog);
+	gtk_window_set_transient_for(GTK_WINDOW(dialog),
+			GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
 #ifdef _WIN32
 	winpidgin_ensure_onscreen(dialog);
 #endif
--- a/pidgin/gtkmain.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkmain.c	Sat Dec 01 03:41:37 2007 +0000
@@ -776,6 +776,7 @@
 	}
 
 	if (opt_si && !purple_core_ensure_single_instance()) {
+		purple_debug_info("main", "exiting because another libpurple client is already running\n");
 		purple_core_quit();
 #ifdef HAVE_SIGNAL_H
 		g_free(segfault_message);
--- a/pidgin/gtkprefs.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkprefs.c	Sat Dec 01 03:41:37 2007 +0000
@@ -995,8 +995,6 @@
 
 	pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
 				PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
-	pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
-				PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox);
 
 	iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"),
 			PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
@@ -1213,6 +1211,9 @@
 	vbox = pidgin_make_frame (ret, _("Ports"));
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
+	pidgin_prefs_checkbox(_("_Enable automatic router port forwarding"),
+			"/purple/network/map_ports", vbox);
+
 	ports_checkbox = pidgin_prefs_checkbox(_("_Manually specify range of ports to listen on"),
 			"/purple/network/ports_range_use", vbox);
 
@@ -2306,6 +2307,7 @@
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type");
+	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/close_immediately");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes");
--- a/pidgin/gtkscrollbook.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/gtkscrollbook.c	Sat Dec 01 03:41:37 2007 +0000
@@ -1,5 +1,5 @@
 /*
- * @file gtkscrollbook.c GTK+ Scrolling notebook widget 
+ * @file gtkscrollbook.c GTK+ Scrolling notebook widget
  * @ingroup pidgin
  */
 
@@ -29,9 +29,9 @@
 
 static void pidgin_scroll_book_init (PidginScrollBook *scroll_book);
 static void pidgin_scroll_book_class_init (PidginScrollBookClass *klass);
-static void pidgin_scroll_book_forall (GtkContainer *c, 
+static void pidgin_scroll_book_forall (GtkContainer *c,
 					 gboolean include_internals,
-			 		 GtkCallback callback,
+					 GtkCallback callback,
 					 gpointer user_data);
 
 GType
@@ -83,8 +83,8 @@
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
 #else
 	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
-#endif	
-	
+#endif
+
 	if (index + 1 < count)
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1);
 }
@@ -93,24 +93,30 @@
 refresh_scroll_box(PidginScrollBook *scroll_book, int index, int count)
 {
 	char *label;
+
 	gtk_widget_show_all(GTK_WIDGET(scroll_book));
-	if (count <= 1)
-		gtk_widget_hide(GTK_WIDGET(scroll_book->hbox));
-	else
-		gtk_widget_show_all(GTK_WIDGET(scroll_book->hbox));
-		
-	
+	if (count < 1)
+		gtk_widget_hide_all(scroll_book->hbox);
+	else {
+		gtk_widget_show_all(scroll_book->hbox);
+		if (count == 1) {
+			gtk_widget_hide(scroll_book->label);
+			gtk_widget_hide(scroll_book->left_arrow);
+			gtk_widget_hide(scroll_book->right_arrow);
+		}
+	}
+
 	label = g_strdup_printf("<span size='smaller' weight='bold'>(%d/%d)</span>", index+1, count);
 	gtk_label_set_markup(GTK_LABEL(scroll_book->label), label);
-	g_free(label);				      
+	g_free(label);
 
 	if (index == 0)
 		gtk_widget_set_sensitive(scroll_book->left_arrow, FALSE);
 	else
 		gtk_widget_set_sensitive(scroll_book->left_arrow, TRUE);
 
-	
-	if (index +1== count)
+
+	if (index + 1 == count)
 		gtk_widget_set_sensitive(scroll_book->right_arrow, FALSE);
 	else
 		gtk_widget_set_sensitive(scroll_book->right_arrow, TRUE);
@@ -128,7 +134,12 @@
 	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
 #endif
 	refresh_scroll_box(scroll_book, index, count);
-	
+}
+
+static void
+scroll_close_cb(PidginScrollBook *scroll_book)
+{
+	gtk_widget_destroy(gtk_notebook_get_nth_page(GTK_NOTEBOOK(scroll_book->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook))));
 }
 
 static void
@@ -146,17 +157,15 @@
 static void
 pidgin_scroll_book_add(GtkContainer *container, GtkWidget *widget)
 {
-	GList *children;
 	PidginScrollBook *scroll_book;
 
 	g_return_if_fail(GTK_IS_WIDGET (widget));
 	g_return_if_fail (widget->parent == NULL);
 
 	scroll_book = PIDGIN_SCROLL_BOOK(container);
-	children = scroll_book->children;
-	children = g_list_append(children, widget);
+	scroll_book->children = g_list_append(scroll_book->children, widget);
 	gtk_widget_show(widget);
-	gtk_notebook_append_page(GTK_NOTEBOOK(PIDGIN_SCROLL_BOOK(container)->notebook), widget, NULL);
+	gtk_notebook_append_page(GTK_NOTEBOOK(scroll_book->notebook), widget, NULL);
 	page_count_change_cb(PIDGIN_SCROLL_BOOK(container));
 }
 
@@ -164,23 +173,12 @@
 pidgin_scroll_book_remove(GtkContainer *container, GtkWidget *widget)
 {
 	int page;
-	GList *children;
-	GtkWidget *child;
 	PidginScrollBook *scroll_book;
 	g_return_if_fail(GTK_IS_WIDGET(widget));
 
 	scroll_book = PIDGIN_SCROLL_BOOK(container);
-	children = scroll_book->children;
-
-	while (children) {
-		child = children->data;
-		if (child == widget) {
-			gtk_widget_unparent (widget);
-			children = g_list_remove_link (scroll_book->children, children);
-			g_list_free(children);
-			break;
-		}
-	}
+	scroll_book->children = g_list_remove(scroll_book->children, widget);
+	/* gtk_widget_unparent(widget); */
 
 	page = gtk_notebook_page_num(GTK_NOTEBOOK(PIDGIN_SCROLL_BOOK(container)->notebook), widget);
 	if (page >= 0) {
@@ -194,7 +192,9 @@
 			   GtkCallback callback,
 			   gpointer callback_data)
 {
+#if 0
 	GList *children;
+#endif
 	PidginScrollBook *scroll_book;
 
 	g_return_if_fail(GTK_IS_CONTAINER(container));
@@ -206,6 +206,7 @@
 		(*callback)(scroll_book->notebook, callback_data);
 	}
 
+#if 0
 	children = scroll_book->children;
 
 	while (children) {
@@ -214,6 +215,7 @@
 		children = children->next;
 		(*callback)(child, callback_data);
 	}
+#endif
 }
 
 static void
@@ -223,26 +225,66 @@
 
 	container_class->add = pidgin_scroll_book_add;
 	container_class->remove = pidgin_scroll_book_remove;
-	container_class->forall = pidgin_scroll_book_forall;	
-	
+	container_class->forall = pidgin_scroll_book_forall;
+}
+
+static gboolean
+close_button_left_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
+{
+	static GdkCursor *ptr = NULL;
+	if (ptr == NULL) {
+		ptr = gdk_cursor_new(GDK_LEFT_PTR);
+	}
+
+	gtk_label_set_markup(label, "×");
+	gdk_window_set_cursor(event->window, ptr);
+	return FALSE;
+}
+
+static gboolean
+close_button_entered_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
+{
+	static GdkCursor *hand = NULL;
+	if (hand == NULL) {
+		hand = gdk_cursor_new(GDK_HAND2);
+	}
+
+	gtk_label_set_markup(label, "<u>×</u>");
+	gdk_window_set_cursor(event->window, hand);
+	return FALSE;
 }
 
 static void
 pidgin_scroll_book_init (PidginScrollBook *scroll_book)
 {
 	GtkWidget *eb;
+	GtkWidget *close_button;
 
 	scroll_book->hbox = gtk_hbox_new(FALSE, 0);
 
+	/* Close */
+	eb = gtk_event_box_new();
+	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
+	gtk_event_box_set_visible_window(GTK_EVENT_BOX(eb), FALSE);
+	gtk_widget_set_events(eb, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+	close_button = gtk_label_new("×");
+	g_signal_connect(G_OBJECT(eb), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_button);
+	g_signal_connect(G_OBJECT(eb), "leave-notify-event", G_CALLBACK(close_button_left_cb), close_button);
+	gtk_container_add(GTK_CONTAINER(eb), close_button);
+	g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_close_cb), scroll_book);
+
+	/* Right arrow */
 	eb = gtk_event_box_new();
 	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
 	scroll_book->right_arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
 	gtk_container_add(GTK_CONTAINER(eb), scroll_book->right_arrow);
 	g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_right_cb), scroll_book);
 
+	/* Count */
 	scroll_book->label = gtk_label_new(NULL);
 	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), scroll_book->label, FALSE, FALSE, 0);
 
+	/* Left arrow */
 	eb = gtk_event_box_new();
 	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
 	scroll_book->left_arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
@@ -250,20 +292,18 @@
 	g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_left_cb), scroll_book);
 
 	gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->hbox, FALSE, FALSE, 0);
-	
+
 	scroll_book->notebook = gtk_notebook_new();
 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(scroll_book->notebook), FALSE);
 	gtk_notebook_set_show_border(GTK_NOTEBOOK(scroll_book->notebook), FALSE);
-	
+
 	gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->notebook, TRUE, TRUE, 0);
-	
+
 	g_signal_connect_swapped(G_OBJECT(scroll_book->notebook), "remove", G_CALLBACK(page_count_change_cb), scroll_book);
 	g_signal_connect(G_OBJECT(scroll_book->notebook), "switch-page", G_CALLBACK(switch_page_cb), scroll_book);
 	gtk_widget_show_all(scroll_book->notebook);
 }
 
-
-
 GtkWidget *
 pidgin_scroll_book_new()
 {
--- a/pidgin/pidginstock.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/pidginstock.c	Sat Dec 01 03:41:37 2007 +0000
@@ -347,7 +347,7 @@
 	size_t i;
 	GtkWidget *win;
 	GtkIconSize microscopic, extra_small, small, medium, large, huge;
-	
+
 	if (stock_initted)
 		return;
 
@@ -372,7 +372,7 @@
 		{
 			/* GTK+ Stock icon */
 			iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win),
-												stock_icons[i].filename);
+					stock_icons[i].filename);
 		}
 		else
 		{
@@ -386,11 +386,11 @@
 			gtk_icon_source_set_direction_wildcarded(source, TRUE);
 			gtk_icon_source_set_size_wildcarded(source, TRUE);
 			gtk_icon_source_set_state_wildcarded(source, TRUE);
-			
+
 
 			iconset = gtk_icon_set_new();
 			gtk_icon_set_add_source(iconset, source);
-			
+
 			gtk_icon_source_free(source);
 			g_free(filename);
 		}
@@ -401,7 +401,7 @@
 	}
 
 	/* register custom icon sizes */
-	
+
 	microscopic =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
 	extra_small =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
 	small =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
@@ -414,61 +414,41 @@
 		GtkIconSet *iconset;
 
 		iconset = gtk_icon_set_new();
-		if (sized_stock_icons[i].microscopic)
-			add_sized_icon(iconset, microscopic,
-					sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-					"11", sized_stock_icons[i].filename);
-		if (sized_stock_icons[i].extra_small)
-			add_sized_icon(iconset, extra_small,
-				       sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-				       "16", sized_stock_icons[i].filename);
-               if (sized_stock_icons[i].small)
-                        add_sized_icon(iconset, small,
-				       sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-                                       "22", sized_stock_icons[i].filename);
-               if (sized_stock_icons[i].medium)
-                        add_sized_icon(iconset, medium,
-			               sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-                                       "32", sized_stock_icons[i].filename);
-	       if (sized_stock_icons[i].large)
-		       add_sized_icon(iconset, large,
-                                      sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-                                      "48", sized_stock_icons[i].filename);
-               if (sized_stock_icons[i].huge)
-                        add_sized_icon(iconset, huge,
-	                               sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-                                       "64", sized_stock_icons[i].filename);
+
+#define ADD_SIZED_ICON(name, size) do { \
+		if (sized_stock_icons[i].name)  \
+			add_sized_icon(iconset, name,  \
+					sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
+					size, sized_stock_icons[i].filename); \
+		} while (0)
+		ADD_SIZED_ICON(microscopic, "11");
+		ADD_SIZED_ICON(extra_small, "16");
+		ADD_SIZED_ICON(small, "22");
+		ADD_SIZED_ICON(medium, "32");
+		ADD_SIZED_ICON(large, "48");
+		ADD_SIZED_ICON(huge, "64");
+#undef ADD_SIZED_ICON
 
 		gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset);
 		gtk_icon_set_unref(iconset);
 
 		if (sized_stock_icons[i].translucent_name) {
 			iconset = gtk_icon_set_new();
-			if (sized_stock_icons[i].microscopic)
-				add_translucent_sized_icon(iconset, microscopic,
-						sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-						"11", sized_stock_icons[i].filename);
-			if (sized_stock_icons[i].extra_small)
-				add_translucent_sized_icon(iconset, extra_small,
-					       sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-					       "16", sized_stock_icons[i].filename);
-	               if (sized_stock_icons[i].small)
-        	                add_translucent_sized_icon(iconset, small,
-					       sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-	                                       "22", sized_stock_icons[i].filename);
-	               if (sized_stock_icons[i].medium)
-	                        add_translucent_sized_icon(iconset, medium,
-				               sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-	                                       "32", sized_stock_icons[i].filename);
-		       if (sized_stock_icons[i].large)
-			       add_translucent_sized_icon(iconset, large,
-	                                      sized_stock_icons[i].dir, sized_stock_icons[i].rtl,
-	                                      "48", sized_stock_icons[i].filename);
-	               if (sized_stock_icons[i].huge)
-	                        add_translucent_sized_icon(iconset, huge,
-		                               sized_stock_icons[i].dir,  sized_stock_icons[i].rtl,
-	                                       "64", sized_stock_icons[i].filename);
-	
+
+#define ADD_TRANS_ICON(name, size) do { \
+			if (sized_stock_icons[i].name) \
+				add_translucent_sized_icon(iconset, name, \
+						sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
+						size, sized_stock_icons[i].filename); \
+			} while (0)
+			ADD_TRANS_ICON(microscopic, "11");
+			ADD_TRANS_ICON(extra_small, "16");
+			ADD_TRANS_ICON(small, "22");
+			ADD_TRANS_ICON(medium, "32");
+			ADD_TRANS_ICON(large, "48");
+			ADD_TRANS_ICON(huge, "64");
+#undef ADD_TRANS_ICON
+
 			gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset);
 			gtk_icon_set_unref(iconset);
 		}
--- a/pidgin/plugins/convcolors.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/plugins/convcolors.c	Sat Dec 01 03:41:37 2007 +0000
@@ -48,22 +48,27 @@
 #define	PREF_SEND	PREF_PREFIX "/send"
 #define	PREF_SEND_C	PREF_SEND "/color"
 #define	PREF_SEND_F	PREF_SEND "/format"
+#define	PREF_SEND_E	PREF_SEND "/enabled"
 
 #define PREF_RECV	PREF_PREFIX "/recv"
 #define	PREF_RECV_C	PREF_RECV "/color"
 #define	PREF_RECV_F	PREF_RECV "/format"
+#define	PREF_RECV_E	PREF_RECV "/enabled"
 
 #define PREF_SYSTEM	PREF_PREFIX "/system"
 #define	PREF_SYSTEM_C	PREF_SYSTEM "/color"
 #define	PREF_SYSTEM_F	PREF_SYSTEM "/format"
+#define	PREF_SYSTEM_E	PREF_SYSTEM "/enabled"
 
 #define PREF_ERROR	PREF_PREFIX "/error"
 #define	PREF_ERROR_C	PREF_ERROR "/color"
 #define	PREF_ERROR_F	PREF_ERROR "/format"
+#define	PREF_ERROR_E	PREF_ERROR "/enabled"
 
 #define PREF_NICK	PREF_PREFIX "/nick"
 #define	PREF_NICK_C	PREF_NICK "/color"
 #define	PREF_NICK_F	PREF_NICK "/format"
+#define	PREF_NICK_E	PREF_NICK "/enabled"
 
 enum
 {
@@ -104,7 +109,10 @@
 	if (!formats[i].prefix)
 		return FALSE;
 
-	if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
+	g_snprintf(tmp, sizeof(tmp), "%s/enabled", formats[i].prefix);
+
+	if (!purple_prefs_get_bool(tmp) ||
+			(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM &&
 			!purple_prefs_get_bool(PREF_IMS)) ||
 			(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT &&
 			!purple_prefs_get_bool(PREF_CHATS)))
@@ -223,17 +231,29 @@
 }
 
 static void
+toggle_enabled(GtkWidget *widget, gpointer data)
+{
+	const char *prefix = (char *)data;
+	gboolean e;
+	char tmp[128];
+
+	g_snprintf(tmp, sizeof(tmp), "%s/enabled", prefix);
+	e = purple_prefs_get_bool(tmp);
+	purple_prefs_set_bool(tmp, !e);
+}
+
+static void
 toggle_something(const char *prefix, int format)
 {
 	int f;
 	char tmp[128];
-	
+
 	g_snprintf(tmp, sizeof(tmp), "%s/format", prefix);
 	f = purple_prefs_get_int(tmp);
 	f ^= format;
 	purple_prefs_set_int(tmp, f);
 }
-		
+
 static void
 toggle_bold(GtkWidget *widget, gpointer data)
 {
@@ -252,6 +272,22 @@
 	toggle_something(data, FONT_UNDERLINE);
 }
 
+static void
+enable_toggled(const char *name, PurplePrefType type, gconstpointer val, gpointer data)
+{
+	GtkWidget *widget = (GtkWidget *)data;
+
+	gtk_widget_set_sensitive(widget, GPOINTER_TO_INT(val));
+}
+
+static void
+disconnect_prefs_callbacks(GtkObject *object, gpointer data)
+{
+	PurplePlugin *plugin = (PurplePlugin *)data;
+
+	purple_prefs_disconnect_by_handle(plugin);
+}
+
 static GtkWidget *
 get_config_frame(PurplePlugin *plugin)
 {
@@ -265,9 +301,14 @@
 	for (i = 0; formats[i].prefix; i++)
 	{
 		char tmp[128];
+		char tmp2[128];
 		int f;
+		gboolean e;
 		GtkWidget *vbox, *hbox, *button;
 
+		g_snprintf(tmp2, sizeof(tmp2), "%s/enabled", formats[i].prefix);
+		e = purple_prefs_get_bool(tmp2);
+
 		g_snprintf(tmp, sizeof(tmp), "%s/format", formats[i].prefix);
 		f = purple_prefs_get_int(tmp);
 
@@ -278,11 +319,20 @@
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 
+		button = gtk_check_button_new_with_label(_("Enabled"));
+		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+		if (e)
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+		g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_enabled),
+				formats[i].prefix);
+
 		button = pidgin_pixbuf_button_from_stock(" Color", GTK_STOCK_SELECT_COLOR,
 				PIDGIN_BUTTON_HORIZONTAL);
 		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 		g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_color),
 				formats[i].prefix);
+		gtk_widget_set_sensitive(button, e);
+		purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
 
 		button = gtk_check_button_new_with_label(_("Bold"));
 		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
@@ -290,22 +340,29 @@
 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
 		g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_bold),
 				formats[i].prefix);
-		
+		gtk_widget_set_sensitive(button, e);
+		purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
+
 		button = gtk_check_button_new_with_label(_("Italic"));
 		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 		if (f & FONT_ITALIC)
 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
 		g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_italic),
 				formats[i].prefix);
-		
+		gtk_widget_set_sensitive(button, e);
+		purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
+
 		button = gtk_check_button_new_with_label(_("Underline"));
 		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 		if (f & FONT_UNDERLINE)
 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
 		g_signal_connect(G_OBJECT(button), "clicked",
 				G_CALLBACK(toggle_underline), formats[i].prefix);
+		gtk_widget_set_sensitive(button, e);
+		purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button);
 	}
 
+	g_signal_connect(GTK_OBJECT(ret), "destroy", G_CALLBACK(disconnect_prefs_callbacks), plugin);
 	frame = pidgin_make_frame(ret, _("General"));
 	pidgin_prefs_checkbox(_("Ignore incoming format"), PREF_IGNORE, frame);
 	pidgin_prefs_checkbox(_("Apply in Chats"), PREF_CHATS, frame);
@@ -388,6 +445,12 @@
 	purple_prefs_add_int(PREF_SYSTEM_F, FONT_ITALIC);
 	purple_prefs_add_int(PREF_ERROR_F, FONT_BOLD | FONT_UNDERLINE);
 	purple_prefs_add_int(PREF_NICK_F, FONT_BOLD);
+
+	purple_prefs_add_bool(PREF_SEND_E, TRUE);
+	purple_prefs_add_bool(PREF_RECV_E, TRUE);
+	purple_prefs_add_bool(PREF_SYSTEM_E, TRUE);
+	purple_prefs_add_bool(PREF_ERROR_E, TRUE);
+	purple_prefs_add_bool(PREF_NICK_E, TRUE);
 }
 
 PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
--- a/pidgin/plugins/pidginrc.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/plugins/pidginrc.c	Sat Dec 01 03:41:37 2007 +0000
@@ -494,7 +494,7 @@
 	gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0);
 
 	tmp = g_strdup_printf(_("Write settings to %s%sgtkrc-2.0"),
-	                      homepath, G_DIR_SEPARATOR_S);
+	                      homepath, G_DIR_SEPARATOR_S ".purple" G_DIR_SEPARATOR_S);
 	check = gtk_button_new_with_label(tmp);
 	g_free(tmp);
 	gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0);
--- a/pidgin/plugins/timestamp.c	Mon Nov 19 23:06:16 2007 +0000
+++ b/pidgin/plugins/timestamp.c	Sat Dec 01 03:41:37 2007 +0000
@@ -49,11 +49,17 @@
 	const char *mdate;
 	int y, height;
 	GdkRectangle rect;
-	
+
 	/* display timestamp */
 	mdate = purple_utf8_strftime(then == 0 ? "%H:%M" : "\n%H:%M",
 		localtime(&now));
 	gtk_text_buffer_get_end_iter(buffer, &iter);
+
+	if (gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "TIMESTAMP") == NULL)
+		gtk_text_buffer_create_tag(buffer, "TIMESTAMP",
+			"foreground", "#888888", "justification", GTK_JUSTIFY_CENTER,
+			"weight", PANGO_WEIGHT_BOLD, NULL);
+
 	gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, mdate,
 		strlen(mdate), "TIMESTAMP", NULL);
 
@@ -95,17 +101,9 @@
 static void
 timestamp_new_convo(PurpleConversation *conv)
 {
-	PidginConversation *gtk_conv = PIDGIN_CONVERSATION(conv);
-	GtkTextBuffer *buffer;
-
 	if (!g_list_find(purple_get_conversations(), conv))
 		return;
 
-	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtk_conv->imhtml));
-	gtk_text_buffer_create_tag(buffer, "TIMESTAMP",
-		"foreground", "#888888", "justification", GTK_JUSTIFY_CENTER,
-		"weight", PANGO_WEIGHT_BOLD, NULL);
-
 	purple_conversation_set_data(conv, "timestamp-last", GINT_TO_POINTER(0));
 }
 
--- a/po/ChangeLog	Mon Nov 19 23:06:16 2007 +0000
+++ b/po/ChangeLog	Sat Dec 01 03:41:37 2007 +0000
@@ -1,5 +1,8 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.3.1
+	* German translation updated (Jochen Kemnade, Björn Voigt)
+
 version 2.3.0
 	* Afrikaans translation added (Friedel Wolff)
 	* Belarusian Latin translation updated (Ihar Hrachyshka)
--- a/po/de.po	Mon Nov 19 23:06:16 2007 +0000
+++ b/po/de.po	Sat Dec 01 03:41:37 2007 +0000
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-17 16:16+0100\n"
-"PO-Revision-Date: 2007-11-17 16:14+0100\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
+"POT-Creation-Date: 2007-11-30 10:22+0100\n"
+"PO-Revision-Date: 2007-11-30 10:21+0100\n"
+"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -21,6 +21,8 @@
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: KBabel 1.11.4\n"
 
+#. Translators may want to transliterate the name.
+#. It is not to be translated.
 msgid "Finch"
 msgstr "Finch"
 
@@ -493,6 +495,14 @@
 msgid "Re-enable Account"
 msgstr "Konten reaktivieren"
 
+msgid ""
+"The account has disconnected and you are no longer in this chat. You will be "
+"automatically rejoined in the chat when the account reconnects."
+msgstr ""
+"Das Konto wurde getrennt und Sie sind nicht mehr in diesem Chat. Sie werden "
+"automatisch wieder mit dem Chat verbunden, wenn das Konto wieder verbunden "
+"ist."
+
 msgid "No such command."
 msgstr "Es gibt kein solches Kommando."
 
@@ -2326,6 +2336,12 @@
 msgid "Loads .NET plugins with Mono."
 msgstr "Lädt .NET-Plugins mit Mono."
 
+msgid "Add new line in IMs"
+msgstr "Neue Zeile in IMs einfügen"
+
+msgid "Add new line in Chats"
+msgstr "Neue Zeile in Chats einfügen"
+
 #. *< magic
 #. *< major version
 #. *< minor version
@@ -4301,6 +4317,9 @@
 msgid "Connect server"
 msgstr "Verbindungsserver"
 
+msgid "File transfer proxies"
+msgstr "Proxys für Dateiübertragungen"
+
 #, c-format
 msgid "%s has left the conversation."
 msgstr "%s hat das Gespräch verlassen."
@@ -9782,6 +9801,9 @@
 msgid "_Merge"
 msgstr "_Zusammenführen"
 
+msgid "Room _List"
+msgstr "Ra_umliste"
+
 msgid ""
 "Please enter the appropriate information about the chat you would like to "
 "join.\n"
@@ -10056,19 +10078,19 @@
 msgid "Re-enable"
 msgstr "Reaktivieren"
 
-msgid "Ignore"
-msgstr "Ignorieren"
-
-#, c-format
-msgid "%d account was disabled because you signed on from another location."
+msgid "Welcome back!"
+msgstr "Willkommen zurück!"
+
+#, c-format
+msgid "%d account was disabled because you signed on from another location:"
 msgid_plural ""
-"%d accounts were disabled because you signed on from another location."
+"%d accounts were disabled because you signed on from another location:"
 msgstr[0] ""
 "%d Konto wurde deaktiviert, da Sie sich von einem anderen Ort angemeldet "
-"haben."
+"haben:"
 msgstr[1] ""
 "%d Konten wurden deaktiviert, da Sie sich von einem anderen Ort angemeldet "
-"haben."
+"haben:"
 
 msgid "<b>Username:</b>"
 msgstr "<b>Benutzername:</b>"
@@ -10152,11 +10174,11 @@
 "Bitte geben Sie einen Alias und geeignete Informationen über den Chat ein, "
 "den Sie in Ihre Buddy-Liste aufnehmen wollen.\n"
 
-msgid "Autojoin when account becomes online."
-msgstr "Automatisch beitreten, wenn das Konto online geht."
-
-msgid "Hide chat when the window is closed."
-msgstr "Chat verstecken, wenn das Fenster geschlossen wird."
+msgid "Auto_join when account becomes online."
+msgstr "Automatisch _beitreten, wenn das Konto online geht."
+
+msgid "_Hide chat when the window is closed."
+msgstr "_Chat verstecken, wenn das Fenster geschlossen wird."
 
 msgid "Please enter the name of the group to be added."
 msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll."
@@ -10234,6 +10256,9 @@
 msgid "Un-Ignore"
 msgstr "Nicht Ignorieren"
 
+msgid "Ignore"
+msgstr "Ignorieren"
+
 msgid "Get Away Message"
 msgstr "Neue Abwesenheitsnachricht abholen"
 
@@ -11030,6 +11055,15 @@
 msgid "_Reset formatting"
 msgstr "Formatierung _zurücksetzen"
 
+msgid "_Smile!"
+msgstr "_Lächeln!"
+
+msgid "_Insert"
+msgstr "_Einfügen"
+
+msgid "_Font"
+msgstr "_Schrift"
+
 msgid "Hyperlink color"
 msgstr "Hyperlink-Farbe"
 
@@ -11124,9 +11158,6 @@
 msgid "Insert Link"
 msgstr "Link einfügen"
 
-msgid "_Insert"
-msgstr "_Einfügen"
-
 #, c-format
 msgid "Failed to store image: %s\n"
 msgstr "Speichern des Bildes fehlgeschlagen: %s\n"
@@ -11141,9 +11172,6 @@
 msgid "Smile!"
 msgstr "Lächeln!"
 
-msgid "_Font"
-msgstr "_Schrift"
-
 msgid "Group Items"
 msgstr "Elemente gruppieren"
 
@@ -11228,9 +11256,6 @@
 msgid "_Horizontal rule"
 msgstr "_Horizontale Linie"
 
-msgid "_Smile!"
-msgstr "_Lächeln!"
-
 #, c-format
 msgid ""
 "Are you sure you want to permanently delete the log of the conversation with "
@@ -11610,9 +11635,6 @@
 msgid "Show _formatting on incoming messages"
 msgstr "Zeige _Formatierung bei ankommenden Nachrichten"
 
-msgid "Close IMs immediately when the tab is closed"
-msgstr "Schließe IMs sofort, wenn der Reiter geschlossen wird"
-
 msgid "Show _detailed information"
 msgstr "_Detaillierte Informationen anzeigen"
 
@@ -13116,6 +13138,3 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr ""
 "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients."
-
-msgid "Buddy"
-msgstr "Buddy"