propagate from branch 'im.pidgin.pidgin' (head fa3096fc03d96aab042c6f0c6d678564ba5aba7e) cpw.qulogic.msnp16

Sat, 06 Feb 2010 05:15:35 +0000

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Sat, 06 Feb 2010 05:15:35 +0000
branch
cpw.qulogic.msnp16
changeset 29455
507fafef57a4
parent 29454
75d293894d85 (current diff)
parent 29303
fa3096fc03d9 (diff)
child 29456
17817920f7a0

propagate from branch 'im.pidgin.pidgin' (head fa3096fc03d96aab042c6f0c6d678564ba5aba7e)
to branch 'im.pidgin.cpw.qulogic.msnp16' (head 75d293894d857681ce55f1cb7fa973165a497cb9)

libpurple/protocols/msn/msn.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/notification.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/state.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/state.h file | annotate | diff | comparison | revisions
libpurple/protocols/msn/switchboard.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/user.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/user.h file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Thu Feb 04 20:57:01 2010 +0000
+++ b/COPYRIGHT	Sat Feb 06 05:15:35 2010 +0000
@@ -147,6 +147,7 @@
 Marc Etcheverry
 David Everly
 Larry Ewing
+Fartash Faghri
 Gábor Farkas
 Jesse Farmer
 Gavan Fantom (gavan)
@@ -154,6 +155,7 @@
 David Fiander
 Rob Flynn <gaim@robflynn.com>
 Rob Foehl (rwf)
+Chris Foote
 Alan Ford
 Nathan Fredrickson
 Chris J. Friesen
@@ -377,6 +379,7 @@
 R. Ramkumar
 Mart Raudsepp
 Etan Reisner
+Luoh Ren-Shan
 Kristian Rietveld
 Pekka Riikonen
 Tim Ringenbach
@@ -388,6 +391,7 @@
 Luciano Miguel Ferreira Rocha
 Andrew Rodland
 Miguel Rodríguez (migrax)
+Adi Roiban
 Martin Rosinski
 Bob Rossi
 Jason Roth
--- a/ChangeLog	Thu Feb 04 20:57:01 2010 +0000
+++ b/ChangeLog	Sat Feb 06 05:15:35 2010 +0000
@@ -1,6 +1,69 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.5 (??/??/20??):
+version 2.6.6 (??/??/20??):
+	libpurple:
+	* Fix 'make check' on OS X. (David Fang)
+	* Fix a quirk in purple_markup_html_to_xhtml that caused some messages
+	  to be improperly converted to XHTML.
+	* Set "controlling-mode" correctly when initializing a media session.
+	  Fixes receiving voice calls from Psi.
+
+	General:
+	* Correctly disable all missing dependencies when using the
+	  --disable-missing-dependencies option.  (Gabriel Schulhof)
+
+	Gadu-Gadu:
+	* Fix display of avatars after a server-side change. (Krzysztof
+	  Klinikowski)
+
+	AIM:
+	* Allow setting and displaying icons between 1x1 and 100x100 pixels.
+	  Previously only icons between 48x48 and 50x50 were allowed.
+
+	MSN:
+	* File transfer requests will no longer cause a crash if you delete the
+	  file before the other side accepts.
+	* Received files will no longer hold an extra lock after completion,
+	  meaning they can be moved or deleted without complaints from your OS.
+	* Buddies who sign in from a second location will no longer cause an
+	  unnecessary chat window to open.
+	* Support setting an animated GIF as a buddy icon.
+
+	XMPP:
+	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
+	  available when built without Cyrus SASL support.
+	* When getting info on a domain-only (server) JID, show uptime
+	  (when given by the result of the "last query") and don't show status as
+	  offline.
+	* Wrap XHTML messages in <p>, as described in XEP-0071, for compatibility
+	  with some clients.
+	* Don't do an SRV lookup for a STUN server associated with the account
+	  if one is already set globally in prefs.
+	* Don't send custom smileys larger than the recommended maximum object size
+	  specified in the BoB XEP.   This prevents a client from being
+	  disconnected by servers that dislike overly-large stanzas.
+
+	Yahoo:
+	* Don't send <span> and </span> tags.  (Fartash Faghri)
+
+	Pidgin:
+	* Correctly size conversation and status box entries when the
+	  interior-focus style property is diabled. (Gabriel Schulhof)
+	* Correctly handle a multiline text field being required in a
+	  request form.  (Thanks to Florian Zeitz for finding this problem)
+	* Search friends by email-addresses in the buddy list. (Luoh Ren-Shan)
+
+	Finch:
+	* Rebindable 'move-first' and 'move-last' actions for tree widgets. So
+	  it is possible to jump to the first or last entry in the buddy list
+	  (and other such lists) by pressing home or end key (defaults)
+	  respectively.
+
+version 2.6.5 (01/08/2010):
+	libpurple:
+	* TLS certificates are actually stored to the local cache once again
+	  (accepting a name mismatch on a certificate should now be remembered)
+
 	General:
 	* Build-time fixes for Solaris.  (Paul Townsend)
 
@@ -9,17 +72,10 @@
 	  Chinese characters (broken in 2.6.4)
 
 	MSN:
-	* File transfer requests will no longer cause a crash if you delete the file
-	  before the other side accepts.
-	* Recieved files will no longer hold an extra lock after completion, meaning
-	  they can be moved or deleted without complaints from your OS.
+	* Fix an issue allowing a remote user to download arbitrary files from
+	  a libpurple client.  (CVE-2010-0013)
 
 	XMPP:
-	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
-	  available when built without Cyrus SASL support.
-	* When getting info on a domain-only (server) JID, show uptime
-	  (when given by the result of the "last query") and don't show status as
-	  offline.
 	* Do not crash when attempting to register for a new account on Windows.
 	* Fix file transfer with clients that do not support Entity Capabilities
 	  (e.g. Spark)
--- a/ChangeLog.API	Thu Feb 04 20:57:01 2010 +0000
+++ b/ChangeLog.API	Sat Feb 06 05:15:35 2010 +0000
@@ -1,6 +1,22 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.5 (??/??/20??):
+version 2.6.6 (??/??/2010):
+	libpurple:
+		Changed:
+		* purple_xfer_cancel_local is now called instead of
+		  purple_xfer_request_denied if an error is found when selecting
+		  a file to send. Request denied is still used when a receive
+		  request is not allowed.
+	Perl:
+		Changed:
+		* Corrected the package names for the PurpleProxyType and
+		  PurpleLogReadFlags enums to have the correct number of colons
+		  (from Purple::ProxyType::::<type> to Purple::ProxyType::<type>
+		  and Purple::Log:ReadFlags::::<type> to
+		  Purple::Log::ReadFlags::<type>)  (Chris Foote)
+
+version 2.6.5 (01/08/2010):
+	No changes
 
 version 2.6.4 (11/29/2009):
 	No changes
--- a/ChangeLog.win32	Thu Feb 04 20:57:01 2010 +0000
+++ b/ChangeLog.win32	Sat Feb 06 05:15:35 2010 +0000
@@ -1,6 +1,10 @@
-version 2.6.5 (??/??/20??):
+
+version 2.6.6 (??/??/2010):
 	* Installer translations for: Norwegian nynorsk
 
+version 2.6.5 (01/08/2010):
+	* No changes
+
 version 2.6.4 (11/29/2009):
 	* Register URL handlers for everything that Windows knows about.  Still
 	  use the HTTP "open" handler for security reasons.
--- a/NEWS	Thu Feb 04 20:57:01 2010 +0000
+++ b/NEWS	Sat Feb 06 05:15:35 2010 +0000
@@ -2,7 +2,12 @@
 
 Our development blog is available at: http://planet.pidgin.im
 
-2.6.5 (??/??/20??):
+2.6.6 (??/??/2010):
+
+2.6.5 (01/08/2010):
+	Paul: This release fixes a pretty serious bug in the MSN code, so we're
+	releasing this build a little earlier than planned with only major
+	bugs fixed.  See the ChangeLog for details.  Enjoy!
 
 2.6.4 (11/29/2009):
 	John:  It's release time again.  Lots of bug fixes this time around, as
--- a/configure.ac	Thu Feb 04 20:57:01 2010 +0000
+++ b/configure.ac	Sat Feb 06 05:15:35 2010 +0000
@@ -46,7 +46,7 @@
 m4_define([purple_lt_current], [6])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [6])
-m4_define([purple_micro_version], [5])
+m4_define([purple_micro_version], [6])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
@@ -55,7 +55,7 @@
 m4_define([gnt_lt_current], [6])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [6])
-m4_define([gnt_micro_version], [5])
+m4_define([gnt_micro_version], [6])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -144,7 +144,7 @@
 	;;
 esac
 
-ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hu hy id it ja ka km kn ko ku lo lt mk mn ms_MY my_MM nb ne nl nn oc pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
+ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hu hy id it ja ka km kn ko ku lo lt mk mn mr ms_MY my_MM nb ne nl nn oc pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
 AM_GLIB_GNU_GETTEXT
 
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
@@ -426,6 +426,7 @@
 					X11_LIBS="$x_libpath_add"
 					X11_CFLAGS="$x_incpath_add"
 				else
+					with_x="no"
 					if test "x$force_deps" = "xyes" ; then
 						AC_MSG_ERROR([
 X11 development headers not found.
@@ -528,6 +529,7 @@
 	if test "x$enable_startup_notification" = "xyes"; then
 		PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [
 			AC_MSG_RESULT(no)
+			enable_startup_notification="no"
 			if test "x$force_deps" = "xyes" ; then
 				AC_MSG_ERROR([
 Startup notification development headers not found.
@@ -548,6 +550,7 @@
 	if test "x$enable_gtkspell" = "xyes" ; then
 		PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [
 			AC_MSG_RESULT(no)
+			enable_gtkspell="no"
 			if test "x$force_deps" = "xyes" ; then
 				AC_MSG_ERROR([
 GtkSpell development headers not found.
@@ -598,6 +601,7 @@
 	if test "x$enable_cap" = "xyes"; then
 		PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.3,,[
 			AC_MSG_RESULT(no)
+			enable_cap="no"
 			if test "x$force_deps" = "xyes" ; then
 				AC_MSG_ERROR([
 sqlite3 development headers not found.
@@ -746,6 +750,7 @@
 			[], [$GSTREAMER_LIBS])
 	], [
 		AC_MSG_RESULT(no)
+		enable_gst="no"
 		if test "x$force_deps" = "xyes" ; then
 			AC_MSG_ERROR([
 GStreamer development headers not found.
@@ -812,6 +817,10 @@
 fi
 AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farsight" != "xno")
 
+dnl #######################################################################
+dnl # Check for Internationalized Domain Name support
+dnl #######################################################################
+
 AC_ARG_ENABLE(idn,
 	[AC_HELP_STRING([--disable-idn], [compile without IDN support])],
 	[enable_idn="$enableval" force_idn=$enableval], [enable_idn="yes" force_idn=no])
@@ -822,6 +831,7 @@
 		AC_SUBST(IDN_LIBS)
 	], [
 		AC_MSG_RESULT(no)
+		enable_idn="no"
 		if test "x$force_deps" = "xyes" ; then
 			AC_MSG_ERROR([
 GNU Libidn development headers not found.
@@ -894,11 +904,14 @@
 	AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS)
 fi
 
-if test "x$enable_avahi" = "xyes" -a "x$force_deps" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then
-	AC_MSG_ERROR([
+if test "x$enable_avahi" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then
+	enable_avahi="no"
+	if test "x$force_deps" = "xyes"; then
+		AC_MSG_ERROR([
 avahi development headers not found.
 Use --disable-avahi if you do not need avahi (Bonjour) support.
 ])
+	fi
 fi
 AC_SUBST(AVAHI_CFLAGS)
 AC_SUBST(AVAHI_LIBS)
--- a/doc/finch.1.in	Thu Feb 04 20:57:01 2010 +0000
+++ b/doc/finch.1.in	Sat Feb 06 05:15:35 2010 +0000
@@ -366,6 +366,10 @@
 .br
 backspace = move-parent
 .br
+home = move-first
+.br
+end = move-last
+.br
 # Following is the default binding for the context-menu
 .br
 menu = context-menu
--- a/finch/gntidle.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/gntidle.c	Sat Feb 06 05:15:35 2010 +0000
@@ -21,6 +21,8 @@
  *
  */
 
+#include <internal.h>
+
 #include "finch.h"
 #include "gntidle.h"
 #include "gntwm.h"
--- a/finch/gntrequest.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/gntrequest.c	Sat Feb 06 05:15:35 2010 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#include <internal.h>
+
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -35,7 +37,6 @@
 #include <gnttree.h>
 
 #include "finch.h"
-#include <internal.h>
 #include "gntrequest.h"
 #include "debug.h"
 #include "util.h"
--- a/finch/gntstatus.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/gntstatus.c	Sat Feb 06 05:15:35 2010 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#include <internal.h>
+
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -34,7 +36,6 @@
 #include <gntutils.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include <notify.h>
 #include <request.h>
--- a/finch/libgnt/gntinternal.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntinternal.h	Sat Feb 06 05:15:35 2010 +0000
@@ -32,6 +32,14 @@
 # define gnt_warning g_warning
 #endif
 
+#ifndef G_GNUC_NULL_TERMINATED
+#	if defined(__GNUC__) && __GNUC__ >= 4
+#		define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#	else
+#		define G_GNUC_NULL_TERMINATED
+#	endif
+#endif
+
 extern int gnt_need_conversation_to_locale;
 extern const char *C_(const char *x);
 
--- a/finch/libgnt/gntline.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntline.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntline.h"
 
 enum
--- a/finch/libgnt/gntmenuitem.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntmenuitem.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 
--- a/finch/libgnt/gntmenuitemcheck.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntmenuitemcheck.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenuitemcheck.h"
 
 static GntMenuItemClass *parent_class = NULL;
--- a/finch/libgnt/gntprogressbar.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntprogressbar.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  **/
 
+#include "gntinternal.h"
 #include "gntprogressbar.h"
 #include "gntutils.h"
 
--- a/finch/libgnt/gntslider.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntslider.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntcolors.h"
 #include "gntkeys.h"
 #include "gntslider.h"
--- a/finch/libgnt/gnttree.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gnttree.c	Sat Feb 06 05:15:35 2010 +0000
@@ -957,6 +957,45 @@
 	return TRUE;
 }
 
+static gboolean
+move_first_action(GntBindable *bind, GList *null)
+{
+	GntTree *tree = GNT_TREE(bind);
+	GntTreeRow *row = tree->root;
+	GntTreeRow *old = tree->current;
+	if (row && !row_matches_search(row))
+		row = get_next(row);
+	if (row) {
+		tree->current = row;
+		redraw_tree(tree);
+		if (old != tree->current)
+			tree_selection_changed(tree, old, tree->current);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+move_last_action(GntBindable *bind, GList *null)
+{
+	GntTree *tree = GNT_TREE(bind);
+	GntTreeRow *old = tree->current;
+	GntTreeRow *row = tree->bottom;
+	GntTreeRow *next;
+
+	while ((next = get_next(row)))
+		row = next;
+
+	if (row) {
+		tree->current = row;
+		redraw_tree(tree);
+		if (old != tree->current)
+			tree_selection_changed(tree, old, tree->current);
+	}
+
+	return TRUE;
+}
+
 static void
 gnt_tree_set_property(GObject *obj, guint prop_id, const GValue *value,
 		GParamSpec *spec)
@@ -1076,6 +1115,10 @@
 				"/", NULL);
 	gnt_bindable_class_register_action(bindable, "end-search", end_search_action,
 				"\033", NULL);
+	gnt_bindable_class_register_action(bindable, "move-first", move_first_action,
+			GNT_KEY_HOME, NULL);
+	gnt_bindable_class_register_action(bindable, "move-last", move_last_action,
+			GNT_KEY_END, NULL);
 
 	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable);
 	GNTDEBUG;
--- a/finch/libgnt/gntutils.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntutils.c	Sat Feb 06 05:15:35 2010 +0000
@@ -373,7 +373,6 @@
 
 	xmlFreeDoc(doc);
 	xmlFreeParserCtxt(ctxt);
-	xmlCleanupParser();
 	va_end(list);
 	g_free(data);
 #endif
@@ -470,7 +469,6 @@
 		ret = TRUE;
 	}
 	xmlFreeParserCtxt(ctxt);
-	xmlCleanupParser();
 	return ret;
 #endif
 }
--- a/finch/libgnt/gntwidget.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntwidget.c	Sat Feb 06 05:15:35 2010 +0000
@@ -22,6 +22,7 @@
 
 /* Stuff brutally ripped from Gflib */
 
+#include "gntinternal.h"
 #include "gntwidget.h"
 #include "gntstyle.h"
 #include "gntmarshal.h"
--- a/finch/libgnt/gntwindow.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntwindow.c	Sat Feb 06 05:15:35 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntstyle.h"
 #include "gntwindow.h"
 
--- a/finch/libgnt/gntwm.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/gntwm.c	Sat Feb 06 05:15:35 2010 +0000
@@ -889,6 +889,13 @@
 	all = g_list_delete_link(all, list);
 	wm->cws->list = all;
 	gnt_ws_draw_taskbar(wm->cws, FALSE);
+	if (wm->cws->ordered) {
+		GntWidget *w = wm->cws->ordered->data;
+		GntNode *node = g_hash_table_lookup(wm->nodes, w);
+		top_panel(node->panel);
+		update_panels();
+		doupdate();
+	}
 }
 
 static gboolean
@@ -1106,12 +1113,20 @@
 refresh_screen(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
+	GList *iter;
 
 	endwin();
 	refresh();
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, GINT_TO_POINTER(TRUE));
 	g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
+
+	for (iter = g_list_last(wm->cws->ordered); iter; iter = iter->prev) {
+		GntWidget *w = iter->data;
+		GntNode *node = g_hash_table_lookup(wm->nodes, w);
+		top_panel(node->panel);
+	}
+
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
 	update_screen(wm);
 	curs_set(0);   /* endwin resets the cursor to normal */
--- a/finch/libgnt/wms/irssi.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/finch/libgnt/wms/irssi.c	Sat Feb 06 05:15:35 2010 +0000
@@ -33,6 +33,8 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "gntinternal.h"
+
 #include "gnt.h"
 #include "gntbox.h"
 #include "gntmenu.h"
@@ -205,11 +207,15 @@
 update_conv_window_title(GntNode *node)
 {
 	char title[256];
+	int x, y;
 	snprintf(title, sizeof(title), "%d: %s",
 			GPOINTER_TO_INT(g_object_get_data(G_OBJECT(node->me), "irssi-index")) + 1,
 			GNT_BOX(node->me)->title);
+
+	getyx(node->window, y, x);
 	wbkgdset(node->window, '\0' | COLOR_PAIR(gnt_widget_has_focus(node->me) ? GNT_COLOR_TITLE : GNT_COLOR_TITLE_D));
 	mvwaddstr(node->window, 0, 0, title);
+	wmove(node->window, y, x);
 	if (!gnt_is_refugee()) {
 		update_panels();
 		doupdate();
@@ -294,10 +300,15 @@
 
 	name = gnt_widget_get_name(widget);
 	if (name && strstr(name, "conversation-window")) {
+		int cx, cy, cw, ch;
+		gnt_widget_get_position(widget, &cx, &cy);
+		gnt_widget_get_size(widget, &cw, &ch);
 		find_window_position(irssi, widget, &hor, &vert);
 		get_xywh_for_frame(irssi, hor, vert, &x, &y, &w, &h);
-		gnt_wm_move_window(GNT_WM(irssi), widget, x, y);
-		gnt_wm_resize_window(GNT_WM(irssi), widget, w, h);
+		if (x != cx || y != cy)
+			gnt_wm_move_window(GNT_WM(irssi), widget, x, y);
+		if (w != cw || h != ch)
+			gnt_wm_resize_window(GNT_WM(irssi), widget, w, h);
 	}
 }
 
--- a/libpurple/account.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/account.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1341,7 +1341,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->auth_cb(info->userdata);
+	if (info->auth_cb != NULL)
+		info->auth_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-granted", info->account, info->user);
@@ -1356,7 +1357,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->deny_cb(info->userdata);
+	if (info->deny_cb != NULL)
+		info->deny_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-denied", info->account, info->user);
@@ -1383,10 +1385,12 @@
 				"account-authorization-requested", account, remote_user));
 
 	if (plugin_return > 0) {
-		auth_cb(user_data);
+		if (auth_cb != NULL)
+			auth_cb(user_data);
 		return NULL;
 	} else if (plugin_return < 0) {
-		deny_cb(user_data);
+		if (deny_cb != NULL)
+			deny_cb(user_data);
 		return NULL;
 	}
 
@@ -2311,7 +2315,7 @@
 
 	gc = purple_account_get_connection(account);
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2328,7 +2332,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2367,7 +2371,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2384,7 +2388,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2412,7 +2416,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2432,7 +2436,7 @@
 	purple_account_set_password(account, new_pw);
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
--- a/libpurple/buddyicon.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/buddyicon.c	Sat Feb 06 05:15:35 2010 +0000
@@ -716,9 +716,8 @@
 	if (read_icon_file(path, &data, &len))
 	{
 		g_free(path);
-		img = purple_buddy_icon_data_new(data, len, account_icon_file);
-		g_hash_table_insert(pointer_icon_cache, account, img);
-		return img;
+		img = purple_buddy_icons_set_account_icon(account, data, len);
+		return purple_imgstore_ref(img);
 	}
 	g_free(path);
 
@@ -841,9 +840,8 @@
 	if (read_icon_file(path, &data, &len))
 	{
 		g_free(path);
-		img = purple_buddy_icon_data_new(data, len, custom_icon_file);
-		g_hash_table_insert(pointer_icon_cache, node, img);
-		return img;
+		img = purple_buddy_icons_node_set_custom_icon(node, data, len);
+		return purple_imgstore_ref(img);
 	}
 	g_free(path);
 
@@ -1302,6 +1300,9 @@
 	g_hash_table_destroy(pointer_icon_cache);
 	g_free(old_icons_dir);
 	g_free(cache_dir);
+
+	cache_dir = NULL;
+	old_icons_dir = NULL;
 }
 
 void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height)
--- a/libpurple/certificate.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/certificate.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1431,9 +1431,8 @@
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,
 						 "tls_peers");
 	if (tls_peers) {
-		if (!purple_certificate_pool_contains(tls_peers, vrq->subject_name) &&
-		        !purple_certificate_pool_store(tls_peers,vrq->subject_name,
-		                                       peer_crt)) {
+		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
+		                                   peer_crt)) {
 			purple_debug_error("certificate/x509/tls_cached",
 			                   "FAILED to cache peer certificate\n");
 		}
--- a/libpurple/conversation.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/conversation.h	Sat Feb 06 05:15:35 2010 +0000
@@ -368,7 +368,8 @@
  * @param type    The type of conversation.
  * @param account The account opening the conversation window on the purple
  *                user's end.
- * @param name    The name of the conversation.
+ * @param name    The name of the conversation.  For PURPLE_CONV_TYPE_IM,
+ *                this is the name of the buddy.
  *
  * @return The new conversation.
  */
--- a/libpurple/dbus-server.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/dbus-server.c	Sat Feb 06 05:15:35 2010 +0000
@@ -601,7 +601,6 @@
 {
 	static DBusObjectPathVTable vtable = {NULL, &purple_dbus_dispatch, NULL, NULL, NULL, NULL};
 	DBusError error;
-	int result;
 
 	dbus_error_init(&error);
 	purple_dbus_connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
@@ -625,16 +624,15 @@
 		return;
 	}
 
-	dbus_request_name_reply =
-	result = dbus_bus_request_name(purple_dbus_connection,
+	dbus_request_name_reply = dbus_bus_request_name(purple_dbus_connection,
 			DBUS_SERVICE_PURPLE, 0, &error);
 
 	if (dbus_error_is_set(&error))
 	{
 		dbus_connection_unref(purple_dbus_connection);
-		dbus_error_free(&error);
 		purple_dbus_connection = NULL;
 		init_error = g_strdup_printf(N_("Failed to get serv name: %s"), error.name);
+		dbus_error_free(&error);
 		return;
 	}
 
--- a/libpurple/dnssrv.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/dnssrv.c	Sat Feb 06 05:15:35 2010 +0000
@@ -366,7 +366,7 @@
 		cp += 6;
 
 		GETSHORT(dlen,cp);
-		if (query.type == T_SRV) {
+		if (type == T_SRV) {
 			GETSHORT(pref,cp);
 
 			GETSHORT(weight,cp);
@@ -386,7 +386,7 @@
 			srvres->weight = weight;
 
 			ret = g_list_prepend(ret, srvres);
-		} else if (query.type == T_TXT) {
+		} else if (type == T_TXT) {
 			txtres = g_new0(PurpleTxtResponse, 1);
 			txtres->content = g_strndup((gchar*)(++cp), dlen-1);
 			ret = g_list_append(ret, txtres);
--- a/libpurple/ft.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/ft.c	Sat Feb 06 05:15:35 2010 +0000
@@ -69,6 +69,30 @@
 	g_free(priv);
 }
 
+static const gchar *
+purple_xfer_status_type_to_string(PurpleXferStatusType type)
+{
+	static const struct {
+		PurpleXferStatusType type;
+		const char *name;
+	} type_names[] = {
+		{ PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
+		{ PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
+		{ PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
+		{ PURPLE_XFER_STATUS_STARTED, "started" },
+		{ PURPLE_XFER_STATUS_DONE, "done" },
+		{ PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
+		{ PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
+	};
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
+		if (type_names[i].type == type)
+			return type_names[i].name;
+
+	return "invalid state";
+}
+
 GList *
 purple_xfers_get_all()
 {
@@ -109,6 +133,10 @@
 		ui_ops->new_xfer(xfer);
 
 	xfers = g_list_prepend(xfers, xfer);
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "new %p [%d]\n", xfer, xfer->ref);
+
 	return xfer;
 }
 
@@ -119,6 +147,9 @@
 
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "destroyed %p [%d]\n", xfer, xfer->ref);
+
 	/* Close the file browser, if it's open */
 	purple_request_close_with_handle(xfer);
 
@@ -148,6 +179,9 @@
 	g_return_if_fail(xfer != NULL);
 
 	xfer->ref++;
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "ref'd %p [%d]\n", xfer, xfer->ref);
 }
 
 void
@@ -158,6 +192,9 @@
 
 	xfer->ref--;
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "unref'd %p [%d]\n", xfer, xfer->ref);
+
 	if (xfer->ref == 0)
 		purple_xfer_destroy(xfer);
 }
@@ -167,6 +204,11 @@
 {
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
+				xfer, purple_xfer_status_type_to_string(xfer->status),
+				purple_xfer_status_type_to_string(status));
+
 	if (xfer->status == status)
 		return;
 
@@ -268,14 +310,16 @@
 purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
 {
 	PurpleXfer *xfer;
+	PurpleXferType type;
 	struct stat st;
 	gchar *dir;
 
 	xfer = (PurpleXfer *)user_data;
+	type = purple_xfer_get_type(xfer);
 
 	if (g_stat(filename, &st) != 0) {
 		/* File not found. */
-		if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+		if (type == PURPLE_XFER_RECEIVE) {
 #ifndef _WIN32
 			int mode = W_OK;
 #else
@@ -297,29 +341,26 @@
 		}
 		else {
 			purple_xfer_show_file_error(xfer, filename);
-			purple_xfer_request_denied(xfer);
+			purple_xfer_cancel_local(xfer);
 		}
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 (st.st_size == 0)) {
+	else if ((type == PURPLE_XFER_SEND) && (st.st_size == 0)) {
 
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a file of 0 bytes."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_SEND) && S_ISDIR(st.st_mode)) {
 		/*
 		 * XXX - Sending a directory should be valid for some protocols.
 		 */
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a directory."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_RECEIVE) && S_ISDIR(st.st_mode)) {
 		char *msg, *utf8;
 		utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
 		msg = g_strdup_printf(
@@ -329,6 +370,23 @@
 		g_free(msg);
 		purple_xfer_request_denied(xfer);
 	}
+	else if (type == PURPLE_XFER_SEND) {
+#ifndef _WIN32
+		int mode = R_OK;
+#else
+		int mode = F_OK;
+#endif
+
+		if (g_access(filename, mode) == 0) {
+			purple_xfer_request_accepted(xfer, filename);
+		} else {
+			purple_xfer_ref(xfer);
+			purple_notify_message(
+				NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
+				_("File is not readable."), NULL,
+				(PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
+		}
+	}
 	else {
 		purple_xfer_request_accepted(xfer, filename);
 	}
@@ -342,7 +400,11 @@
 	PurpleXfer *xfer = (PurpleXfer *)user_data;
 
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
-	purple_xfer_request_denied(xfer);
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+		purple_xfer_cancel_local(xfer);
+	else
+		purple_xfer_request_denied(xfer);
+	purple_xfer_unref(xfer);
 }
 
 static int
@@ -506,6 +568,8 @@
 	type = purple_xfer_get_type(xfer);
 	account = purple_xfer_get_account(xfer);
 
+	purple_debug_misc("xfer", "request accepted for %p\n", xfer); 
+
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
 		xfer->ops.init(xfer);
@@ -583,6 +647,8 @@
 {
 	g_return_if_fail(xfer != NULL);
 
+	purple_debug_misc("xfer", "xfer %p denied\n", xfer);
+
 	if (xfer->ops.request_denied != NULL)
 		xfer->ops.request_denied(xfer);
 
@@ -1024,17 +1090,22 @@
 		if (ui_ops && ui_ops->ui_read) {
 			gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
 			if (tmp == 0) {
+				PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+
 				/*
-				 * UI isn't ready to send data. It will call
-				 * purple_xfer_ui_ready when ready, which sets back up this
-				 * watcher.
+				 * The UI claimed it was ready, but didn't have any data for
+				 * us...  It will call purple_xfer_ui_ready when ready, which
+				 * sets back up this watcher.
 				 */
 				if (xfer->watcher != 0) {
 					purple_input_remove(xfer->watcher);
 					xfer->watcher = 0;
 				}
 
-				return;
+				/* Need to indicate the prpl is still ready... */
+				priv->ready |= PURPLE_XFER_READY_PRPL;
+
+				g_return_if_reached();
 			} else if (tmp < 0) {
 				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
 				purple_xfer_cancel_local(xfer);
@@ -1111,8 +1182,12 @@
 
 			purple_input_remove(xfer->watcher);
 			xfer->watcher = 0;
+
+			purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
 			return;
 		}
+
+		priv->ready = PURPLE_XFER_READY_NONE;
 	}
 
 	do_transfer(xfer);
@@ -1173,8 +1248,12 @@
 	priv = g_hash_table_lookup(xfers_data, xfer);
 	priv->ready |= PURPLE_XFER_READY_UI;
 
-	if (0 == (priv->ready & PURPLE_XFER_READY_PRPL))
+	if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) {
+		purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer);
 		return;
+	}
+
+	purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer);
 
 	type = purple_xfer_get_type(xfer);
 	if (type == PURPLE_XFER_SEND)
@@ -1201,8 +1280,12 @@
 	priv->ready |= PURPLE_XFER_READY_PRPL;
 
 	/* I don't think fwrite/fread are ever *not* ready */
-	if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI))
+	if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+		purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
 		return;
+	}
+
+	purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer);
 
 	priv->ready = PURPLE_XFER_READY_NONE;
 
--- a/libpurple/media.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/media.c	Sat Feb 06 05:15:35 2010 +0000
@@ -2754,15 +2754,21 @@
 		FsStream *fsstream = NULL;
 		const gchar *stun_ip = purple_network_get_stun_ip();
 		const gchar *turn_ip = purple_network_get_turn_ip();
-
-		if (stun_ip || turn_ip) {
-			guint new_num_params = 
+		guint new_num_params =
+					!stun_ip && !turn_ip ? num_params + 1 :
 					(stun_ip && is_nice) && turn_ip ?
-					num_params + 2 : num_params + 1;
-			guint next_param_index = num_params;
-			GParameter *param = g_new0(GParameter, new_num_params);
-			memcpy(param, params, sizeof(GParameter) * num_params);
-
+					num_params + 3 : num_params + 2;
+		guint next_param_index = num_params;
+		GParameter *param = g_new0(GParameter, new_num_params);
+		memcpy(param, params, sizeof(GParameter) * num_params);
+
+		/* set controlling mode according to direction */
+		param[next_param_index].name = "controlling-mode";
+		g_value_init(&param[next_param_index].value, G_TYPE_BOOLEAN);
+		g_value_set_boolean(&param[next_param_index].value, initiator);
+		next_param_index++;
+		
+		if (stun_ip || turn_ip) {
 			if (stun_ip) {
 				purple_debug_info("media", 
 					"setting property stun-ip on new stream: %s\n", stun_ip);
@@ -2813,20 +2819,14 @@
 					return FALSE;
 				}
 			}
-
-			fsstream = fs_session_new_stream(session->session,
+		}
+
+		fsstream = fs_session_new_stream(session->session,
 					participant, initiator == TRUE ?
 					type_direction : (type_direction &
 					FS_DIRECTION_RECV), transmitter,
 					new_num_params, param, &err);
-			g_free(param);
-		} else {
-			fsstream = fs_session_new_stream(session->session,
-					participant, initiator == TRUE ?
-					type_direction : (type_direction &
-					FS_DIRECTION_RECV), transmitter,
-					num_params, params, &err);
-		}
+		g_free(param);
 
 		if (fsstream == NULL) {
 			purple_debug_error("media",
--- a/libpurple/media.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/media.h	Sat Feb 06 05:15:35 2010 +0000
@@ -75,7 +75,7 @@
 	PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3,
 	PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4,
 	PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5,
-	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6,
+	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6
 } PurpleMediaCaps;
 
 /** Media session types */
@@ -93,7 +93,7 @@
 typedef enum {
 	PURPLE_MEDIA_STATE_NEW = 0,
 	PURPLE_MEDIA_STATE_CONNECTED,
-	PURPLE_MEDIA_STATE_END,
+	PURPLE_MEDIA_STATE_END
 } PurpleMediaState;
 
 /** Media info types */
@@ -106,7 +106,7 @@
 	PURPLE_MEDIA_INFO_PAUSE,
 	PURPLE_MEDIA_INFO_UNPAUSE,
 	PURPLE_MEDIA_INFO_HOLD,
-	PURPLE_MEDIA_INFO_UNHOLD,
+	PURPLE_MEDIA_INFO_UNHOLD
 } PurpleMediaInfoType;
 
 typedef enum {
@@ -114,18 +114,18 @@
 	PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
 	PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
 	PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
-	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
+	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST
 } PurpleMediaCandidateType;
 
 typedef enum {
 	PURPLE_MEDIA_COMPONENT_NONE = 0,
 	PURPLE_MEDIA_COMPONENT_RTP = 1,
-	PURPLE_MEDIA_COMPONENT_RTCP = 2,
+	PURPLE_MEDIA_COMPONENT_RTCP = 2
 } PurpleMediaComponentType;
 
 typedef enum {
 	PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
 } PurpleMediaNetworkProtocol;
 
 #include "signals.h"
--- a/libpurple/network.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/network.h	Sat Feb 06 05:15:35 2010 +0000
@@ -252,7 +252,7 @@
 void purple_network_set_turn_server(const gchar *turn_server);
 	
 /**
- * Get the IP address of the STUN server as a string representation
+ * Get the IP address of the TURN server as a string representation
  *
  * @return the IP address
  * @since 2.6.0
--- a/libpurple/plugins/perl/common/Log.xs	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/plugins/perl/common/Log.xs	Sat Feb 06 05:15:35 2010 +0000
@@ -6,7 +6,7 @@
 BOOT:
 {
 	HV *type_stash = gv_stashpv("Purple::Log::Type", 1);
-	HV *flags_stash = gv_stashpv("Purple::Log:ReadFlags::", 1);
+	HV *flags_stash = gv_stashpv("Purple::Log::ReadFlags", 1);
 
 	static const constiv *civ, type_const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_LOG_##name}
--- a/libpurple/plugins/perl/common/Proxy.xs	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/plugins/perl/common/Proxy.xs	Sat Feb 06 05:15:35 2010 +0000
@@ -5,7 +5,7 @@
 
 BOOT:
 {
-	HV *stash = gv_stashpv("Purple::ProxyType::", 1);
+	HV *stash = gv_stashpv("Purple::ProxyType", 1);
 
 	static const constiv *civ, const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_PROXY_##name}
--- a/libpurple/plugins/signals-test.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/plugins/signals-test.c	Sat Feb 06 05:15:35 2010 +0000
@@ -547,6 +547,26 @@
 	purple_debug_misc("signals test", "quitting ()\n");
 }
 
+static void
+printhash(gpointer key, gpointer value, gpointer data)
+{
+	char *a = (char *)key;
+	char *b = (char *)value;
+	GString *str = (GString *)data;
+	g_string_append_printf(str, "   [%s] = [%s]\n", a, b ? b : "(null)");
+}
+
+static gboolean
+uri_handler(const char *proto, const char *cmd, GHashTable *params)
+{
+	GString *str = g_string_new("\n{\n");
+	g_hash_table_foreach(params, printhash, str);
+	g_string_append_c(str, '}');
+	purple_debug_misc("signals test", "uri handler (%s, %s, %s)\n", proto, cmd, str->str);
+	g_string_free(str, TRUE);
+	return FALSE;
+}
+
 /**************************************************************************
  * File transfer signal callbacks
  **************************************************************************/
@@ -820,6 +840,8 @@
 	/* Core signals */
 	purple_signal_connect(core_handle, "quitting",
 						plugin, PURPLE_CALLBACK(quitting_cb), NULL);
+	purple_signal_connect(core_handle, "uri-handler",
+						plugin,	PURPLE_CALLBACK(uri_handler), NULL);
 
 	/* File transfer signals */
 	purple_signal_connect(ft_handle, "file-recv-accept",
--- a/libpurple/prefs.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/prefs.h	Sat Feb 06 05:15:35 2010 +0000
@@ -185,7 +185,16 @@
  *
  * @param name  The name of the pref
  * @param value The value to set
+ *
+ * @deprecated We're not really sure what purpose this function serves, so it
+ *             will be removed in 3.0.0.  Preferences values set using this
+ *             function aren't serialized to prefs.xml, which could be
+ *             misleading.  There is also no purple_prefs_get_generic, which
+ *             means that if you can't really get the value (other in a
+ *             connected callback).  If you think you have a use for this then
+ *             please let us know.
  */
+/* TODO: When this is removed, also remove struct purple_pref->value.generic */
 void purple_prefs_set_generic(const char *name, gpointer value);
 
 /**
--- a/libpurple/protocols/bonjour/bonjour.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Feb 06 05:15:35 2010 +0000
@@ -392,11 +392,8 @@
 		purple_notify_user_info_add_pair(user_info, _("XMPP Account"), bb->jid);
 }
 
-static void
-bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
-{
+static void bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group) {
 	PurpleBlistNodeFlags oldflags;
-	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 
 	if (buddy == NULL)
 		return;
@@ -404,13 +401,38 @@
 	oldflags = purple_blist_node_get_flags((PurpleBlistNode *)buddy);
 
 	/* If we're moving them out of the bonjour group, make them persistent */
-	if (strcmp(new_group, BONJOUR_GROUP_NAME) == 0)
+	if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags | PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 	else
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags ^ PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 
 }
 
+static void
+bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
+{
+	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+
+	bonjour_do_group_change(buddy, new_group);
+
+}
+
+static void
+bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleGroup *group, GList *moved_buddies)
+{
+	GList *cur;
+	const char *new_group;
+	PurpleBuddy *buddy;
+
+	new_group = purple_group_get_name(group);
+
+	for (cur = moved_buddies; cur; cur = cur->next) {
+		buddy = cur->data;
+		bonjour_do_group_change(buddy, new_group);
+	}
+
+}
+
 static gboolean
 bonjour_can_receive_file(PurpleConnection *connection, const char *who)
 {
@@ -478,7 +500,7 @@
 	NULL,                                                    /* get_cb_away */
 	NULL,                                                    /* alias_buddy */
 	bonjour_group_buddy,                                     /* group_buddy */
-	NULL,                                                    /* rename_group */
+	bonjour_rename_group,                                    /* rename_group */
 	NULL,                                                    /* buddy_free */
 	bonjour_convo_closed,                                    /* convo_closed */
 	NULL,                                                    /* normalize */
--- a/libpurple/protocols/gg/gg.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/gg/gg.c	Sat Feb 06 05:15:35 2010 +0000
@@ -947,7 +947,7 @@
 		if (xmlnode_avatar == NULL)
 			goto out;
 
-		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "bigAvatar");
+		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar");
 		if (xmlnode_bigavatar == NULL)
 			goto out;
 
--- a/libpurple/protocols/irc/msgs.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/irc/msgs.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1069,7 +1069,7 @@
 
 	nick = irc_mask_nick(from);
 	if (!purple_utf8_strcasecmp(nick, purple_connection_get_display_name(gc))) {
-		char *escaped = g_markup_escape_text(args[1], -1);
+		char *escaped = args[1] ? g_markup_escape_text(args[1], -1) : NULL;
 		msg = g_strdup_printf(_("You have parted the channel%s%s"),
 		                      (args[1] && *args[1]) ? ": " : "",
 		                      (escaped && *escaped) ? escaped : "");
--- a/libpurple/protocols/jabber/auth.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/auth.c	Sat Feb 06 05:15:35 2010 +0000
@@ -485,9 +485,12 @@
 		xmlnode *stanza = NULL;
 		JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg);
 
-		if (state != JABBER_SASL_STATE_FAIL && stanza) {
-			jabber_send(js, stanza);
-			xmlnode_free(stanza);
+		if (state != JABBER_SASL_STATE_FAIL) {
+			if (stanza) {
+				jabber_send(js, stanza);
+				xmlnode_free(stanza);
+			}
+
 			return;
 		}
 	}
--- a/libpurple/protocols/jabber/buddy.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sat Feb 06 05:15:35 2010 +0000
@@ -823,7 +823,8 @@
 			const gchar *title = NULL;
 			if (is_domain) {
 				title = _("Uptime");
-				message = g_strdup_printf(_("%s"), last);
+				message = last;
+				last = NULL;
 			} else {
 				title = _("Logged Off");
 				message = g_strdup_printf(_("%s ago"), last);
--- a/libpurple/protocols/jabber/data.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/data.h	Sat Feb 06 05:15:35 2010 +0000
@@ -26,6 +26,8 @@
 
 #include <glib.h>
 
+#define JABBER_DATA_MAX_SIZE 8192
+
 typedef struct {
 	char *cid;
 	char *type;
--- a/libpurple/protocols/jabber/disco.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sat Feb 06 05:15:35 2010 +0000
@@ -22,6 +22,7 @@
  */
 
 #include "internal.h"
+#include "network.h"
 #include "prefs.h"
 #include "debug.h"
 #include "request.h"
@@ -534,8 +535,12 @@
 			js->googletalk = TRUE;
 
 			/* autodiscover stun and relays */
-			jabber_google_send_jingle_info(js);
-		} else {
+			if (purple_network_get_stun_ip() == NULL ||
+		    	purple_strequal(purple_network_get_stun_ip(), "")) {
+				jabber_google_send_jingle_info(js);
+			}
+		} else if (purple_network_get_stun_ip() == NULL ||
+		    purple_strequal(purple_network_get_stun_ip(), "")) {
 			js->srv_query_data = 
 				purple_srv_resolve("stun", "udp", js->user->domain,
 					jabber_disco_stun_srv_resolve_cb, js);
--- a/libpurple/protocols/jabber/jabber.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Feb 06 05:15:35 2010 +0000
@@ -356,11 +356,20 @@
 	}
 
 	if (ret < 0 && errno != EAGAIN) {
-		gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(js->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
+		PurpleAccount *account = purple_connection_get_account(js->gc);
+		/*
+		 * The server may have closed the socket (on a stream error), so if
+		 * we're disconnecting, don't generate (possibly another) error that
+		 * (for some UIs) would mask the first.
+		 */
+		if (!account->disconnecting) {
+			gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
+					g_strerror(errno));
+			purple_connection_error_reason(js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+			g_free(tmp);
+		}
+
 		success = FALSE;
 	} else if (ret < len) {
 		if (ret < 0)
@@ -418,13 +427,12 @@
 		g_free(text);
 	}
 
-	/* If we've got a security layer, we need to encode the data,
-	 * splitting it on the maximum buffer length negotiated */
-
 	purple_signal_emit(purple_connection_get_prpl(js->gc), "jabber-sending-text", js->gc, &data);
 	if (data == NULL)
 		return;
 
+	/* If we've got a security layer, we need to encode the data,
+	 * splitting it on the maximum buffer length negotiated */
 #ifdef HAVE_CYRUS_SASL
 	if (js->sasl_maxbuf>0) {
 		int pos = 0;
@@ -639,10 +647,11 @@
 	js->srv_query_data = NULL;
 
 	if (responses == NULL) {
+		purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
+				  "methods after failing to connect directly.\n");
 		purple_connection_error_reason(js->gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Unable to find alternative XMPP connection "
-				  "methods after failing to connect directly."));
+				_("Unable to connect"));
 		return;
 	}
 
@@ -677,14 +686,14 @@
 jabber_login_callback(gpointer data, gint source, const gchar *error)
 {
 	PurpleConnection *gc = data;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	if (source < 0) {
 		if (js->srv_rec != NULL) {
-			purple_debug_error("jabber", "Unable to connect to server: %s.  Trying next SRV record.\n", error);
+			purple_debug_error("jabber", "Unable to connect to server: %s.  Trying next SRV record or connecting directly.\n", error);
 			try_srv_connect(js);
 		} else {
-			purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
+			purple_debug_info("jabber","Couldn't connect directly to %s.  Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
 			js->srv_query_data = purple_txt_resolve("_xmppconnect",
 					js->user->domain, txt_resolved_cb, js);
 		}
@@ -1434,30 +1443,21 @@
  */
 void jabber_close(PurpleConnection *gc)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	/* Close all of the open Jingle sessions on this stream */
 	jingle_terminate_sessions(js);
 
-	/* Don't perform any actions on the ssl connection
-	 * if we were forcibly disconnected because it will crash
-	 * on some SSL backends.
-	 */
-	if (!gc->disconnect_timeout) {
-		if (js->bosh)
-			jabber_bosh_connection_close(js->bosh);
-		else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
-			jabber_send_raw(js, "</stream:stream>", -1);
-	}
+	if (js->bosh)
+		jabber_bosh_connection_close(js->bosh);
+	else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
+		jabber_send_raw(js, "</stream:stream>", -1);
 
 	if (js->srv_query_data)
 		purple_srv_cancel(js->srv_query_data);
 
 	if(js->gsc) {
-#ifdef HAVE_OPENSSL
-		if (!gc->disconnect_timeout)
-#endif
-			purple_ssl_close(js->gsc);
+		purple_ssl_close(js->gsc);
 	} else if (js->fd > 0) {
 		if(js->gc->inpa)
 			purple_input_remove(js->gc->inpa);
--- a/libpurple/protocols/jabber/libxmpp.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sat Feb 06 05:15:35 2010 +0000
@@ -431,7 +431,7 @@
 	jabber_data_init();
 	jabber_bosh_init();
 
-	#warning implement adding and retrieving own features via IPC API
+	/* TODO: Implement adding and retrieving own features via IPC API */
 
 	jabber_ibb_init();
 	jabber_si_init();
--- a/libpurple/protocols/jabber/message.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/message.c	Sat Feb 06 05:15:35 2010 +0000
@@ -862,6 +862,7 @@
 
 	for (; smileys ; smileys = g_list_delete_link(smileys, smileys)) {
 		PurpleSmiley *smiley = (PurpleSmiley *) smileys->data;
+
 		const gchar *shortcut = purple_smiley_get_shortcut(smiley);
 		const gssize len = strlen(shortcut);
 
@@ -983,36 +984,52 @@
 		if (found_smileys) {
 			gchar *smileyfied_xhtml = NULL;
 			const GList *iterator;
-
+			GList *valid_smileys = NULL;
+			gboolean has_too_large_smiley = FALSE;
+			
 			for (iterator = found_smileys; iterator ;
 				iterator = g_list_next(iterator)) {
-				const PurpleSmiley *smiley =
-						(PurpleSmiley *) iterator->data;
+				PurpleSmiley *smiley = (PurpleSmiley *) iterator->data;
 				const gchar *shortcut = purple_smiley_get_shortcut(smiley);
 				const JabberData *data =
 					jabber_data_find_local_by_alt(shortcut);
+				PurpleStoredImage *image = purple_smiley_get_stored_image(smiley);
 
-				/* the object has not been sent before */
-				if (!data) {
-					PurpleStoredImage *image =
-							purple_smiley_get_stored_image(smiley);
-					const gchar *ext = purple_imgstore_get_extension(image);
-					JabberStream *js = jm->js;
+				if (purple_imgstore_get_size(image) <= JABBER_DATA_MAX_SIZE) {
+					/* the object has not been sent before */
+					if (!data) {
+						const gchar *ext = purple_imgstore_get_extension(image);
+						JabberStream *js = jm->js;
 
-					JabberData *new_data =
-						jabber_data_create_from_data(purple_imgstore_get_data(image),
-							purple_imgstore_get_size(image),
-							jabber_message_get_mimetype_from_ext(ext), js);
-					purple_debug_info("jabber",
-						"cache local smiley alt = %s, cid = %s\n",
-						shortcut, jabber_data_get_cid(new_data));
-					jabber_data_associate_local(new_data, shortcut);
-				}
+						JabberData *new_data =
+							jabber_data_create_from_data(purple_imgstore_get_data(image),
+								purple_imgstore_get_size(image),
+								jabber_message_get_mimetype_from_ext(ext), js);
+						purple_debug_info("jabber",
+							"cache local smiley alt = %s, cid = %s\n",
+							shortcut, jabber_data_get_cid(new_data));
+						jabber_data_associate_local(new_data, shortcut);
+					}
+					valid_smileys = g_list_append(valid_smileys, smiley);
+				} else {
+					has_too_large_smiley = TRUE;
+					purple_debug_warning("jabber", "Refusing to send smiley %s "
+							"(too large, max is %d)\n",
+							purple_smiley_get_shortcut(smiley),
+							JABBER_DATA_MAX_SIZE);
+				}				
+			}
+
+			if (has_too_large_smiley) {
+				purple_conversation_write(conv, NULL,
+				    _("A custom smiley in the message is too large to send."),
+					PURPLE_MESSAGE_ERROR, time(NULL));
 			}
 
 			smileyfied_xhtml =
-				jabber_message_get_smileyfied_xhtml(xhtml, found_smileys);
+				jabber_message_get_smileyfied_xhtml(xhtml, valid_smileys);
 			g_list_free(found_smileys);
+			g_list_free(valid_smileys);
 
 			return smileyfied_xhtml;
 		}
@@ -1193,6 +1210,7 @@
 	tmp = purple_utf8_strip_unprintables(msg);
 	purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body);
 	g_free(tmp);
+
 	tmp = jabber_message_smileyfy_xhtml(jm, xhtml);
 	if (tmp) {
 		g_free(xhtml);
@@ -1206,7 +1224,8 @@
 	if (!jbr || !jbr->caps.info ||
 			jabber_resource_has_capability(jbr, NS_XHTML_IM)) {
 		if (!jabber_xhtml_plain_equal(xhtml, jm->body))
-			jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'>%s</body></html>", xhtml);
+			/* Wrap the message in <p/> for great interoperability justice. */
+			jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml);
 	}
 
 	g_free(xhtml);
@@ -1249,7 +1268,8 @@
 	}
 
 	if (chat->xhtml && !jabber_xhtml_plain_equal(xhtml, jm->body))
-		jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'>%s</body></html>", xhtml);
+		/* Wrap the message in <p/> for greater interoperability justice. */
+		jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml);
 
 	g_free(xhtml);
 
--- a/libpurple/protocols/jabber/presence.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sat Feb 06 05:15:35 2010 +0000
@@ -790,8 +790,9 @@
 				g_free(room_jid);
 			}
 
-			jabber_buddy_track_resource(jb, jid->resource, priority, state,
+			jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state,
 					status);
+			jbr->commands_fetched = TRUE;
 
 			jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
 
--- a/libpurple/protocols/jabber/roster.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/jabber/roster.c	Sat Feb 06 05:15:35 2010 +0000
@@ -193,7 +193,9 @@
                          JabberIqType type, const char *id, xmlnode *query)
 {
 	xmlnode *item, *group;
+#if 0
 	const char *ver;
+#endif
 
 	if (!jabber_is_own_account(js, from)) {
 		purple_debug_warning("jabber", "Received bogon roster push from %s\n",
@@ -266,11 +268,13 @@
 		}
 	}
 
+#if 0
 	ver = xmlnode_get_attrib(query, "ver");
 	if (ver) {
 		 PurpleAccount *account = purple_connection_get_account(js->gc);
 		 purple_account_set_string(account, "roster_ver", ver);
 	}
+#endif
 
 	js->currently_parsing_roster_push = FALSE;
 }
--- a/libpurple/protocols/msn/contact.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/contact.c	Sat Feb 06 05:15:35 2010 +0000
@@ -406,8 +406,8 @@
 	msn_user_set_network(user, nid);
 	msn_user_set_invite_message(user, invite);
 
-	if (member_id) {
-		user->membership_id[list] = atoi(member_id);
+	if (list == MSN_LIST_PL && member_id) {
+		user->member_id_on_pending_list = atoi(member_id);
 	}
 
 	msn_got_lst_user(session, user, 1 << list, NULL);
@@ -1567,11 +1567,11 @@
 		if (user && user->networkid != MSN_NETWORK_PASSPORT)
 			member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
 			                         "EmailMember", "Email",
-			                         user->membership_id[MSN_LIST_PL]);
+			                         user->member_id_on_pending_list);
 		else
 			member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
 			                         "PassportMember", "Passport",
-			                         user->membership_id[MSN_LIST_PL]);
+			                         user->member_id_on_pending_list);
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
--- a/libpurple/protocols/msn/msn.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Feb 06 05:15:35 2010 +0000
@@ -345,17 +345,23 @@
 msn_show_set_friendly_name(PurplePluginAction *action)
 {
 	PurpleConnection *gc;
+	PurpleAccount *account;
+	char *tmp;
 
 	gc = (PurpleConnection *) action->context;
-
-	purple_request_input(gc, NULL, _("Set your friendly name."),
+	account = purple_connection_get_account(gc);
+
+	tmp = g_strdup_printf(_("Set friendly name for %s."),
+	                      purple_account_get_username(account));
+	purple_request_input(gc, _("Set your friendly name."), tmp,
 					   _("This is the name that other MSN buddies will "
 						 "see you as."),
 					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_act_id),
 					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
+					   account, NULL, NULL,
 					   gc);
+	g_free(tmp);
 }
 
 typedef struct MsnLocationData {
@@ -1655,6 +1661,8 @@
 {
 	const char *bname;
 	MsnAddReqData *data;
+	MsnSession *session;
+	MsnUser *user;
 
 	bname = purple_buddy_get_name(buddy);
 
@@ -1676,12 +1684,18 @@
 	data->buddy = buddy;
 	data->group = group;
 
-	purple_request_input(gc, NULL, _("Authorization Request Message:"),
-	                     NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
-	                     _("_OK"), G_CALLBACK(finish_auth_request),
-	                     _("_Cancel"), G_CALLBACK(cancel_auth_request),
-	                     purple_connection_get_account(gc), bname, NULL,
-	                     data);
+	session = purple_connection_get_protocol_data(gc);
+	user = msn_userlist_find_user(session->userlist, bname);
+	if (user && user->authorized) {
+		finish_auth_request(data, NULL);
+	} else {
+		purple_request_input(gc, NULL, _("Authorization Request Message:"),
+		                     NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
+		                     _("_OK"), G_CALLBACK(finish_auth_request),
+		                     _("_Cancel"), G_CALLBACK(cancel_auth_request),
+		                     purple_connection_get_account(gc), bname, NULL,
+		                     data);
+	}
 }
 
 static void
@@ -2771,7 +2785,7 @@
 	OPT_PROTO_MAIL_CHECK,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
-	{"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
+	{"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
 	msn_list_icon,			/* list_icon */
 	msn_list_emblems,		/* list_emblems */
 	msn_status_text,		/* status_text */
--- a/libpurple/protocols/msn/notification.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1173,7 +1173,7 @@
 
 	msn_user_set_object(user, msnobj);
 
-	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
+	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+');
 	msn_user_set_clientid(user, clientid);
 	msn_user_set_extcaps(user, extcaps);
 	msn_user_set_network(user, networkid);
@@ -1354,7 +1354,8 @@
 		extcaps = strtoul(extcap_str+1, NULL, 10);
 	else
 		extcaps = 0;
-	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
+
+	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+');
 
 	msn_user_set_clientid(user, clientid);
 	msn_user_set_extcaps(user, extcaps);
@@ -1709,6 +1710,63 @@
 	}
 }
 
+static void parse_currentmedia(MsnUser *user, const char *cmedia)
+{
+	char **cmedia_array;
+	int strings = 0;
+
+	if (!cmedia || cmedia[0] == '\0') {
+		purple_debug_info("msn", "No currentmedia string\n");
+		return;
+	}
+
+	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
+
+	cmedia_array = g_strsplit(cmedia, "\\0", 0);
+
+	/*
+	 * 0: Application
+	 * 1: 'Music'/'Games'/'Office'
+	 * 2: '1' if enabled, '0' if not
+	 * 3: Format (eg. {0} by {1})
+	 * 4: Title
+	 * If 'Music':
+	 *  5: Artist
+	 *  6: Album
+	 *  7: ?
+	 */
+#if GLIB_CHECK_VERSION(2,6,0)
+	strings  = g_strv_length(cmedia_array);
+#else
+	while (cmedia_array[++strings] != NULL);
+#endif
+
+	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
+		if (user->extinfo == NULL)
+			user->extinfo = g_new0(MsnUserExtendedInfo, 1);
+		else {
+			g_free(user->extinfo->media_album);
+			g_free(user->extinfo->media_artist);
+			g_free(user->extinfo->media_title);
+		}
+
+		if (!strcmp(cmedia_array[1], "Music"))
+			user->extinfo->media_type = CURRENT_MEDIA_MUSIC;
+		else if (!strcmp(cmedia_array[1], "Games"))
+			user->extinfo->media_type = CURRENT_MEDIA_GAMES;
+		else if (!strcmp(cmedia_array[1], "Office"))
+			user->extinfo->media_type = CURRENT_MEDIA_OFFICE;
+		else
+			user->extinfo->media_type = CURRENT_MEDIA_UNKNOWN;
+
+		user->extinfo->media_title = g_strdup(cmedia_array[strings == 4 ? 3 : 4]);
+		user->extinfo->media_artist = strings > 5 ? g_strdup(cmedia_array[5]) : NULL;
+		user->extinfo->media_album = strings > 6 ? g_strdup(cmedia_array[6]) : NULL;
+	}
+
+	g_strfreev(cmedia_array);
+}
+
 /*
  * Get the UBX's PSM info
  * Post it to the User status
@@ -1724,7 +1782,6 @@
 	const char *passport;
 	xmlnode *payloadNode;
 	char *psm_str, *str;
-	CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL};
 
 	session = cmdproc->session;
 	account = session->account;
@@ -1742,13 +1799,22 @@
 		return;
 	}
 
+	/* Free any existing media info for this user */
+	if (user->extinfo) {
+		g_free(user->extinfo->media_album);
+		g_free(user->extinfo->media_artist);
+		g_free(user->extinfo->media_title);
+		user->extinfo->media_album = NULL;
+		user->extinfo->media_artist = NULL;
+		user->extinfo->media_title = NULL;
+	}
+
 	if (len != 0) {
 		payloadNode = xmlnode_from_str(payload, len);
 		if (!payloadNode) {
 			purple_debug_error("msn", "UBX XML parse Error!\n");
 
 			msn_user_set_statusline(user, NULL);
-			msn_user_set_currentmedia(user, NULL);
 
 			msn_user_update(user);
 			return;
@@ -1759,13 +1825,7 @@
 		g_free(psm_str);
 
 		str = msn_get_currentmedia(payloadNode);
-		if (msn_parse_currentmedia(str, &media))
-			msn_user_set_currentmedia(user, &media);
-		else
-			msn_user_set_currentmedia(user, NULL);
-		g_free(media.title);
-		g_free(media.album);
-		g_free(media.artist);
+		parse_currentmedia(user, str);
 		g_free(str);
 
 		parse_user_endpoints(user, payloadNode);
@@ -1774,7 +1834,6 @@
 
 	} else {
 		msn_user_set_statusline(user, NULL);
-		msn_user_set_currentmedia(user, NULL);
 	}
 
 	msn_user_update(user);
--- a/libpurple/protocols/msn/slp.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/slp.c	Sat Feb 06 05:15:35 2010 +0000
@@ -276,6 +276,38 @@
 	msn_slplink_queue_slpmsg(slplink, slpmsg);
 }
 
+/* XXX: this could be improved if we tracked custom smileys
+ * per-protocol, per-account, per-session or (ideally) per-conversation
+ */
+static PurpleStoredImage *
+find_valid_emoticon(PurpleAccount *account, const char *path)
+{
+	GList *smileys;
+
+	if (!purple_account_get_bool(account, "custom_smileys", TRUE))
+		return NULL;
+
+	smileys = purple_smileys_get_all();
+
+	for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
+		PurpleSmiley *smiley;
+		PurpleStoredImage *img;
+
+		smiley = smileys->data;
+		img = purple_smiley_get_stored_image(smiley);
+
+		if (purple_strequal(path, purple_imgstore_get_filename(img))) {
+			g_list_free(smileys);
+			return img;
+		}
+
+		purple_imgstore_unref(img);
+	}
+
+	purple_debug_error("msn", "Received illegal request for file %s\n", path);
+	return NULL;
+}
+
 #define MAX_FILE_NAME_LEN 0x226
 
 static void
@@ -293,7 +325,7 @@
 		MsnSlpMessage *slpmsg;
 		MsnObject *obj;
 		char *msnobj_data;
-		PurpleStoredImage *img;
+		PurpleStoredImage *img = NULL;
 		int type;
 
 		/* Send Ok */
@@ -311,51 +343,38 @@
 		obj = msn_object_new_from_string(msnobj_data);
 		type = msn_object_get_type(obj);
 		g_free(msnobj_data);
-
-		if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON))
-		{
-			purple_debug_error("msn", "Wrong object?\n");
-			msn_object_destroy(obj);
-			g_return_if_reached();
-		}
-
 		if (type == MSN_OBJECT_EMOTICON) {
-			char *path;
-			path = g_build_filename(purple_smileys_get_storing_dir(),
-					obj->location, NULL);
-			img = purple_imgstore_new_from_file(path);
-			g_free(path);
-		} else {
+			img = find_valid_emoticon(slplink->session->account, obj->location);
+		} else if (type == MSN_OBJECT_USERTILE) {
 			img = msn_object_get_image(obj);
 			if (img)
 				purple_imgstore_ref(img);
 		}
 		msn_object_destroy(obj);
 
-		if (img == NULL)
-		{
-			purple_debug_error("msn", "Wrong object.\n");
-			g_return_if_reached();
-		}
+		if (img != NULL) {
+			/* DATA PREP */
+			slpmsg = msn_slpmsg_new(slplink);
+			slpmsg->slpcall = slpcall;
+			slpmsg->session_id = slpcall->session_id;
+			msn_slpmsg_set_body(slpmsg, NULL, 4);
+			slpmsg->info = "SLP DATA PREP";
+			msn_slplink_queue_slpmsg(slplink, slpmsg);
 
-		/* DATA PREP */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->session_id = slpcall->session_id;
-		msn_slpmsg_set_body(slpmsg, NULL, 4);
-		slpmsg->info = "SLP DATA PREP";
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
+			/* DATA */
+			slpmsg = msn_slpmsg_new(slplink);
+			slpmsg->slpcall = slpcall;
+			slpmsg->flags = 0x20;
+			slpmsg->info = "SLP DATA";
+			msn_slpmsg_set_image(slpmsg, img);
+			msn_slplink_queue_slpmsg(slplink, slpmsg);
+			purple_imgstore_unref(img);
 
-		/* DATA */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->flags = 0x20;
-		slpmsg->info = "SLP DATA";
-		msn_slpmsg_set_image(slpmsg, img);
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
-		purple_imgstore_unref(img);
+			accepted = TRUE;
 
-		accepted = TRUE;
+		} else {
+			purple_debug_error("msn", "Wrong object.\n");
+		}
 	}
 
 	else if (!strcmp(euf_guid, MSN_FT_GUID))
--- a/libpurple/protocols/msn/slplink.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/slplink.c	Sat Feb 06 05:15:35 2010 +0000
@@ -669,9 +669,8 @@
 #define MAX_FILE_NAME_LEN 0x226
 
 static gchar *
-gen_context(const char *file_name, const char *file_path)
+gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
 {
-	struct stat st;
 	gsize size = 0;
 	MsnContextHeader header;
 	gchar *u8 = NULL;
@@ -683,8 +682,7 @@
 	glong uni_len = 0;
 	gsize len;
 
-	if (g_stat(file_path, &st) == 0)
-		size = st.st_size;
+	size = purple_xfer_get_size(xfer);
 
 	if(!file_name) {
 		gchar *basename = g_path_get_basename(file_path);
@@ -761,7 +759,7 @@
 
 	xfer->data = slpcall;
 
-	context = gen_context(fn, fp);
+	context = gen_context(xfer, fn, fp);
 
 	msn_slpcall_invite(slpcall, MSN_FT_GUID, 2, context);
 
--- a/libpurple/protocols/msn/state.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/state.c	Sat Feb 06 05:15:35 2010 +0000
@@ -89,78 +89,6 @@
 	return result;
 }
 
-/* parse CurrentMedia string */
-gboolean
-msn_parse_currentmedia(const char *cmedia, CurrentMedia *media)
-{
-	char **cmedia_array;
-	int strings = 0;
-	gboolean parsed = FALSE;
-
-	if ((cmedia == NULL) || (*cmedia == '\0')) {
-		purple_debug_info("msn", "No currentmedia string\n");
-		return FALSE;
-	}
-
-	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
-
-	cmedia_array = g_strsplit(cmedia, "\\0", 0);
-
-	/*
-	 * 0: Application
-	 * 1: 'Music'/'Games'/'Office'
-	 * 2: '1' if enabled, '0' if not
-	 * 3: Format (eg. {0} by {1})
-	 * 4: Title
-	 * If 'Music':
-	 *  5: Artist
-	 *  6: Album
-	 *  7: ?
-	 */
-#if GLIB_CHECK_VERSION(2,6,0)
-	strings  = g_strv_length(cmedia_array);
-#else
-	while (cmedia_array[++strings] != NULL);
-#endif
-
-	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
-		parsed = TRUE;
-
-		if (!strcmp(cmedia_array[1], "Music"))
-			media->type = CURRENT_MEDIA_MUSIC;
-		else if (!strcmp(cmedia_array[1], "Games"))
-			media->type = CURRENT_MEDIA_GAMES;
-		else if (!strcmp(cmedia_array[1], "Office"))
-			media->type = CURRENT_MEDIA_OFFICE;
-		else
-			media->type = CURRENT_MEDIA_UNKNOWN;
-
-		g_free(media->title);
-		if (strings == 4) {
-			media->title = g_strdup(cmedia_array[3]);
-		} else {
-			media->title = g_strdup(cmedia_array[4]);
-		}
-
-		g_free(media->artist);
-		if (strings > 5)
-			media->artist = g_strdup(cmedia_array[5]);
-		else
-			media->artist = NULL;
-
-		g_free(media->album);
-		if (strings > 6)
-			media->album = g_strdup(cmedia_array[6]);
-		else
-			media->album = NULL;
-
-	}
-
-	g_strfreev(cmedia_array);
-
-	return parsed;
-}
-
 /* get the CurrentMedia info from the XML node */
 char *
 msn_get_currentmedia(xmlnode *payloadNode)
--- a/libpurple/protocols/msn/state.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/state.h	Sat Feb 06 05:15:35 2010 +0000
@@ -61,9 +61,6 @@
 
 void msn_set_psm(MsnSession *session);
 
-/* Parse CurrentMedia string */
-gboolean msn_parse_currentmedia(const char *cmedia, CurrentMedia *media);
-
 /* Get the CurrentMedia info from the XML node */
 char * msn_get_currentmedia(xmlnode *payloadNode);
 
--- a/libpurple/protocols/msn/user.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/user.c	Sat Feb 06 05:15:35 2010 +0000
@@ -75,12 +75,15 @@
 	g_free(user->passport);
 	g_free(user->friendly_name);
 	g_free(user->uid);
-	g_free(user->phone.home);
-	g_free(user->phone.work);
-	g_free(user->phone.mobile);
-	g_free(user->media.artist);
-	g_free(user->media.title);
-	g_free(user->media.album);
+	if (user->extinfo) {
+		g_free(user->extinfo->media_album);
+		g_free(user->extinfo->media_artist);
+		g_free(user->extinfo->media_title);
+		g_free(user->extinfo->phone_home);
+		g_free(user->extinfo->phone_mobile);
+		g_free(user->extinfo->phone_work);
+		g_free(user->extinfo);
+	}
 	g_free(user->statusline);
 	g_free(user->invite_message);
 
@@ -115,24 +118,24 @@
 		purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
 	}
 
-	if (!offline && user->media.type != CURRENT_MEDIA_UNKNOWN) {
-		if (user->media.type == CURRENT_MEDIA_MUSIC) {
+	if (!offline && user->extinfo && user->extinfo->media_type != CURRENT_MEDIA_UNKNOWN) {
+		if (user->extinfo->media_type == CURRENT_MEDIA_MUSIC) {
 			purple_prpl_got_user_status(account, user->passport, "tune",
-			                            PURPLE_TUNE_ARTIST, user->media.artist,
-			                            PURPLE_TUNE_ALBUM, user->media.album,
-			                            PURPLE_TUNE_TITLE, user->media.title,
+			                            PURPLE_TUNE_ARTIST, user->extinfo->media_artist,
+			                            PURPLE_TUNE_ALBUM, user->extinfo->media_album,
+			                            PURPLE_TUNE_TITLE, user->extinfo->media_title,
 			                            NULL);
-		} else if (user->media.type == CURRENT_MEDIA_GAMES) {
+		} else if (user->extinfo->media_type == CURRENT_MEDIA_GAMES) {
 			purple_prpl_got_user_status(account, user->passport, "tune",
-			                            "game", user->media.title,
+			                            "game", user->extinfo->media_title,
 			                            NULL);
-		} else if (user->media.type == CURRENT_MEDIA_OFFICE) {
+		} else if (user->extinfo->media_type == CURRENT_MEDIA_OFFICE) {
 			purple_prpl_got_user_status(account, user->passport, "tune",
-			                            "office", user->media.title,
+			                            "office", user->extinfo->media_title,
 			                            NULL);
 		} else {
 			purple_debug_warning("msn", "Got CurrentMedia with unknown type %d.\n",
-			                     user->media.type);
+			                     user->extinfo->media_type);
 		}
 	} else {
 		purple_prpl_got_user_status_deactive(account, user->passport, "tune");
@@ -216,21 +219,6 @@
 }
 
 void
-msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *media)
-{
-	g_return_if_fail(user != NULL);
-
-	g_free(user->media.title);
-	g_free(user->media.album);
-	g_free(user->media.artist);
-
-	user->media.type   = media ? media->type : CURRENT_MEDIA_UNKNOWN;
-	user->media.title  = media ? g_strdup(media->title) : NULL;
-	user->media.artist = media ? g_strdup(media->artist) : NULL;
-	user->media.album  = media ? g_strdup(media->album) : NULL;
-}
-
-void
 msn_user_set_uid(MsnUser *user, const char *uid)
 {
 	g_return_if_fail(user != NULL);
@@ -415,8 +403,15 @@
 {
 	g_return_if_fail(user != NULL);
 
-	g_free(user->phone.home);
-	user->phone.home = g_strdup(number);
+	if (!number && !user->extinfo)
+		return;
+
+	if (user->extinfo)
+		g_free(user->extinfo->phone_home);
+	else
+		user->extinfo = g_new0(MsnUserExtendedInfo, 1);
+
+	user->extinfo->phone_home = g_strdup(number);
 }
 
 void
@@ -424,8 +419,15 @@
 {
 	g_return_if_fail(user != NULL);
 
-	g_free(user->phone.work);
-	user->phone.work = g_strdup(number);
+	if (!number && !user->extinfo)
+		return;
+
+	if (user->extinfo)
+		g_free(user->extinfo->phone_work);
+	else
+		user->extinfo = g_new0(MsnUserExtendedInfo, 1);
+
+	user->extinfo->phone_work = g_strdup(number);
 }
 
 void
@@ -433,8 +435,15 @@
 {
 	g_return_if_fail(user != NULL);
 
-	g_free(user->phone.mobile);
-	user->phone.mobile = g_strdup(number);
+	if (!number && !user->extinfo)
+		return;
+
+	if (user->extinfo)
+		g_free(user->extinfo->phone_mobile);
+	else
+		user->extinfo = g_new0(MsnUserExtendedInfo, 1);
+
+	user->extinfo->phone_mobile = g_strdup(number);
 }
 
 void
@@ -517,7 +526,7 @@
 {
 	g_return_val_if_fail(user != NULL, NULL);
 
-	return user->phone.home;
+	return user->extinfo ? user->extinfo->phone_home : NULL;
 }
 
 const char *
@@ -525,7 +534,7 @@
 {
 	g_return_val_if_fail(user != NULL, NULL);
 
-	return user->phone.work;
+	return user->extinfo ? user->extinfo->phone_work : NULL;
 }
 
 const char *
@@ -533,7 +542,7 @@
 {
 	g_return_val_if_fail(user != NULL, NULL);
 
-	return user->phone.mobile;
+	return user->extinfo ? user->extinfo->phone_mobile : NULL;
 }
 
 guint
--- a/libpurple/protocols/msn/user.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/user.h	Sat Feb 06 05:15:35 2010 +0000
@@ -53,13 +53,25 @@
 	CURRENT_MEDIA_OFFICE
 } CurrentMediaType;
 
-typedef struct _CurrentMedia
+/**
+ * Contains optional info about a user that is fairly uncommon.  We
+ * put this info in in a separate struct to save memory because we
+ * allocate an MsnUser struct for each buddy, but we generally only
+ * need this information for a small percentage of our buddies
+ * (usually less than 1%).  Putting it in a separate struct saves
+ * makes MsnUser smaller by the size of a few pointers.
+ */
+typedef struct _MsnUserExtendedInfo
 {
-	CurrentMediaType type;     /**< Type.   */
-	char *title;    /**< Title.  */
-	char *artist;   /**< Artist. */
-	char *album;    /**< Album.  */
-} CurrentMedia;
+	CurrentMediaType media_type; /**< Type of the user's current media.   */
+	char *media_title;  /**< Title of the user's current media.  */
+	char *media_artist; /**< Artist of the user's current media. */
+	char *media_album;  /**< Album of the user's current media.  */
+
+	char *phone_home;   /**< E.T. uses this.                     */
+	char *phone_work;   /**< Work phone number.                  */
+	char *phone_mobile; /**< Mobile phone number.                */
+} MsnUserExtendedInfo;
 
 /**
  * A user.
@@ -71,22 +83,15 @@
 	char *passport;         /**< The passport account.          */
 	char *friendly_name;    /**< The friendly name.             */
 
-	char * uid;				/*< User Id							*/
-	GHashTable *endpoints;	/*< Endpoint-specific data          */
+	char *uid;              /*< User ID                         */
+	GHashTable *endpoints;  /*< Endpoint-specific data          */
 
 	const char *status;     /**< The state of the user.         */
 	char *statusline;       /**< The state of the user.         */
-	CurrentMedia media;     /**< Current media of the user.     */
 
 	gboolean idle;          /**< The idle state of the user.    */
 
-	struct
-	{
-		char *home;         /**< Home phone number.             */
-		char *work;         /**< Work phone number.             */
-		char *mobile;       /**< Mobile phone number.           */
-
-	} phone;
+	MsnUserExtendedInfo *extinfo; /**< Extended info for the user. */
 
 	gboolean authorized;    /**< Authorized to add this user.   */
 	gboolean mobile;        /**< Signed up with MSN Mobile.     */
@@ -105,8 +110,11 @@
 
 	int list_op;            /**< Which lists the user is in     */
 
-	guint membership_id[5];	/**< The membershipId sent by the contacts server,
-				     indexed by the list it belongs to		*/
+	/**
+	 * The membershipId for this buddy on our pending list.  Sent by
+	 * the contact's server
+	 */
+	guint member_id_on_pending_list;
 
 	char *invite_message;   /**< Invite message of user request */
 };
@@ -165,14 +173,6 @@
   */
 void msn_user_set_statusline(MsnUser *user, const char *statusline);
 
- /**
-  *  Sets the current media of user.
-  *
-  *  @param user   The user.
-  *  @param cmedia Current media.
-  */
-void msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *cmedia);
-
 /**
  * Sets the new state of user.
  *
--- a/libpurple/protocols/msn/userlist.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sat Feb 06 05:15:35 2010 +0000
@@ -119,26 +119,16 @@
 	if (group_id == NULL)
 		return FALSE;
 
-	if (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp))
-		return TRUE;
-
-	return FALSE;
+	return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL;
 }
 
 gboolean
 msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
 {
-	int list_op;
-
 	if (user == NULL)
 		return FALSE;
 
-	list_op = 1 << list_id;
-
-	if (user->list_op & list_op)
-		return TRUE;
-	else
-		return FALSE;
+	return (user->list_op & (1 << list_id));
 }
 
 /**************************************************************************
@@ -210,6 +200,7 @@
 
 	if (list_op & MSN_LIST_PL_OP)
 	{
+		user->authorized = TRUE;
 		got_new_entry(gc, passport, store, message);
 	}
 }
@@ -343,14 +334,10 @@
 
 	for (l = userlist->users; l != NULL; l = l->next) {
 		MsnUser *user = (MsnUser *)l->data;
+		const char *user_number = msn_user_get_mobile_phone(user);
 
-		if (user->phone.mobile == NULL) {
-			continue;
-		}
-
-		if (!g_ascii_strcasecmp(number, user->phone.mobile)) {
+		if (user_number && !g_ascii_strcasecmp(number, user_number))
 			return user;
-		}
 	}
 
 	return NULL;
--- a/libpurple/protocols/msn/userlist.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/msn/userlist.h	Sat Feb 06 05:15:35 2010 +0000
@@ -31,12 +31,11 @@
 
 typedef enum
 {
-	MSN_LIST_FL,
-	MSN_LIST_AL,
-	MSN_LIST_BL,
-	MSN_LIST_RL,
-	MSN_LIST_PL
-
+	MSN_LIST_FL, /**< Forward list */
+	MSN_LIST_AL, /**< Allow list */
+	MSN_LIST_BL, /**< Block list */
+	MSN_LIST_RL, /**< Reverse list */
+	MSN_LIST_PL  /**< Pending list */
 } MsnListId;
 
 
--- a/libpurple/protocols/mxit/splashscreen.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/mxit/splashscreen.c	Sat Feb 06 05:15:35 2010 +0000
@@ -24,7 +24,6 @@
  */
 
 #include "internal.h"
-#include <glib/gstdio.h>
 
 #include "purple.h"
 #include "imgstore.h"
--- a/libpurple/protocols/myspace/myspace.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sat Feb 06 05:15:35 2010 +0000
@@ -251,7 +251,6 @@
 	MsimMessage *msg;
 	const gchar *from_username;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(who != NULL, FALSE);
 	g_return_val_if_fail(text != NULL, FALSE);
 
@@ -344,8 +343,6 @@
 {
 	guint rid;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
-
 	rid = session->next_rid++;
 
 	g_hash_table_insert(session->user_lookup_cb, GUINT_TO_POINTER(rid), cb);
@@ -394,8 +391,6 @@
 	gc = purple_account_get_connection(account);
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), NULL);
-
 	display_name = headline = NULL;
 
 	/* Retrieve display name and/or headline, depending on user preference. */
@@ -442,11 +437,9 @@
 		MsimSession *session;
 		PurpleAccount *account = purple_buddy_get_account(buddy);
 		PurpleConnection *gc = purple_account_get_connection(account);
- 
+
 		session = (MsimSession *)gc->proto_data;
 
-		g_return_if_fail(MSIM_SESSION_VALID(session));
-
 		/* TODO: if (full), do something different? */
 
 		/* TODO: request information? have to figure out how to do
@@ -694,7 +687,6 @@
 	gsize nc_len;
 	gboolean ret;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	g_return_val_if_fail(msim_msg_get_binary(msg, "nc", &nc, &nc_len), FALSE);
@@ -783,7 +775,6 @@
 static gboolean
 msim_is_username_set(MsimSession *session, MsimMessage *msg)
 {
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 	g_return_val_if_fail(session->gc != NULL, FALSE);
 
@@ -842,8 +833,6 @@
 
 	session = (MsimSession *)data;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
-
 	delta = time(NULL) - session->last_comm;
 
 	/* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
@@ -970,11 +959,6 @@
 
 	session = (MsimSession *)data;
 
-	if (!MSIM_SESSION_VALID(session)) {
-		purple_debug_info("msim", "msim_check_inbox: session invalid, stopping the mail check.\n");
-		return FALSE;
-	}
-
 	purple_debug_info("msim", "msim_check_inbox: checking mail\n");
 	g_return_val_if_fail(msim_send(session,
 			"persist", MSIM_TYPE_INTEGER, 1,
@@ -1215,8 +1199,6 @@
 {
 	MsimMessage *body;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
-
 	/* Set display name to username (otherwise will show email address) */
 	purple_connection_set_display_name(session->gc, session->username);
 
@@ -1382,7 +1364,6 @@
 	gchar *username;
 	gchar *unrecognized_msg;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	/* Helpfully looked up by msim_incoming_resolve() for us. */
@@ -1577,7 +1558,6 @@
 	gchar *msg_text, *username;
 	gboolean rc;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	msg_text = msim_msg_get_string(msg, "msg");
@@ -1797,7 +1777,6 @@
 	gpointer data;
 	guint rid, cmd, dsn, lid;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	msim_store_user_info(session, msg, NULL);
@@ -1848,7 +1827,6 @@
 	gchar *errmsg, *full_errmsg;
 	guint err;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	err = msim_msg_get_integer(msg, "err");
@@ -1962,7 +1940,6 @@
 	gchar *username;
 	MsimMessage *msg, *body;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 	g_return_if_fail(userinfo != NULL);
 
 	body = msim_msg_get_dictionary(userinfo, "body");
@@ -1998,7 +1975,6 @@
 static gboolean
 msim_preprocess_incoming(MsimSession *session, MsimMessage *msg)
 {
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	if (msim_msg_get(msg, "bm") && msim_msg_get(msg, "f")) {
@@ -2071,7 +2047,6 @@
 	}
 
 	g_return_if_fail(cond == PURPLE_INPUT_READ);
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 
 	/* Mark down that we got data, so we don't timeout. */
 	session->last_comm = time(NULL);
@@ -2264,6 +2239,13 @@
 	}
 }
 
+static void
+msim_buddy_free(PurpleBuddy *buddy)
+{
+	msim_user_free(purple_buddy_get_protocol_data(buddy));
+	purple_buddy_set_protocol_data(buddy, NULL);
+}
+
 /**
  * Close the connection.
  *
@@ -2272,22 +2254,30 @@
 static void
 msim_close(PurpleConnection *gc)
 {
+	GSList *buddies;
 	MsimSession *session;
 
 	if (gc == NULL) {
 		return;
 	}
 
+	/*
+	 * Free our protocol-specific buddy data.  It almost seems like libpurple
+	 * should call our buddy_free prpl callback so that we don't need to do
+	 * this... but it doesn't, so we do.
+	 */
+	buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+	while (buddies != NULL) {
+		msim_buddy_free(buddies->data);
+		buddies = g_slist_delete_link(buddies, buddies);
+	}
+
 	session = (MsimSession *)gc->proto_data;
 	if (session == NULL)
 		return;
 
 	gc->proto_data = NULL;
 
-	if (!MSIM_SESSION_VALID(session)) {
-		return;
-	}
-
 	if (session->gc->inpa) {
 		purple_input_remove(session->gc->inpa);
 	}
@@ -2329,8 +2319,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
-
 	message_msim = html_to_msim_markup(session, message);
 
 	if (msim_send_bm(session, who, message_msim, MSIM_BM_ACTION_OR_IM_DELAYABLE)) {
@@ -2371,8 +2359,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
-
 	switch (state) {
 		case PURPLE_TYPING:
 			typing_str = "%typing%";
@@ -2402,8 +2388,6 @@
 	PurpleNotifyUserInfo *user_info;
 	MsimUser *user;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	/* Get user{name,id} from msim_get_info, passed as an MsimMessage for
 	   orthogonality. */
 	msg = (MsimMessage *)data;
@@ -2439,16 +2423,8 @@
 
 	purple_notify_user_info_destroy(user_info);
 
-	if (user->temporary_user) {
-		g_free(user->client_info);
-		g_free(user->gender);
-		g_free(user->location);
-		g_free(user->headline);
-		g_free(user->display_name);
-		g_free(user->username);
-		g_free(user->image_url);
-		g_free(user);
-	}
+	if (user->temporary_user)
+		msim_user_free(user);
 	g_free(username);
 }
 
@@ -2469,8 +2445,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	/* Obtain uid of buddy. */
 	user = msim_find_user(session, username);
 
@@ -2504,7 +2478,6 @@
 static void
 msim_set_status_code(MsimSession *session, guint status_code, gchar *statstring)
 {
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 	g_return_if_fail(statstring != NULL);
 
 	purple_debug_info("msim", "msim_set_status_code: going to set status to code=%d,str=%s\n",
@@ -2537,8 +2510,6 @@
 
 	session = (MsimSession *)account->gc->proto_data;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	type = purple_status_get_type(status);
 	pres = purple_status_get_presence(status);
 
@@ -2602,8 +2573,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	status = purple_account_get_active_status(session->account);
 
 	if (time == 0) {
@@ -2742,13 +2711,6 @@
 	msim_update_blocklist_for_buddy(session, name, TRUE, FALSE);
 }
 
-static void
-msim_buddy_free(PurpleBuddy *buddy)
-{
-	msim_user_free(purple_buddy_get_protocol_data(buddy));
-	purple_buddy_set_protocol_data(buddy, NULL);
-}
-
 /**
  * Remove a buddy from the user's buddy list.
  */
@@ -3001,8 +2963,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), -1);
-
 	/* Loop until all data is sent, or a failure occurs. */
 	total_bytes_sent = 0;
 	do {
@@ -3037,7 +2997,6 @@
 {
 	size_t len;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg);
--- a/libpurple/protocols/myspace/session.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/myspace/session.c	Sat Feb 06 05:15:35 2010 +0000
@@ -76,8 +76,6 @@
 void
 msim_session_destroy(MsimSession *session)
 {
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	session->magic = -1;
 
 	g_free(session->rxbuf);
--- a/libpurple/protocols/myspace/session.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/myspace/session.h	Sat Feb 06 05:15:35 2010 +0000
@@ -54,9 +54,6 @@
 	guint inbox_handle;                 /**< The handle for the mail check timer */
 } MsimSession;
 
-/* Check if an MsimSession is valid */
-#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC)
-
 MsimSession *msim_session_new(PurpleAccount *acct);
 void msim_session_destroy(MsimSession *session);
 
--- a/libpurple/protocols/myspace/user.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/myspace/user.c	Sat Feb 06 05:15:35 2010 +0000
@@ -70,6 +70,9 @@
 	if (!user)
 		return;
 
+	if (user->url_data != NULL)
+		purple_util_fetch_url_cancel(user->url_data);
+
 	g_free(user->client_info);
 	g_free(user->gender);
 	g_free(user->location);
@@ -89,16 +92,13 @@
 msim_find_user(MsimSession *session, const gchar *username)
 {
 	PurpleBuddy *buddy;
-	MsimUser *user;
 
 	buddy = purple_find_buddy(session->account, username);
 	if (!buddy) {
 		return NULL;
 	}
 
-	user = msim_get_user_from_buddy(buddy, TRUE);
-
-	return user;
+	return msim_get_user_from_buddy(buddy, TRUE);
 }
 
 /**
@@ -212,6 +212,8 @@
 	const char *name = purple_buddy_get_name(user->buddy);
 	PurpleAccount *account;
 
+	user->url_data = NULL;
+
 	purple_debug_info("msim_downloaded_buddy_icon",
 			"Downloaded %" G_GSIZE_FORMAT " bytes\n", len);
 
@@ -350,14 +352,6 @@
 			return;
 		}
 
-		if (user->temporary_user) {
-			/* This user will be destroyed soon; don't try to look up its image or avatar,
-			 * since that won't return immediately and we will end up accessing freed data.
-			 */
-			g_free(value_str);
-			return;
-		}
-
 		g_free(user->image_url);
 
 		user->image_url = value_str;
@@ -375,7 +369,9 @@
 
 		/* Only download if URL changed */
 		if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
-			purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
+			if (user->url_data != NULL)
+				purple_util_fetch_url_cancel(user->url_data);
+			user->url_data = purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
 		}
 	} else if (g_str_equal(key_str, "LastImageUpdated")) {
 		/* TODO: use somewhere */
@@ -418,7 +414,6 @@
 	gchar *username;
 	MsimMessage *body, *body_node;
 
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
 
 	body = msim_msg_get_dictionary(msg, "body");
@@ -570,7 +565,6 @@
 	gchar *field_name;
 	guint rid, cmd, dsn, lid;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 	g_return_if_fail(user != NULL);
 	/* Callback can be null to not call anything, just lookup & store information. */
 	/*g_return_if_fail(cb != NULL);*/
@@ -630,8 +624,6 @@
 
 	purple_debug_info("msim","username_is_set made\n");
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	cmd = msim_msg_get_integer(userinfo, "cmd");
 	dsn = msim_msg_get_integer(userinfo, "dsn");
 	uid = msim_msg_get_integer(userinfo, "uid");
@@ -711,7 +703,6 @@
 	MsimMessage *body;
 	guint rid;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 	g_return_if_fail(username != NULL);
 	g_return_if_fail(cb != NULL);
 
@@ -758,9 +749,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
-
 	user_msg = msim_msg_new(
 			"user", MSIM_TYPE_STRING, g_strdup(msim_username_to_set),
 			NULL);
@@ -787,7 +775,6 @@
 	purple_debug_info("msim_username_is_available_cb", "Look up username callback made\n");
 
 	msg = (MsimMessage *)data;
-	g_return_if_fail(MSIM_SESSION_VALID(session));
 	g_return_if_fail(msg != NULL);
 
 	username = msim_msg_get_string(msg, "user");
@@ -855,8 +842,6 @@
 
 	session = (MsimSession *)gc->proto_data;
 
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-
 	purple_debug_info("msim_check_username_availability_cb", "Checking username: %s\n", username_to_check);
 
 	user_msg = msim_msg_new(
--- a/libpurple/protocols/myspace/user.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/myspace/user.h	Sat Feb 06 05:15:35 2010 +0000
@@ -40,6 +40,7 @@
 	gchar *image_url;
 	guint last_image_updated;
 	gboolean temporary_user;
+	PurpleUtilFetchUrlData *url_data;
 } MsimUser;
 
 /* Callback function pointer type for when a user's information is received,
--- a/libpurple/protocols/oscar/clientlogin.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/clientlogin.c	Sat Feb 06 05:15:35 2010 +0000
@@ -62,6 +62,26 @@
 			DEFAULT_CLIENT_KEY);
 }
 
+static gchar *generate_error_message(xmlnode *resp, const char *url)
+{
+	xmlnode *text;
+	gchar *err = NULL;
+	gchar *details = NULL;
+
+	if (resp && (text = xmlnode_get_child(resp, "statusText"))) {
+		details = xmlnode_get_data(text);
+	}
+
+	if (details && *details) {
+		err = g_strdup_printf(_("Received unexpected response from %s: %s"), url, details);
+	} else {
+		err = g_strdup_printf(_("Received unexpected response from %s"), url);
+	}
+
+	g_free(details);
+	return err;
+}
+
 /**
  * @return A null-terminated base64 encoded version of the HMAC
  *         calculated using the given key and data.
@@ -121,7 +141,7 @@
 		purple_debug_error("oscar", "startOSCARSession could not parse "
 				"response as XML: %s\n", response);
 		/* Note to translators: %s in this string is a URL */
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_START_OSCAR_SESSION);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -144,7 +164,7 @@
 		char *msg;
 		purple_debug_error("oscar", "startOSCARSession response was "
 				"missing statusCode: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_START_OSCAR_SESSION);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -169,7 +189,7 @@
 					  "longer."));
 		else {
 			char *msg;
-			msg = g_strdup_printf(_("Received unexpected response from %s"),
+			msg = generate_error_message(response_node,
 					URL_START_OSCAR_SESSION);
 			purple_connection_error_reason(gc,
 					PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
@@ -190,7 +210,7 @@
 		char *msg;
 		purple_debug_error("oscar", "startOSCARSession response was missing "
 				"something: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_START_OSCAR_SESSION);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -213,7 +233,7 @@
 		char *msg;
 		purple_debug_error("oscar", "startOSCARSession response was missing "
 				"something: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_START_OSCAR_SESSION);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -340,7 +360,7 @@
 		char *msg;
 		purple_debug_error("oscar", "clientLogin could not parse "
 				"response as XML: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_CLIENT_LOGIN);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -364,7 +384,7 @@
 		char *msg;
 		purple_debug_error("oscar", "clientLogin response was "
 				"missing statusCode: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_CLIENT_LOGIN);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -399,7 +419,7 @@
 					_("AOL does not allow your screen name to authenticate here"));
 		} else {
 			char *msg;
-			msg = g_strdup_printf(_("Received unexpected response from %s"),
+			msg = generate_error_message(response_node,
 					URL_CLIENT_LOGIN);
 			purple_connection_error_reason(gc,
 					PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
@@ -418,7 +438,7 @@
 		char *msg;
 		purple_debug_error("oscar", "clientLogin response was missing "
 				"something: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_CLIENT_LOGIN);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
@@ -436,7 +456,7 @@
 		char *msg;
 		purple_debug_error("oscar", "clientLogin response was missing "
 				"something: %s\n", response);
-		msg = g_strdup_printf(_("Received unexpected response from %s"),
+		msg = generate_error_message(response_node,
 				URL_CLIENT_LOGIN);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
--- a/libpurple/protocols/oscar/family_oservice.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Sat Feb 06 05:15:35 2010 +0000
@@ -27,6 +27,24 @@
 
 #include "cipher.h"
 
+/*
+ * Each time we make a FLAP connection to an oscar server the server gives
+ * us a list of rate classes.  Each rate class has different properties for
+ * how frequently we can send SNACs in that rate class before we become
+ * throttled or disconnected.
+ *
+ * The server also gives us a list of every available SNAC and tells us which
+ * rate class it's in.  There are a lot of different SNACs, so this list can be
+ * fairly large.  One important characteristic of these rate classes is that
+ * currently (and since at least 2004) most SNACs are in the same rate class.
+ *
+ * One optimization we can do to save memory is to only keep track of SNACs
+ * that are in classes other than this default rate class.  So if we try to
+ * look up a SNAC and it's not in our hash table then we can assume that it's
+ * in the default rate class.
+ */
+#define OSCAR_DEFAULT_RATECLASS 1
+
 /* Subtype 0x0002 - Client Online */
 void
 aim_srv_clientready(OscarData *od, FlapConnection *conn)
@@ -323,7 +341,7 @@
 		struct timeval now;
 
 		gettimeofday(&now, NULL);
-		rateclass = g_new0(struct rateclass, 1);
+		rateclass = g_new(struct rateclass, 1);
 
 		rateclass->classid = byte_stream_get16(bs);
 		rateclass->windowsize = byte_stream_get32(bs);
@@ -333,34 +351,21 @@
 		rateclass->disconnect = byte_stream_get32(bs);
 		rateclass->current = byte_stream_get32(bs);
 		rateclass->max = byte_stream_get32(bs);
-
-		/*
-		 * The server will send an extra five bytes of parameters
-		 * depending on the version we advertised in 1/17.  If we
-		 * didn't send 1/17 (evil!), then this will crash and you
-		 * die, as it will default to the old version but we have
-		 * the new version hardcoded here.
-		 */
-		if (mod->version >= 3)
-		{
-			rateclass->delta = byte_stream_get32(bs);
+		if (mod->version >= 3) {
+			delta = byte_stream_get32(bs);
 			rateclass->dropping_snacs = byte_stream_get8(bs);
-
-			delta = rateclass->delta;
-
-			rateclass->last.tv_sec = now.tv_sec - delta / 1000;
-			delta %= 1000;
-			rateclass->last.tv_usec = now.tv_usec - delta * 1000;
-		}
-		else
-		{
-			rateclass->delta = rateclass->dropping_snacs = 0;
-			rateclass->last.tv_sec = now.tv_sec;
-			rateclass->last.tv_usec = now.tv_usec;
+		} else {
+			delta = 0;
+			rateclass->dropping_snacs = 0;
 		}
 
-		rateclass->members = g_hash_table_new(g_direct_hash, g_direct_equal);
+		rateclass->last.tv_sec = now.tv_sec - delta / 1000;
+		rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
+
 		conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
+
+		if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
+			conn->default_rateclass = rateclass;
 	}
 	conn->rateclasses = g_slist_reverse(conn->rateclasses);
 
@@ -376,6 +381,15 @@
 		classid = byte_stream_get16(bs);
 		count = byte_stream_get16(bs);
 
+		if (classid == OSCAR_DEFAULT_RATECLASS) {
+			/*
+			 * Don't bother adding these SNACs to the hash table.  See the
+			 * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
+			 */
+			byte_stream_advance(bs, 4 * count);
+			continue;
+		}
+
 		rateclass = rateclass_find(conn->rateclasses, classid);
 
 		for (j = 0; j < count; j++)
@@ -386,9 +400,9 @@
 			subtype = byte_stream_get16(bs);
 
 			if (rateclass != NULL)
-				g_hash_table_insert(rateclass->members,
+				g_hash_table_insert(conn->rateclass_members,
 						GUINT_TO_POINTER((group << 16) + subtype),
-						GUINT_TO_POINTER(TRUE));
+						rateclass);
 		}
 	}
 
@@ -462,12 +476,17 @@
 static int
 ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	int ret = 0;
-	aim_rxcallback_t userfunc;
 	guint16 code, classid;
 	struct rateclass *rateclass;
 	guint32 delta;
 	struct timeval now;
+	static const char *codes[5] = {
+		"invalid",
+		"change",
+		"warning",
+		"limit",
+		"limit cleared",
+	};
 
 	gettimeofday(&now, NULL);
 	code = byte_stream_get16(bs);
@@ -485,32 +504,32 @@
 	rateclass->disconnect = byte_stream_get32(bs);
 	rateclass->current = byte_stream_get32(bs);
 	rateclass->max = byte_stream_get32(bs);
-
-	if (mod->version >= 3)
-	{
-		rateclass->delta = byte_stream_get32(bs);
+	if (mod->version >= 3) {
+		delta = byte_stream_get32(bs);
 		rateclass->dropping_snacs = byte_stream_get8(bs);
-
-		delta = rateclass->delta;
-
-		rateclass->last.tv_sec = now.tv_sec - delta / 1000;
-		delta %= 1000;
-		rateclass->last.tv_usec = now.tv_usec - delta * 1000;
-	}
-	else
-	{
-		rateclass->delta = rateclass->dropping_snacs = 0;
-		rateclass->last.tv_sec = now.tv_sec;
-		rateclass->last.tv_usec = now.tv_usec;
+	} else {
+		delta = 0;
+		rateclass->dropping_snacs = 0;
 	}
 
-	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) {
-		/* Can't pass in guint8 via ... varargs, so we use an unsigned int */
-		unsigned int dropping_snacs = rateclass->dropping_snacs;
-		ret = userfunc(od, conn, frame, code, classid, rateclass->windowsize, rateclass->clear, rateclass->alert, rateclass->limit, rateclass->disconnect, rateclass->current, rateclass->max, rateclass->delta, dropping_snacs);
+	rateclass->last.tv_sec = now.tv_sec - delta / 1000;
+	rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
+
+	purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
+			"maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
+			"disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
+			(code < 5) ? codes[code] : codes[0], rateclass->classid,
+			rateclass->current, rateclass->max, rateclass->alert,
+			rateclass->clear, rateclass->limit, rateclass->disconnect,
+			delta, rateclass->dropping_snacs, rateclass->windowsize);
+
+	if (code == AIM_RATE_CODE_LIMIT) {
+		purple_debug_warning("oscar",  _("The last action you attempted "
+				"could not be performed because you are over the rate "
+				"limit. Please wait 10 seconds and try again.\n"));
 	}
 
-	return ret;
+	return 1;
 }
 
 /*
--- a/libpurple/protocols/oscar/flap_connection.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Sat Feb 06 05:15:35 2010 +0000
@@ -106,21 +106,15 @@
 static struct rateclass *
 flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
 {
-	GSList *tmp1;
 	gconstpointer key;
+	gpointer rateclass;
 
 	key = GUINT_TO_POINTER((family << 16) + subtype);
-
-	for (tmp1 = conn->rateclasses; tmp1 != NULL; tmp1 = tmp1->next)
-	{
-		struct rateclass *rateclass;
-		rateclass = tmp1->data;
+	rateclass = g_hash_table_lookup(conn->rateclass_members, key);
+	if (rateclass != NULL)
+		return rateclass;
 
-		if (g_hash_table_lookup(rateclass->members, key))
-			return rateclass;
-	}
-
-	return NULL;
+	return conn->default_rateclass;
 }
 
 /*
@@ -133,10 +127,10 @@
 	unsigned long timediff; /* In milliseconds */
 	guint32 current;
 
+	/* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
 	timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000;
 	current = ((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize;
 
-	/* This formula is taken from http://dev.aol.com/aim/oscar/#RATELIMIT */
 	return MIN(current, rateclass->max);
 }
 
@@ -258,14 +252,6 @@
 			rateclass->last.tv_sec = now.tv_sec;
 			rateclass->last.tv_usec = now.tv_usec;
 		}
-	} else {
-		/*
-		 * It's normal for SNACs 0x0001/0x0006 and 0x0001/0x0017 to be
-		 * sent before we receive rate info from the server, so don't
-		 * bother warning about them.
-		 */
-		if (family != 0x0001 || (subtype != 0x0006 && subtype != 0x0017))
-			purple_debug_warning("oscar", "No rate class found for family 0x%04hx subtype 0x%04hx\n", family, subtype);
 	}
 
 	if (enqueue)
@@ -354,6 +340,7 @@
 	conn->fd = -1;
 	conn->subtype = -1;
 	conn->type = type;
+	conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
 
 	od->oscar_connections = g_slist_prepend(od->oscar_connections, conn);
 
@@ -421,13 +408,6 @@
 	conn->buffer_outgoing = NULL;
 }
 
-static void
-flap_connection_destroy_rateclass(struct rateclass *rateclass)
-{
-	g_hash_table_destroy(rateclass->members);
-	g_free(rateclass);
-}
-
 /**
  * Free a FlapFrame
  *
@@ -515,10 +495,12 @@
 	g_slist_free(conn->groups);
 	while (conn->rateclasses != NULL)
 	{
-		flap_connection_destroy_rateclass(conn->rateclasses->data);
+		g_free(conn->rateclasses->data);
 		conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses);
 	}
 
+	g_hash_table_destroy(conn->rateclass_members);
+
 	if (conn->queued_snacs) {
 		while (!g_queue_is_empty(conn->queued_snacs))
 		{
--- a/libpurple/protocols/oscar/libaim.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/libaim.c	Sat Feb 06 05:15:35 2010 +0000
@@ -32,8 +32,7 @@
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
 	/* The mimimum icon size below is not needed in AIM 6.0 */
-	{"gif,jpeg,bmp,ico", 48, 48, 50, 50, 7168,
-		PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
+	{"gif,jpeg,bmp,ico", 0, 0, 100, 100, 7168, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	oscar_list_icon_aim,		/* list_icon */
 	oscar_list_emblem,		/* list_emblems */
 	oscar_status_text,		/* status_text */
--- a/libpurple/protocols/oscar/oscar.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sat Feb 06 05:15:35 2010 +0000
@@ -190,7 +190,6 @@
 static int purple_icon_parseicon   (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int oscar_icon_req        (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_msgack     (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_parse_ratechange (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_evilnotify (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -378,12 +377,12 @@
 }
 
 static gchar *
-oscar_utf8_try_convert(PurpleAccount *account, const gchar *msg)
+oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
 {
 	const char *charset = NULL;
 	char *ret = NULL;
 
-	if(oscar_util_valid_name_icq(purple_account_get_username(account)))
+	if (od->icq)
 		charset = purple_account_get_string(account, "encoding", NULL);
 
 	if(charset && *charset)
@@ -798,24 +797,24 @@
 }
 
 static void
-oscar_user_info_convert_and_add_pair(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
+oscar_user_info_convert_and_add_pair(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
 									 const char *name, const char *value)
 {
 	gchar *utf8;
 
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
+	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
 	}
 }
 
 static void
-oscar_user_info_convert_and_add(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
+oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
 								const char *name, const char *value)
 {
 	gchar *utf8;
 
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
+	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
 	}
@@ -1022,7 +1021,7 @@
 			char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
 			g_free(tmp);
 
-			oscar_user_info_convert_and_add_pair(account, user_info, _("Buddy Comment"), tmp2);
+			oscar_user_info_convert_and_add_pair(account, od, user_info, _("Buddy Comment"), tmp2);
 			g_free(tmp2);
 		}
 	}
@@ -1526,7 +1525,6 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x001f, purple_memrequest, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0021, oscar_icon_req,0);
-	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_RATECHANGE, purple_parse_ratechange, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_EVIL, purple_parse_evilnotify, 0);
@@ -2450,7 +2448,7 @@
 	 * Note: There *may* be some clients which send messages as HTML formatted -
 	 *       they need to be special-cased somehow.
 	 */
-	if (oscar_util_valid_name_icq(purple_account_get_username(account)) && oscar_util_valid_name_icq(userinfo->bn)) {
+	if (od->icq && oscar_util_valid_name_icq(userinfo->bn)) {
 		/* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
 		gchar *tmp2 = g_markup_escape_text(tmp, -1);
 		g_free(tmp);
@@ -3838,56 +3836,6 @@
 	return 1;
 }
 
-static int purple_parse_ratechange(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-	static const char *codes[5] = {
-		"invalid",
-		"change",
-		"warning",
-		"limit",
-		"limit cleared",
-	};
-	va_list ap;
-	guint16 code, rateclass;
-	guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg, delta;
-	guint8 dropping_snacs;
-
-	va_start(ap, fr);
-	code = (guint16)va_arg(ap, unsigned int);
-	rateclass= (guint16)va_arg(ap, unsigned int);
-	windowsize = va_arg(ap, guint32);
-	clear = va_arg(ap, guint32);
-	alert = va_arg(ap, guint32);
-	limit = va_arg(ap, guint32);
-	disconnect = va_arg(ap, guint32);
-	currentavg = va_arg(ap, guint32);
-	maxavg = va_arg(ap, guint32);
-	delta = va_arg(ap, guint32);
-	dropping_snacs = (guint8)va_arg(ap, unsigned int);
-	va_end(ap);
-
-	purple_debug_misc("oscar",
-			   "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, "
-		     "clear warning at %u, limit at %u, disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
-		     (code < 5) ? codes[code] : codes[0],
-		     rateclass,
-		     currentavg, maxavg,
-		     alert, clear,
-		     limit, disconnect,
-		     delta,
-		     dropping_snacs,
-		     windowsize
-		     );
-
-	if (code == AIM_RATE_CODE_LIMIT)
-	{
-		purple_debug_warning("oscar",  _("The last action you attempted could not be "
-				"performed because you are over the rate limit. "
-				"Please wait 10 seconds and try again.\n"));
-	}
-
-	return 1;
-}
-
 static int purple_parse_evilnotify(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 #ifdef CRAZY_WARNING
 	va_list ap;
@@ -4173,7 +4121,7 @@
 		bi = NULL;
 
 	purple_notify_user_info_add_pair(user_info, _("UIN"), who);
-	oscar_user_info_convert_and_add(account, user_info, _("Nick"), info->nick);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
 	if ((bi != NULL) && (bi->ipaddr != 0)) {
 		char *tstr =  g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
 						(bi->ipaddr & 0xff000000) >> 24,
@@ -4183,9 +4131,9 @@
 		purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
 		g_free(tstr);
 	}
-	oscar_user_info_convert_and_add(account, user_info, _("First Name"), info->first);
-	oscar_user_info_convert_and_add(account, user_info, _("Last Name"), info->last);
-	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, info->email))) {
+	oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
+	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, od, info->email))) {
 		buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 		g_free(buf);
@@ -4194,7 +4142,7 @@
 	if (info->numaddresses && info->email2) {
 		int i;
 		for (i = 0; i < info->numaddresses; i++) {
-			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, info->email2[i]))) {
+			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, od, info->email2[i]))) {
 				buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 				purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 				g_free(buf);
@@ -4202,7 +4150,7 @@
 			}
 		}
 	}
-	oscar_user_info_convert_and_add(account, user_info, _("Mobile Phone"), info->mobile);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
 
 	if (info->gender != 0)
 		purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
@@ -4222,14 +4170,14 @@
 		 * feel free to remove it.  --rlaager */
 		mktime(tm);
 
-		oscar_user_info_convert_and_add(account, user_info, _("Birthday"), purple_date_format_short(tm));
+		oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
 	}
 	if ((info->age > 0) && (info->age < 255)) {
 		char age[5];
 		snprintf(age, sizeof(age), "%hhd", info->age);
 		purple_notify_user_info_add_pair(user_info, _("Age"), age);
 	}
-	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->personalwebpage))) {
+	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->personalwebpage))) {
 		buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
 		g_free(buf);
@@ -4239,33 +4187,33 @@
 	if (buddy != NULL)
 		oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
 
-	oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
 	purple_notify_user_info_add_section_break(user_info);
 
 	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Home Address"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->homeaddr);
-		oscar_user_info_convert_and_add(account, user_info, _("City"), info->homecity);
-		oscar_user_info_convert_and_add(account, user_info, _("State"), info->homestate);
-		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->homezip);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
+		oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
+		oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
 	}
 	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Work Address"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->workaddr);
-		oscar_user_info_convert_and_add(account, user_info, _("City"), info->workcity);
-		oscar_user_info_convert_and_add(account, user_info, _("State"), info->workstate);
-		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->workzip);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
+		oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
+		oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
 	}
 	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Work Information"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Company"), info->workcompany);
-		oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
-		oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
-
-		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->workwebpage))) {
+		oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
+
+		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->workwebpage))) {
 			char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 			purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
 			g_free(webpage);
@@ -4296,7 +4244,7 @@
 	info = va_arg(ap, struct aim_icq_info *);
 	va_end(ap);
 
-	if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, info->nick))) {
+	if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) {
 		g_snprintf(who, sizeof(who), "%u", info->uin);
 		serv_got_alias(gc, who, utf8);
 		if ((b = purple_find_buddy(account, who))) {
@@ -4751,7 +4699,7 @@
 			/* Messaging an SMS (mobile) user */
 			tmp2 = purple_markup_strip_html(tmp1);
 			is_html = FALSE;
-		} else if (oscar_util_valid_name_icq(purple_account_get_username(account))) {
+		} else if (od->icq) {
 			if (oscar_util_valid_name_icq(name)) {
 				/* From ICQ to ICQ */
 				tmp2 = purple_markup_strip_html(tmp1);
@@ -5047,6 +4995,9 @@
 void
 oscar_set_status(PurpleAccount *account, PurpleStatus *status)
 {
+	PurpleConnection *pc;
+	OscarData *od;
+
 	purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status));
 
 	if (!purple_status_is_active(status))
@@ -5055,11 +5006,14 @@
 	if (!purple_account_is_connected(account))
 		return;
 
+	pc = purple_account_get_connection(account);
+	od = purple_connection_get_protocol_data(pc);
+
 	/* Set the AIM-style away message for both AIM and ICQ accounts */
 	oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
 
 	/* Set the ICQ status for ICQ accounts only */
-	if (oscar_util_valid_name_icq(purple_account_get_username(account)))
+	if (od->icq)
 		oscar_set_status_icq(account);
 }
 
@@ -5404,7 +5358,7 @@
 						if (g_utf8_validate(gname, -1, NULL))
 							gname_utf8 = g_strdup(gname);
 						else
-							gname_utf8 = oscar_utf8_try_convert(account, gname);
+							gname_utf8 = oscar_utf8_try_convert(account, od, gname);
 					} else
 						gname_utf8 = NULL;
 
@@ -5419,7 +5373,7 @@
 						if (g_utf8_validate(alias, -1, NULL))
 							alias_utf8 = g_strdup(alias);
 						else
-							alias_utf8 = oscar_utf8_try_convert(account, alias);
+							alias_utf8 = oscar_utf8_try_convert(account, od, alias);
 						g_free(alias);
 					} else
 						alias_utf8 = NULL;
@@ -5468,7 +5422,7 @@
 					if (g_utf8_validate(gname, -1, NULL))
 						gname_utf8 = g_strdup(gname);
 					else
-						gname_utf8 = oscar_utf8_try_convert(account, gname);
+						gname_utf8 = oscar_utf8_try_convert(account, od, gname);
 				} else
 					gname_utf8 = NULL;
 
@@ -5636,7 +5590,7 @@
 		return 1;
 
 	gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
-	gname_utf8 = gname ? oscar_utf8_try_convert(account, gname) : NULL;
+	gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
 
 	alias = aim_ssi_getalias(od->ssi.local, gname, name);
 	if (alias != NULL)
@@ -5644,7 +5598,7 @@
 		if (g_utf8_validate(alias, -1, NULL))
 			alias_utf8 = g_strdup(alias);
 		else
-			alias_utf8 = oscar_utf8_try_convert(account, alias);
+			alias_utf8 = oscar_utf8_try_convert(account, od, alias);
 	}
 	else
 		alias_utf8 = NULL;
@@ -6417,7 +6371,7 @@
 	data = g_new(struct name_data, 1);
 
 	comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
-	comment_utf8 = comment ? oscar_utf8_try_convert(account, comment) : NULL;
+	comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
 
 	data->gc = gc;
 	data->name = g_strdup(name);
--- a/libpurple/protocols/oscar/oscar.h	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sat Feb 06 05:15:35 2010 +0000
@@ -445,6 +445,8 @@
 	guint16 seqnum_in; /**< The sequence number of most recently received packet. */
 	GSList *groups;
 	GSList *rateclasses; /* Contains nodes of struct rateclass. */
+	struct rateclass *default_rateclass;
+	GHashTable *rateclass_members; /* Key is family and subtype, value is pointer to the rateclass struct to use. */
 
 	GQueue *queued_snacs; /**< Contains QueuedSnacs. */
 	GQueue *queued_lowpriority_snacs; /**< Contains QueuedSnacs to send only once queued_snacs is empty */
@@ -1685,9 +1687,7 @@
 	guint32 disconnect;
 	guint32 current;
 	guint32 max;
-	guint32 delta;
 	guint8 dropping_snacs;
-	GHashTable *members; /* Key is family and subtype, value is TRUE. */
 
 	struct timeval last; /**< The time when we last sent a SNAC of this rate class. */
 };
--- a/libpurple/protocols/qq/Makefile.am	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Sat Feb 06 05:15:35 2010 +0000
@@ -65,12 +65,19 @@
 else
 
 st =
+noinst_LTLIBRARIES =
 pkg_LTLIBRARIES  = libqq.la
 libqq_la_SOURCES = $(QQSOURCES)
 libqq_la_LIBADD  = $(GLIB_LIBS)
 
 endif
 
+# For 'make check'
+noinst_LTLIBRARIES += libqq_tmp.la
+libqq_tmp_la_SOURCES = $(libqq_la_SOURCES)
+libqq_tmp_la_LIBADD = $(libqq_la_LIBADD)
+
+
 # QQ_BUDDY_ICON_DIR is the path where a bunch of stock icons can be
 # places for users to choose from when setting their buddy icon.  We
 # don't distribute those icons ourselves because of possibly copyright
--- a/libpurple/protocols/yahoo/util.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/protocols/yahoo/util.c	Sat Feb 06 05:15:35 2010 +0000
@@ -884,6 +884,8 @@
 						}
 						g_free(etag);
 					}
+				} else if (g_str_equal(tag_name, "span") || g_str_equal(tag_name, "/span")) {
+					/* Do nothing */
 				} else {
 					/* We don't know what the tag is. Send it unmodified. */
 					g_string_append(dest, tag);
--- a/libpurple/purple-uninstalled.pc.in	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/purple-uninstalled.pc.in	Sat Feb 06 05:15:35 2010 +0000
@@ -6,9 +6,12 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
 Name: libpurple
 Description: libpurple is a GLib-based instant messenger library.
 Version: @VERSION@
 Requires: glib-2.0
-Cflags: -I${pcfiledir}
-Libs: ${pcfiledir}/libpurple.la
+Cflags: -I${abs_srcdir} -I${abs_builddir}
+Libs: ${abs_builddir}/libpurple.la
--- a/libpurple/tests/Makefile.am	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/tests/Makefile.am	Sat Feb 06 05:15:35 2010 +0000
@@ -28,7 +28,7 @@
 
 check_libpurple_LDADD=\
 		$(top_builddir)/libpurple/protocols/jabber/libjabber.la \
-		$(top_builddir)/libpurple/protocols/qq/libqq.la \
+		$(top_builddir)/libpurple/protocols/qq/libqq_tmp.la \
 		$(top_builddir)/libpurple/protocols/yahoo/libymsg.la \
 		$(top_builddir)/libpurple/libpurple.la \
         @CHECK_LIBS@ \
--- a/libpurple/tests/test_util.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/tests/test_util.c	Sat Feb 06 05:15:35 2010 +0000
@@ -113,6 +113,11 @@
 	purple_markup_html_to_xhtml("<a>", &xhtml, &plaintext);
 	assert_string_equal_free("<a href=\"\"></a>", xhtml);
 	assert_string_equal_free("", plaintext);
+
+
+	purple_markup_html_to_xhtml("<FONT>x</FONT>", &xhtml, &plaintext);
+	assert_string_equal_free("x", xhtml);
+	assert_string_equal_free("x", plaintext);
 }
 END_TEST
 
--- a/libpurple/util.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/libpurple/util.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1519,7 +1519,7 @@
 				if(tag) {
 					while(tags) {
 						struct purple_parse_tag *pt = tags->data;
-						if(xhtml)
+						if(xhtml && !pt->ignore)
 							g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
 						if(plain && purple_strequal(pt->src_tag, "a")) {
 							/* if this is a link, we have to add the url to the plaintext, too */
--- a/pidgin/gtkaccount.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkaccount.c	Sat Feb 06 05:15:35 2010 +0000
@@ -381,6 +381,7 @@
 static void
 update_editable(PurpleConnection *gc, AccountPrefsDialog *dialog)
 {
+	GtkStyle *style;
 	gboolean set;
 	GList *l;
 
@@ -392,10 +393,21 @@
 
 	set = !(purple_account_is_connected(dialog->account) || purple_account_is_connecting(dialog->account));
 	gtk_widget_set_sensitive(dialog->protocol_menu, set);
-	gtk_widget_set_sensitive(dialog->username_entry, set);
-
-	for (l = dialog->user_split_entries ; l != NULL ; l = l->next)
-		gtk_widget_set_sensitive((GtkWidget *)l->data, set);
+	gtk_editable_set_editable(GTK_EDITABLE(dialog->username_entry), set);
+	style = set ? NULL : gtk_widget_get_style(dialog->username_entry);
+	gtk_widget_modify_base(dialog->username_entry, GTK_STATE_NORMAL,
+			style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);
+
+	for (l = dialog->user_split_entries ; l != NULL ; l = l->next) {
+		if (GTK_IS_EDITABLE(l->data)) {
+			gtk_editable_set_editable(GTK_EDITABLE(l->data), set);
+			style = set ? NULL : gtk_widget_get_style(GTK_WIDGET(l->data));
+			gtk_widget_modify_base(GTK_WIDGET(l->data), GTK_STATE_NORMAL,
+					style ? &style->base[GTK_STATE_INSENSITIVE] : NULL);
+		} else {
+			gtk_widget_set_sensitive(GTK_WIDGET(l->data), set);
+		}
+	}
 }
 
 static void
--- a/pidgin/gtkblist-theme-loader.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkblist-theme-loader.c	Sat Feb 06 05:15:35 2010 +0000
@@ -58,6 +58,20 @@
 	return pidgin_theme_font_new(font, &color);
 }
 
+static GdkColor *
+parse_color(xmlnode *node, const char *tag)
+{
+	const char *temp = xmlnode_get_attrib(node, tag);
+	GdkColor color;
+
+	if (temp && gdk_color_parse(temp, &color)) {
+		gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);
+		return gdk_color_copy(&color);
+	} else {
+		return NULL;
+	}
+}
+
 static PurpleTheme *
 pidgin_blist_loader_build(const gchar *dir)
 {
@@ -65,7 +79,7 @@
 	gchar *filename_full, *data = NULL;
 	const gchar *temp, *name;
 	gboolean success = TRUE;
-	GdkColor bgcolor, expanded_bgcolor, collapsed_bgcolor, contact_color;
+	GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color;
 	PidginThemeFont *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status;
 	PidginBlistLayout layout;
 	PidginBlistTheme *theme;
@@ -119,14 +133,10 @@
 		purple_debug_warning("gtkblist-theme-loader", "Missing attribute or problem with the root element\n");
 
 	if (success) {
-		if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) {
-
-			if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, &bgcolor))
-				gdk_colormap_alloc_color(gdk_colormap_get_system(), &bgcolor, FALSE, TRUE);
-			else
-				memset(&bgcolor, 0, sizeof(GdkColor));
-
-		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <blist>.\n");
+		if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL))
+			bgcolor = parse_color(sub_node, "color");
+		else 
+			purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <blist>.\n");
 	}
 
 	/* <groups> */
@@ -134,26 +144,17 @@
 		if ((success = (sub_node = xmlnode_get_child(root_node, "groups")) != NULL
 			     && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) {
 			expanded = pidgin_theme_font_parse(sub_sub_node);
-
-			if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &expanded_bgcolor))
-				gdk_colormap_alloc_color(gdk_colormap_get_system(), &expanded_bgcolor, FALSE, TRUE);
-			else
-				memset(&expanded_bgcolor, 0, sizeof(GdkColor));
-
-		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <expanded>.\n");
+			expanded_bgcolor = parse_color(sub_sub_node, "background");
+		} else
+			purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <expanded>.\n");
 	}
 
 	if (success) {
 		if ((success = sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) {
-
 			collapsed = pidgin_theme_font_parse(sub_sub_node);
-
-			if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &collapsed_bgcolor))
-				gdk_colormap_alloc_color(gdk_colormap_get_system(), &collapsed_bgcolor, FALSE, TRUE);
-			else
-				memset(&collapsed_bgcolor, 0, sizeof(GdkColor));
-
-		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <collapsed>.\n");
+			collapsed_bgcolor = parse_color(sub_sub_node, "background");
+		} else
+			purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <groups> <collapsed>.\n");
 	}
 
 	/* <buddys> */
@@ -172,13 +173,10 @@
 	}
 
 	if (success) {
-		if ((success = (sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL))) {
-			if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), &contact_color))
-				gdk_colormap_alloc_color(gdk_colormap_get_system(), &contact_color, FALSE, TRUE);
-			else
-				memset(&contact_color, 0, sizeof(GdkColor));
-
-		} else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <background>.\n");
+		if ((success = (sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL)))
+			contact_color = parse_color(sub_sub_node, "color");
+		else
+			purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: <buddys> <background>.\n");
 	}
 
 	for (i = 0; success && lookups[i].tag; i++) {
@@ -201,13 +199,13 @@
 			"image", xmlnode_get_attrib(root_node, "image"),
 			"directory", dir,
 			"description", data,
-			"background-color", &bgcolor,
+			"background-color", bgcolor,
 			"layout", &layout,
-			"expanded-color", &expanded_bgcolor,
+			"expanded-color", expanded_bgcolor,
 			"expanded-text", expanded,
-			"collapsed-color", &collapsed_bgcolor,
+			"collapsed-color", collapsed_bgcolor,
 			"collapsed-text", collapsed,
-			"contact-color", &contact_color,
+			"contact-color", contact_color,
 			"contact", contact,
 			"online", online,
 			"away", away,
@@ -235,6 +233,15 @@
 		theme = NULL;
 	}
 
+	if (bgcolor)
+		gdk_color_free(bgcolor);
+	if (expanded_bgcolor)
+		gdk_color_free(expanded_bgcolor);
+	if (collapsed_bgcolor)
+		gdk_color_free(collapsed_bgcolor);
+	if (contact_color)
+		gdk_color_free(contact_color);
+
 	return PURPLE_THEME(theme);
 }
 
--- a/pidgin/gtkblist-theme.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkblist-theme.c	Sat Feb 06 05:15:35 2010 +0000
@@ -133,7 +133,7 @@
 	copy->font  = g_strdup(pair->font);
 	strncpy(copy->color, pair->color, sizeof(copy->color) - 1);
 	if (pair->gdkcolor)
-		copy->gdkcolor = gdk_color_copy(pair->gdkcolor);
+		copy->gdkcolor = pair->gdkcolor ? gdk_color_copy(pair->gdkcolor) : NULL;
 	return copy;
 }
 
@@ -704,7 +704,7 @@
 
 	if (priv->bgcolor)
 		gdk_color_free(priv->bgcolor);
-	priv->bgcolor = gdk_color_copy(color);
+	priv->bgcolor = color ? gdk_color_copy(color) : NULL;
 }
 
 void
@@ -743,7 +743,7 @@
 
 	if (priv->expanded_color)
 		gdk_color_free(priv->expanded_color);
-	priv->expanded_color = gdk_color_copy(color);
+	priv->expanded_color = color ? gdk_color_copy(color) : NULL;
 }
 
 void
@@ -770,7 +770,7 @@
 
 	if (priv->collapsed_color)
 		gdk_color_free(priv->collapsed_color);
-	priv->collapsed_color = gdk_color_copy(color);
+	priv->collapsed_color = color ? gdk_color_copy(color) : NULL;
 }
 
 void
@@ -797,7 +797,7 @@
 
 	if (priv->contact_color)
 		gdk_color_free(priv->contact_color);
-	priv->contact_color = gdk_color_copy(color);
+	priv->contact_color = color ? gdk_color_copy(color) : NULL;
 }
 
 void
--- a/pidgin/gtkblist.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkblist.c	Sat Feb 06 05:15:35 2010 +0000
@@ -470,6 +470,9 @@
 	case PURPLE_BLIST_GROUP_NODE:
 		text = purple_group_get_name(PURPLE_GROUP(node));
 		break;
+	case PURPLE_BLIST_CHAT_NODE:
+		text = purple_chat_get_name(PURPLE_CHAT(node));
+		break;
 	default:
 		g_return_if_reached();
 	}
@@ -5613,6 +5616,51 @@
 
 }
 
+static gboolean
+pidgin_blist_search_equal_func(GtkTreeModel *model, gint column,
+			const gchar *key, GtkTreeIter *iter, gpointer data)
+{
+	PurpleBlistNode *node = NULL;
+	gboolean res = TRUE;
+	const char *compare = NULL;
+
+	if (!pidgin_tree_view_search_equal_func(model, column, key, iter, data))
+		return FALSE;
+
+	/* If the search string does not match the displayed label, then look
+	 * at the alternate labels for the nodes and search in them. Currently,
+	 * alternate labels that make sense are usernames/email addresses for
+	 * buddies (but only for the ones who don't have a local alias).
+	 */
+
+	gtk_tree_model_get(model, iter, NODE_COLUMN, &node, -1);
+	if (!node)
+		return TRUE;
+
+	compare = NULL;
+	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+		PurpleBuddy *b = purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
+		if (!purple_buddy_get_local_buddy_alias(b))
+			compare = purple_buddy_get_name(b);
+	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		if (!purple_buddy_get_local_buddy_alias(PURPLE_BUDDY(node)))
+			compare = purple_buddy_get_name(PURPLE_BUDDY(node));
+	}
+
+	if (compare) {
+		char *tmp, *enteredstring;
+		tmp = g_utf8_normalize(key, -1, G_NORMALIZE_DEFAULT);
+		enteredstring = g_utf8_casefold(tmp, -1);
+		g_free(tmp);
+
+		if (purple_str_has_prefix(compare, enteredstring))
+			res = FALSE;
+		g_free(enteredstring);
+	}
+
+	return res;
+}
+
 static void pidgin_blist_show(PurpleBuddyList *list)
 {
 	PidginBuddyListPrivate *priv;
@@ -5849,7 +5897,8 @@
 
 	/* Enable CTRL+F searching */
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN);
-	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview), pidgin_tree_view_search_equal_func, NULL, NULL);
+	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview),
+			pidgin_blist_search_equal_func, NULL, NULL);
 
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0);
 	gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
--- a/pidgin/gtkconv.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkconv.c	Sat Feb 06 05:15:35 2010 +0000
@@ -497,17 +497,15 @@
 						prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 
 					if ((prpl_info != NULL) && (prpl_info->options & OPT_PROTO_SLASH_COMMANDS_NATIVE)) {
-						char *firstspace;
-						char *slash;
-
-						firstspace = strchr(cmdline, ' ');
-						if (firstspace != NULL) {
-							slash = strrchr(firstspace, '/');
-						} else {
-							slash = strchr(cmdline, '/');
-						}
-
-						if (slash == NULL) {
+						char *spaceslash;
+
+						/* If the first word in the entered text has a '/' in it, then the user
+						 * probably didn't mean it as a command. So send the text as message. */
+						spaceslash = cmdline;
+						while (*spaceslash && *spaceslash != ' ' && *spaceslash != '/')
+							spaceslash++;
+
+						if (*spaceslash != '/') {
 							purple_conversation_write(conv, "", _("Unknown command."), PURPLE_MESSAGE_NO_LOG, time(NULL));
 							retval = TRUE;
 						}
@@ -4652,6 +4650,8 @@
 	int max_height = total_height / 2;
 	int min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines");
 	int min_height;
+	gboolean interior_focus;
+	int focus_width;
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4678,6 +4678,13 @@
 	min_height = min_lines * (oneline.height + MAX(pad_inside, pad_top + pad_bottom));
 	height = CLAMP(height, MIN(min_height, max_height), max_height);
 
+	gtk_widget_style_get(gtkconv->entry,
+	                     "interior-focus", &interior_focus,
+	                     "focus-line-width", &focus_width,
+	                     NULL);
+	if (!interior_focus)
+		height += 2 * focus_width;
+
 	diff = height - gtkconv->entry->allocation.height;
 	if (ABS(diff) < oneline.height / 2)
 		return FALSE;
@@ -5493,10 +5500,12 @@
 	}
 
 	/* Somebody wants to keep this conversation around, so don't time it out */
-	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
-	if (timer) {
-		purple_timeout_remove(timer);
-		purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(0));
+	if (conv) {
+		timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+		if (timer) {
+			purple_timeout_remove(timer);
+			purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(0));
+		}
 	}
 }
 
@@ -7622,16 +7631,24 @@
 	}
 }
 
+struct _status_timeout_user {
+	gchar *name;
+	PurpleAccount *account;
+};
+
 static gboolean
-update_buddy_status_timeout(PurpleBuddy *buddy)
+update_buddy_status_timeout(struct _status_timeout_user *user)
 {
 	/* To remove the signing-on/off door icon */
 	PurpleConversation *conv;
 
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, user->name, user->account);
 	if (conv)
 		pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON);
 
+	g_free(user->name);
+	g_free(user);
+
 	return FALSE;
 }
 
@@ -7640,6 +7657,7 @@
 {
 	PidginConversation *gtkconv;
 	PurpleConversation *conv;
+	struct _status_timeout_user *user;
 
 	gtkconv = get_gtkconv_with_contact(purple_buddy_get_contact(buddy));
 	if (gtkconv)
@@ -7652,8 +7670,12 @@
 			pidgin_conv_update_fields(conv, PIDGIN_CONV_MENU);
 	}
 
+	user = g_malloc(sizeof(struct _status_timeout_user));
+	user->name = g_strdup(buddy->name);
+	user->account = buddy->account;
+
 	/* In case a conversation is started after the buddy has signed-on/off */
-	purple_timeout_add_seconds(11, (GSourceFunc)update_buddy_status_timeout, buddy);
+	purple_timeout_add_seconds(11, (GSourceFunc)update_buddy_status_timeout, user);
 }
 
 static void
@@ -9484,6 +9506,24 @@
 	return FALSE;
 }
 
+static gboolean
+gtkconv_tab_set_tip(GtkWidget *widget, GdkEventCrossing *event, PidginConversation *gtkconv)
+{
+#if GTK_CHECK_VERSION(2, 12, 0)
+#define gtk_tooltips_set_tip(tips, w, l, p)  gtk_widget_set_tooltip_text(w, l)
+#endif
+	PangoLayout *layout;
+
+	layout = gtk_label_get_layout(GTK_LABEL(gtkconv->tab_label));
+	gtk_tooltips_set_tip(gtkconv->tooltips, widget,
+			pango_layout_is_ellipsized(layout) ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : NULL,
+			NULL);
+	return FALSE;
+#if GTK_CHECK_VERSION(2, 12, 0)
+#undef gtk_tooltips_set_tip
+#endif
+}
+
 void
 pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
 {
@@ -9642,6 +9682,8 @@
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
 #endif
 	gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby);
+	g_signal_connect(G_OBJECT(ebox), "enter-notify-event",
+			G_CALLBACK(gtkconv_tab_set_tip), gtkconv);
 
 	if (gtkconv->tab_label->parent == NULL) {
 		/* Pack if it's a new widget */
--- a/pidgin/gtkdebug.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkdebug.c	Sat Feb 06 05:15:35 2010 +0000
@@ -766,11 +766,12 @@
 		gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
 
 		/* regex toggle button */
+		image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
 		win->filter =
 			gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
 									   GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
 									   NULL, _("Filter"), _("Filter"),
-									   NULL, NULL,
+									   NULL, image,
 									   G_CALLBACK(regex_filter_toggled_cb),
 									   win);
 		/* we purposely disable the toggle button here in case
--- a/pidgin/gtkdialogs.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkdialogs.c	Sat Feb 06 05:15:35 2010 +0000
@@ -197,11 +197,11 @@
 	{N_("Kurdish"),             "ku", "Amed Ç. Jiyan", "amedcj@hotmail.com"},
 	{N_("Kurdish"),             "ku", "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
 	{N_("Lao"),                 "lo", "Anousak Souphavah", "anousak@gmail.com"},
-	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
 	{N_("Macedonian"),          "mk", "Arangel Angov ", "arangel@linux.net.mk"},
 	{N_("Macedonian"),          "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
 	{N_("Macedonian"),          "mk", "Jovan Naumovski", "jovan@lugola.net"},
 	{N_("Mongolian"),           "mn", "gooyo", NULL},
+	{N_("Marathi"),             "mr", "Sandeep Shedmake", "sandeep.shedmake@gmail.com"},
 	{N_("Malay"),               "ms_MY", "Muhammad Najmi bin Ahmad Zabidi", "najmi.zabidi@gmail.com"},
 	{N_("Bokmål Norwegian"),    "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"},
 	{N_("Nepali"),              "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"},
@@ -276,8 +276,9 @@
 	{N_("Georgian"),            "ka", "Temuri Doghonadze", NULL},
 	{N_("Korean"),              "ko", "Sang-hyun S, A Ho-seok Lee", NULL},
 	{N_("Korean"),              "ko", "Kyeong-uk Son", NULL},
+	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
+	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Lithuanian"),          "lt", "Andrius Štikonas", NULL},
-	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Macedonian"),          "mk", "Tomislav Markovski", NULL},
 	{N_("Bokmål Norwegian"),    "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
 	{N_("Bokmål Norwegian"),    "nb", "Petter Johan Olsen", NULL},
--- a/pidgin/gtkft.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkft.c	Sat Feb 06 05:15:35 2010 +0000
@@ -730,6 +730,7 @@
 	GtkWidget *sw;
 	GtkWidget *button;
 	GtkWidget *expander;
+	GtkWidget *alignment;
 	GtkWidget *table;
 	GtkWidget *checkbox;
 
@@ -787,9 +788,15 @@
 
 	gtk_widget_set_sensitive(expander, FALSE);
 
+	/* Small indent make table fall under GtkExpander's label */
+	alignment = gtk_alignment_new(1, 0, 1, 1);
+	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 20, 0);
+	gtk_container_add(GTK_CONTAINER(expander), alignment);
+	gtk_widget_show(alignment);
+
 	/* The table of information. */
 	table = make_info_table(dialog);
-	gtk_container_add(GTK_CONTAINER(expander), table);
+	gtk_container_add(GTK_CONTAINER(alignment), table);
 	gtk_widget_show(table);
 
 	/* Open button */
--- a/pidgin/gtkimhtml.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkimhtml.c	Sat Feb 06 05:15:35 2010 +0000
@@ -549,6 +549,7 @@
 	imhtml->tip_timer = 0;
 	imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
 	gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
+	gtk_window_set_title(GTK_WINDOW(imhtml->tip_window), "GtkIMHtml");
 	gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
 	gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
 #if GTK_CHECK_VERSION(2,10,0)
@@ -4933,7 +4934,9 @@
 	mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
 
 	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
+	gtk_text_buffer_begin_user_action(imhtml->text_buffer);
 	gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, &iter);
+	gtk_text_buffer_end_user_action(imhtml->text_buffer);
 }
 
 static gboolean
--- a/pidgin/gtklog.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtklog.c	Sat Feb 06 05:15:35 2010 +0000
@@ -423,6 +423,7 @@
 {
 	PidginLogViewer *viewer = data;
 	gtk_imhtml_search_find(GTK_IMHTML(viewer->imhtml), viewer->search);
+	g_object_steal_data(G_OBJECT(viewer->entry), "search-find-cb");
 	return FALSE;
 }
 
@@ -475,8 +476,11 @@
 	g_free(read);
 
 	if (viewer->search != NULL) {
+		guint source;
 		gtk_imhtml_search_clear(GTK_IMHTML(viewer->imhtml));
-		g_idle_add(search_find_cb, viewer);
+		source = g_idle_add(search_find_cb, viewer);
+		g_object_set_data_full(G_OBJECT(viewer->entry), "search-find-cb",
+		                       GINT_TO_POINTER(source), (GDestroyNotify)g_source_remove);
 	}
 
 	pidgin_clear_cursor(viewer->window);
--- a/pidgin/gtkprefs.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkprefs.c	Sat Feb 06 05:15:35 2010 +0000
@@ -70,27 +70,33 @@
 	gchar *original_name;
 };
 
-static int sound_row_sel = 0;
-static GtkWidget *prefsnotebook;
-
+/* Main dialog */
+static GtkWidget *prefs = NULL;
+
+/* Notebook */
+static GtkWidget *prefsnotebook = NULL;
+static int notebook_page = 0;
+
+/* Conversations page */
+static GtkWidget *sample_imhtml = NULL;
+
+/* Themes page */
+static GtkWidget *prefs_sound_themes_combo_box;
+static GtkWidget *prefs_blist_themes_combo_box;
+static GtkWidget *prefs_status_themes_combo_box;
+static GtkWidget *prefs_smiley_themes_combo_box;
+
+/* Sound theme specific */
 static GtkWidget *sound_entry = NULL;
-
-static GtkWidget *prefs = NULL;
-static GtkWidget *debugbutton = NULL;
-static int notebook_page = 0;
-
+static int sound_row_sel = 0;
+static gboolean prefs_sound_themes_loading;
+
+/* These exist outside the lifetime of the prefs dialog */
 static GtkListStore *prefs_sound_themes;
 static GtkListStore *prefs_blist_themes;
 static GtkListStore *prefs_status_icon_themes;
 static GtkListStore *prefs_smiley_themes;
 
-static GtkWidget *prefs_sound_themes_combo_box;
-static GtkWidget *prefs_blist_themes_combo_box;
-static GtkWidget *prefs_status_themes_combo_box;
-static GtkWidget *prefs_smiley_themes_combo_box;
-
-static gboolean prefs_sound_themes_loading;
-
 /*
  * PROTOTYPES
  */
@@ -334,10 +340,21 @@
 	/* Unregister callbacks. */
 	purple_prefs_disconnect_by_handle(prefs);
 
-	prefs = NULL;
+	/* NULL-ify globals */
 	sound_entry = NULL;
-	debugbutton = NULL;
+	sound_row_sel = 0;
+	prefs_sound_themes_loading = FALSE;
+
+	prefs_sound_themes_combo_box = NULL;
+	prefs_blist_themes_combo_box = NULL;
+	prefs_status_themes_combo_box = NULL;
+	prefs_smiley_themes_combo_box = NULL;
+
+	sample_imhtml = NULL;
+
 	notebook_page = 0;
+	prefsnotebook = NULL;
+	prefs = NULL;
 }
 
 static gchar *
@@ -949,6 +966,7 @@
 		gtk_tree_model_get(GTK_TREE_MODEL(prefs_smiley_themes), &new_iter, 2, &new_theme, -1);
 
 		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme);
+		pidgin_themes_smiley_themeize(sample_imhtml);
 
 		g_free(new_theme);
 	}
@@ -1537,7 +1555,7 @@
 					 G_CALLBACK(formatting_toggle_cb), toolbar);
 	g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear",
 					 G_CALLBACK(formatting_clear_cb), NULL);
-
+	sample_imhtml = imhtml;
 
 	gtk_widget_show(ret);
 
@@ -2842,7 +2860,7 @@
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
 
 	/* Smiley Callbacks */
-	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
+	purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
 								smiley_theme_pref_cb, NULL);
 
 	pidgin_prefs_update_old();
--- a/pidgin/gtkrequest.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkrequest.c	Sat Feb 06 05:15:35 2010 +0000
@@ -719,9 +719,25 @@
 {
 	PurpleRequestFieldGroup *group;
 	PidginRequestData *req_data;
-	const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+	if (purple_request_field_string_is_multiline(field))
+	{
+		char *text;
+		GtkTextIter start_iter, end_iter;
+
+		gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(entry), &start_iter);
+		gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(entry), &end_iter);
 
-	purple_request_field_string_set_value(field, (*text == '\0' ? NULL : text));
+		text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(entry), &start_iter, &end_iter, FALSE);
+		purple_request_field_string_set_value(field, (!text || !*text) ? NULL : text);
+		g_free(text);
+	}
+	else
+	{
+		const char *text = NULL;
+		text = gtk_entry_get_text(GTK_ENTRY(entry));
+		purple_request_field_string_set_value(field, (*text == '\0') ? NULL : text);
+	}
 
 	group = purple_request_field_get_group(field);
 	req_data = (PidginRequestData *)group->fields_list->ui_data;
@@ -824,6 +840,13 @@
 
 		g_signal_connect(G_OBJECT(textview), "focus-out-event",
 						 G_CALLBACK(field_string_focus_out_cb), field);
+
+	    if (purple_request_field_is_required(field))
+	    {
+			GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
+			g_signal_connect(G_OBJECT(buffer), "changed",
+							 G_CALLBACK(req_entry_field_changed_cb), field);
+	    }
 	}
 	else
 	{
--- a/pidgin/gtkstatusbox.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkstatusbox.c	Sat Feb 06 05:15:35 2010 +0000
@@ -2541,6 +2541,8 @@
 	GdkRectangle oneline;
 	int height;
 	int pad_top, pad_inside, pad_bottom;
+	gboolean interior_focus;
+	int focus_width;
 
 	if (!status_box->imhtml_visible)
 	{
@@ -2591,6 +2593,13 @@
 	height += (pad_top + pad_bottom) * lines;
 	height += (pad_inside) * (display_lines - lines);
 
+	gtk_widget_style_get(status_box->imhtml,
+	                     "interior-focus", &interior_focus,
+	                     "focus-line-width", &focus_width,
+	                     NULL);
+	if (!interior_focus)
+		height += 2 * focus_width;
+
 	gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE);
 }
 
@@ -2621,6 +2630,7 @@
 	gpointer data;
 	GList *accounts = NULL, *node;
 	int active;
+	gboolean wastyping = FALSE;
 
 
 	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(status_box->dropdown_store), &iter, path))
@@ -2633,7 +2643,7 @@
 			   TYPE_COLUMN, &type,
 			   DATA_COLUMN, &data,
 			   -1);
-	if (status_box->typing != 0)
+	if ((wastyping = (status_box->typing != 0)))
 		purple_timeout_remove(status_box->typing);
 	status_box->typing = 0;
 
@@ -2657,14 +2667,18 @@
 			pidgin_status_editor_show(FALSE,
 				purple_savedstatus_is_transient(saved_status)
 					? saved_status : NULL);
-			status_menu_refresh_iter(status_box, FALSE);
+			status_menu_refresh_iter(status_box, wastyping);
+			if (wastyping)
+				pidgin_status_box_refresh(status_box);
 			return;
 		}
 
 		if (type == PIDGIN_STATUS_BOX_TYPE_SAVED)
 		{
 			pidgin_status_window_show();
-			status_menu_refresh_iter(status_box, FALSE);
+			status_menu_refresh_iter(status_box, wastyping);
+			if (wastyping)
+				pidgin_status_box_refresh(status_box);
 			return;
 		}
 	}
--- a/pidgin/gtkthemes.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkthemes.c	Sat Feb 06 05:15:35 2010 +0000
@@ -263,7 +263,14 @@
 
 		if (buf[0] == '#' || buf[0] == '\0')
 			continue;
-
+		else {
+			int len = strlen(buf);
+			while (len && (buf[len - 1] == '\r' || buf[len - 1] == '\n'))
+				buf[--len] = '\0';
+			if (len == 0)
+				continue;
+		}
+		
 		i = buf;
 		while (isspace(*i))
 			i++;
@@ -280,25 +287,17 @@
 				list->smileys = g_slist_reverse(list->smileys);
 			list = child;
 		} else if (!g_ascii_strncasecmp(i, "Name=", strlen("Name="))) {
-			int len;
 			g_free(theme->name);
 			theme->name = g_strdup(i + strlen("Name="));
-			len = strlen(theme->name);
-			theme->name[len-1] = 0;
-			if(len > 2 && theme->name[len-2] == '\r')
-				theme->name[len-2] = 0;
 		} else if (!g_ascii_strncasecmp(i, "Description=", strlen("Description="))) {
 			g_free(theme->desc);
 			theme->desc = g_strdup(i + strlen("Description="));
-			theme->desc[strlen(theme->desc)-1] = 0;
 		} else if (!g_ascii_strncasecmp(i, "Icon=", strlen("Icon="))) {
 			g_free(theme->icon);
 			theme->icon = g_build_filename(dirname, i + strlen("Icon="), NULL);
-			theme->icon[strlen(theme->icon)-1] = 0;
 		} else if (!g_ascii_strncasecmp(i, "Author=", strlen("Author="))) {
 			g_free(theme->author);
 			theme->author = g_strdup(i + strlen("Author="));
-			theme->author[strlen(theme->author)-1] = 0;
 		} else if (load && list) {
 			gboolean hidden = FALSE;
 			char *sfile = NULL;
@@ -310,8 +309,8 @@
 			while  (*i) {
 				char l[64];
 				int li = 0;
-				while (!isspace(*i) && li < sizeof(l) - 1) {
-					if (*i == '\\' && *(i+1) != '\0' && *(i+1) != '\n' && *(i+1) != '\r')
+				while (*i && !isspace(*i) && li < sizeof(l) - 1) {
+					if (*i == '\\' && *(i+1) != '\0')
 						i++;
 					l[li++] = *(i++);
 				}
--- a/pidgin/gtkutils.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/gtkutils.c	Sat Feb 06 05:15:35 2010 +0000
@@ -1586,8 +1586,8 @@
 						    _("You have dragged an image"),
 						    _("You can send this image as a file transfer, "
 						      "embed it into this message, or use it as the buddy icon for this user."),
-						    DND_FILE_TRANSFER, "OK", (GCallback)dnd_image_ok_callback,
-						    "Cancel", (GCallback)dnd_image_cancel_callback,
+						    DND_FILE_TRANSFER, _("OK"), (GCallback)dnd_image_ok_callback,
+						    _("Cancel"), (GCallback)dnd_image_cancel_callback,
 							account, who, NULL,
 							data,
 							_("Set as buddy icon"), DND_BUDDY_ICON,
@@ -1606,8 +1606,8 @@
 						    (ft ? _("You can send this image as a file transfer, or use it as the buddy icon for this user.") :
 						    _("You can insert this image into this message, or use it as the buddy icon for this user")),
 						    (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE),
-							"OK", (GCallback)dnd_image_ok_callback,
-						    "Cancel", (GCallback)dnd_image_cancel_callback,
+							_("OK"), (GCallback)dnd_image_ok_callback,
+						    _("Cancel"), (GCallback)dnd_image_cancel_callback,
 							account, who, NULL,
 							data,
 						    _("Set as buddy icon"), DND_BUDDY_ICON,
@@ -2723,6 +2723,7 @@
 		g_object_unref(G_OBJECT(pixbuf));
 		if (!success) {
 			purple_debug_error("buddyicon", "Could not convert icon to usable format.\n");
+			g_free(filename);
 			return NULL;
 		}
 
--- a/pidgin/pidgin-uninstalled.pc.in	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/pidgin-uninstalled.pc.in	Sat Feb 06 05:15:35 2010 +0000
@@ -6,8 +6,11 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
 Name: Pidgin
 Description: Pidgin is a GTK2-based instant messenger application.
 Version: @VERSION@
 Requires: gtk+-2.0 purple
-Cflags: -I${pcfiledir}
+Cflags: -I${abs_srcdir}
--- a/pidgin/plugins/timestamp.c	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/plugins/timestamp.c	Sat Feb 06 05:15:35 2010 +0000
@@ -49,25 +49,33 @@
 	const char *mdate;
 	int y, height;
 	GdkRectangle rect;
+	gboolean scrolled = FALSE;
+	GtkTextTag *tag;
 
 	/* display timestamp */
 	mdate = purple_utf8_strftime(then == 0 ? "%H:%M" : "\n%H:%M",
 		localtime(&now));
 	gtk_text_buffer_get_end_iter(buffer, &iter);
 
-	if (gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "TIMESTAMP") == NULL)
-		gtk_text_buffer_create_tag(buffer, "TIMESTAMP",
+	/* is the view already scrolled? */
+	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
+	gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
+	if (((y + height) - (rect.y + rect.height)) > height)
+		scrolled = TRUE;
+
+	if ((tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "TIMESTAMP")) == NULL)
+		tag = gtk_text_buffer_create_tag(buffer, "TIMESTAMP",
 			"foreground", "#888888", "justification", GTK_JUSTIFY_CENTER,
 			"weight", PANGO_WEIGHT_BOLD, NULL);
 
-	gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, mdate,
-		strlen(mdate), "TIMESTAMP", NULL);
+	gtk_text_buffer_insert_with_tags(buffer, &iter, mdate,
+		strlen(mdate), tag, NULL);
 
 	/* scroll view if necessary */
 	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
 	gtk_text_view_get_line_yrange(
 		GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
-	if (((y + height) - (rect.y + rect.height)) > height &&
+	if (!scrolled && ((y + height) - (rect.y + rect.height)) > height &&
 	    gtk_text_buffer_get_char_count(buffer)) {
 		gboolean smooth = purple_prefs_get_bool(
 			PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling");
--- a/pidgin/win32/nsis/translations/polish.nsh	Thu Feb 04 20:57:01 2010 +0000
+++ b/pidgin/win32/nsis/translations/polish.nsh	Sat Feb 06 05:15:35 2010 +0000
@@ -4,33 +4,82 @@
 ;;  Polish language strings for the Windows Pidgin NSIS installer.
 ;;  Windows Code page: 1250
 ;;
-;;  Author: Jan Eldenmalm <jan.eldenmalm@amazingports.com>
-;;  Version 2
-;;
+;;  Version 3
+;;  Note: If translating this file, replace '!insertmacro PIDGIN_MACRO_DEFAULT_STRING'
+;;  with '!define'.