changeset 18043:6b7b13adb9b1

propagate from branch 'im.pidgin.pidgin' (head ac83216c7b78e652b47f1fd0bcb91f1eaf2cdf36) to branch 'im.pidgin.pidgin.2.1.0' (head 30a48520e9bc26b0d3914edad456b063cd6cf9fe)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Mon, 04 Jun 2007 23:48:54 +0000
parents ee9f7ee0be66 (current diff) 38a72d6dce7f (diff)
children 37469ac119b6 58e28ccf08e6
files COPYRIGHT ChangeLog ChangeLog.API libpurple/blist.c libpurple/conversation.c libpurple/conversation.h libpurple/idle.c libpurple/protocols/silc/silc.c libpurple/purple-remote libpurple/util.c pidgin/gtkaccount.c pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkimhtml.h pidgin/gtkpounce.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 56 files changed, 1246 insertions(+), 776 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Jun 03 23:11:57 2007 +0000
+++ b/COPYRIGHT	Mon Jun 04 23:48:54 2007 +0000
@@ -8,6 +8,7 @@
 Dave Ahlswede
 Manuel Amador
 Matt Amato
+Elliott Sales de Andrade
 Geoffrey Antos
 Daniel Atallah
 Paul Aurich
--- a/ChangeLog	Sun Jun 03 23:11:57 2007 +0000
+++ b/ChangeLog	Mon Jun 04 23:48:54 2007 +0000
@@ -19,12 +19,16 @@
 version 2.0.2 (??/??/????):
 	Pidgin:
 	* Added a custom conversation font option to preferences
+	* Fixed smiley ordering in the insert smiley popup to be more intuitive
 
 	libpurple:
 	* Moving an ICQ buddy from one group to another no longer
 	  re-requests authorization from that person (Rene Hausleitner)
 	* Added nullprpl, an example protocol plugin (Ryan Barrett)
 	* Fixed SOCKS5 bug which caused Jabber file receiving to fail
+	* Remove MSN's random "Authorization Failed" dialogs
+	* Fix MSN to correctly detect incorrect passwords and disable the account
+	* Get User Info on MSN is now more reliable & accurate
 
 	Finch:
 	* Auto account reconnecting
--- a/ChangeLog.API	Sun Jun 03 23:11:57 2007 +0000
+++ b/ChangeLog.API	Mon Jun 04 23:48:54 2007 +0000
@@ -18,6 +18,23 @@
 	* purple_timeout_add_seconds()
 	    Callers should prefer this to purple_timeout_add() for timers
 	    longer than 1 second away.  Be aware of the rounding, though.
+	* purple_conversation_get_extended_menu
+	* purple_conversation_do_command
+	* pidgin_retrieve_user_info, shows immediate feedback when getting
+	  information about a user.
+	* gtk_imhtml_setup_entry
+	* purple_xfer_get_remote_user
+	* purple_blist_node_get_type
+
+	Changed:
+	* pidgin_separator returns the separator added to the menu.
+	* pidgin_append_menu_action returns the menuitem added to the menu.
+
+	Signals - Added: (See the Doxygen docs for details on all signals.)
+	* "conversation-extended-menu"
+
+	Finch - Added:
+	* finch_retrieve_user_info
 
 version 2.0.0 (5/3/2007):
 	Please note all functions, defines, and data structures have been
--- a/doc/conversation-signals.dox	Sun Jun 03 23:11:57 2007 +0000
+++ b/doc/conversation-signals.dox	Mon Jun 04 23:48:54 2007 +0000
@@ -29,6 +29,7 @@
   @signal chat-joined
   @signal chat-left
   @signal chat-topic-changed
+  @signal conversation-extended-menu
  @endsignals
 
  @signaldef writing-im-msg
@@ -417,5 +418,15 @@
   @param topic The new topic.
  @endsignaldef
 
+ @signaldef conversation-extended-menu
+  @signalproto
+void (*conversation_extended_menu)(PurpleConversation *conv, GList **list);
+  @endsignalproto
+  @signaldesc
+   Emitted when the UI requests a list of plugin actions for a
+   conversation.
+  @param conv   The conversation.
+  @param list   A pointer to the list of actions.
+ @endsignaldef
 */
 // vim: syntax=c tw=75 et
--- a/finch/gntaccount.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/gntaccount.c	Mon Jun 04 23:48:54 2007 +0000
@@ -31,6 +31,7 @@
 #include <gntlabel.h>
 #include <gntline.h>
 #include <gnttree.h>
+#include <gntwindow.h>
 
 #include <account.h>
 #include <accountopt.h>
@@ -40,6 +41,7 @@
 #include <request.h>
 
 #include "gntaccount.h"
+#include "gntblist.h"
 #include "finch.h"
 
 #include <string.h>
@@ -743,12 +745,18 @@
 			finch_accounts_get_handle(),
 			PURPLE_CALLBACK(account_abled_cb), GINT_TO_POINTER(TRUE));
 
-	for (iter = purple_accounts_get_all(); iter; iter = iter->next) {
-		if (purple_account_get_enabled(iter->data, FINCH_UI))
-			break;
+	iter = purple_accounts_get_all();
+	if (iter) {
+		for (; iter; iter = iter->next) {
+			if (purple_account_get_enabled(iter->data, FINCH_UI))
+				break;
+		}
+		if (!iter)
+			finch_accounts_show_all();
+	} else {
+		edit_account(NULL);
+		finch_accounts_show_all();
 	}
-	if (!iter)
-		finch_accounts_show_all();
 }
 
 void finch_accounts_uninit()
@@ -865,25 +873,25 @@
 } auth_and_add;
 
 static void
-authorize_and_add_cb(auth_and_add *aa)
+free_auth_and_add(auth_and_add *aa)
 {
-	aa->auth_cb(aa->data);
-	purple_blist_request_add_buddy(aa->account, aa->username,
-	 	                    NULL, aa->alias);
-
 	g_free(aa->username);
 	g_free(aa->alias);
 	g_free(aa);
 }
 
 static void
+authorize_and_add_cb(auth_and_add *aa)
+{
+	aa->auth_cb(aa->data);
+	purple_blist_request_add_buddy(aa->account, aa->username,
+	 	                    NULL, aa->alias);
+}
+
+static void
 deny_no_add_cb(auth_and_add *aa)
 {
 	aa->deny_cb(aa->data);
-
-	g_free(aa->username);
-	g_free(aa->alias);
-	g_free(aa);
 }
 
 static void *
@@ -912,19 +920,47 @@
 		                (message != NULL ? ": " : "."),
 		                (message != NULL ? message  : ""));
 	if (!on_list) {
+		GntWidget *widget;
+		GList *iter;
 		auth_and_add *aa = g_new(auth_and_add, 1);
+
 		aa->auth_cb = (PurpleAccountRequestAuthorizationCb)auth_cb;
 		aa->deny_cb = (PurpleAccountRequestAuthorizationCb)deny_cb;
 		aa->data = user_data;
 		aa->username = g_strdup(remote_user);
 		aa->alias = g_strdup(alias);
 		aa->account = account;
-		uihandle = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
+
+		uihandle = gnt_vwindow_new(FALSE);
+		gnt_box_set_title(GNT_BOX(uihandle), _("Authorize buddy?"));
+		gnt_box_set_pad(GNT_BOX(uihandle), 0);
+
+		widget = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
 			PURPLE_DEFAULT_ACTION_NONE,
 			account, remote_user, NULL,
 			aa, 2,
 			_("Authorize"), authorize_and_add_cb,
 			_("Deny"), deny_no_add_cb);
+		gnt_screen_release(widget);
+		gnt_box_set_toplevel(GNT_BOX(widget), FALSE);
+		gnt_box_add_widget(GNT_BOX(uihandle), widget);
+
+		gnt_box_add_widget(GNT_BOX(uihandle), gnt_hline_new());
+
+		widget = finch_retrieve_user_info(account->gc, remote_user);
+		for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) {
+			if (GNT_IS_BUTTON(iter->data)) {
+				gnt_widget_destroy(iter->data);
+				gnt_box_remove(GNT_BOX(widget), iter->data);
+				break;
+			}
+		}
+		gnt_box_set_toplevel(GNT_BOX(widget), FALSE);
+		gnt_screen_release(widget);
+		gnt_box_add_widget(GNT_BOX(uihandle), widget);
+		gnt_widget_show(uihandle);
+
+		g_signal_connect_swapped(G_OBJECT(uihandle), "destroy", G_CALLBACK(free_auth_and_add), aa);
 	} else {
 		uihandle = purple_request_action(NULL, _("Authorize buddy?"), buffer, NULL,
 			PURPLE_DEFAULT_ACTION_NONE,
--- a/finch/gntblist.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/gntblist.c	Mon Jun 04 23:48:54 2007 +0000
@@ -104,6 +104,7 @@
 static void add_group(PurpleGroup *group, FinchBlist *ggblist);
 static void add_chat(PurpleChat *chat, FinchBlist *ggblist);
 static void add_node(PurpleBlistNode *node, FinchBlist *ggblist);
+static void node_update(PurpleBuddyList *list, PurpleBlistNode *node);
 static void draw_tooltip(FinchBlist *ggblist);
 static gboolean remove_typing_cb(gpointer null);
 static void remove_peripherals(FinchBlist *ggblist);
@@ -189,6 +190,8 @@
 		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) ||
 				contact->currentsize < 1)
 			node_remove(list, (PurpleBlistNode*)contact);
+		else
+			node_update(list, (PurpleBlistNode*)contact);
 	} else if (!PURPLE_BLIST_NODE_IS_GROUP(node)) {
 		PurpleGroup *group = (PurpleGroup*)node->parent;
 		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) ||
@@ -215,6 +218,9 @@
 	if (list->ui_data == NULL)
 		return;   /* XXX: this is probably the place to auto-join chats */
 
+	if (ggblist->window == NULL)
+		return;
+
 	if (node->ui_data != NULL) {
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
@@ -824,17 +830,22 @@
 			PURPLE_CALLBACK(finch_add_group), group);
 }
 
+gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name)
+{
+	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+	gpointer uihandle;
+	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	uihandle = purple_notify_userinfo(conn, name, info, NULL, NULL);
+	purple_notify_user_info_destroy(info);
+
+	serv_get_info(conn, name);
+	return uihandle;
+}
+
 static void
 finch_blist_get_buddy_info_cb(PurpleBuddy *buddy, PurpleBlistNode *selected)
 {
-	/* Add a userinfo with a "Retrieving information", which will later be updated
-	 * when the server finally returns the information. */
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(buddy->account->gc, purple_buddy_get_name(buddy), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(buddy->account->gc, purple_buddy_get_name(buddy));
+	finch_retrieve_user_info(buddy->account->gc, purple_buddy_get_name(buddy));
 }
 
 static void
--- a/finch/gntblist.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/gntblist.h	Mon Jun 04 23:48:54 2007 +0000
@@ -90,6 +90,16 @@
  */
 void finch_blist_set_size(int width, int height);
 
+/**
+ * Get information about a user. Show immediate feedback.
+ *
+ * @param conn   The connection to get information fro
+ * @param name   The user to get information about.
+ *
+ * @return  Returns the ui-handle for the userinfo notification.
+ */
+gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
+
 /*@}*/
 
 #endif
--- a/finch/gntconv.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/gntconv.c	Mon Jun 04 23:48:54 2007 +0000
@@ -64,7 +64,7 @@
 send_typing_notification(GntWidget *w, FinchConv *ggconv)
 {
 	const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry));
-	gboolean empty = (!text || !*text);
+	gboolean empty = (!text || !*text || (*text == '/'));
 	if (purple_prefs_get_bool("/finch/conversations/notify_typing")) {
 		PurpleConversation *conv = ggconv->active_conv;
 		PurpleConvIm *im = PURPLE_CONV_IM(conv);
@@ -313,12 +313,7 @@
 get_info_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(ggc->active_conv->account->gc, purple_conversation_get_name(ggc->active_conv), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(purple_conversation_get_gc(ggc->active_conv),
+	finch_retrieve_user_info(purple_conversation_get_gc(ggc->active_conv),
 			purple_conversation_get_name(ggc->active_conv));
 }
 
--- a/finch/gntnotify.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/gntnotify.c	Mon Jun 04 23:48:54 2007 +0000
@@ -69,6 +69,7 @@
 	gnt_box_set_title(GNT_BOX(window), title);
 	gnt_box_set_fill(GNT_BOX(window), FALSE);
 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
+	gnt_box_set_pad(GNT_BOX(window), 0);
 
 	if (primary)
 		gnt_box_add_widget(GNT_BOX(window),
@@ -168,7 +169,7 @@
 			gnt_label_new_with_format(_("You have mail!"), GNT_TEXT_FLAG_BOLD));
 
 	emaildialog.tree = tree = gnt_tree_new_with_columns(3);
-	gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("From"), _("Subject"));
+	gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Sender"), _("Subject"));
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
 	gnt_tree_set_col_width(GNT_TREE(tree), 0, 15);
 	gnt_tree_set_col_width(GNT_TREE(tree), 1, 25);
@@ -268,11 +269,11 @@
 		char *strip = purple_markup_strip_html(info);
 		int tvw, tvh, width, height, ntvw, ntvh;
 
+		while (GNT_WIDGET(ui_handle)->parent)
+			ui_handle = GNT_WIDGET(ui_handle)->parent;
 		gnt_widget_get_size(GNT_WIDGET(ui_handle), &width, &height);
 		gnt_widget_get_size(GNT_WIDGET(msg), &tvw, &tvh);
 
-		/* Ideally, I would replace the information in "info". But replacing tagged text is a
-		 * bit nasty right now. So clear the view and add the new stuff instead. */
 		gnt_text_view_clear(msg);
 		gnt_text_view_append_text_with_flags(msg, strip, GNT_TEXT_FLAG_NORMAL);
 		gnt_text_view_scroll(msg, 0);
@@ -280,7 +281,7 @@
 		ntvw += 3;
 		ntvh++;
 
-		gnt_screen_resize_widget(GNT_WIDGET(ui_handle), width + (ntvw - tvw), height + (ntvh - tvh));
+		gnt_screen_resize_widget(GNT_WIDGET(ui_handle), width + MAX(0, ntvw - tvw), height + MAX(0, ntvh - tvh));
 		g_free(strip);
 		g_free(key);
 	} else {
--- a/finch/libgnt/gntbox.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/gntbox.c	Mon Jun 04 23:48:54 2007 +0000
@@ -293,6 +293,10 @@
 		{
 			find_next_focus(box);
 		}
+		else if (strcmp(text, GNT_KEY_BACK_TAB) == 0)
+		{
+			find_prev_focus(box);
+		}
 	}
 	else if (text[0] == '\t')
 	{
--- a/finch/libgnt/gntkeys.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/gntkeys.c	Mon Jun 04 23:48:54 2007 +0000
@@ -50,6 +50,7 @@
 	INSERT_KEY("pagedown", GNT_KEY_PGDOWN);
 	INSERT_KEY("insert",   GNT_KEY_INS);
 	INSERT_KEY("delete",   GNT_KEY_DEL);
+	INSERT_KEY("back_tab", GNT_KEY_BACK_TAB);
 
 	INSERT_KEY("left",   GNT_KEY_LEFT);
 	INSERT_KEY("right",  GNT_KEY_RIGHT);
--- a/finch/libgnt/gntkeys.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/gntkeys.h	Mon Jun 04 23:48:54 2007 +0000
@@ -39,6 +39,7 @@
 #define GNT_KEY_BACKSPACE SAFE(key_backspace)
 #define GNT_KEY_DEL    SAFE(key_dc)
 #define GNT_KEY_INS    SAFE(key_ic)
+#define GNT_KEY_BACK_TAB SAFE(back_tab)
 
 #define GNT_KEY_CTRL_A     "\001"
 #define GNT_KEY_CTRL_B     "\002"
--- a/finch/libgnt/gntmain.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/gntmain.c	Mon Jun 04 23:48:54 2007 +0000
@@ -1,5 +1,5 @@
 #define _GNU_SOURCE
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__unix__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
 
@@ -359,8 +359,7 @@
 	switch (sig) {
 #ifdef SIGWINCH
 	case SIGWINCH:
-		werase(stdscr);
-		wrefresh(stdscr);
+		erase();
 		g_idle_add(refresh_screen, NULL);
 		org_winch_handler(sig);
 		signal(SIGWINCH, sighandler);
--- a/finch/libgnt/gntwm.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/gntwm.c	Mon Jun 04 23:48:54 2007 +0000
@@ -1,5 +1,5 @@
 #define _GNU_SOURCE
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__unix__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
 
@@ -980,12 +980,11 @@
 	GntWM *wm = GNT_WM(bindable);
 
 	endwin();
-	refresh();
-	curs_set(0);   /* endwin resets the cursor to normal */
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
 	update_screen(wm);
 	draw_taskbar(wm, TRUE);
+	curs_set(0);   /* endwin resets the cursor to normal */
 
 	return FALSE;
 }
--- a/finch/libgnt/wms/Makefile.am	Sun Jun 03 23:11:57 2007 +0000
+++ b/finch/libgnt/wms/Makefile.am	Mon Jun 04 23:48:54 2007 +0000
@@ -1,9 +1,16 @@
 s_la_LDFLAGS             = -module -avoid-version
+irssi_la_LDFLAGS         = -module -avoid-version
 
 plugin_LTLIBRARIES = \
-	s.la
+	s.la \
+	irssi.la
+
+plugindir = $(libdir)/gnt
 
-plugindir = $(libdir)/finch
+irssi_la_SOURCES = irssi.c
+irssi_la_LIBADD =  \
+  $(GLIB_LIBS) \
+  $(top_builddir)/finch/libgnt/libgnt.la
 
 s_la_SOURCES = s.c
 s_la_LIBADD =  \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/libgnt/wms/irssi.c	Mon Jun 04 23:48:54 2007 +0000
@@ -0,0 +1,161 @@
+/**
+ * 1. Buddylist and conversation windows are borderless and full height of the screen.
+ * 2. Conversation windows will have (full-screen-width - buddylist-width) width
+ * 	- It's possible to auto-resize the conversation windows when the buddylist
+ * 	  is closed/opened/resized to keep this always true. But resizing the textview
+ * 	  in conversation window is rather costly, especially when there's a lot of text
+ * 	  in the scrollback. So it's not done yet.
+ * 3. All the other windows are always centered.
+ */
+#include <string.h>
+#include <sys/types.h>
+
+#include "gnt.h"
+#include "gntbox.h"
+#include "gntmenu.h"
+#include "gntstyle.h"
+#include "gntwm.h"
+#include "gntwindow.h"
+#include "gntlabel.h"
+
+#define TYPE_IRSSI				(irssi_get_gtype())
+
+typedef struct _Irssi
+{
+	GntWM inherit;
+} Irssi;
+
+typedef struct _IrssiClass
+{
+	GntWMClass inherit;
+} IrssiClass;
+
+GType irssi_get_gtype(void);
+void gntwm_init(GntWM **wm);
+
+static void (*org_new_window)(GntWM *wm, GntWidget *win);
+
+/* This is changed whenever the buddylist is opened/closed or resized. */
+static int buddylistwidth;
+
+static gboolean
+is_budddylist(GntWidget *win)
+{
+	const char *name = gnt_widget_get_name(win);
+	if (name && strcmp(name, "buddylist") == 0)
+		return TRUE;
+	return FALSE;
+}
+
+static void
+remove_border_set_position_size(GntWM *wm, GntWidget *win, int x, int y, int w, int h)
+{
+	gnt_box_set_toplevel(GNT_BOX(win), FALSE);
+	GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_CAN_TAKE_FOCUS);
+
+	gnt_widget_set_position(win, x, y);
+	mvwin(win->window, y, x);
+	gnt_widget_set_size(win, (w < 0) ? -1 : w + 2, h + 2);
+}
+
+static void
+irssi_new_window(GntWM *wm, GntWidget *win)
+{
+	const char *name;
+
+	name = gnt_widget_get_name(win);
+	if (!name || strcmp(name, "conversation-window")) {
+		if (!GNT_IS_MENU(win) && !GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) {
+			if ((!name || strcmp(name, "buddylist"))) {
+				int w, h, x, y;
+				gnt_widget_get_size(win, &w, &h);
+				x = (getmaxx(stdscr) - w) / 2;
+				y = (getmaxy(stdscr) - h) / 2;
+				gnt_widget_set_position(win, x, y);
+				mvwin(win->window, y, x);
+			} else {
+				remove_border_set_position_size(wm, win, 0, 0, -1, getmaxy(stdscr) - 1);
+				gnt_widget_get_size(win, &buddylistwidth, NULL);
+				mvwvline(stdscr, 0, buddylistwidth, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), getmaxy(stdscr) - 1);
+				buddylistwidth++;
+			}
+		}
+		org_new_window(wm, win);
+		return;
+	}
+
+	/* The window we have here is a conversation window. */
+	remove_border_set_position_size(wm, win, buddylistwidth, 0,
+			getmaxx(stdscr) - buddylistwidth, getmaxy(stdscr) - 1);
+	org_new_window(wm, win);
+}
+
+static void
+irssi_window_resized(GntWM *wm, GntNode *node)
+{
+	if (!is_budddylist(node->me))
+		return;
+
+	if (buddylistwidth)
+		mvwvline(stdscr, 0, buddylistwidth - 1,
+				' ' | COLOR_PAIR(GNT_COLOR_NORMAL), getmaxy(stdscr) - 1);
+	gnt_widget_get_size(node->me, &buddylistwidth, NULL);
+	mvwvline(stdscr, 0, buddylistwidth, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), getmaxy(stdscr) - 1);
+	buddylistwidth++;
+}
+
+static gboolean
+irssi_close_window(GntWM *wm, GntWidget *win)
+{
+	if (is_budddylist(win))
+		buddylistwidth = 0;
+	return FALSE;
+}
+
+static void
+irssi_class_init(IrssiClass *klass)
+{
+	GntWMClass *pclass = GNT_WM_CLASS(klass);
+
+	org_new_window = pclass->new_window;
+
+	pclass->new_window = irssi_new_window;
+	pclass->window_resized = irssi_window_resized;
+	pclass->close_window = irssi_close_window;
+
+	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
+	GNTDEBUG;
+}
+
+void gntwm_init(GntWM **wm)
+{
+	buddylistwidth = 0;
+	*wm = g_object_new(TYPE_IRSSI, NULL);
+}
+
+GType irssi_get_gtype(void)
+{
+	static GType type = 0;
+
+	if(type == 0) {
+		static const GTypeInfo info = {
+			sizeof(IrssiClass),
+			NULL,           /* base_init		*/
+			NULL,           /* base_finalize	*/
+			(GClassInitFunc)irssi_class_init,
+			NULL,
+			NULL,           /* class_data		*/
+			sizeof(Irssi),
+			0,              /* n_preallocs		*/
+			NULL,	        /* instance_init	*/
+			NULL
+		};
+
+		type = g_type_register_static(GNT_TYPE_WM,
+		                              "GntIrssi",
+		                              &info, 0);
+	}
+
+	return type;
+}
+
--- a/libpurple/Makefile.am	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/Makefile.am	Mon Jun 04 23:48:54 2007 +0000
@@ -151,7 +151,7 @@
 dbus_headers  = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h
 
 dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \
-                connection.h conversation.h core.h log.h notify.h prefs.h roomlist.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
 
 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders))
--- a/libpurple/blist.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/blist.c	Mon Jun 04 23:48:54 2007 +0000
@@ -2498,6 +2498,13 @@
 	return node->flags;
 }
 
+PurpleBlistNodeType
+purple_blist_node_get_type(PurpleBlistNode *node)
+{
+	g_return_val_if_fail(node != NULL, PURPLE_BLIST_OTHER_NODE);
+	return node->type;
+}
+
 void
 purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
 {
--- a/libpurple/blist.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/blist.h	Mon Jun 04 23:48:54 2007 +0000
@@ -864,6 +864,15 @@
  */
 PurpleBlistNodeFlags purple_blist_node_get_flags(PurpleBlistNode *node);
 
+/**
+ * Get the type of a given node.
+ *
+ * @param node The node.
+ *
+ * @return The type of the node.
+ */
+PurpleBlistNodeType purple_blist_node_get_type(PurpleBlistNode *node);
+
 /*@}*/
 
 /**
--- a/libpurple/conversation.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/conversation.c	Mon Jun 04 23:48:54 2007 +0000
@@ -21,6 +21,7 @@
  */
 #include "internal.h"
 #include "blist.h"
+#include "cmds.h"
 #include "conversation.h"
 #include "dbus-maybe.h"
 #include "debug.h"
@@ -1999,6 +2000,29 @@
 	return cb->name;
 }
 
+GList *
+purple_conversation_get_extended_menu(PurpleConversation *conv)
+{
+	GList *menu = NULL;
+
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	purple_signal_emit(purple_conversations_get_handle(),
+			"conversation-extended-menu", conv, &menu);
+	return menu;
+}
+
+gboolean
+purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
+				const gchar *markup, gchar **error)
+{
+	char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
+	PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
+	g_free(mark);
+	g_free(err);
+	return (status == PURPLE_CMD_STATUS_OK);
+}
+
 void *
 purple_conversations_get_handle(void)
 {
@@ -2262,6 +2286,12 @@
 										PURPLE_SUBTYPE_CONVERSATION),
 						 purple_value_new(PURPLE_TYPE_STRING),
 						 purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_register(handle, "conversation-extended-menu",
+			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+			     purple_value_new(PURPLE_TYPE_SUBTYPE,
+					    PURPLE_SUBTYPE_CONVERSATION),
+			     purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
 }
 
 void
--- a/libpurple/conversation.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/conversation.h	Mon Jun 04 23:48:54 2007 +0000
@@ -1192,6 +1192,30 @@
  */
 void purple_conv_chat_cb_destroy(PurpleConvChatBuddy *cb);
 
+/**
+ * Retrieves the extended menu items for the conversation.
+ *
+ * @param conv The conversation.
+ * 
+ * @return  A list of PurpleMenuAction items, harvested by the
+ *          chat-extended-menu signal. The list and the menuaction
+ *          items should be freed by the caller.
+ */
+GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
+
+/**
+ * Perform a command in a conversation. Similar to @see purple_cmd_do_command
+ *
+ * @param conv    The conversation.
+ * @param cmdline The entire command including the arguments.
+ * @param markup  @c NULL, or the formatted command line.
+ * @param error   If the command failed errormsg is filled in with the appropriate error
+ *                message, if not @c NULL. It must be freed by the caller with g_free().
+ *
+ * @return  @c TRUE if the command was executed successfully, @c FALSE otherwise.
+ */
+gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/ft.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/ft.c	Mon Jun 04 23:48:54 2007 +0000
@@ -23,6 +23,7 @@
  *
  */
 #include "internal.h"
+#include "dbus-maybe.h"
 #include "ft.h"
 #include "network.h"
 #include "notify.h"
@@ -56,6 +57,7 @@
 	g_return_val_if_fail(who     != NULL,              NULL);
 
 	xfer = g_new0(PurpleXfer, 1);
+	PURPLE_DBUS_REGISTER_POINTER(xfer, PurpleXfer);
 
 	xfer->ref = 1;
 	xfer->type    = type;
@@ -97,6 +99,7 @@
 	g_free(xfer->remote_ip);
 	g_free(xfer->local_filename);
 
+	PURPLE_DBUS_UNREGISTER_POINTER(xfer);
 	g_free(xfer);
 	xfers = g_list_remove(xfers, xfer);
 }
@@ -551,6 +554,13 @@
 	return xfer->account;
 }
 
+const char *
+purple_xfer_get_remote_user(const PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, NULL);
+	return xfer->who;
+}
+
 PurpleXferStatusType
 purple_xfer_get_status(const PurpleXfer *xfer)
 {
--- a/libpurple/ft.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/ft.h	Mon Jun 04 23:48:54 2007 +0000
@@ -237,6 +237,15 @@
 PurpleAccount *purple_xfer_get_account(const PurpleXfer *xfer);
 
 /**
+ * Returns the name of the remote user.
+ *
+ * @param xfer The file transfer.
+ *
+ * @return The name of the remote user.
+ */
+const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
+
+/**
  * Returns the status of the xfer.
  *
  * @param xfer The file transfer.
--- a/libpurple/idle.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/idle.c	Mon Jun 04 23:48:54 2007 +0000
@@ -121,7 +121,7 @@
 	gboolean report_idle;
 	GList *l;
 	gint away_seconds = 0;
-	gint idle_recheck_interval;
+	gint idle_recheck_interval = 0;
 
 	purple_signal_emit(purple_blist_get_handle(), "update-idle");
 
--- a/libpurple/imgstore.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/imgstore.c	Mon Jun 04 23:48:54 2007 +0000
@@ -25,6 +25,7 @@
 */
 
 #include <glib.h>
+#include "dbus-maybe.h"
 #include "debug.h"
 #include "imgstore.h"
 #include "util.h"
@@ -56,6 +57,7 @@
 	g_return_val_if_fail(size > 0, 0);
 
 	img = g_new(PurpleStoredImage, 1);
+	PURPLE_DBUS_REGISTER_POINTER(img, PurpleStoredImage);
 	img->data = data;
 	img->size = size;
 	img->filename = g_strdup(filename);
@@ -159,6 +161,7 @@
 
 		g_free(img->data);
 		g_free(img->filename);
+		PURPLE_DBUS_UNREGISTER_POINTER(img);
 		g_free(img);
 		img = NULL;
 	}
--- a/libpurple/plugins/joinpart.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/joinpart.c	Mon Jun 04 23:48:54 2007 +0000
@@ -210,7 +210,7 @@
 	 * we don't have to worry one will be called after this. */
 	g_hash_table_destroy((GHashTable *)data[0]);
 
-	g_source_remove(GPOINTER_TO_UINT(data[1]));
+	purple_timeout_remove(GPOINTER_TO_UINT(data[1]));
 	g_free(data);
 
 	return TRUE;
--- a/libpurple/plugins/log_reader.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Mon Jun 04 23:48:54 2007 +0000
@@ -1482,46 +1482,66 @@
 			 * ">
 			 * Then, replace the next " " (or add this if the end-of-line is reached) with:
 			 * </a>
+			 * 
+			 * As implemented, this isn't perfect, but it should cover common cases.
 			 */
 			link_temp_line = NULL;
-			while ((link = g_strstr_len(line, strlen(line), "(Link: "))) {
-				GString *temp;
+			while ((link = strstr(line, "(Link: ")))
+			{
+				char *tmp = link;
 
-				if (!*link)
-					continue;
+				link += 7;
+				if (*link)
+				{
+					char *end_paren;
+					char *space;
+					GString *temp;
 
-				*link = '\0';
-				link++;
+					if (!(end_paren = strstr(link, ")")))
+					{
+						/* Something is not as we expect.  Bail out. */
+						break;
+					}
 
-				temp = g_string_new(line);
-				g_string_append(temp, "<a href=\"");
+					*tmp = '\0';
+					temp = g_string_new(line);
+
+					/* Start an <a> tag. */
+					g_string_append(temp, "<a href=\"");
+
+					/* Append up to the ) */
+					g_string_append_len(temp, link, end_paren - link);
 
-				if (strlen(link) >= 6) {
-					link += (sizeof("(Link: ") - 1);
+					/* Finish the <a> tag. */
+					g_string_append(temp, "\">");
+
+					/* The \r is a bit of a hack to keep there from being a \r in
+					 * the link text, which may not matter. */
+					if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r")))
+					{
+						g_string_append_len(temp, end_paren + 1, space - end_paren - 1);
 
-					while (*link && *link != ')') {
-						g_string_append_c(temp, *link);
-						link++;
+						/* Close the <a> tag. */
+						g_string_append(temp, "</a>");
+
+						space++;
+						if (*space)
+						{
+							g_string_append_c(temp, ' ');
+							/* Keep the rest of the line. */
+							g_string_append(temp, space);
+						}
 					}
-					if (link) {
-						link++;
-
-						g_string_append(temp, "\">");
-						while (*link && *link != ' ') {
-							g_string_append_c(temp, *link);
-							link++;
-						}
+					else
+					{
+						/* There is no space before the end of the line. */
+						g_string_append(temp, end_paren + 1);
+						/* Close the <a> tag. */
 						g_string_append(temp, "</a>");
 					}
 
-					g_string_append(temp, link);
-
-					/* Free the last round's line. */
-					if (link_temp_line)
-						g_free(line);
-
-					line = temp->str;
-					g_string_free(temp, FALSE);
+					g_free(link_temp_line);
+					line = g_string_free(temp, FALSE);
 
 					/* Save this memory location so we can free it later. */
 					link_temp_line = line;
@@ -1661,8 +1681,7 @@
 
 			g_string_append_c(formatted, '\n');
 
-			if (link_temp_line)
-				g_free(link_temp_line);
+			g_free(link_temp_line);
 
 			c++;
 			line = c;
--- a/libpurple/plugins/perl/common/Account.xs	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Mon Jun 04 23:48:54 2007 +0000
@@ -215,6 +215,7 @@
         t_GL = g_list_append(t_GL, SvPV(*av_fetch((AV *)SvRV(list), i, 0), t_sl));
     }
     purple_account_add_buddies(account, t_GL);
+    g_list_free(t_GL);
 
 void
 purple_account_add_buddy(account, buddy)
@@ -252,6 +253,8 @@
         t_GL2 = g_list_append(t_GL2, SvPV(*av_fetch((AV *)SvRV(B), i, 0), t_sl));
     }
     purple_account_remove_buddies(account, t_GL1, t_GL2);
+    g_list_free(t_GL1);
+    g_list_free(t_GL2);
 
 void
 purple_account_remove_buddy(account, buddy, group)
--- a/libpurple/plugins/perl/common/BuddyList.xs	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Mon Jun 04 23:48:54 2007 +0000
@@ -112,6 +112,10 @@
 	Purple::BuddyList::Group  group
 	Purple::Account account
 
+const char *
+purple_group_get_name(group)
+	Purple::BuddyList::Group group
+
 MODULE = Purple::BuddyList  PACKAGE = Purple::BuddyList  PREFIX = purple_blist_
 PROTOTYPES: ENABLE
 
@@ -248,6 +252,9 @@
 Purple::Handle
 purple_blist_get_handle()
 
+Purple::BuddyList::Node
+purple_blist_get_root()
+
 void
 purple_blist_init()
 
@@ -263,7 +270,7 @@
 PREINIT:
 	GList *l;
 PPCODE:
-	for (l = purple_blist_node_get_extended_menu(node); l != NULL; l = l->next) {
+	for (l = purple_blist_node_get_extended_menu(node); l != NULL; l = g_list_delete_link(l, l)) {
 		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Menu::Action")));
 	}
 
@@ -308,6 +315,15 @@
 purple_blist_node_get_flags(node)
 	Purple::BuddyList::Node node
 
+Purple::BuddyList::NodeType
+purple_blist_node_get_type(node)
+	Purple::BuddyList::Node node
+
+Purple::BuddyList::Node
+purple_blist_node_next(node, offline)
+	Purple::BuddyList::Node node
+	gboolean offline
+
 MODULE = Purple::BuddyList  PACKAGE = Purple::BuddyList::Chat  PREFIX = purple_chat_
 PROTOTYPES: ENABLE
 
--- a/libpurple/plugins/perl/common/Conversation.xs	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/Conversation.xs	Mon Jun 04 23:48:54 2007 +0000
@@ -218,6 +218,21 @@
 	Purple::Conversation conv
 	Purple::Account account
 
+void
+purple_conversation_write(conv, who, message, flags, mtime)
+	Purple::Conversation conv
+	const char *who
+	const char *message
+	Purple::MessageFlags flags
+	time_t mtime
+
+gboolean
+purple_conversation_do_command(conv, cmdline, markup, error)
+	Purple::Conversation conv
+	const char *cmdline
+	const char *markup
+	char **error
+
 MODULE = Purple::Conversation  PACKAGE = Purple::Conversation::IM  PREFIX = purple_conv_im_
 PROTOTYPES: ENABLE
 
--- a/libpurple/plugins/perl/common/Prefs.xs	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/Prefs.xs	Mon Jun 04 23:48:54 2007 +0000
@@ -94,8 +94,9 @@
 PREINIT:
 	GList *l;
 PPCODE:
-	for (l = purple_prefs_get_string_list(name); l != NULL; l = l->next) {
-		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::PrefValue")));
+	for (l = purple_prefs_get_string_list(name); l != NULL; l = g_list_delete_link(l, l)) {
+		XPUSHs(sv_2mortal(newSVpv(l->data, 0)));
+		g_free(l->data);
 	}
 
 Purple::PrefType
--- a/libpurple/plugins/perl/common/module.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/module.h	Mon Jun 04 23:48:54 2007 +0000
@@ -69,6 +69,7 @@
 /* blist.h */
 typedef PurpleBlistNode *			Purple__BuddyList__Node;
 typedef PurpleBlistNodeFlags		Purple__BuddyList__NodeFlags;
+typedef PurpleBlistNodeType		Purple__BuddyList__NodeType;
 typedef PurpleBlistUiOps *		Purple__BuddyList__UiOps;
 typedef PurpleBuddyList *			Purple__BuddyList;
 typedef PurpleBuddy *			Purple__BuddyList__Buddy;
--- a/libpurple/plugins/perl/common/typemap	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/common/typemap	Mon Jun 04 23:48:54 2007 +0000
@@ -52,6 +52,7 @@
 Purple::BuddyList::Group			T_PurpleObj
 Purple::BuddyList::Node			T_PurpleObj
 Purple::BuddyList::NodeFlags		T_IV
+Purple::BuddyList::NodeType		T_IV
 Purple::BuddyList::UiOps			T_PurpleObj
 
 Purple::Cipher				T_PurpleObj
--- a/libpurple/plugins/perl/perl-handlers.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Mon Jun 04 23:48:54 2007 +0000
@@ -184,7 +184,7 @@
 	timeout_handlers = g_list_remove(timeout_handlers, handler);
 
 	if (handler->iotag > 0)
-		g_source_remove(handler->iotag);
+		purple_timeout_remove(handler->iotag);
 
 	if (handler->callback != NULL)
 		SvREFCNT_dec(handler->callback);
@@ -405,7 +405,7 @@
 
 	timeout_handlers = g_list_append(timeout_handlers, handler);
 
-	handler->iotag = g_timeout_add(seconds * 1000, perl_timeout_cb, handler);
+	handler->iotag = purple_timeout_add(seconds * 1000, perl_timeout_cb, handler);
 }
 
 void
--- a/libpurple/protocols/oscar/flap_connection.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Mon Jun 04 23:48:54 2007 +0000
@@ -303,7 +303,7 @@
 		}
 	}
 
-	if (conn->fd != -1)
+	if (conn->fd >= 0)
 	{
 		if (conn->type == SNAC_FAMILY_LOCATE)
 			flap_connection_send_close(od, conn);
@@ -792,7 +792,7 @@
 			}
 
 			/* If there was an error then close the connection */
-			if (read == -1)
+			if (read < 0)
 			{
 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 					/* No worries */
@@ -853,7 +853,7 @@
 				break;
 			}
 
-			if (read == -1)
+			if (read < 0)
 			{
 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 					/* No worries */
@@ -902,7 +902,7 @@
 	ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 	if (ret <= 0)
 	{
-		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+		if (ret < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 			/* No worries */
 			return;
 
@@ -936,7 +936,7 @@
 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
 
 	/* If we haven't already started writing stuff, then start the cycle */
-	if ((conn->watcher_outgoing == 0) && (conn->fd != -1))
+	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
 	{
 		conn->watcher_outgoing = purple_input_add(conn->fd,
 				PURPLE_INPUT_WRITE, send_cb, conn);
--- a/libpurple/protocols/oscar/odc.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/oscar/odc.c	Mon Jun 04 23:48:54 2007 +0000
@@ -447,7 +447,7 @@
 		return;
 	}
 
-	if (read == -1)
+	if (read < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No worries */
--- a/libpurple/protocols/oscar/oscar.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Jun 04 23:48:54 2007 +0000
@@ -1604,8 +1604,7 @@
 			straight_to_hell, pos) == NULL)
 	{
 		char buf[256];
-		if (pos->modname)
-			g_free(pos->modname);
+		g_free(pos->modname);
 		g_free(pos);
 		g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  "
 			"Check %s for updates."), PURPLE_WEBSITE);
--- a/libpurple/protocols/oscar/peer.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/oscar/peer.c	Mon Jun 04 23:48:54 2007 +0000
@@ -173,12 +173,12 @@
 		purple_input_remove(conn->watcher_outgoing);
 		conn->watcher_outgoing = 0;
 	}
-	if (conn->listenerfd != -1)
+	if (conn->listenerfd >= 0)
 	{
 		close(conn->listenerfd);
 		conn->listenerfd = -1;
 	}
-	if (conn->fd != -1)
+	if (conn->fd >= 0)
 	{
 		close(conn->fd);
 		conn->fd = -1;
@@ -310,7 +310,7 @@
 		}
 
 		/* If there was an error then close the connection */
-		if (read == -1)
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
@@ -360,7 +360,7 @@
 		return;
 	}
 
-	if (read == -1)
+	if (read < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No worries */
@@ -422,7 +422,7 @@
 	wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 	if (wrotelen <= 0)
 	{
-		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+		if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 			/* No worries */
 			return;
 
@@ -462,7 +462,7 @@
 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
 
 	/* If we haven't already started writing stuff, then start the cycle */
-	if ((conn->watcher_outgoing == 0) && (conn->fd != -1))
+	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
 	{
 		conn->watcher_outgoing = purple_input_add(conn->fd,
 				PURPLE_INPUT_WRITE, send_cb, conn);
@@ -596,7 +596,7 @@
 	purple_debug_info("oscar", "Accepting connection on listener socket.\n");
 
 	conn->fd = accept(conn->listenerfd, &addr, &addrlen);
-	if (conn->fd == -1)
+	if (conn->fd < 0)
 	{
 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 			/* No connection yet--no worries */
@@ -640,7 +640,7 @@
 	conn = data;
 	conn->listen_data = NULL;
 
-	if (listenerfd == -1)
+	if (listenerfd < 0)
 	{
 		/* Could not open listener socket */
 		peer_connection_trynext(conn);
--- a/libpurple/protocols/oscar/peer_proxy.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/oscar/peer_proxy.c	Mon Jun 04 23:48:54 2007 +0000
@@ -224,7 +224,7 @@
 		}
 
 		/* If there was an error then close the connection */
-		if (read == -1)
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
@@ -285,7 +285,8 @@
 			return;
 		}
 
-		if (read == -1)
+		/* If there was an error then close the connection */
+		if (read < 0)
 		{
 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
 				/* No worries */
--- a/libpurple/protocols/silc/silc.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Mon Jun 04 23:48:54 2007 +0000
@@ -119,12 +119,12 @@
 				NULL, 0);
 }
 
-static int
+static gboolean
 silcpurple_scheduler(gpointer *context)
 {
 	SilcPurple sg = (SilcPurple)context;
 	silc_client_run_one(sg->client);
-	return 1;
+	return TRUE;
 }
 
 static void
@@ -361,11 +361,7 @@
 	}
 
 	/* Schedule SILC using Glib's event loop */
-#ifndef _WIN32
-	sg->scheduler = g_timeout_add(5, (GSourceFunc)silcpurple_scheduler, sg);
-#else
-	sg->scheduler = g_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
-#endif
+	sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
 }
 
 static int
@@ -396,8 +392,8 @@
 	if (sg->conn)
 		silc_client_close_connection(sg->client, sg->conn);
 
-	g_source_remove(sg->scheduler);
-	g_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
+	purple_timeout_remove(sg->scheduler);
+	purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
 }
 
 
--- a/libpurple/protocols/yahoo/yahoo.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Mon Jun 04 23:48:54 2007 +0000
@@ -2310,7 +2310,7 @@
 			 * are you trying to pull? */
 			guchar *start;
 
-			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!");
+			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
 
 			start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
 			if (start) {
@@ -2377,7 +2377,11 @@
 	}
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+				error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2405,7 +2409,11 @@
 	}
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+				error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2507,12 +2515,16 @@
 	if (written < 0 && errno == EAGAIN)
 		written = 0;
 	else if (written <= 0) {
+		gchar *tmp;
 		g_free(yd->auth);
 		yd->auth = NULL;
 		if (gc->inpa)
 			purple_input_remove(gc->inpa);
 		gc->inpa = 0;
-		purple_connection_error(gc, _("Unable to connect."));
+		tmp = g_strdup_printf(_("Lost connection with %s:\n%s"),
+				"login.yahoo.com:80", strerror(errno));
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2533,7 +2545,11 @@
 	PurpleConnection *gc = data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Unable to connect."));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with %s:\n%s"),
+				"login.yahoo.com:80", error_message);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
@@ -2616,8 +2632,7 @@
 
 	if (error_message != NULL)
 	{
-		/* TODO: Include error_message in the message below */
-		purple_connection_error(gc, _("Unable to connect."));
+		purple_connection_error(gc, error_message);
 		return;
 	}
 
--- a/libpurple/purple-remote	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/purple-remote	Mon Jun 04 23:48:54 2007 +0000
@@ -31,7 +31,7 @@
         return result
             
 def show_help():
-    print """This program uses DBus to communicate with purple.
+    print """This program uses D-Bus to communicate with purple.
 
 Usage:
 
@@ -96,8 +96,6 @@
     protocol = match.group(2)
     if protocol == "xmpp":
         protocol = "jabber"
-    if protocol == "aim" or protocol == "icq":
-        protocol = "oscar"
     if protocol is not None:
         protocol = "prpl-" + protocol
     command = match.group(5)
--- a/libpurple/util.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/util.c	Mon Jun 04 23:48:54 2007 +0000
@@ -46,6 +46,7 @@
 	} website;
 
 	char *url;
+	int num_times_redirected;
 	gboolean full;
 	char *user_agent;
 	gboolean http11;
@@ -1284,14 +1285,17 @@
 								pt->dest_tag = y; \
 								tags = g_list_prepend(tags, pt); \
 							} \
-							xhtml = g_string_append(xhtml, "<" y); \
-							c += strlen("<" x ); \
-							xhtml = g_string_append(xhtml, innards->str); \
-							xhtml = g_string_append_c(xhtml, '>'); \
+							if(xhtml) { \
+								xhtml = g_string_append(xhtml, "<" y); \
+								xhtml = g_string_append(xhtml, innards->str); \
+								xhtml = g_string_append_c(xhtml, '>'); \
+							} \
 							c = p + 1; \
 						} else { \
-							xhtml = g_string_append(xhtml, "&lt;"); \
-							plain = g_string_append_c(plain, '<'); \
+							if(xhtml) \
+								xhtml = g_string_append(xhtml, "&lt;"); \
+							if(plain) \
+								plain = g_string_append_c(plain, '<'); \
 							c++; \
 						} \
 						g_string_free(innards, TRUE); \
@@ -1300,16 +1304,19 @@
 						if(!g_ascii_strncasecmp(c, "<" x, strlen("<" x)) && \
 								(*(c+strlen("<" x)) == '>' || \
 								 !g_ascii_strncasecmp(c+strlen("<" x), "/>", 2))) { \
-							xhtml = g_string_append(xhtml, "<" y); \
+							if(xhtml) \
+								xhtml = g_string_append(xhtml, "<" y); \
 							c += strlen("<" x); \
 							if(*c != '/') { \
 								struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); \
 								pt->src_tag = x; \
 								pt->dest_tag = y; \
 								tags = g_list_prepend(tags, pt); \
-								xhtml = g_string_append_c(xhtml, '>'); \
+								if(xhtml) \
+									xhtml = g_string_append_c(xhtml, '>'); \
 							} else { \
-								xhtml = g_string_append(xhtml, "/>");\
+								if(xhtml) \
+									xhtml = g_string_append(xhtml, "/>");\
 							} \
 							c = strchr(c, '>') + 1; \
 							continue; \
@@ -1319,11 +1326,18 @@
 purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
 						  char **plain_out)
 {
-	GString *xhtml = g_string_new("");
-	GString *plain = g_string_new("");
+	GString *xhtml = NULL;
+	GString *plain = NULL;
 	GList *tags = NULL, *tag;
 	const char *c = html;
 
+	g_return_if_fail(xhtml_out != NULL || plain_out != NULL);
+
+	if(xhtml_out)
+		xhtml = g_string_new("");
+	if(plain_out)
+		plain = g_string_new("");
+
 	while(c && *c) {
 		if(*c == '<') {
 			if(*(c+1) == '/') { /* closing tag */
@@ -1339,7 +1353,8 @@
 				if(tag) {
 					while(tags) {
 						struct purple_parse_tag *pt = tags->data;
-						g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
+						if(xhtml)
+							g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
 						if(tags == tag)
 							break;
 						tags = g_list_remove(tags, pt);
@@ -1357,8 +1372,10 @@
 					if(*end == '>') {
 						c = end+1;
 					} else {
-						xhtml = g_string_append(xhtml, "&lt;");
-						plain = g_string_append_c(plain, '<');
+						if(xhtml)
+							xhtml = g_string_append(xhtml, "&lt;");
+						if(plain)
+							plain = g_string_append_c(plain, '<');
 						c++;
 					}
 				}
@@ -1387,7 +1404,8 @@
 				ALLOW_TAG("span");
 				ALLOW_TAG("strong");
 				ALLOW_TAG("ul");
-
+				ALLOW_TAG("img");
+				
 				/* we skip <HR> because it's not legal in XHTML-IM.  However,
 				 * we still want to send something sensible, so we put a
 				 * linebreak in its place. <BR> also needs special handling
@@ -1398,8 +1416,9 @@
 							!g_ascii_strncasecmp(c+3, "/>", 2) ||
 							!g_ascii_strncasecmp(c+3, " />", 3))) {
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<br/>");
-					if(*c != '\n')
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<br/>");
+					if(plain && *c != '\n')
 						plain = g_string_append_c(plain, '\n');
 					continue;
 				}
@@ -1409,7 +1428,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<u>", 3) || !g_ascii_strncasecmp(c, "<underline>", strlen("<underline>"))) {
@@ -1418,7 +1438,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>");
+					if (xhtml)
+						xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<s>", 3) || !g_ascii_strncasecmp(c, "<strike>", strlen("<strike>"))) {
@@ -1427,7 +1448,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<sub>", 5)) {
@@ -1436,7 +1458,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<sup>", 5)) {
@@ -1445,7 +1468,8 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					c = strchr(c, '>') + 1;
-					xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
+					if(xhtml)
+						xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
@@ -1539,7 +1563,10 @@
 					pt->dest_tag = "span";
 					tags = g_list_prepend(tags, pt);
 					if(style->len)
-						g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str));
+					{
+						if(xhtml)
+							g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str));
+					}
 					else
 						pt->ignore = TRUE;
 					g_string_free(style, TRUE);
@@ -1559,7 +1586,8 @@
 								color = g_string_append_c(color, *q);
 								q++;
 							}
-							g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
+							if(xhtml)
+								g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
 							g_string_free(color, TRUE);
 							if ((c = strchr(c, '>')) != NULL)
 								c++;
@@ -1580,14 +1608,17 @@
 				if(!g_ascii_strncasecmp(c, "<!--", strlen("<!--"))) {
 					char *p = strstr(c + strlen("<!--"), "-->");
 					if(p) {
-						xhtml = g_string_append(xhtml, "<!--");
+						if(xhtml)
+							xhtml = g_string_append(xhtml, "<!--");
 						c += strlen("<!--");
 						continue;
 					}
 				}
 
-				xhtml = g_string_append(xhtml, "&lt;");
-				plain = g_string_append_c(plain, '<');
+				if(xhtml)
+					xhtml = g_string_append(xhtml, "&lt;");
+				if(plain)
+					plain = g_string_append_c(plain, '<');
 				c++;
 			}
 		} else if(*c == '&') {
@@ -1600,29 +1631,31 @@
 				g_snprintf(buf, sizeof(buf), "%c", *c);
 				pln = buf;
 			}
-			xhtml = g_string_append_len(xhtml, c, len);
-			plain = g_string_append(plain, pln);
+			if(xhtml)
+				xhtml = g_string_append_len(xhtml, c, len);
+			if(plain)
+				plain = g_string_append(plain, pln);
 			c += len;
 		} else {
-			xhtml = g_string_append_c(xhtml, *c);
-			plain = g_string_append_c(plain, *c);
+			if(xhtml)
+				xhtml = g_string_append_c(xhtml, *c);
+			if(plain)
+				plain = g_string_append_c(plain, *c);
 			c++;
 		}
 	}
-	tag = tags;
-	while(tag) {
-		struct purple_parse_tag *pt = tag->data;
-		if(!pt->ignore)
-			g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
-		tag = tag->next;
+	if(xhtml) {
+		for (tag = tags; tag ; tag = tag->next) {
+			struct purple_parse_tag *pt = tag->data;
+			if(!pt->ignore)
+				g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
+		}
 	}
 	g_list_free(tags);
 	if(xhtml_out)
-		*xhtml_out = g_strdup(xhtml->str);
+		*xhtml_out = g_string_free(xhtml, FALSE);
 	if(plain_out)
-		*plain_out = g_strdup(plain->str);
-	g_string_free(xhtml, TRUE);
-	g_string_free(plain, TRUE);
+		*plain_out = g_string_free(plain, FALSE);
 }
 
 /* The following are probably reasonable changes:
@@ -3211,6 +3244,17 @@
 		g_hash_table_destroy(params);
 }
 
+/*
+ * TODO: Should probably add a "gboolean *ret_ishttps" parameter that
+ *       is set to TRUE if this URL is https, otherwise it is set to
+ *       FALSE.  But that change will break the API.
+ *
+ *       This is important for Yahoo! web messenger login.  They now
+ *       force https login, and if you access the web messenger login
+ *       page via http then it redirects you to the https version, but
+ *       purple_util_fetch_url() ignores the "https" and attempts to
+ *       fetch the URL via http again, which gets redirected again.
+ */
 gboolean
 purple_url_parse(const char *url, char **ret_host, int *ret_port,
 			   char **ret_path, char **ret_user, char **ret_passwd)
@@ -3231,12 +3275,16 @@
 
 	g_return_val_if_fail(url != NULL, FALSE);
 
-	if ((turl = strstr(url, "http://")) != NULL ||
-		(turl = strstr(url, "HTTP://")) != NULL)
+	if ((turl = purple_strcasestr(url, "http://")) != NULL)
 	{
 		turl += 7;
 		url = turl;
 	}
+	else if ((turl = purple_strcasestr(url, "https://")) != NULL)
+	{
+		turl += 8;
+		url = turl;
+	}
 
 	/* parse out authentication information if supplied */
 	/* Only care about @ char BEFORE the first / */
@@ -3316,85 +3364,92 @@
 			   PurpleUtilFetchUrlData *gfud)
 {
 	gchar *s;
-
-	if ((s = g_strstr_len(data, data_len, "Location: ")) != NULL)
+	gchar *new_url, *temp_url, *end;
+	gboolean full;
+	int len;
+
+	if ((s = g_strstr_len(data, data_len, "Location: ")) == NULL)
+		/* We're not being redirected */
+		return FALSE;
+
+	s += strlen("Location: ");
+	end = strchr(s, '\r');
+
+	/* Just in case :) */
+	if (end == NULL)
+		end = strchr(s, '\n');
+
+	if (end == NULL)
+		return FALSE;
+
+	len = end - s;
+
+	new_url = g_malloc(len + 1);
+	strncpy(new_url, s, len);
+	new_url[len] = '\0';
+
+	full = gfud->full;
+
+	if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
 	{
-		gchar *new_url, *temp_url, *end;
-		gboolean full;
-		int len;
-
-		s += strlen("Location: ");
-		end = strchr(s, '\r');
-
-		/* Just in case :) */
-		if (end == NULL)
-			end = strchr(s, '\n');
-
-		if (end == NULL)
-			return FALSE;
-
-		len = end - s;
-
-		new_url = g_malloc(len + 1);
-		strncpy(new_url, s, len);
-		new_url[len] = '\0';
-
-		full = gfud->full;
-
-		if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
-		{
-			temp_url = new_url;
-
-			new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
-									  gfud->website.port, temp_url);
-
-			g_free(temp_url);
-
-			full = FALSE;
-		}
-
-		purple_debug_info("util", "Redirecting to %s\n", new_url);
-
-		/*
-		 * Try again, with this new location.  This code is somewhat
-		 * ugly, but we need to reuse the gfud because whoever called
-		 * us is holding a reference to it.
-		 */
-		g_free(gfud->url);
-		gfud->url = new_url;
-		gfud->full = full;
-		g_free(gfud->request);
-		gfud->request = NULL;
-
-		purple_input_remove(gfud->inpa);
-		gfud->inpa = 0;
-		close(gfud->fd);
-		gfud->fd = -1;
-		gfud->request_written = 0;
-		gfud->len = 0;
-		gfud->data_len = 0;
-
-		g_free(gfud->website.user);
-		g_free(gfud->website.passwd);
-		g_free(gfud->website.address);
-		g_free(gfud->website.page);
-		purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
-					   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
-		gfud->connect_data = purple_proxy_connect(NULL, NULL,
-				gfud->website.address, gfud->website.port,
-				url_fetch_connect_cb, gfud);
-
-		if (gfud->connect_data == NULL)
-		{
-			purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
-					gfud->website.address);
-		}
-
+		temp_url = new_url;
+
+		new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
+								  gfud->website.port, temp_url);
+
+		g_free(temp_url);
+
+		full = FALSE;
+	}
+
+	purple_debug_info("util", "Redirecting to %s\n", new_url);
+
+	gfud->num_times_redirected++;
+	if (gfud->num_times_redirected >= 5)
+	{
+		purple_util_fetch_url_error(gfud,
+				_("Could not open %s: Redirected too many times"),
+				gfud->url);
 		return TRUE;
 	}
 
-	return FALSE;
+	/*
+	 * Try again, with this new location.  This code is somewhat
+	 * ugly, but we need to reuse the gfud because whoever called
+	 * us is holding a reference to it.
+	 */
+	g_free(gfud->url);
+	gfud->url = new_url;
+	gfud->full = full;
+	g_free(gfud->request);
+	gfud->request = NULL;
+
+	purple_input_remove(gfud->inpa);
+	gfud->inpa = 0;
+	close(gfud->fd);
+	gfud->fd = -1;
+	gfud->request_written = 0;
+	gfud->len = 0;
+	gfud->data_len = 0;
+
+	g_free(gfud->website.user);
+	g_free(gfud->website.passwd);
+	g_free(gfud->website.address);
+	g_free(gfud->website.page);
+	purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
+				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
+
+	gfud->connect_data = purple_proxy_connect(NULL, NULL,
+			gfud->website.address, gfud->website.port,
+			url_fetch_connect_cb, gfud);
+
+	if (gfud->connect_data == NULL)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
+				gfud->website.address);
+	}
+
+	return TRUE;
 }
 
 static size_t
--- a/libpurple/xmlnode.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/libpurple/xmlnode.c	Mon Jun 04 23:48:54 2007 +0000
@@ -519,38 +519,38 @@
 }
 
 static xmlSAXHandler xmlnode_parser_libxml = {
-	.internalSubset         = NULL,
-	.isStandalone           = NULL,
-	.hasInternalSubset      = NULL,
-	.hasExternalSubset      = NULL,
-	.resolveEntity          = NULL,
-	.getEntity              = NULL,
-	.entityDecl             = NULL,
-	.notationDecl           = NULL,
-	.attributeDecl          = NULL,
-	.elementDecl            = NULL,
-	.unparsedEntityDecl     = NULL,
-	.setDocumentLocator     = NULL,
-	.startDocument          = NULL,
-	.endDocument            = NULL,
-	.startElement           = NULL,
-	.endElement             = NULL,
-	.reference              = NULL,
-	.characters             = xmlnode_parser_element_text_libxml,
-	.ignorableWhitespace    = NULL,
-	.processingInstruction  = NULL,
-	.comment                = NULL,
-	.warning                = NULL,
-	.error                  = xmlnode_parser_error_libxml,
-	.fatalError             = NULL,
-	.getParameterEntity     = NULL,
-	.cdataBlock             = NULL,
-	.externalSubset         = NULL,
-	.initialized            = XML_SAX2_MAGIC,
-	._private               = NULL,
-	.startElementNs         = xmlnode_parser_element_start_libxml,
-	.endElementNs           = xmlnode_parser_element_end_libxml,
-	.serror                 = NULL
+	NULL, /* internalSubset */
+	NULL, /* isStandalone */
+	NULL, /* hasInternalSubset */
+	NULL, /* hasExternalSubset */
+	NULL, /* resolveEntity */
+	NULL, /* getEntity */
+	NULL, /* entityDecl */
+	NULL, /* notationDecl */
+	NULL, /* attributeDecl */
+	NULL, /* elementDecl */
+	NULL, /* unparsedEntityDecl */
+	NULL, /* setDocumentLocator */
+	NULL, /* startDocument */
+	NULL, /* endDocument */
+	NULL, /* startElement */
+	NULL, /* endElement */
+	NULL, /* reference */
+	xmlnode_parser_element_text_libxml, /* characters */
+	NULL, /* ignorableWhitespace */
+	NULL, /* processingInstruction */
+	NULL, /* comment */
+	NULL, /* warning */
+	xmlnode_parser_error_libxml, /* error */
+	NULL, /* fatalError */
+	NULL, /* getParameterEntity */
+	NULL, /* cdataBlock */
+	NULL, /* externalSubset */
+	XML_SAX2_MAGIC, /* initialized */
+	NULL, /* _private */
+	xmlnode_parser_element_start_libxml, /* startElementNs */
+	xmlnode_parser_element_end_libxml,   /* endElementNs   */
+	NULL, /* serror */
 };
 
 xmlnode *
--- a/pidgin/gtkaccount.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkaccount.c	Mon Jun 04 23:48:54 2007 +0000
@@ -184,6 +184,7 @@
 	gtk_size_group_add_widget(dialog->sg, label);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
 	gtk_widget_show(label);
 
 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER);
@@ -426,7 +427,7 @@
 		gtk_widget_ref(dialog->protocol_menu);
 	}
 
-	hbox = add_pref_box(dialog, vbox, _("Protocol:"), dialog->protocol_menu);
+	hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu);
 	g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox);
 
 	gtk_widget_unref(dialog->protocol_menu);
@@ -437,7 +438,7 @@
 	g_object_set(G_OBJECT(dialog->screenname_entry), "truncate-multiline", TRUE, NULL);
 #endif
 
-	add_pref_box(dialog, vbox, _("Screen name:"), dialog->screenname_entry);
+	add_pref_box(dialog, vbox, _("Screen _name:"), dialog->screenname_entry);
 
 	g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed",
 					 G_CALLBACK(screenname_changed_cb), dialog);
@@ -479,7 +480,7 @@
 
 		GtkWidget *entry = l->data;
 		PurpleAccountUserSplit *split = l2->data;
-		const char *value = NULL, *protocol = NULL;
+		const char *value = NULL;
 		char *c;
 
 		if (dialog->account != NULL) {
@@ -499,9 +500,8 @@
 		/* Google Talk default domain hackery! */
 		menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
 		item = gtk_menu_get_active(GTK_MENU(menu));
-		protocol = g_object_get_data(G_OBJECT(item), "protocol");
-		if (value == NULL && protocol != NULL && !strcmp(protocol, "prpl-fake") &&
-				!strcmp(purple_account_user_split_get_text(split), _("Domain")))
+		if (value == NULL && g_object_get_data(G_OBJECT(item), "fake") && 
+			!strcmp(purple_account_user_split_get_text(split), _("Domain")))
 			value = "gmail.com";
 
 		if (value != NULL)
@@ -519,16 +519,16 @@
 	gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE);
 	if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*')
 		gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR);
-	dialog->password_box = add_pref_box(dialog, vbox, _("Password:"),
+	dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"),
 										  dialog->password_entry);
 
 	/* Alias */
 	dialog->alias_entry = gtk_entry_new();
-	add_pref_box(dialog, vbox, _("Local alias:"), dialog->alias_entry);
+	add_pref_box(dialog, vbox, _("Local _alias:"), dialog->alias_entry);
 
 	/* Remember Password */
 	dialog->remember_pass_check =
-		gtk_check_button_new_with_label(_("Remember password"));
+		gtk_check_button_new_with_mnemonic(_("Remember pass_word"));
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
 								 FALSE);
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->remember_pass_check,
@@ -599,12 +599,12 @@
 
 	/* New mail notifications */
 	dialog->new_mail_check =
-		gtk_check_button_new_with_label(_("New mail notifications"));
+		gtk_check_button_new_with_mnemonic(_("New _mail notifications"));
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->new_mail_check, FALSE, FALSE, 0);
 	gtk_widget_show(dialog->new_mail_check);
 
 	/* Buddy icon */
-	dialog->icon_check = gtk_check_button_new_with_label(_("Use this buddy icon for this account:"));
+	dialog->icon_check = gtk_check_button_new_with_mnemonic(_("Use this buddy _icon for this account:"));
 	g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog);
 	gtk_widget_show(dialog->icon_check);
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0);
@@ -702,7 +702,7 @@
 	GList *l;
 	char buf[1024];
 	char *title;
-	const char *str_value, *protocol;
+	const char *str_value;
 	gboolean bool_value;
 
 	if (dialog->protocol_frame != NULL) {
@@ -828,10 +828,9 @@
 				/* Google Talk default domain hackery! */
 				menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
 				item = gtk_menu_get_active(GTK_MENU(menu));
-				protocol = g_object_get_data(G_OBJECT(item), "protocol");
-				if (str_value == NULL && protocol != NULL && !strcmp(protocol, "prpl-fake") &&
+				if (str_value == NULL && g_object_get_data(G_OBJECT(item), "fake") &&
 					!strcmp(_("Connect server"),  purple_account_option_get_text(option)))
-					str_value = "talk.google.com";
+					str_value = "talk.google.com";	
 		
 				if (str_value != NULL)
 					gtk_entry_set_text(GTK_ENTRY(entry), str_value);
@@ -1023,7 +1022,7 @@
 	if (dialog->proxy_frame != NULL)
 		gtk_widget_destroy(dialog->proxy_frame);
 
-	frame = pidgin_make_frame(parent, _("Proxy Options"));
+	frame = pidgin_make_frame(parent, _("Pro_xy Options"));
 	dialog->proxy_frame = gtk_widget_get_parent(gtk_widget_get_parent(frame));
 
 	gtk_box_reorder_child(GTK_BOX(parent), dialog->proxy_frame, 1);
@@ -1466,7 +1465,6 @@
 	if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL)
 		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 
-
 	dialog->window = win = pidgin_create_window((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
 		PIDGIN_HIG_BORDER, "account", FALSE);
 
--- a/pidgin/gtkblist.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkblist.c	Mon Jun 04 23:48:54 2007 +0000
@@ -274,12 +274,7 @@
 
 static void gtk_blist_menu_info_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-	purple_notify_userinfo(b->account->gc, purple_buddy_get_name(b), info, NULL, NULL);
-	purple_notify_user_info_destroy(info);
-
-	serv_get_info(b->account->gc, b->name);
+	pidgin_retrieve_user_info(b->account->gc, purple_buddy_get_name(b));
 }
 
 static void gtk_blist_menu_im_cb(GtkWidget *w, PurpleBuddy *b)
@@ -1124,10 +1119,10 @@
 	if (((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) {
 		pidgin_separator(menu);
 		pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy);
-		pidgin_new_item_from_stock(menu, _("Alias..."), PIDGIN_STOCK_ALIAS,
+		pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
 				G_CALLBACK(gtk_blist_menu_alias_cb),
 				contact, 0, 0, NULL);
-		pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE,
+		pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
 				G_CALLBACK(pidgin_blist_remove_cb),
 				contact, 0, 0, NULL);
 	} else if (!sub || contact_expanded) {
@@ -1142,7 +1137,8 @@
 }
 
 static gboolean
-gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data) {
+gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data)
+{
 	PurpleBlistNode *node;
 	GValue val;
 	GtkTreeIter iter;
@@ -1169,7 +1165,7 @@
 			return FALSE;
 		}
 		if(buddy)
-			serv_get_info(buddy->account->gc, buddy->name);
+			pidgin_retrieve_user_info(buddy->account->gc, buddy->name);
 	} else if (event->keyval == GDK_F2) {
 		gtk_blist_menu_alias_cb(tv, node);
 	}
@@ -1423,7 +1419,7 @@
 			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
 		if (prpl && prpl_info->get_info)
-			serv_get_info(b->account->gc, b->name);
+			pidgin_retrieve_user_info(b->account->gc, b->name);
 		handled = TRUE;
 	}
 
@@ -3837,15 +3833,17 @@
 static gboolean
 gtk_blist_window_key_press_cb(GtkWidget *w, GdkEventKey *event, PidginBuddyList *gtkblist)
 {
-	GtkWidget *imhtml;
+	GtkWidget *widget;
 
 	if (!gtkblist)
 		return FALSE;
 
-	imhtml = gtk_window_get_focus(GTK_WINDOW(gtkblist->window));
-
-	if (GTK_IS_IMHTML(imhtml) && gtk_bindings_activate(GTK_OBJECT(imhtml), event->keyval, event->state))
-		return TRUE;
+	widget = gtk_window_get_focus(GTK_WINDOW(gtkblist->window));
+
+	if (GTK_IS_IMHTML(widget) || GTK_IS_ENTRY(widget)) {
+		if (gtk_bindings_activate(GTK_OBJECT(widget), event->keyval, event->state))
+			return TRUE;
+	}
 	return FALSE;
 }
 
@@ -4735,8 +4733,15 @@
 				NODE_COLUMN, &new_selection, -1);
 	}
 
-	/* we set this up as a timeout, otherwise the blist flickers */
-	g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
+	/* we set this up as a timeout, otherwise the blist flickers ...
+	 * but we don't do it for groups, because it causes total bizarness -
+	 * the previously selected buddy node might rendered at half height.
+	 */
+	if ((new_selection != NULL) && PURPLE_BLIST_NODE_IS_GROUP(new_selection)) {
+		do_selection_changed(new_selection);
+	} else {
+		g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection);
+	}
 }
 
 static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter)
@@ -5397,12 +5402,25 @@
 	gtk_container_set_border_width(GTK_CONTAINER(table), 0);
 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Screen name:"));
+	/* Set up stuff for the account box */
+	label = gtk_label_new_with_mnemonic(_("_Account:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
 
+	data->account_box = pidgin_account_option_menu_new(account, FALSE,
+			G_CALLBACK(add_buddy_select_account_cb), NULL, data);
+
+	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 0, 1);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_box);
+	pidgin_set_accessible_label (data->account_box, label);
+	/* End of account box */
+
+	label = gtk_label_new_with_mnemonic(_("_Screen name:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+
 	data->entry = gtk_entry_new();
-	gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1);
+	gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 1, 2);
 	gtk_widget_grab_focus(data->entry);
 
 	if (username != NULL)
@@ -5412,19 +5430,20 @@
 										  GTK_RESPONSE_OK, FALSE);
 
 	gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry);
 	pidgin_set_accessible_label (data->entry, label);
 
 	g_signal_connect(G_OBJECT(data->entry), "changed",
 					 G_CALLBACK(pidgin_set_sensitive_if_input),
 					 data->window);
 
-	label = gtk_label_new(_("Alias:"));
+	label = gtk_label_new_with_mnemonic(_("A_lias:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
 
 	data->entry_for_alias = gtk_entry_new();
 	gtk_table_attach_defaults(GTK_TABLE(table),
-							  data->entry_for_alias, 1, 2, 1, 2);
+							  data->entry_for_alias, 1, 2, 2, 3);
 
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias);
@@ -5433,29 +5452,19 @@
 		gtk_widget_grab_focus(GTK_WIDGET(data->entry_for_alias));
 
 	gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry_for_alias);
 	pidgin_set_accessible_label (data->entry_for_alias, label);
 
-	label = gtk_label_new(_("Group:"));
+	label = gtk_label_new_with_mnemonic(_("_Group:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
 
 	data->combo = gtk_combo_new();
 	gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree());
-	gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3);
+	gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 3, 4);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->combo)->entry);
 	pidgin_set_accessible_label (data->combo, label);
 
-	/* Set up stuff for the account box */
-	label = gtk_label_new(_("Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
-
-	data->account_box = pidgin_account_option_menu_new(account, FALSE,
-			G_CALLBACK(add_buddy_select_account_cb), NULL, data);
-
-	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4);
-	pidgin_set_accessible_label (data->account_box, label);
-	/* End of account box */
-
 	g_signal_connect(G_OBJECT(data->window), "response",
 					 G_CALLBACK(add_buddy_cb), data);
 
@@ -5761,7 +5770,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Account:"));
+	label = gtk_label_new_with_mnemonic(_("_Account:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5770,6 +5779,7 @@
 			G_CALLBACK(addchat_select_account_cb),
 			chat_account_filter_func, data);
 	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu);
 	pidgin_set_accessible_label (data->account_menu, label);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
@@ -5781,7 +5791,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Alias:"));
+	label = gtk_label_new_with_mnemonic(_("A_lias:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5791,6 +5801,7 @@
 		gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias);
 	gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0);
 	gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry);
 	pidgin_set_accessible_label (data->alias_entry, label);
 	if (name != NULL)
 		gtk_widget_grab_focus(data->alias_entry);
@@ -5798,7 +5809,7 @@
 	rowbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
 
-	label = gtk_label_new(_("Group:"));
+	label = gtk_label_new_with_mnemonic(_("_Group:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_size_group_add_widget(data->sg, label);
 	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
@@ -5812,6 +5823,7 @@
 		gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry),
 						   group->name);
 	}
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->group_combo)->entry);
 	pidgin_set_accessible_label (data->group_combo, label);
 
 	g_signal_connect(G_OBJECT(data->window), "response",
--- a/pidgin/gtkconv.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkconv.c	Mon Jun 04 23:48:54 2007 +0000
@@ -271,65 +271,7 @@
 default_formatize(PidginConversation *c)
 {
 	PurpleConversation *conv = c->active_conv;
-
-	if (conv->features & PURPLE_CONNECTION_HTML)
-	{
-		char color[8];
-		GdkColor fg_color, bg_color;
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != GTK_IMHTML(c->entry)->edit.bold)
-			gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry));
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != GTK_IMHTML(c->entry)->edit.italic)
-			gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry));
-
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != GTK_IMHTML(c->entry)->edit.underline)
-			gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry));
-
-		gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry),
-			purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
-
-		if (!(conv->features & PURPLE_CONNECTION_NO_FONTSIZE))
-		{
-			int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
-
-			/* 3 is the default. */
-			if (size != 3)
-				gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), size);
-		}
-
-		if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
-		{
-			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
-							&fg_color);
-			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
-									fg_color.red   / 256,
-									fg_color.green / 256,
-									fg_color.blue  / 256);
-		} else
-			strcpy(color, "");
-
-		gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color);
-
-		if(!(conv->features & PURPLE_CONNECTION_NO_BGCOLOR) &&
-		   strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
-		{
-			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
-							&bg_color);
-			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
-									bg_color.red   / 256,
-									bg_color.green / 256,
-									bg_color.blue  / 256);
-		} else
-			strcpy(color, "");
-
-		gtk_imhtml_toggle_background(GTK_IMHTML(c->entry), color);
-
-		if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO)
-			gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), TRUE);
-		else
-			gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), FALSE);
-	}
+	gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), conv->features);
 }
 
 static void
@@ -695,7 +637,7 @@
 				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
 		}
 		else
-			prpl_info->get_info(gc, who);
+			pidgin_retrieve_user_info(gc, who);
 	}
 }
 
@@ -706,14 +648,8 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-		PurpleNotifyUserInfo *info = purple_notify_user_info_new();
-		purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
-		purple_notify_userinfo(conv->account->gc, purple_conversation_get_name(conv), info, NULL, NULL);
-		purple_notify_user_info_destroy(info);
-
-		serv_get_info(purple_conversation_get_gc(conv),
+		pidgin_retrieve_user_info(purple_conversation_get_gc(conv),
 					  purple_conversation_get_name(conv));
-
 		gtk_widget_grab_focus(gtkconv->entry);
 	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		/* Get info of the person currently selected in the GtkTreeView */
@@ -2257,34 +2193,18 @@
 static GList *get_prpl_icon_list(PurpleAccount *account)
 {
 	GList *l = NULL;
-	GdkPixbuf *pixbuf;
-	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
-	const char *prpl = prpl_info->list_icon(account, NULL);
-	char *filename, *path;
-	l = g_hash_table_lookup(prpl_lists, prpl);
+	PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	const char *prplname = prpl_info->list_icon(account, NULL);
+	l = g_hash_table_lookup(prpl_lists, prplname);
 	if (l)
 		return l;
-	filename = g_strdup_printf("%s.png", prpl);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", filename, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-	if (pixbuf)
-		l = g_list_append(l, pixbuf);
-	g_free(path);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", filename, NULL);
-        pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-        if (pixbuf)
-                l = g_list_append(l, pixbuf);
-        g_free(path);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "48", filename, NULL);
-        pixbuf = gdk_pixbuf_new_from_file(path, NULL);
-        if (pixbuf)
-                l = g_list_append(l, pixbuf);
-        g_free(path);
-
-	g_hash_table_insert(prpl_lists, g_strdup(prpl), l);
+
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE));
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM));
+	l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL));
+
+	g_hash_table_insert(prpl_lists, g_strdup(prplname), l);
 	return l;
 }
 
@@ -2961,10 +2881,65 @@
 	gtk_widget_show_all(menu);
 }
 
+static void
+remove_from_list(GtkWidget *widget, PidginWindow *win)
+{
+	GList *list = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
+	list = g_list_remove(list, widget);
+	g_object_set_data(G_OBJECT(win->window), "plugin-actions", list);
+}
+
+static void
+regenerate_plugins_items(PidginWindow *win)
+{
+	GList *action_items;
+	GtkWidget *menu;
+	GList *list;
+	PidginConversation *gtkconv;
+	PurpleConversation *conv;
+	GtkWidget *item;
+
+	if (win->window == NULL || win == hidden_convwin)
+		return;
+
+	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
+	if (gtkconv == NULL)
+		return;
+
+	conv = gtkconv->active_conv;
+	action_items = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
+
+	/* Remove the old menuitems */
+	while (action_items) {
+		g_signal_handlers_disconnect_by_func(G_OBJECT(action_items->data),
+					G_CALLBACK(remove_from_list), win);
+		gtk_widget_destroy(action_items->data);
+		action_items = g_list_delete_link(action_items, action_items);
+	}
+
+	menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
+
+	list = purple_conversation_get_extended_menu(conv);
+	if (list) {
+		action_items = g_list_prepend(NULL, (item = pidgin_separator(menu)));
+		g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
+	}
+
+	for(; list; list = g_list_delete_link(list, list)) {
+		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
+		item = pidgin_append_menu_action(menu, act, conv);
+		action_items = g_list_prepend(action_items, item);
+		gtk_widget_show_all(item);
+		g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
+	}
+	g_object_set_data(G_OBJECT(win->window), "plugin-actions", action_items);
+}
+
 static void menubar_activated(GtkWidget *item, gpointer data)
 {
 	PidginWindow *win = data;
 	regenerate_options_items(win);
+	regenerate_plugins_items(win);
 
 	/* The following are to make sure the 'More' submenu is not regenerated every time
 	 * the focus shifts from 'Conversations' to some other menu and back. */
@@ -4164,45 +4139,17 @@
 	}
 }
 
-static GtkWidget *
-setup_chat_pane(PidginConversation *gtkconv)
-{
-	PurplePluginProtocolInfo *prpl_info;
+static void
+setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox)
+{
 	PurpleConversation *conv = gtkconv->active_conv;
-	PidginChatPane *gtkchat;
-	PurpleConnection *gc;
-	GtkWidget *vpaned, *hpaned;
-	GtkWidget *vbox, *hbox, *frame;
-	GtkWidget *imhtml_sw;
-	GtkPolicyType imhtml_sw_hscroll;
-	GtkWidget *lbox;
-	GtkWidget *label;
-	GtkWidget *list;
-	GtkWidget *sw;
-	GtkListStore *ls;
-	GtkCellRenderer *rend;
-	GtkTreeViewColumn *col;
-	void *blist_handle = purple_blist_get_handle();
-	GList *focus_chain = NULL;
-	int ul_width;
-
-	gtkchat = gtkconv->u.chat;
-	gc      = purple_conversation_get_gc(conv);
-	g_return_val_if_fail(gc != NULL, NULL);
-	g_return_val_if_fail(gc->prpl != NULL, NULL);
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-	/* Setup the outer pane. */
-	vpaned = gtk_vpaned_new();
-	gtk_widget_show(vpaned);
-
-	/* Setup the top part of the pane. */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, TRUE);
-	gtk_widget_show(vbox);
-
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 	if (prpl_info->options & OPT_PROTO_CHAT_TOPIC)
 	{
+		GtkWidget *hbox, *label;
+		PidginChatPane *gtkchat = gtkconv->u.chat;
+		
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 		gtk_widget_show(hbox);
@@ -4223,35 +4170,19 @@
 		gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0);
 		gtk_widget_show(gtkchat->topic_text);
 	}
-
-	/* Setup the horizontal pane. */
-	hpaned = gtk_hpaned_new();
-	gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
-	gtk_widget_show(hpaned);
-
-	/* Setup gtkihmtml. */
-	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
-	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), TRUE);
-	gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
-	gtk_widget_show(frame);
-	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               &imhtml_sw_hscroll, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
-
-	gtk_widget_set_size_request(gtkconv->imhtml,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"),
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"));
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
-					 G_CALLBACK(size_allocate_cb), gtkconv);
-
-	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
-						   G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
-						   G_CALLBACK(refocus_entry_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
-						   G_CALLBACK(refocus_entry_cb), gtkconv);
+}
+
+static void
+setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
+{
+	PidginChatPane *gtkchat = gtkconv->u.chat;
+	GtkWidget *lbox, *sw, *list;
+	GtkListStore *ls;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *col;
+	int ul_width;
+	void *blist_handle = purple_blist_get_handle();
+	PurpleConversation *conv = gtkconv->active_conv;
 
 	/* Build the right pane. */
 	lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -4299,9 +4230,7 @@
 			 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv);
 	g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv);
 
-
 	rend = gtk_cell_renderer_text_new();
-
 	g_object_set(rend,
 				 "foreground-set", TRUE,
 				 "weight-set", TRUE,
@@ -4332,10 +4261,72 @@
 	gtkchat->list = list;
 
 	gtk_container_add(GTK_CONTAINER(sw), list);
+}
+
+static GtkWidget *
+setup_common_pane(PidginConversation *gtkconv)
+{
+	GtkWidget *paned, *vbox, *frame, *imhtml_sw;
+	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 gtkimhtml widget */
+	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
+	if (chat) {
+		GtkWidget *hpaned;
+
+		/* Add the topic */
+		setup_chat_topic(gtkconv, vbox);
+
+		/* Add the gtkimhtml frame */
+		hpaned = gtk_hpaned_new();
+		gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
+		gtk_widget_show(hpaned);
+		gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
+
+		/* Now add the userlist */
+		setup_chat_userlist(gtkconv, hpaned);
+	} else {
+		gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+	}
+	gtk_widget_show(frame);
+
+	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
+	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
+
+	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
+	                               &imhtml_sw_hscroll, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
+	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
+
+	gtk_widget_set_size_request(gtkconv->imhtml,
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
+			chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height") :
+			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
+
+	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
+	                 G_CALLBACK(size_allocate_cb), gtkconv);
+
+	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
+	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
+	                 G_CALLBACK(refocus_entry_cb), gtkconv);
+	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(vpaned), vbox, FALSE, TRUE);
+	gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE);
 	gtk_widget_show(vbox);
 
 	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -4351,20 +4342,15 @@
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
-	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
-					 G_CALLBACK(entry_popup_menu_cb), gtkconv);
-
 	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));
+			purple_account_get_protocol_name(conv->account));
 	gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height"));
-	gtkconv->entry_buffer =
-		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
-	g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
-	g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
-                                 G_CALLBACK(resize_imhtml_cb), gtkconv);
-
+			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);
 	g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
 	                 G_CALLBACK(entry_key_press_cb), gtkconv);
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
@@ -4374,129 +4360,28 @@
 	g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
 	                 G_CALLBACK(size_allocate_cb), gtkconv);
 
-	default_formatize(gtkconv);
-
-	/*
-	 * Focus for chat windows should be as follows:
-	 * Tab title -> chat topic -> conversation scrollback -> user list ->
-	 *   user list buttons -> entry -> buttons at bottom
-	 */
-	focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
-	gtk_container_set_focus_chain(GTK_CONTAINER(vbox), focus_chain);
-
-	return vpaned;
-}
-
-static GtkWidget *
-setup_im_pane(PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-	GtkWidget *frame;
-	GtkWidget *imhtml_sw;
-	GtkPolicyType imhtml_sw_hscroll;
-	GtkWidget *paned;
-	GtkWidget *vbox;
-	GtkWidget *vbox2;
-	GList *focus_chain = NULL;
-
-	/* Setup the outer pane */
-	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 gtkimhtml widget */
-	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
-	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
-	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               &imhtml_sw_hscroll, NULL);
-	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
-	                               imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
-
-	gtk_widget_set_size_request(gtkconv->imhtml,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
-	                 G_CALLBACK(size_allocate_cb), gtkconv);
-
-	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
-	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
-	                 G_CALLBACK(refocus_entry_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
-	                 G_CALLBACK(refocus_entry_cb), gtkconv);
-
-	/* Setup the bottom half of the conversation window */
-	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, TRUE);
-	gtk_widget_show(vbox2);
-
-	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->lower_hbox, TRUE, TRUE, 0);
-	gtk_widget_show(gtkconv->lower_hbox);
-
-	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox2, TRUE, TRUE, 0);
-	gtk_widget_show(vbox2);
-
-	/* Setup the toolbar, entry widget and all signals */
-	frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
-	gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0);
-	gtk_widget_show(frame);
-
-	g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
-					 G_CALLBACK(entry_popup_menu_cb), gtkconv);
-
-	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,
-			purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
 	gtkconv->entry_buffer =
 		gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
 	g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
 
-	g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
-	                 G_CALLBACK(entry_key_press_cb), gtkconv);
-	g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
-	                       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);
-
-	g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
-	                 G_CALLBACK(insert_text_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
-	                 G_CALLBACK(delete_text_cb), gtkconv);
+	if (!chat) {
+		/* For sending typing notifications for IMs */
+		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
+						 G_CALLBACK(insert_text_cb), gtkconv);
+		g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
+						 G_CALLBACK(delete_text_cb), gtkconv);
+		gtkconv->u.im->typing_timer = 0;
+		gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
+		gtkconv->u.im->show_icon = TRUE;
+	}
+
 	g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
 				 G_CALLBACK(resize_imhtml_cb), gtkconv);
 
-	/* had to move this after the imtoolbar is attached so that the
-	 * signals get fired to toggle the buttons on the toolbar as well.
-	 */
 	default_formatize(gtkconv);
-
 	g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
-						   G_CALLBACK(clear_formatting_cb), gtkconv);
-
-	gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
-	gtkconv->u.im->show_icon = TRUE;
-
-	/*
-	 * Focus for IM windows should be as follows:
-	 * Tab title -> conversation scrollback -> entry
-	 */
-	focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
-	gtk_container_set_focus_chain(GTK_CONTAINER(vbox2), focus_chain);
-
-	gtkconv->u.im->typing_timer = 0;
+	                       G_CALLBACK(clear_formatting_cb), gtkconv);
+
 	return paned;
 }
 
@@ -4692,12 +4577,10 @@
 
 	if (conv_type == PURPLE_CONV_TYPE_IM) {
 		gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
-
-		pane = setup_im_pane(gtkconv);
 	} else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
 		gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane));
-		pane = setup_chat_pane(gtkconv);
-	}
+	}
+	pane = setup_common_pane(gtkconv);
 
 	gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
 			gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
@@ -8006,6 +7889,7 @@
 
 	generate_send_to_items(win);
 	regenerate_options_items(win);
+	regenerate_plugins_items(win);
 
 	pidgin_conv_switch_active_conversation(conv);
 
@@ -8076,6 +7960,12 @@
 	prpl_lists = g_hash_table_new(g_str_hash, g_str_equal);
 }
 
+static void
+plugin_changed_cb(PurplePlugin *p, gpointer data)
+{
+	regenerate_plugins_items(data);
+}
+
 PidginWindow *
 pidgin_conv_window_new()
 {
@@ -8147,6 +8037,13 @@
 
 	gtk_widget_show(testidea);
 
+	/* Update the plugin actions when plugins are (un)loaded */
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-load",
+			win, PURPLE_CALLBACK(plugin_changed_cb), win);
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload",
+			win, PURPLE_CALLBACK(plugin_changed_cb), win);
+
+
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(win->window), "show",
 	                 G_CALLBACK(winpidgin_ensure_onscreen), win->window);
@@ -8186,6 +8083,7 @@
 	g_object_unref(G_OBJECT(win->menu.item_factory));
 
 	purple_notify_close_with_handle(win);
+	purple_signals_disconnect_by_handle(win);
 
 	g_free(win);
 }
--- a/pidgin/gtkdialogs.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkdialogs.c	Mon Jun 04 23:48:54 2007 +0000
@@ -823,7 +823,7 @@
 		found = pidgin_dialogs_ee(username);
 
 	if (!found && username != NULL && *username != '\0' && account != NULL)
-		serv_get_info(purple_account_get_connection(account), username);
+		pidgin_retrieve_user_info(purple_account_get_connection(account), username);
 
 	g_free(username);
 }
--- a/pidgin/gtkimhtml.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkimhtml.c	Mon Jun 04 23:48:54 2007 +0000
@@ -1344,9 +1344,9 @@
 	gtk_binding_entry_add_signal (binding_set, GDK_r, GDK_CONTROL_MASK, "format_function_clear", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "message_send", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "message_send", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
-        gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
+	gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
 
 }
 
@@ -4907,3 +4907,70 @@
 	g_return_if_fail(imhtml != NULL);
 	imhtml->funcs = f;
 }
+
+void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags)
+{
+	if (flags & PURPLE_CONNECTION_HTML) {
+		char color[8];
+		GdkColor fg_color, bg_color;
+
+		gtk_imhtml_set_format_functions(imhtml, GTK_IMHTML_ALL);
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != imhtml->edit.bold)
+			gtk_imhtml_toggle_bold(imhtml);
+
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != imhtml->edit.italic)
+			gtk_imhtml_toggle_italic(imhtml);
+
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != imhtml->edit.underline)
+			gtk_imhtml_toggle_underline(imhtml);
+
+		gtk_imhtml_toggle_fontface(imhtml,
+			purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
+
+		if (!(flags & PURPLE_CONNECTION_NO_FONTSIZE))
+		{
+			int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
+
+			/* 3 is the default. */
+			if (size != 3)
+				gtk_imhtml_font_set_size(imhtml, size);
+		}
+
+		if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
+		{
+			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
+							&fg_color);
+			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
+									fg_color.red   / 256,
+									fg_color.green / 256,
+									fg_color.blue  / 256);
+		} else
+			strcpy(color, "");
+
+		gtk_imhtml_toggle_forecolor(imhtml, color);
+
+		if(!(flags & PURPLE_CONNECTION_NO_BGCOLOR) &&
+		   strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
+		{
+			gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
+							&bg_color);
+			g_snprintf(color, sizeof(color), "#%02x%02x%02x",
+									bg_color.red   / 256,
+									bg_color.green / 256,
+									bg_color.blue  / 256);
+		} else
+			strcpy(color, "");
+
+		gtk_imhtml_toggle_background(imhtml, color);
+
+		if (flags & PURPLE_CONNECTION_FORMATTING_WBFO)
+			gtk_imhtml_set_whole_buffer_formatting_only(imhtml, TRUE);
+		else
+			gtk_imhtml_set_whole_buffer_formatting_only(imhtml, FALSE);
+	} else {
+		imhtml_clear_formatting(imhtml);
+		gtk_imhtml_set_format_functions(imhtml, 0);
+	}
+}
+
+
--- a/pidgin/gtkimhtml.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkimhtml.h	Mon Jun 04 23:48:54 2007 +0000
@@ -29,6 +29,8 @@
 #include <gtk/gtkimage.h>
 #include "gtksourceundomanager.h"
 
+#include "connection.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -790,6 +792,14 @@
  */
 char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop);
 
+/**
+ * Setup formatting for an imhtml depending on the flags specified.
+ *
+ * @param imhtml  The GTK+ IM/HTML.
+ * @param flags   The connection flag which describes the allowed types of formatting.
+ */
+void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
+
 /*@}*/
 
 #ifdef __cplusplus
--- a/pidgin/gtkpounce.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkpounce.c	Mon Jun 04 23:48:54 2007 +0000
@@ -38,6 +38,7 @@
 
 #include "gtkblist.h"
 #include "gtkdialogs.h"
+#include "gtkimhtml.h"
 #include "gtkpounce.h"
 #include "pidginstock.h"
 #include "gtkutils.h"
@@ -241,7 +242,8 @@
 save_pounce_cb(GtkWidget *w, PidginPounceDialog *dialog)
 {
 	const char *name;
-	const char *message, *command, *sound, *reason;
+	const char *command, *sound, *reason;
+	char *message;
 	PurplePounceEvent events   = PURPLE_POUNCE_NONE;
 	PurplePounceOption options = PURPLE_POUNCE_OPTION_NONE;
 
@@ -290,13 +292,16 @@
 		events |= PURPLE_POUNCE_MESSAGE_RECEIVED;
 
 	/* Data fields */
-	message = gtk_entry_get_text(GTK_ENTRY(dialog->send_msg_entry));
+	message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->send_msg_entry));
 	command = gtk_entry_get_text(GTK_ENTRY(dialog->exec_cmd_entry));
 	sound   = gtk_entry_get_text(GTK_ENTRY(dialog->play_sound_entry));
 	reason  = gtk_entry_get_text(GTK_ENTRY(dialog->popup_entry));
 
 	if (*reason == '\0') reason = NULL;
-	if (*message == '\0') message = NULL;
+	if (*message == '\0') {
+		g_free(message);
+		message = NULL;
+	}
 	if (*command == '\0') command = NULL;
 	if (*sound   == '\0') sound   = NULL;
 
@@ -349,6 +354,7 @@
 		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->save_pounce)));
 
 	update_pounces();
+	g_free(message);
 
 	delete_win_cb(NULL, NULL, dialog);
 }
@@ -446,6 +452,14 @@
 	{"application/x-im-contact", 0, 1}
 };
 
+static void
+reset_send_msg_entry(PidginPounceDialog *dialog, GtkWidget *dontcare)
+{
+	PurpleAccount *account = pidgin_account_option_menu_get_selected(dialog->account_menu);
+	gtk_imhtml_setup_entry(GTK_IMHTML(dialog->send_msg_entry),
+			(account && account->gc) ? account->gc->flags : PURPLE_CONNECTION_HTML);
+}
+
 void
 pidgin_pounce_editor_show(PurpleAccount *account, const char *name,
 							PurplePounce *cur_pounce)
@@ -462,6 +476,7 @@
 	GtkSizeGroup *sg;
 	GPtrArray *sound_widgets;
 	GPtrArray *exec_widgets;
+	GtkWidget *send_msg_imhtml;
 
 	g_return_if_fail((cur_pounce != NULL) ||
 	                 (account != NULL) ||
@@ -647,7 +662,8 @@
 	dialog->play_sound
 		= gtk_check_button_new_with_mnemonic(_("P_lay a sound"));
 
-	dialog->send_msg_entry    = gtk_entry_new();
+	send_msg_imhtml = pidgin_create_imhtml(TRUE, &dialog->send_msg_entry, NULL, NULL);
+	reset_send_msg_entry(dialog, NULL);
 	dialog->exec_cmd_entry    = gtk_entry_new();
 	dialog->popup_entry       = gtk_entry_new();
 	dialog->exec_cmd_browse   = gtk_button_new_with_mnemonic(_("Brows_e..."));
@@ -655,7 +671,7 @@
 	dialog->play_sound_browse = gtk_button_new_with_mnemonic(_("Br_owse..."));
 	dialog->play_sound_test   = gtk_button_new_with_mnemonic(_("Pre_view"));
 
-	gtk_widget_set_sensitive(dialog->send_msg_entry,    FALSE);
+	gtk_widget_set_sensitive(send_msg_imhtml,           FALSE);
 	gtk_widget_set_sensitive(dialog->exec_cmd_entry,    FALSE);
 	gtk_widget_set_sensitive(dialog->popup_entry,       FALSE);
 	gtk_widget_set_sensitive(dialog->exec_cmd_browse,   FALSE);
@@ -667,8 +683,6 @@
 	gtk_size_group_add_widget(sg, dialog->open_win);
 	gtk_size_group_add_widget(sg, dialog->popup);
 	gtk_size_group_add_widget(sg, dialog->popup_entry);
-	gtk_size_group_add_widget(sg, dialog->send_msg);
-	gtk_size_group_add_widget(sg, dialog->send_msg_entry);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd_entry);
 	gtk_size_group_add_widget(sg, dialog->exec_cmd_browse);
@@ -683,23 +697,23 @@
 					 GTK_FILL, 0, 0, 0);
 	gtk_table_attach(GTK_TABLE(table), dialog->popup_entry,      1, 4, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->send_msg,         0, 1, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), dialog->send_msg,         0, 4, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->send_msg_entry,   1, 4, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), send_msg_imhtml,          0, 4, 3, 4,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd,         0, 1, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd,         0, 1, 4, 5,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_entry,   1, 2, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_entry,   1, 2, 4, 5,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_browse,   2, 3, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd_browse,  2, 3, 4, 5,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound,       0, 1, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound,       0, 1, 5, 6,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_entry, 1, 2, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_entry, 1, 2, 5, 6,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_browse, 2, 3, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_browse,2, 3, 5, 6,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test, 3, 4, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test,  3, 4, 5, 6,
 					 GTK_FILL | GTK_EXPAND, 0, 0, 0);
 
 	gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE / 2);
@@ -708,7 +722,7 @@
 	gtk_widget_show(dialog->popup);
 	gtk_widget_show(dialog->popup_entry);
 	gtk_widget_show(dialog->send_msg);
-	gtk_widget_show(dialog->send_msg_entry);
+	gtk_widget_show(send_msg_imhtml);
 	gtk_widget_show(dialog->exec_cmd);
 	gtk_widget_show(dialog->exec_cmd_entry);
 	gtk_widget_show(dialog->exec_cmd_browse);
@@ -723,7 +737,7 @@
 
 	g_signal_connect(G_OBJECT(dialog->send_msg), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive),
-					 dialog->send_msg_entry);
+					 send_msg_imhtml);
 
 	g_signal_connect(G_OBJECT(dialog->popup), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive),
@@ -759,7 +773,12 @@
 	g_object_set_data_full(G_OBJECT(dialog->window), "sound-widgets",
 				sound_widgets, (GDestroyNotify)g_ptr_array_free);
 
-	g_signal_connect(G_OBJECT(dialog->send_msg_entry), "activate",
+	g_signal_connect_swapped(G_OBJECT(dialog->send_msg_entry), "format_function_clear",
+			G_CALLBACK(reset_send_msg_entry), dialog);
+	g_signal_connect_swapped(G_OBJECT(dialog->account_menu), "changed",
+			G_CALLBACK(reset_send_msg_entry), dialog);
+
+	g_signal_connect(G_OBJECT(dialog->send_msg_entry), "message_send",
 					 G_CALLBACK(save_pounce_cb), dialog);
 	g_signal_connect(G_OBJECT(dialog->popup_entry), "activate",
 					 G_CALLBACK(save_pounce_cb), dialog);
@@ -886,7 +905,7 @@
 													  "send-message",
 													  "message")) != NULL)
 		{
-			gtk_entry_set_text(GTK_ENTRY(dialog->send_msg_entry), value);
+			gtk_imhtml_append_text(GTK_IMHTML(dialog->send_msg_entry), value, 0);
 		}
 
 		if ((value = purple_pounce_action_get_attribute(cur_pounce,
--- a/pidgin/gtkprefs.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkprefs.c	Mon Jun 04 23:48:54 2007 +0000
@@ -979,17 +979,7 @@
 
 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
 
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold"))
-		gtk_imhtml_toggle_bold(GTK_IMHTML(imhtml));
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic"))
-		gtk_imhtml_toggle_italic(GTK_IMHTML(imhtml));
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline"))
-		gtk_imhtml_toggle_underline(GTK_IMHTML(imhtml));
-
-	gtk_imhtml_font_set_size(GTK_IMHTML(imhtml), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size"));
-	gtk_imhtml_toggle_forecolor(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"));
-	gtk_imhtml_toggle_background(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"));
-	gtk_imhtml_toggle_fontface(GTK_IMHTML(imhtml), purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
+	gtk_imhtml_setup_entry(GTK_IMHTML(imhtml), PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO);
 
 	g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle",
 					 G_CALLBACK(formatting_toggle_cb), toolbar);
--- a/pidgin/gtkutils.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkutils.c	Mon Jun 04 23:48:54 2007 +0000
@@ -261,13 +261,14 @@
 		gtk_widget_show(to_toggle);
 }
 
-void pidgin_separator(GtkWidget *menu)
+GtkWidget *pidgin_separator(GtkWidget *menu)
 {
 	GtkWidget *menuitem;
 
 	menuitem = gtk_separator_menu_item_new();
 	gtk_widget_show(menuitem);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	return menuitem;
 }
 
 GtkWidget *pidgin_new_item(GtkWidget *menu, const char *str)
@@ -478,7 +479,7 @@
 }
 
 static GtkWidget *
-aop_menu_item_new(GtkSizeGroup *sg, GdkPixbuf *pixbuf, const char *lbl, gpointer per_item_data)
+aop_menu_item_new(GtkSizeGroup *sg, GdkPixbuf *pixbuf, const char *lbl, gpointer per_item_data, const char *data)
 {
 	GtkWidget *item;
 	GtkWidget *hbox;
@@ -511,6 +512,7 @@
 	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 
+	g_object_set_data(G_OBJECT (item), data, per_item_data);
 	g_object_set_data(G_OBJECT (item), "aop_per_item_data", per_item_data);
 
 	pidgin_set_accessible_label(item, label);
@@ -518,6 +520,39 @@
 	return item;
 }
 
+static GdkPixbuf *
+pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, PurpleAccount *account)
+{
+	PurplePluginProtocolInfo *prpl_info;
+	const char *protoname = NULL;
+	char buf[MAXPATHLEN];
+	char *filename = NULL;
+	GdkPixbuf *pixbuf;
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	if (prpl_info->list_icon == NULL)
+		return NULL;
+
+	protoname = prpl_info->list_icon(account, NULL);
+	if (protoname == NULL)
+		return NULL;
+
+	/*
+	 * Status icons will be themeable too, and then it will look up
+	 * protoname from the theme
+	 */
+	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
+
+	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
+				    size == PIDGIN_PRPL_ICON_SMALL ? "16" :
+				    size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48",
+				    buf, NULL);
+	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
+	return pixbuf;
+}
+
 static GtkWidget *
 aop_option_menu_new(AopMenu *aop_menu, GCallback cb, gpointer user_data)
 {
@@ -568,25 +603,6 @@
 	}
 }
 
-static GdkPixbuf *
-get_prpl_pixbuf(PurplePluginProtocolInfo *prpl_info)
-{
-	const char *proto_name;
-	GdkPixbuf *pixbuf = NULL;
-	char *filename;
-	char buf[256];
-
-	proto_name = prpl_info->list_icon(NULL, NULL);
-	g_return_val_if_fail(proto_name != NULL, NULL);
-
-	g_snprintf(buf, sizeof(buf), "%s.png", proto_name);
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", buf, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	g_free(filename);
-
-	return pixbuf;
-}
-
 static AopMenu *
 create_protocols_menu(const char *default_proto_id)
 {
@@ -618,11 +634,14 @@
 		if (gtalk_name && strcmp(gtalk_name, plugin->info->name) < 0) {
 			char *filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
 			                                  "16", "google-talk.png", NULL);
+			GtkWidget *item;
+
 			pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 			g_free(filename);
 
 			gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
-				aop_menu_item_new(sg, pixbuf, gtalk_name, "prpl-jabber"));
+				item = aop_menu_item_new(sg, pixbuf, gtalk_name, "prpl-jabber", "protocol"));
+			g_object_set_data(G_OBJECT(item), "fake", GINT_TO_POINTER(1));
 
 			if (pixbuf)
 				g_object_unref(pixbuf);
@@ -631,10 +650,10 @@
 			i++;
 		}
 
-		pixbuf = get_prpl_pixbuf(prpl_info);
+		pixbuf = pidgin_create_prpl_icon_from_prpl(plugin, PIDGIN_PRPL_ICON_SMALL, NULL);
 
 		gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
-			aop_menu_item_new(sg, pixbuf, plugin->info->name, plugin->info->id));
+			aop_menu_item_new(sg, pixbuf, plugin->info->name, plugin->info->id, "protocol"));
 
 		if (pixbuf)
 			g_object_unref(pixbuf);
@@ -686,7 +705,6 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	for (p = list, i = 0; p != NULL; p = p->next, i++) {
-		PurplePluginProtocolInfo *prpl_info = NULL;
 		PurplePlugin *plugin;
 
 		if (show_all)
@@ -704,18 +722,12 @@
 
 		plugin = purple_find_prpl(purple_account_get_protocol_id(account));
 
-		if (plugin)
-			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
-
-		/* Load the image. */
-		if (prpl_info) {
-			pixbuf = get_prpl_pixbuf(prpl_info);
-
-			if (pixbuf) {
-				if (purple_account_is_disconnected(account) && show_all &&
-						purple_connections_get_all())
-					gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
-			}
+		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+
+		if (pixbuf) {
+			if (purple_account_is_disconnected(account) && show_all &&
+					purple_connections_get_all())
+				gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
 		}
 
 		if (purple_account_get_alias(account)) {
@@ -730,7 +742,7 @@
 		}
 
 		gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
-			aop_menu_item_new(sg, pixbuf, buf, account));
+			aop_menu_item_new(sg, pixbuf, buf, account, "account"));
 
 		if (pixbuf)
 			g_object_unref(pixbuf);
@@ -899,6 +911,15 @@
 	g_free(filename);
 }
 
+void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name)
+{
+	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	purple_notify_userinfo(conn, name, info, NULL, NULL);
+	purple_notify_user_info_destroy(info);
+	serv_get_info(conn, name);
+}
+
 gboolean
 pidgin_parse_x_im_contact(const char *msg, gboolean all_accounts,
 							PurpleAccount **ret_account, char **ret_protocol,
@@ -1599,40 +1620,13 @@
 pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size)
 {
 	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info;
-	const char *protoname = NULL;
-	char buf[256]; /* TODO: We should use a define for max file length */
-	char *filename = NULL;
-	GdkPixbuf *pixbuf;
 
 	g_return_val_if_fail(account != NULL, NULL);
 
 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
 	if (prpl == NULL)
 		return NULL;
-
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-	if (prpl_info->list_icon == NULL)
-		return NULL;
-
-	protoname = prpl_info->list_icon(account, NULL);
-	if (protoname == NULL)
-		return NULL;
-
-	/*
-	 * Status icons will be themeable too, and then it will look up
-	 * protoname from the theme
-	 */
-	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
-
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols",
-				    size == PIDGIN_PRPL_ICON_SMALL ? "16" :
-				    size == PIDGIN_PRPL_ICON_MEDIUM ? "22" : "48",
-				    buf, NULL);
-	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	g_free(filename);
-
-	return pixbuf;
+	return pidgin_create_prpl_icon_from_prpl(prpl, size, account);
 }
 
 static void
@@ -1648,62 +1642,63 @@
 		callback(object, data);
 }
 
-void
+GtkWidget *
 pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
                             gpointer object)
 {
+	GtkWidget *menuitem;
+
 	if (act == NULL) {
-		pidgin_separator(menu);
-	} else {
-		GtkWidget *menuitem;
-
-		if (act->children == NULL) {
-			menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-
-			if (act->callback != NULL) {
-				g_object_set_data(G_OBJECT(menuitem),
-				                  "purplecallback",
-				                  act->callback);
-				g_object_set_data(G_OBJECT(menuitem),
-				                  "purplecallbackdata",
-				                  act->data);
-				g_signal_connect(G_OBJECT(menuitem), "activate",
-				                 G_CALLBACK(menu_action_cb),
-				                 object);
-			} else {
-				gtk_widget_set_sensitive(menuitem, FALSE);
-			}
-
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+		return pidgin_separator(menu);
+	}
+
+	if (act->children == NULL) {
+		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+
+		if (act->callback != NULL) {
+			g_object_set_data(G_OBJECT(menuitem),
+							  "purplecallback",
+							  act->callback);
+			g_object_set_data(G_OBJECT(menuitem),
+							  "purplecallbackdata",
+							  act->data);
+			g_signal_connect(G_OBJECT(menuitem), "activate",
+							 G_CALLBACK(menu_action_cb),
+							 object);
 		} else {
-			GList *l = NULL;
-			GtkWidget *submenu = NULL;
-			GtkAccelGroup *group;
-
-			menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-
-			submenu = gtk_menu_new();
-			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-
-			group = gtk_menu_get_accel_group(GTK_MENU(menu));
-			if (group) {
-				char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
-				gtk_menu_set_accel_path(GTK_MENU(submenu), path);
-				g_free(path);
-				gtk_menu_set_accel_group(GTK_MENU(submenu), group);
-			}
-
-			for (l = act->children; l; l = l->next) {
-				PurpleMenuAction *act = (PurpleMenuAction *)l->data;
-
-				pidgin_append_menu_action(submenu, act, object);
-			}
-			g_list_free(act->children);
-			act->children = NULL;
+			gtk_widget_set_sensitive(menuitem, FALSE);
 		}
-		purple_menu_action_free(act);
+
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	} else {
+		GList *l = NULL;
+		GtkWidget *submenu = NULL;
+		GtkAccelGroup *group;
+
+		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+		submenu = gtk_menu_new();
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+
+		group = gtk_menu_get_accel_group(GTK_MENU(menu));
+		if (group) {
+			char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
+			gtk_menu_set_accel_path(GTK_MENU(submenu), path);
+			g_free(path);
+			gtk_menu_set_accel_group(GTK_MENU(submenu), group);
+		}
+
+		for (l = act->children; l; l = l->next) {
+			PurpleMenuAction *act = (PurpleMenuAction *)l->data;
+
+			pidgin_append_menu_action(submenu, act, object);
+		}
+		g_list_free(act->children);
+		act->children = NULL;
 	}
+	purple_menu_action_free(act);
+	return menuitem;
 }
 
 #if GTK_CHECK_VERSION(2,3,0)
--- a/pidgin/gtkutils.h	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/gtkutils.h	Mon Jun 04 23:48:54 2007 +0000
@@ -142,8 +142,10 @@
  * Adds a separator to a menu.
  *
  * @param menu The menu to add a separator to.
+ *
+ * @return The separator.
  */
-void pidgin_separator(GtkWidget *menu);
+GtkWidget *pidgin_separator(GtkWidget *menu);
 
 /**
  * Creates a menu item.
@@ -319,6 +321,14 @@
 void pidgin_load_accels(void);
 
 /**
+ * Get information about a user. Show immediate feedback.
+ *
+ * @param conn   The connection to get information from.
+ * @param name   The user to get information about.
+ */
+void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name);
+
+/**
  * Parses an application/x-im-contact MIME message and returns the
  * data inside.
  *
@@ -416,8 +426,10 @@
  * @param menu    The menu to append to.
  * @param act     The PurpleMenuAction to append.
  * @param gobject The object to be passed to the action callback.
+ *
+ * @return   The menuitem added.
  */
-void pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
+GtkWidget *pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
                                  gpointer gobject);
 
 /**
--- a/pidgin/plugins/cap/cap.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/plugins/cap/cap.c	Mon Jun 04 23:48:54 2007 +0000
@@ -918,7 +918,8 @@
 
 static PidginPluginUiInfo ui_info = {
 	get_config_frame,
-	0 /* page_num (reserved) */
+	0 /* page_num (reserved) */,
+	NULL,NULL,NULL,NULL
 };
 
 static PurplePluginInfo info = {
@@ -944,7 +945,8 @@
 	&ui_info,										/**< ui_info		*/
 	NULL,											/**< extra_info	 */
 	NULL,											/**< prefs_info		*/
-	NULL
+	NULL,
+	NULL,NULL,NULL,NULL
 };
 
 static GtkWidget * get_config_frame(PurplePlugin *plugin) {
--- a/pidgin/plugins/relnot.c	Sun Jun 03 23:11:57 2007 +0000
+++ b/pidgin/plugins/relnot.c	Mon Jun 04 23:48:54 2007 +0000
@@ -69,20 +69,17 @@
 
 	message = g_string_new("");
 	g_string_append_printf(message, _("You are using %s version %s.  The "
-			"current version is %s.<hr>"),
+			"current version is %s.  You can get it from "
+			"<a href=\"" PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</a><hr>"),
 			PIDGIN_NAME, purple_core_get_version(), cur_ver);
 
 	if(*changelog) {
 		formatted = purple_strdup_withhtml(changelog);
-		g_string_append_printf(message, _("<b>ChangeLog:</b>\n%s<br><br>"),
+		g_string_append_printf(message, _("<b>ChangeLog:</b><br>%s"),
 				formatted);
 		g_free(formatted);
 	}
 
-	g_string_append_printf(message, _("You can get version %s from:<br>"
-			"<a href=\"http://pidgin.im/\">"
-			"http://pidgin.im</a>."), cur_ver);
-
 	purple_notify_formatted(NULL, _("New Version Available"),
 			_("New Version Available"), NULL, message->str,
 			NULL, NULL);