propagate from branch 'im.pidgin.pidgin' (head 45e375cfc6695c752631c5a2710c8bccf1db3e46) soc.2007.certmgr

Fri, 01 Jun 2007 23:53:05 +0000

author
William Ehlhardt <williamehlhardt@gmail.com>
date
Fri, 01 Jun 2007 23:53:05 +0000
branch
soc.2007.certmgr
changeset 17505
b19270fadc24
parent 17442
9bdc2503513a (current diff)
parent 17504
45e375cfc669 (diff)
child 17506
eff6bdd382f3

propagate from branch 'im.pidgin.pidgin' (head 45e375cfc6695c752631c5a2710c8bccf1db3e46)
to branch 'im.pidgin.soc.2007.certmgr' (head 9bdc2503513a3fc9c9479926dd4895c9bf6ec54b)

pidgin/pixmaps/emotes/default/22/default.theme.in file | annotate | diff | comparison | revisions
pidgin/pixmaps/emotes/default/22/theme file | annotate | diff | comparison | revisions
pidgin/pixmaps/emotes/none/none.theme.in file | annotate | diff | comparison | revisions
pidgin/pixmaps/emotes/none/theme file | annotate | diff | comparison | revisions
--- a/.mtn-ignore	Thu May 31 00:40:46 2007 +0000
+++ b/.mtn-ignore	Fri Jun 01 23:53:05 2007 +0000
@@ -12,6 +12,7 @@
 Doxyfile$
 aclocal.m4
 compile
+config.cache
 config.guess
 config.h$
 config.h.in
@@ -32,6 +33,8 @@
 pidgin-.*.tar.gz
 pidgin-.*.tar.bz2
 pidgin/pidgin$
+pidgin/pixmaps/emotes/default/22/theme
+pidgin/pixmaps/emotes/none/theme
 pidgin/plugins/musicmessaging/music-messaging-bindings.c
 pidgin/plugins/perl/common/Makefile.PL$
 pidgin/win32/pidgin_dll_rc.rc$
--- a/COPYRIGHT	Thu May 31 00:40:46 2007 +0000
+++ b/COPYRIGHT	Fri Jun 01 23:53:05 2007 +0000
@@ -129,6 +129,7 @@
 Decklin Foster
 Francesco Fracassi
 Adam Fritzler
+Takao Fujiwara
 Max G.
 François Gagné
 Andrew Gaul
@@ -149,6 +150,7 @@
 Casey Harkins
 Andy Harrison
 Andrew Hart (arhart)
+Rene Hausleitner
 G. Sumner Hayes
 Michael R. Head
 Nick Hebner
@@ -333,8 +335,10 @@
 Richard Stellingwerff
 Charlie Stockman
 David Stoddard
+Oleg Sukhodolsky
 Sun Microsystems
 Mårten Svantesson (fursten)
+Amir Szekely (kichik)
 Robert T.
 Greg Taeger
 Peter Tang
--- a/ChangeLog	Thu May 31 00:40:46 2007 +0000
+++ b/ChangeLog	Fri Jun 01 23:53:05 2007 +0000
@@ -1,6 +1,14 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.0.2 (??/??/????):
+	Pidgin:
+	* Added a custom conversation font option to preferences
+
+	libpurple:
+	* Moving an ICQ buddy from one group to another no longer
+	  re-requests authorization from that person (Rene Hausleitner)
+	* Added nullprpl, an example protocol plugin (Ryan Barrett)
+	* Fixed SOCKS5 bug which caused Jabber file receiving to fail
 
 	Finch:
 	* Auto account reconnecting
--- a/configure.ac	Thu May 31 00:40:46 2007 +0000
+++ b/configure.ac	Fri Jun 01 23:53:05 2007 +0000
@@ -2085,6 +2085,7 @@
 		   libpurple/protocols/jabber/Makefile
 		   libpurple/protocols/msn/Makefile
 		   libpurple/protocols/novell/Makefile
+		   libpurple/protocols/null/Makefile
 		   libpurple/protocols/oscar/Makefile
 		   libpurple/protocols/qq/Makefile
 		   libpurple/protocols/sametime/Makefile
--- a/finch/gntrequest.c	Thu May 31 00:40:46 2007 +0000
+++ b/finch/gntrequest.c	Fri Jun 01 23:53:05 2007 +0000
@@ -672,10 +672,13 @@
 			pt = purple_prefs_get_type(id);
 			switch (pt) {
 				case PURPLE_PREF_INT:
+				{
+					long int tmp;
 					if (type == PURPLE_REQUEST_FIELD_LIST) /* Lists always return string */
-						sscanf(val, "%ld", (long int *)&val);
-					purple_prefs_set_int(id, GPOINTER_TO_INT(val));
+						sscanf(val, "%ld", &tmp);
+					purple_prefs_set_int(id, (gint)tmp);
 					break;
+				}
 				case PURPLE_PREF_BOOLEAN:
 					purple_prefs_set_bool(id, GPOINTER_TO_INT(val));
 					break;
--- a/libpurple/connection.h	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/connection.h	Fri Jun 01 23:53:05 2007 +0000
@@ -279,7 +279,7 @@
  * TODO: Eventually this bad boy will be removed, because it is
  *       a gross fix for a crashy problem.
  */
-#define PURPLE_CONNECTION_IS_VALID(gc) (g_list_find(purple_connections_get_all(), (gc)))
+#define PURPLE_CONNECTION_IS_VALID(gc) (g_list_find(purple_connections_get_all(), (gc)) != NULL)
 
 /*@}*/
 
--- a/libpurple/dbus-analyze-functions.py	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/dbus-analyze-functions.py	Fri Jun 01 23:53:05 2007 +0000
@@ -166,8 +166,11 @@
         self.returncode = []
 
     def flush(self):
+	paramslist = ", ".join(self.paramshdr)
+	if (paramslist == "") :
+	    paramslist = "void"
         print "%s %s(%s)" % (self.functiontype, self.function.name,
-                             ", ".join(self.paramshdr)),
+                             paramslist),
 
         if self.headersonly:
             print ";"
--- a/libpurple/plugins/perl/perl-common.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/plugins/perl/perl-common.c	Fri Jun 01 23:53:05 2007 +0000
@@ -205,7 +205,7 @@
 		purple_debug(PURPLE_DEBUG_ERROR, "perl",
 				   "Perl function %s exited abnormally: %s\n",
 				   function, SvPV(ERRSV, na));
-		POPs;
+		(void)POPs;
 	} else if (count != 1) {
 		/*
 		 * This should NEVER happen.  G_SCALAR ensures that we WILL
--- a/libpurple/plugins/tcl/tcl_signals.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/plugins/tcl/tcl_signals.c	Fri Jun 01 23:53:05 2007 +0000
@@ -49,7 +49,9 @@
 
 	Tcl_DecrRefCount(handler->signal);
 	if (handler->namespace)
+	{
 		Tcl_DecrRefCount(handler->namespace);
+	}
 	g_free(handler);
 }
 
--- a/libpurple/protocols/Makefile.am	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/Makefile.am	Fri Jun 01 23:53:05 2007 +0000
@@ -1,5 +1,5 @@
-EXTRA_DIST = Makefile.mingw
+EXTRA_DIST = Makefile.mingw null/
 
-DIST_SUBDIRS = bonjour gg irc jabber msn novell oscar qq sametime silc toc simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc toc simple yahoo zephyr
 
 SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/Makefile.mingw	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/Makefile.mingw	Fri Jun 01 23:53:05 2007 +0000
@@ -8,7 +8,7 @@
 PIDGIN_TREE_TOP := ../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
-SUBDIRS = gg irc jabber msn novell oscar qq sametime silc simple yahoo
+SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo
 
 .PHONY: all install clean
 
--- a/libpurple/protocols/gg/gg.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/gg/gg.c	Fri Jun 01 23:53:05 2007 +0000
@@ -253,8 +253,8 @@
 
 /*
  */
-/* static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, gchar *file) {{{ */
-static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, gchar *file)
+/* static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *file) {{{ */
+static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *file)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 
@@ -277,7 +277,7 @@
 		purple_debug_error("gg", "Could not open file: %s\n", file);
 		purple_notify_error(account, _("Couldn't open file"), msg, NULL);
 		g_free(msg);
-		g_free(file);
+		g_free(buddylist);
 		return;
 	}
 
--- a/libpurple/protocols/msn/sync.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Fri Jun 01 23:53:05 2007 +0000
@@ -195,6 +195,8 @@
 
 	user = sync->last_user;
 
+	g_return_if_fail(user != NULL);
+
 	type     = cmd->params[0];
 	value    = cmd->params[1];
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/null/Makefile.am	Fri Jun 01 23:53:05 2007 +0000
@@ -0,0 +1,21 @@
+EXTRA_DIST = README Makefile.mingw
+
+pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+
+NULLSOURCES = nullprpl.c
+
+AM_CFLAGS = $(st)
+
+libnull_la_LDFLAGS = -module -avoid-version
+
+# nullprpl isn't built by default. when it is built, it's dynamically linked.
+st =
+pkg_LTLIBRARIES   = libnull.la
+libnull_la_SOURCES = $(NULLSOURCES)
+libnull_la_LIBADD  = $(GLIB_LIBS)
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/libpurple \
+	-I$(top_builddir)/libpurple \
+	$(GLIB_CFLAGS) \
+	$(DEBUG_CFLAGS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/null/Makefile.mingw	Fri Jun 01 23:53:05 2007 +0000
@@ -0,0 +1,77 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of libnull
+#
+
+PIDGIN_TREE_TOP := ../../..
+include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
+TARGET = libnull
+TYPE = PLUGIN
+
+# Static or Plugin...
+ifeq ($(TYPE),STATIC)
+  DEFINES += -DSTATIC
+  DLL_INSTALL_DIR =	$(PURPLE_INSTALL_DIR)
+else
+ifeq ($(TYPE),PLUGIN)
+  DLL_INSTALL_DIR =	$(PURPLE_INSTALL_PLUGINS_DIR)
+endif
+endif
+
+##
+## INCLUDE PATHS
+##
+INCLUDE_PATHS +=	-I. \
+			-I$(GTK_TOP)/include \
+			-I$(GTK_TOP)/include/glib-2.0 \
+			-I$(GTK_TOP)/lib/glib-2.0/include \
+			-I$(PURPLE_TOP) \
+			-I$(PURPLE_TOP)/win32 \
+			-I$(PIDGIN_TREE_TOP)
+
+LIB_PATHS +=		-L$(GTK_TOP)/lib \
+			-L$(PURPLE_TOP)
+
+##
+##  SOURCES, OBJECTS
+##
+C_SRC =	nullprpl.c
+
+OBJECTS = $(C_SRC:%.c=%.o)
+
+##
+## LIBRARIES
+##
+LIBS =	\
+			-lglib-2.0 \
+			-lintl \
+			-lws2_32 \
+			-lpurple
+
+include $(PIDGIN_COMMON_RULES)
+
+##
+## TARGET DEFINITIONS
+##
+.PHONY: all install clean
+
+all: $(TARGET).dll
+
+install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR)
+	cp $(TARGET).dll $(DLL_INSTALL_DIR)
+
+$(OBJECTS): $(PURPLE_CONFIG_H)
+
+$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
+	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll
+
+##
+## CLEAN RULES
+##
+clean:
+	rm -f $(OBJECTS)
+	rm -f $(TARGET).dll
+
+include $(PIDGIN_COMMON_TARGETS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/null/README	Fri Jun 01 23:53:05 2007 +0000
@@ -0,0 +1,46 @@
+nullprpl
+
+--------
+OVERVIEW
+--------
+Nullprpl is a mock protocol plugin for Pidgin and libpurple. You can create
+accounts with it, sign on and off, add buddies, and send and receive IMs, all
+without connecting to a server!
+
+Beyond that basic functionality, nullprpl supports presence and away/available
+messages, offline messages, user info, typing notification, privacy
+allow/block lists, chat rooms, whispering, room lists, and protocol icons and
+emblems. Notable missing features are file transfer and account registration
+and authentication.
+
+Nullprpl is intended as an example of how to write a libpurple protocol
+plugin. It doesn't contain networking code or an event loop, but it does
+demonstrate how to use the libpurple API to do pretty much everything a prpl
+might need to do.
+
+Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other
+libpurple clients. It's a full-featured protocol plugin, but doesn't depend on
+an external server, so it's a quick and easy way to exercise test new code. It
+also allows you to work while you're disconnected.
+
+-----------------------
+BUILDING AND INSTALLING
+-----------------------
+
+To build, just run ./configure as usual in the root directory of the pidgin
+source distribution. Then cd libpurple/protocols/null and type make. To
+install, copy libnull.la and .libs/libnull.so into your ~/.purple/plugins
+directory. Then run Pidgin.
+
+To build nullprpl on Windows (with Cygwin/MinGW), use Makefile.mingw.
+
+-----
+USAGE
+-----
+To add a nullprpl account, go to the account editor window and click Add.
+Select Nullprpl from the protocol drop-down list, and enter any username you
+want.
+
+Now, use Pidgin like normal. You can add buddies, send IMs, set away messages,
+etc. If you send IMs to your own username, they will be echoed back to you.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Fri Jun 01 23:53:05 2007 +0000
@@ -0,0 +1,1202 @@
+/**
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * Nullprpl is a mock protocol plugin for Pidgin and libpurple. You can create
+ * accounts with it, sign on and off, add buddies, and send and receive IMs,
+ * all without connecting to a server!
+ * 
+ * Beyond that basic functionality, nullprpl supports presence and
+ * away/available messages, offline messages, user info, typing notification,
+ * privacy allow/block lists, chat rooms, whispering, room lists, and protocol
+ * icons and emblems. Notable missing features are file transfer and account
+ * registration and authentication.
+ * 
+ * Nullprpl is intended as an example of how to write a libpurple protocol
+ * plugin. It doesn't contain networking code or an event loop, but it does
+ * demonstrate how to use the libpurple API to do pretty much everything a prpl
+ * might need to do.
+ * 
+ * Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other
+ * libpurple clients. It's a full-featured protocol plugin, but doesn't depend
+ * on an external server, so it's a quick and easy way to exercise test new
+ * code. It also allows you to work while you're disconnected.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "internal.h"
+#include "config.h"
+#include "account.h"
+#include "accountopt.h"
+#include "blist.h"
+#include "cmds.h"
+#include "conversation.h"
+#include "connection.h"
+#include "debug.h"
+#include "notify.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "roomlist.h"
+#include "status.h"
+#include "util.h"
+#include "version.h"
+
+
+#define NULLPRPL_ID "prpl-null"
+static PurplePlugin *_null_protocol = NULL;
+
+#define NULL_STATUS_ONLINE   "online"
+#define NULL_STATUS_AWAY     "away"
+#define NULL_STATUS_OFFLINE  "offline"
+
+typedef void (*GcFunc)(PurpleConnection *from,
+                       PurpleConnection *to,
+                       gpointer userdata);
+
+typedef struct {
+  GcFunc fn;
+  PurpleConnection *from;
+  gpointer userdata;
+} GcFuncData;
+
+/*
+ * stores offline messages that haven't been delivered yet. maps username
+ * (char *) to GList * of GOfflineMessages. initialized in nullprpl_init.
+ */
+GHashTable* goffline_messages = NULL;
+
+typedef struct {
+  char *from;
+  char *message;
+  time_t mtime;
+  PurpleMessageFlags flags;
+} GOfflineMessage;
+
+/*
+ * helpers
+ */
+static PurpleConnection *get_nullprpl_gc(const char *username) {
+  PurpleAccount *acct = purple_accounts_find(username, NULLPRPL_ID);
+  if (acct && purple_account_is_connected(acct))
+    return acct->gc;
+  else
+    return NULL;
+}
+
+static void call_if_nullprpl(gpointer data, gpointer userdata) {
+  PurpleConnection *gc = (PurpleConnection *)(data);
+  GcFuncData *gcfdata = (GcFuncData *)userdata;
+
+  if (!strcmp(gc->account->protocol_id, NULLPRPL_ID))
+    gcfdata->fn(gcfdata->from, gc, gcfdata->userdata);
+}
+
+static void foreach_nullprpl_gc(GcFunc fn, PurpleConnection *from,
+                                gpointer userdata) {
+  GcFuncData gcfdata = { fn, from, userdata };
+  g_list_foreach(purple_connections_get_all(), call_if_nullprpl,
+                 &gcfdata);
+}
+
+
+typedef void(*ChatFunc)(PurpleConvChat *from, PurpleConvChat *to,
+                        int id, const char *room, gpointer userdata);
+
+typedef struct {
+  ChatFunc fn;
+  PurpleConvChat *from_chat;
+  gpointer userdata;
+} ChatFuncData;
+
+static void call_chat_func(gpointer data, gpointer userdata) {
+  PurpleConnection *to = (PurpleConnection *)data;
+  ChatFuncData *cfdata = (ChatFuncData *)userdata;
+
+  int id = cfdata->from_chat->id;
+  PurpleConversation *conv = purple_find_chat(to, id);
+  if (conv) {
+    PurpleConvChat *chat = purple_conversation_get_chat_data(conv);
+    cfdata->fn(cfdata->from_chat, chat, id, conv->name, cfdata->userdata);
+  }
+}
+
+static void foreach_gc_in_chat(ChatFunc fn, PurpleConnection *from,
+                               int id, gpointer userdata) {
+  PurpleConversation *conv = purple_find_chat(from, id);
+  ChatFuncData cfdata = { fn,
+                          purple_conversation_get_chat_data(conv),
+                          userdata };
+
+  g_list_foreach(purple_connections_get_all(), call_chat_func,
+                 &cfdata);
+}
+
+
+static void discover_status(PurpleConnection *from, PurpleConnection *to,
+                            gpointer userdata) {
+  char *from_username = from->account->username;
+  char *to_username = to->account->username;
+
+  if (purple_find_buddy(from->account, to_username)) {
+    PurpleStatus *status = purple_account_get_active_status(to->account);
+    const char *status_id = purple_status_get_id(status);
+    const char *message = purple_status_get_attr_string(status, "message");
+
+    if (!strcmp(status_id, NULL_STATUS_ONLINE) ||
+        !strcmp(status_id, NULL_STATUS_AWAY) ||
+        !strcmp(status_id, NULL_STATUS_OFFLINE)) {
+      purple_debug_info("nullprpl", "%s sees that %s is %s: %s\n",
+                        from_username, to_username, status_id, message);
+      purple_prpl_got_user_status(from->account, to_username, status_id,
+                                  (message) ? "message" : NULL, message, NULL);
+    } else {
+      purple_debug_error("nullprpl",
+                         "%s's buddy %s has an unknown status: %s, %s",
+                         from_username, to_username, status_id, message);
+    }
+  }
+}
+
+static void report_status_change(PurpleConnection *from, PurpleConnection *to,
+                                 gpointer userdata) {
+  purple_debug_info("nullprpl", "notifying %s that %s changed status\n",
+                    to->account->username, from->account->username);
+  discover_status(to, from, NULL);
+}
+
+
+/* 
+ * UI callbacks
+ */
+static void nullprpl_input_user_info(PurplePluginAction *action)
+{
+  PurpleConnection *gc = (PurpleConnection *)action->context;
+  PurpleAccount *acct = purple_connection_get_account(gc);
+  purple_debug_info("nullprpl", "showing 'Set User Info' dialog for %s\n",
+                    acct->username);
+
+  purple_account_request_change_user_info(acct);
+}
+
+/* this is set to the actions member of the PurplePluginInfo struct at the
+ * bottom.
+ */
+static GList *nullprpl_actions(PurplePlugin *plugin, gpointer context)
+{
+  PurplePluginAction *action = purple_plugin_action_new(
+    _("Set User Info..."), nullprpl_input_user_info);
+  return g_list_append(NULL, action);
+}
+
+
+/*
+ * prpl functions
+ */
+static const char *nullprpl_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
+{
+  /* shamelessly steal (er, borrow) the meanwhile protocol icon. it's cute! */
+  return "meanwhile";
+}
+
+static const char *nullprpl_list_emblem(PurpleBuddy *buddy)
+{
+  const char* emblem;
+
+  if (get_nullprpl_gc(buddy->name)) {
+    PurplePresence *presence = purple_buddy_get_presence(buddy);
+    PurpleStatus *status = purple_presence_get_active_status(presence);
+    emblem = purple_status_get_name(status);
+  } else {
+    emblem = "offline";
+  }
+
+  purple_debug_info("nullprpl", "using emblem %s for %s's buddy %s\n",
+                    emblem, buddy->account->username, buddy->name);
+  return emblem;
+}
+
+static char *nullprpl_status_text(PurpleBuddy *buddy) {
+  purple_debug_info("nullprpl", "getting %s's status text for %s\n",
+                    buddy->name, buddy->account->username);
+
+  if (purple_find_buddy(buddy->account, buddy->name)) {
+    PurplePresence *presence = purple_buddy_get_presence(buddy);
+    PurpleStatus *status = purple_presence_get_active_status(presence);
+    const char *name = purple_status_get_name(status);
+    const char *message = purple_status_get_attr_string(status, "message");
+
+    char *text;
+    if (message && strlen(message) > 0)
+      text = g_strdup_printf("%s: %s", name, message);
+    else
+      text = g_strdup(name);
+
+    purple_debug_info("nullprpl", "%s's status text is %s\n", buddy->name, text);
+    return text;
+
+  } else {
+    purple_debug_info("nullprpl", "...but %s is not logged in\n", buddy->name);
+    return "Not logged in";
+  }
+}
+
+static void nullprpl_tooltip_text(PurpleBuddy *buddy,
+                                  PurpleNotifyUserInfo *info,
+                                  gboolean full) {
+  PurpleConnection *gc = get_nullprpl_gc(buddy->name);
+
+  if (gc) {
+    /* they're logged in */
+    PurplePresence *presence = purple_buddy_get_presence(buddy);
+    PurpleStatus *status = purple_presence_get_active_status(presence);
+    const char *msg = nullprpl_status_text(buddy);
+    purple_notify_user_info_add_pair(info, purple_status_get_name(status),
+                                     msg);
+
+    if (full) {
+      const char *user_info = purple_account_get_user_info(gc->account);
+      if (user_info)
+        purple_notify_user_info_add_pair(info, _("User info"), user_info);
+    }
+
+  } else {
+    /* they're not logged in */
+    purple_notify_user_info_add_pair(info, _("User info"), _("not logged in"));
+  }
+    
+  purple_debug_info("nullprpl", "showing %s tooltip for %s\n",
+                    (full) ? "full" : "short", buddy->name);
+}
+
+static GList *nullprpl_status_types(PurpleAccount *acct)
+{
+  GList *types = NULL;
+  PurpleStatusType *type;
+
+  purple_debug_info("nullprpl", "returning status types for %s: %s, %s, %s\n",
+                    acct->username,
+                    NULL_STATUS_ONLINE, NULL_STATUS_AWAY, NULL_STATUS_OFFLINE);
+
+  type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL_STATUS_ONLINE,
+                                NULL_STATUS_ONLINE, TRUE);
+  purple_status_type_add_attr(type, "message", _("Online"),
+                              purple_value_new(PURPLE_TYPE_STRING));
+  types = g_list_append(types, type);
+
+  type = purple_status_type_new(PURPLE_STATUS_AWAY, NULL_STATUS_AWAY,
+                                NULL_STATUS_AWAY, TRUE);
+  purple_status_type_add_attr(type, "message", _("Away"),
+                              purple_value_new(PURPLE_TYPE_STRING));
+  types = g_list_append(types, type);
+  
+  type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL_STATUS_OFFLINE,
+                                NULL_STATUS_OFFLINE, TRUE);
+  purple_status_type_add_attr(type, "message", _("Offline"),
+                              purple_value_new(PURPLE_TYPE_STRING));
+  types = g_list_append(types, type);
+
+  return types;
+}
+
+static void blist_example_menu_item(PurpleBlistNode *node, gpointer userdata) {
+  purple_debug_info("nullprpl", "example menu item clicked on user",
+                    ((PurpleBuddy *)node)->name);
+
+  purple_notify_info(NULL,  /* plugin handle or PurpleConnection */
+                     _("Primary title"),
+                     _("Secondary title"),
+                     _("This is the callback for the nullprpl menu item."));
+}
+
+static GList *nullprpl_blist_node_menu(PurpleBlistNode *node) {
+  purple_debug_info("nullprpl", "providing buddy list context menu item\n");
+
+  if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+    PurpleMenuAction *action = purple_menu_action_new(
+      _("Nullprpl example menu item"),
+      PURPLE_CALLBACK(blist_example_menu_item),
+      NULL,   /* userdata passed to the callback */
+      NULL);  /* child menu items */
+    return g_list_append(NULL, action);
+  } else {
+    return NULL;
+  }
+}
+
+static GList *nullprpl_chat_info(PurpleConnection *gc) {
+  struct proto_chat_entry *pce; /* defined in prpl.h */
+
+  purple_debug_info("nullprpl", "returning chat setting 'room'\n");
+
+  pce = g_new0(struct proto_chat_entry, 1);
+  pce->label = _(_("Chat _room"));
+  pce->identifier = "room";
+  pce->required = TRUE;
+
+  return g_list_append(NULL, pce);
+}
+
+static GHashTable *nullprpl_chat_info_defaults(PurpleConnection *gc,
+                                               const char *room) {
+  GHashTable *defaults;
+
+  purple_debug_info("nullprpl", "returning chat default setting "
+                    "'room' = 'default'\n");
+
+  defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+  g_hash_table_insert(defaults, "room", g_strdup("default"));
+  return defaults;
+}  
+
+static void nullprpl_login(PurpleAccount *acct)
+{
+  PurpleConnection *gc = purple_account_get_connection(acct);
+  GList *offline_messages;
+
+  purple_debug_info("nullprpl", "logging in %s\n", acct->username);
+
+  purple_connection_update_progress(gc, _("Connecting"),
+                                    0,   /* which connection step this is */
+                                    2);  /* total number of steps */
+
+  purple_connection_update_progress(gc, _("Connected"),
+                                    1,   /* which connection step this is */
+                                    2);  /* total number of steps */
+  purple_connection_set_state(gc, PURPLE_CONNECTED);
+
+  /* tell purple about everyone on our buddy list who's connected */
+  foreach_nullprpl_gc(discover_status, gc, NULL);
+
+  /* notify other nullprpl accounts */
+  foreach_nullprpl_gc(report_status_change, gc, NULL);
+
+  /* fetch stored offline messages */
+  purple_debug_info("nullprpl", "checking for offline messages for %s\n",
+                    acct->username);
+  offline_messages = g_hash_table_lookup(goffline_messages, acct->username); 
+  while (offline_messages) {
+    GOfflineMessage *message = (GOfflineMessage *)offline_messages->data;
+    purple_debug_info("nullprpl", "delivering offline message to %s: %s\n",
+                      acct->username, message->message);
+    serv_got_im(gc, message->from, message->message, message->flags,
+                message->mtime);
+    offline_messages = g_list_next(offline_messages);
+
+    g_free(message->from);
+    g_free(message->message);
+    g_free(message);
+  }
+
+  g_list_free(offline_messages);
+  g_hash_table_remove(goffline_messages, &acct->username);
+}
+
+static void nullprpl_close(PurpleConnection *gc)
+{
+  /* notify other nullprpl accounts */
+  foreach_nullprpl_gc(report_status_change, gc, NULL);
+}
+
+static int nullprpl_send_im(PurpleConnection *gc, const char *who,
+                            const char *message, PurpleMessageFlags flags)
+{
+  const char *from_username = gc->account->username;
+  PurpleMessageFlags receive_flags = ((flags & ~PURPLE_MESSAGE_SEND)
+                                      | PURPLE_MESSAGE_RECV);
+  PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID);
+  PurpleConnection *to;
+
+  purple_debug_info("nullprpl", "sending message from %s to %s: %s\n",
+                    from_username, who, message);
+
+  /* is the sender blocked by the recipient's privacy settings? */
+  if (!purple_privacy_check(to_acct, gc->account->username)) {
+    char *msg = g_strdup_printf(
+      _("Your message was blocked by %s's privacy settings."), who);
+    purple_debug_info("nullprpl",
+                      "discarding; %s is blocked by %s's privacy settings\n",
+                      from_username, who);
+    purple_conv_present_error(who, gc->account, msg);
+    g_free(msg);
+    return 0;
+  }
+
+  /* is the recipient online? */
+  to = get_nullprpl_gc(who);
+  if (to) {  /* yes, send */
+    serv_got_im(to, from_username, message, receive_flags, time(NULL));
+
+  } else {  /* nope, store as an offline message */
+    GOfflineMessage *offline_message;
+    GList *messages;
+
+    purple_debug_info("nullprpl",
+                      "%s is offline, sending as offline message\n", who);
+    offline_message = g_new0(GOfflineMessage, 1);
+    offline_message->from = g_strdup(from_username);
+    offline_message->message = g_strdup(message);
+    offline_message->mtime = time(NULL);
+    offline_message->flags = receive_flags;
+
+    messages = g_hash_table_lookup(goffline_messages, who);
+    messages = g_list_append(messages, offline_message);
+    g_hash_table_insert(goffline_messages, g_strdup(who), messages);
+  }
+
+   return 1;
+}
+
+static void nullprpl_set_info(PurpleConnection *gc, const char *info) {
+  purple_debug_info("nullprpl", "setting %s's user info to %s\n",
+                    gc->account->username, info);
+}
+
+static char *typing_state_to_string(PurpleTypingState typing) {
+  switch (typing) {
+  case PURPLE_NOT_TYPING:  return "is not typing";
+  case PURPLE_TYPING:      return "is typing";
+  case PURPLE_TYPED:       return "stopped typing momentarily";
+  default:               return "unknown typing state";
+  }
+}
+
+static void notify_typing(PurpleConnection *from, PurpleConnection *to,
+                          gpointer typing) {
+  char *from_username = from->account->username;
+  char *action = typing_state_to_string((PurpleTypingState)typing);
+  purple_debug_info("nullprpl", "notifying %s that %s %s\n",
+                    to->account->username, from_username, action);
+
+  serv_got_typing(to,
+                  from_username,
+                  0, /* if non-zero, a timeout in seconds after which to
+                      * reset the typing status to PURPLE_NOT_TYPING */
+                  (PurpleTypingState)typing);
+}
+
+static unsigned int nullprpl_send_typing(PurpleConnection *gc, const char *name,
+                                         PurpleTypingState typing) {
+  purple_debug_info("nullprpl", "%s %s\n", gc->account->username,
+                    typing_state_to_string(typing));
+  foreach_nullprpl_gc(notify_typing, gc, (gpointer)typing);
+  return 0;
+}
+
+static void nullprpl_get_info(PurpleConnection *gc, const char *username) {
+  const char *body;
+  PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+  PurpleAccount *acct;
+
+  purple_debug_info("nullprpl", "Fetching %s's user info for %s\n", username,
+                    gc->account->username);
+
+  if (!get_nullprpl_gc(username)) {
+    char *msg = g_strdup_printf(_("%s is not logged in."), username);
+    purple_notify_error(gc, _("User Info"), _("User info not available. "), msg);
+    g_free(msg);
+  }
+
+  acct = purple_accounts_find(username, NULLPRPL_ID);
+  if (acct)
+    body = purple_account_get_user_info(acct);
+  else
+    body = _("No user info.");
+  purple_notify_user_info_add_pair(info, "Info", body);
+
+  /* show a buddy's user info in a nice dialog box */
+  purple_notify_userinfo(gc,        /* connection the buddy info came through */
+                         username,  /* buddy's username */
+                         info,      /* body */
+                         NULL,      /* callback called when dialog closed */
+                         NULL);     /* userdata for callback */
+}
+
+static void nullprpl_set_status(PurpleAccount *acct, PurpleStatus *status) {
+  const char *msg = purple_status_get_attr_string(status, "message");
+  purple_debug_info("nullprpl", "setting %s's status to %s: %s\n",
+                    acct->username, purple_status_get_name(status), msg);
+
+  foreach_nullprpl_gc(report_status_change, get_nullprpl_gc(acct->username),
+                      NULL);
+}
+
+static void nullprpl_set_idle(PurpleConnection *gc, int idletime) {
+  purple_debug_info("nullprpl",
+                    "purple reports that %s has been idle for %d seconds\n",
+                    gc->account->username, idletime);
+}
+
+static void nullprpl_change_passwd(PurpleConnection *gc, const char *old_pass,
+                                   const char *new_pass) {
+  purple_debug_info("nullprpl", "%s wants to change their password\n",
+                    gc->account->username);
+}
+
+static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+                               PurpleGroup *group)
+{
+  char *username = gc->account->username;
+  PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name);
+
+  purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", buddy->name,
+                    username);
+
+  if (buddy_gc) {
+    PurpleAccount *buddy_acct = buddy_gc->account;
+
+    discover_status(gc, buddy_gc, NULL);
+
+    if (purple_find_buddy(buddy_acct, username)) {
+      purple_debug_info("nullprpl", "%s is already on %s's buddy list\n",
+                        username, buddy->name);
+    } else {
+      purple_debug_info("nullprpl", "asking %s if they want to add %s\n",
+                        buddy->name, username);
+      purple_account_request_add(buddy_acct,
+                                 username,
+                                 NULL,   /* local account id (rarely used) */
+                                 NULL,   /* alias */
+                                 NULL);  /* message */
+    }
+  }
+}
+
+static void nullprpl_add_buddies(PurpleConnection *gc, GList *buddies,
+                                 GList *groups) {
+  GList *buddy = buddies;
+  GList *group = groups;
+
+  purple_debug_info("nullprpl", "adding multiple buddies\n");
+
+  while (buddy && group) {
+    nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data);
+    buddy = g_list_next(buddy);
+    group = g_list_next(group);
+  }
+}
+
+static void nullprpl_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
+                                  PurpleGroup *group)
+{
+  purple_debug_info("nullprpl", "removing %s from %s's buddy list\n",
+                    buddy->name, gc->account->username);
+}
+
+static void nullprpl_remove_buddies(PurpleConnection *gc, GList *buddies,
+                                    GList *groups) {
+  GList *buddy = buddies;
+  GList *group = groups;
+
+  purple_debug_info("nullprpl", "removing multiple buddies\n");
+
+  while (buddy && group) {
+    nullprpl_remove_buddy(gc, (PurpleBuddy *)buddy->data,
+                          (PurpleGroup *)group->data);
+    buddy = g_list_next(buddy);
+    group = g_list_next(group);
+  }
+}
+
+/*
+ * nullprpl uses purple's local whitelist and blacklist, stored in blist.xml, as
+ * its authoritative privacy settings, and uses purple's logic (specifically
+ * purple_privacy_check(), from privacy.h), to determine whether messages are
+ * allowed or blocked.
+ */
+static void nullprpl_add_permit(PurpleConnection *gc, const char *name) {
+  purple_debug_info("nullprpl", "%s adds %s to their allowed list\n",
+                    gc->account->username, name);
+}
+
+static void nullprpl_add_deny(PurpleConnection *gc, const char *name) {
+  purple_debug_info("nullprpl", "%s adds %s to their blocked list\n",
+                    gc->account->username, name);
+}
+
+static void nullprpl_rem_permit(PurpleConnection *gc, const char *name) {
+  purple_debug_info("nullprpl", "%s removes %s from their allowed list\n",
+                    gc->account->username, name);
+}
+
+static void nullprpl_rem_deny(PurpleConnection *gc, const char *name) {
+  purple_debug_info("nullprpl", "%s removes %s from their blocked list\n",
+                    gc->account->username, name);
+}
+
+static void nullprpl_set_permit_deny(PurpleConnection *gc) {
+  /* this is for synchronizing the local black/whitelist with the server.
+   * for nullprpl, it's a noop.
+   */
+}
+
+static void joined_chat(PurpleConvChat *from, PurpleConvChat *to,
+                        int id, const char *room, gpointer userdata) {
+  /*  tell their chat window that we joined */
+  purple_debug_info("nullprpl", "%s sees that %s joined chat room %s\n",
+                    to->nick, from->nick, room);
+  purple_conv_chat_add_user(to,
+                            from->nick,
+                            NULL,   /* user-provided join message, IRC style */
+                            PURPLE_CBFLAGS_NONE,
+                            TRUE);  /* show a join message */
+
+  if (from != to) {
+    /* add them to our chat window */
+    purple_debug_info("nullprpl", "%s sees that %s is in chat room %s\n",
+                      from->nick, to->nick, room);
+    purple_conv_chat_add_user(from,
+                              to->nick,
+                              NULL,   /* user-provided join message, IRC style */
+                              PURPLE_CBFLAGS_NONE,
+                              FALSE);  /* show a join message */
+  }
+}
+
+static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) {
+  char *username = gc->account->username;
+  char *room = g_hash_table_lookup(components, "room");
+  int chat_id = g_str_hash(room);
+  purple_debug_info("nullprpl", "%s is joining chat room %s\n", username, room);
+
+  if (!purple_find_chat(gc, chat_id)) {
+    serv_got_joined_chat(gc, chat_id, room);
+
+    /* tell everyone that we joined, and add them if they're already there */
+    foreach_gc_in_chat(joined_chat, gc, chat_id, NULL);
+  } else {
+    purple_debug_info("nullprpl", "%s is already in chat room %s\n", username,
+                      room);
+    purple_notify_info(gc,
+                       _("Join chat"),
+                       _("Join chat"),
+                       g_strdup_printf("%s is already in chat room %s.",
+                                       username, room));
+  }
+}
+
+static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) {
+  char *invited_by = g_hash_table_lookup(components, "invited_by");
+  char *room = g_hash_table_lookup(components, "room");
+  char *username = gc->account->username;
+  PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by);
+  char *message = g_strdup_printf(
+    "%s %s %s.",
+    username,
+    _("has rejected your invitation to join the chat room"),
+    room);
+
+  purple_debug_info("nullprpl",
+                    "%s has rejected %s's invitation to join chat room %s\n",
+                    username, invited_by, room);
+
+  purple_notify_info(invited_by_gc,
+                     _("Chat invitation rejected"),
+                     _("Chat invitation rejected"),
+                     message);
+}
+
+static char *nullprpl_get_chat_name(GHashTable *components) {
+  char *room = g_hash_table_lookup(components, "room");
+  purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room);
+  return room;
+}
+
+static void nullprpl_chat_invite(PurpleConnection *gc, int id,
+                                 const char *message, const char *who) {
+  char *username = gc->account->username;
+  PurpleConversation *conv = purple_find_chat(gc, id);
+  char *room = conv->name;
+  PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID);
+
+  purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n",
+                    username, who, room);
+
+  if (to_acct) {
+    PurpleConversation *to_conv = purple_find_chat(to_acct->gc, id);
+    if (to_conv) {
+      purple_debug_info("nullprpl",
+                        "%s is already in chat room %s; "
+                        "ignoring invitation from %s\n",
+                        who, room, username);
+      purple_notify_info(gc,
+                         _("Chat invitation"),
+                         _("Chat invitation"),
+                         g_strdup_printf("%s is already in chat room %s.",
+                                         who, room));
+    } else {
+      GHashTable *components;
+      components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free);
+      g_hash_table_replace(components, "room", g_strdup(room));
+      g_hash_table_replace(components, "invited_by", g_strdup(username));
+      serv_got_chat_invite(to_acct->gc, room, username, message, components);
+    }
+  }
+}
+
+static void left_chat_room(PurpleConvChat *from, PurpleConvChat *to,
+                           int id, const char *room, gpointer userdata) {
+  if (from != to) {
+    /*  tell their chat window that we left */
+    purple_debug_info("nullprpl", "%s sees that %s left chat room %s\n",
+                      to->nick, from->nick, room);
+    purple_conv_chat_remove_user(to,
+                                 from->nick,
+                                 NULL);  /* user-provided message, IRC style */
+  }
+}
+
+static void nullprpl_chat_leave(PurpleConnection *gc, int id) {
+  PurpleConversation *conv = purple_find_chat(gc, id);
+  purple_debug_info("nullprpl", "%s is leaving chat room %s\n",
+                    gc->account->username, conv->name);
+
+  /* tell everyone that we left */
+  foreach_gc_in_chat(left_chat_room, gc, id, NULL);
+}
+
+static PurpleCmdRet send_whisper(PurpleConversation *conv, const gchar *cmd,
+                                 gchar **args, gchar **error, void *userdata) {
+  const char *to_username;
+  const char *message;
+  const char *from_username;
+  PurpleConvChat *chat;
+  PurpleConvChatBuddy *chat_buddy;
+  PurpleConnection *to;
+
+  /* parse args */
+  to_username = args[0];
+  message = args[1];
+
+  if (!to_username || strlen(to_username) == 0) {
+    *error = g_strdup(_("Whisper is missing recipient."));
+    return PURPLE_CMD_RET_FAILED;
+  } else if (!message || strlen(message) == 0) {
+    *error = g_strdup(_("Whisper is missing message."));
+    return PURPLE_CMD_RET_FAILED;
+  }
+
+  from_username = conv->account->username;
+  purple_debug_info("nullprpl", "%s whispers to %s in chat room %s: %s\n",
+                    from_username, to_username, conv->name, message);
+
+  chat = purple_conversation_get_chat_data(conv);
+  chat_buddy = purple_conv_chat_cb_find(chat, to_username);
+  to = get_nullprpl_gc(to_username);
+
+  if (!chat_buddy) {
+    /* this will be freed by the caller */
+    *error = g_strdup_printf(_("%s is not logged in."), to_username);
+    return PURPLE_CMD_RET_FAILED;
+  } else if (!to) {
+    *error = g_strdup_printf(_("%s is not in this chat room."), to_username);
+    return PURPLE_CMD_RET_FAILED;
+  } else {
+    /* write the whisper in the sender's chat window  */
+    char *message_to = g_strdup_printf("%s (to %s)", message, to_username);
+    purple_conv_chat_write(chat, from_username, message_to,
+                           PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_WHISPER,
+                           time(NULL));
+    g_free(message_to);
+
+    /* send the whisper */
+    serv_chat_whisper(to, chat->id, from_username, message);
+
+    return PURPLE_CMD_RET_OK;
+  }
+}
+
+static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who,
+                                  const char *message) {
+  char *username = gc->account->username;
+  PurpleConversation *conv = purple_find_chat(gc, id);
+  purple_debug_info("nullprpl",
+                    "%s receives whisper from %s in chat room %s: %s\n",
+                    username, who, conv->name, message);
+
+  /* receive whisper on recipient's account */
+  serv_got_chat_in(gc, id, who, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_WHISPER,
+                   message, time(NULL));
+}
+
+static void receive_chat_message(PurpleConvChat *from, PurpleConvChat *to,
+                                 int id, const char *room, gpointer userdata) {
+  const char *message = (const char *)userdata;
+  PurpleConnection *to_gc = get_nullprpl_gc(to->nick);
+
+  purple_debug_info("nullprpl",
+                    "%s receives message from %s in chat room %s: %s\n",
+                    to->nick, from->nick, room, message);
+  serv_got_chat_in(to_gc, id, from->nick, PURPLE_MESSAGE_RECV, message,
+                   time(NULL));
+}
+
+static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message,
+                              PurpleMessageFlags flags) {
+  char *username = gc->account->username;
+  PurpleConversation *conv = purple_find_chat(gc, id);
+
+  if (conv) {
+    purple_debug_info("nullprpl",
+                      "%s is sending message to chat room %s: %s\n", username,
+                      conv->name, message);
+
+    /* send message to everyone in the chat room */
+    foreach_gc_in_chat(receive_chat_message, gc, id, (gpointer)message);
+    return 0;
+  } else {
+    purple_debug_info("nullprpl",
+                      "tried to send message from %s to chat room #%d: %s\n"
+                      "but couldn't find chat room",
+                      username, id, message);
+    return -1;
+  }
+}
+
+static void nullprpl_register_user(PurpleAccount *acct) {
+ purple_debug_info("nullprpl", "registering account for %s\n",
+                   acct->username);
+}
+
+static void nullprpl_get_cb_info(PurpleConnection *gc, int id, const char *who) {
+  PurpleConversation *conv = purple_find_chat(gc, id);
+  purple_debug_info("nullprpl",
+                    "retrieving %s's info for %s in chat room %s\n", who,
+                    gc->account->username, conv->name);
+
+  nullprpl_get_info(gc, who);
+}
+
+static void nullprpl_alias_buddy(PurpleConnection *gc, const char *who,
+                                 const char *alias) {
+ purple_debug_info("nullprpl", "%s sets %'s alias to %s\n",
+                   gc->account->username, who, alias);
+}
+
+static void nullprpl_group_buddy(PurpleConnection *gc, const char *who,
+                                 const char *old_group,
+                                 const char *new_group) {
+  purple_debug_info("nullprpl", "%s has moved %s from group %s to group %s\n",
+                    who, old_group, new_group);
+}
+
+static void nullprpl_rename_group(PurpleConnection *gc, const char *old_name,
+                                  PurpleGroup *group, GList *moved_buddies) {
+  purple_debug_info("nullprpl", "%s has renamed group %s to %s\n",
+                    gc->account->username, old_name, group->name);
+}
+
+static void nullprpl_convo_closed(PurpleConnection *gc, const char *who) {
+  purple_debug_info("nullprpl", "%s's conversation with %s was closed\n",
+                    gc->account->username, who);
+}
+
+/* normalize a username (e.g. remove whitespace, add default domain, etc.)
+ * for nullprpl, this is a noop.
+ */
+static const char *nullprpl_normalize(const PurpleAccount *acct,
+                                      const char *input) {
+  return NULL;
+}
+
+static void nullprpl_set_buddy_icon(PurpleConnection *gc,
+                                    PurpleStoredImage *img) {
+ purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n",
+                   gc->account->username, purple_imgstore_get_filename(img));
+}
+
+static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) {
+  purple_debug_info("nullprpl", "%s has removed group %s\n",
+                    gc->account->username, group->name);
+}
+
+
+static void set_chat_topic_fn(PurpleConvChat *from, PurpleConvChat *to,
+                              int id, const char *room, gpointer userdata) {
+  const char *topic = (const char *)userdata;
+  const char *username = from->conv->account->username;
+  char *msg;
+
+  purple_conv_chat_set_topic(to, username, topic);
+
+  if (topic && strlen(topic) > 0)
+    msg = g_strdup_printf(_("%s sets topic to: %s"), username, topic);
+  else
+    msg = g_strdup_printf(_("%s clears topic"), username);
+
+  purple_conv_chat_write(to, username, msg,
+                         PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,
+                         time(NULL));
+  g_free(msg);
+}
+
+static void nullprpl_set_chat_topic(PurpleConnection *gc, int id,
+                                    const char *topic) {
+  PurpleConversation *conv = purple_find_chat(gc, id);
+  PurpleConvChat *chat = purple_conversation_get_chat_data(conv);
+  const char *last_topic;
+
+  if (!chat)
+    return;
+
+  purple_debug_info("nullprpl", "%s sets topic of chat room '%s' to '%s'\n",
+                    gc->account->username, conv->name, topic);
+
+  last_topic = purple_conv_chat_get_topic(chat);
+  if ((!topic && !last_topic) ||
+      (topic && last_topic && !strcmp(topic, last_topic)))
+    return;  /* topic is unchanged, this is a noop */
+
+  foreach_gc_in_chat(set_chat_topic_fn, gc, id, (gpointer)topic);
+}
+
+static gboolean nullprpl_finish_get_roomlist(gpointer roomlist) {
+  purple_roomlist_set_in_progress((PurpleRoomlist *)roomlist, FALSE);
+  return FALSE;
+}
+
+static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
+  char *username = gc->account->username;
+  PurpleRoomlist *roomlist = purple_roomlist_new(gc->account);
+  GList *fields = NULL;
+  PurpleRoomlistField *field;
+  GList *chats;
+  GList *seen_ids = NULL;
+
+  purple_debug_info("nullprpl", "%s asks for room list; returning:\n", username);
+
+  /* set up the room list */
+  field = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "room",
+                                    "room", TRUE /* hidden */);
+  fields = g_list_append(fields, field);
+
+  field = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, "Id", "Id", FALSE);
+  fields = g_list_append(fields, field);
+
+  purple_roomlist_set_fields(roomlist, fields);
+
+  /* add each chat room. the chat ids are cached in seen_ids so that each room
+   * is only returned once, even if multiple users are in it. */
+  for (chats  = purple_get_chats(); chats; chats = g_list_next(chats)) {
+    PurpleConversation *conv = (PurpleConversation *)chats->data;
+    PurpleRoomlistRoom *room;
+    char *name = conv->name;
+    int id = purple_conversation_get_chat_data(conv)->id;
+
+    /* have we already added this room? */
+    if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp))
+      continue;                                /* yes! try the next one. */
+
+    seen_ids = g_list_append(seen_ids, name);  /* no, it's new. */
+    purple_debug_info("nullprpl", "%s (%d), ", name, id);
+
+    room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
+    purple_roomlist_room_add_field(roomlist, room, name);
+    purple_roomlist_room_add_field(roomlist, room, &id);
+    purple_roomlist_room_add(roomlist, room);
+  }
+
+  purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, roomlist);
+  return roomlist;
+}
+
+static void nullprpl_roomlist_cancel(PurpleRoomlist *list) {
+ purple_debug_info("nullprpl", "%s asked to cancel room list request\n",
+                   list->account->username);
+}
+
+static void nullprpl_roomlist_expand_category(PurpleRoomlist *list,
+                                              PurpleRoomlistRoom *category) {
+ purple_debug_info("nullprpl", "%s asked to expand room list category %s\n",
+                   list->account->username, category->name);
+}
+
+/* nullprpl doesn't support file transfer...yet... */
+static gboolean nullprpl_can_receive_file(PurpleConnection *gc,
+                                          const char *who) {
+  return FALSE;
+}
+
+static gboolean nullprpl_offline_message(const PurpleBuddy *buddy) {
+  purple_debug_info("nullprpl",
+                    "reporting that offline messages are supported for %s\n",
+                    buddy->name);
+  return TRUE;
+}
+
+
+/*
+ * prpl stuff. see prpl.h for more information.
+ */
+
+static PurplePluginProtocolInfo prpl_info =
+{
+  OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC,  /* options */
+  NULL,               /* user_splits, initialized in nullprpl_init() */
+  NULL,               /* protocol_options, initialized in nullprpl_init() */
+  {   /* icon_spec, a PurpleBuddyIconSpec */
+      "png,jpg,gif",                   /* format */
+      0,                               /* min_width */
+      0,                               /* min_height */
+      128,                             /* max_width */
+      128,                             /* max_height */
+      10000,                           /* max_filesize */
+      PURPLE_ICON_SCALE_DISPLAY,       /* scale_rules */
+  },
+  nullprpl_list_icon,                  /* list_icon */
+  nullprpl_list_emblem,                /* list_emblem */
+  nullprpl_status_text,                /* status_text */
+  nullprpl_tooltip_text,               /* tooltip_text */
+  nullprpl_status_types,               /* status_types */
+  nullprpl_blist_node_menu,            /* blist_node_menu */
+  nullprpl_chat_info,                  /* chat_info */
+  nullprpl_chat_info_defaults,         /* chat_info_defaults */
+  nullprpl_login,                      /* login */
+  nullprpl_close,                      /* close */
+  nullprpl_send_im,                    /* send_im */
+  nullprpl_set_info,                   /* set_info */
+  nullprpl_send_typing,                /* send_typing */
+  nullprpl_get_info,                   /* get_info */
+  nullprpl_set_status,                 /* set_status */
+  nullprpl_set_idle,                   /* set_idle */
+  nullprpl_change_passwd,              /* change_passwd */
+  nullprpl_add_buddy,                  /* add_buddy */
+  nullprpl_add_buddies,                /* add_buddies */
+  nullprpl_remove_buddy,               /* remove_buddy */
+  nullprpl_remove_buddies,             /* remove_buddies */
+  nullprpl_add_permit,                 /* add_permit */
+  nullprpl_add_deny,                   /* add_deny */
+  nullprpl_rem_permit,                 /* rem_permit */
+  nullprpl_rem_deny,                   /* rem_deny */
+  nullprpl_set_permit_deny,            /* set_permit_deny */
+  nullprpl_join_chat,                  /* join_chat */
+  nullprpl_reject_chat,                /* reject_chat */
+  nullprpl_get_chat_name,              /* get_chat_name */
+  nullprpl_chat_invite,                /* chat_invite */
+  nullprpl_chat_leave,                 /* chat_leave */
+  nullprpl_chat_whisper,               /* chat_whisper */
+  nullprpl_chat_send,                  /* chat_send */
+  NULL,                                /* keepalive */
+  nullprpl_register_user,              /* register_user */
+  nullprpl_get_cb_info,                /* get_cb_info */
+  NULL,                                /* get_cb_away */
+  nullprpl_alias_buddy,                /* alias_buddy */
+  nullprpl_group_buddy,                /* group_buddy */
+  nullprpl_rename_group,               /* rename_group */
+  NULL,                                /* buddy_free */
+  nullprpl_convo_closed,               /* convo_closed */
+  nullprpl_normalize,                  /* normalize */
+  nullprpl_set_buddy_icon,             /* set_buddy_icon */
+  nullprpl_remove_group,               /* remove_group */
+  NULL,                                /* get_cb_real_name */
+  nullprpl_set_chat_topic,             /* set_chat_topic */
+  NULL,                                /* find_blist_chat */
+  nullprpl_roomlist_get_list,          /* roomlist_get_list */
+  nullprpl_roomlist_cancel,            /* roomlist_cancel */
+  nullprpl_roomlist_expand_category,   /* roomlist_expand_category */
+  nullprpl_can_receive_file,           /* can_receive_file */
+  NULL,                                /* send_file */
+  NULL,                                /* new_xfer */
+  nullprpl_offline_message,            /* offline_message */
+  NULL,                                /* whiteboard_prpl_ops */
+  NULL,                                /* send_raw */
+  NULL,                                /* roomlist_room_serialize */
+  NULL,                                /* padding... */
+  NULL,
+  NULL,
+  NULL,
+};
+
+static void nullprpl_init(PurplePlugin *plugin)
+{
+  /* see accountopt.h for information about user splits and protocol options */
+  PurpleAccountUserSplit *split = purple_account_user_split_new(
+    _("Example user split (unused)"),  /* text shown to user */
+    "default",                         /* default value */
+    '@');                              /* field separator */
+  PurpleAccountOption *option = purple_account_option_string_new(
+    _("Example option (unused)"),      /* text shown to user */
+    "example",                         /* pref name */
+    "default");                        /* default value */
+
+  purple_debug_info("nullprpl", "starting up\n");
+
+  prpl_info.user_splits = g_list_append(NULL, split);
+  prpl_info.protocol_options = g_list_append(NULL, option);
+
+  /* register whisper chat command, /msg */
+  purple_cmd_register("msg",
+                    "ws",                /* args: recipient and message */
+                    PURPLE_CMD_P_DEFAULT,  /* priority */
+                    PURPLE_CMD_FLAG_CHAT,
+                    "prpl-null",
+                    send_whisper,
+                    "msg &lt;username&gt; &lt;message&gt;: send a private message, aka a whisper",
+                    NULL);               /* userdata */
+
+  /* get ready to store offline messages */
+  goffline_messages = g_hash_table_new_full(g_str_hash,  /* hash fn */
+                                            g_str_equal, /* key comparison fn */
+                                            g_free,      /* key free fn */
+                                            NULL);       /* value free fn */
+
+  _null_protocol = plugin;
+}
+
+static void nullprpl_destroy(PurplePlugin *plugin) {
+  purple_debug_info("nullprpl", "shutting down\n");
+}
+
+
+static PurplePluginInfo info =
+{
+  PURPLE_PLUGIN_MAGIC,                                     /* magic */
+  PURPLE_MAJOR_VERSION,                                    /* major_version */
+  PURPLE_MINOR_VERSION,                                    /* minor_version */
+  PURPLE_PLUGIN_PROTOCOL,                                  /* type */
+  NULL,                                                    /* ui_requirement */
+  0,                                                       /* flags */
+  NULL,                                                    /* dependencies */
+  PURPLE_PRIORITY_DEFAULT,                                 /* priority */
+  NULLPRPL_ID,                                             /* id */
+  "Nullprpl",                                              /* name */
+  "0.3",                                                   /* version */
+  "Null Protocol Plugin",                                  /* summary */
+  "Null Protocol Plugin",                                  /* description */
+  "Ryan Barrett <nullprpl@ryanb.org>",                     /* author */
+  "http://snarfed.org/space/pidgin+null+protocol+plugin",  /* homepage */
+  NULL,                                                    /* load */
+  NULL,                                                    /* unload */
+  nullprpl_destroy,                                        /* destroy */
+  NULL,                                                    /* ui_info */
+  &prpl_info,                                              /* extra_info */
+  NULL,                                                    /* prefs_info */
+  nullprpl_actions,                                        /* actions */
+  NULL,                                                    /* padding... */
+  NULL,
+  NULL,
+  NULL,
+};
+
+PURPLE_INIT_PLUGIN(null, nullprpl_init, info);
--- a/libpurple/protocols/oscar/family_admin.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_admin.c	Fri Jun 01 23:53:05 2007 +0000
@@ -125,17 +125,17 @@
 {
 	FlapFrame *fr;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	fr = flap_frame_new(od, 0x02, 10+2+2+strlen(newnick));
 
 	snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
-	aim_tlvlist_add_str(&tl, 0x0001, newnick);
+	aim_tlvlist_add_str(&tlvlist, 0x0001, newnick);
 
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&fr->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, fr);
 
@@ -151,7 +151,7 @@
 aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, const char *curpw)
 {
 	FlapFrame *fr;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 	aim_snacid_t snacid;
 
 	fr = flap_frame_new(od, 0x02, 10+4+strlen(curpw)+4+strlen(newpw));
@@ -160,13 +160,13 @@
 	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
 	/* new password TLV t(0002) */
-	aim_tlvlist_add_str(&tl, 0x0002, newpw);
+	aim_tlvlist_add_str(&tlvlist, 0x0002, newpw);
 
 	/* current password TLV t(0012) */
-	aim_tlvlist_add_str(&tl, 0x0012, curpw);
+	aim_tlvlist_add_str(&tlvlist, 0x0012, curpw);
 
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&fr->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, fr);
 
@@ -182,17 +182,17 @@
 {
 	FlapFrame *fr;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	fr = flap_frame_new(od, 0x02, 10+2+2+strlen(newemail));
 
 	snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
-	aim_tlvlist_add_str(&tl, 0x0011, newemail);
+	aim_tlvlist_add_str(&tlvlist, 0x0011, newemail);
 
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&fr->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, fr);
 
@@ -223,17 +223,17 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint16 status;
-	/* aim_tlvlist_t *tl; */
+	/* GSList *tlvlist; */
 
 	status = byte_stream_get16(bs);
 	/* Status is 0x0013 if unable to confirm at this time */
 
-	/* tl = aim_tlvlist_read(bs); */
+	/* tlvlist = aim_tlvlist_read(bs); */
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, status);
 
-	/* aim_tlvlist_free(&tl); */
+	/* aim_tlvlist_free(tlvlist); */
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_alert.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_alert.c	Fri Jun 01 23:53:05 2007 +0000
@@ -98,7 +98,7 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	struct aim_emailinfo *new;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	guint8 *cookie8, *cookie16;
 	int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */
 
@@ -152,7 +152,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL));
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	g_free(alertitle);
 	g_free(alerturl);
--- a/libpurple/protocols/oscar/family_auth.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Fri Jun 01 23:53:05 2007 +0000
@@ -129,7 +129,7 @@
 goddamnicq2(OscarData *od, FlapConnection *conn, const char *sn, const char *password, ClientInfo *ci)
 {
 	FlapFrame *frame;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 	int passwdlen;
 	guint8 *password_encoded;
 
@@ -143,24 +143,24 @@
 	aim_encode_password(password, password_encoded);
 
 	byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
-	aim_tlvlist_add_raw(&tl, 0x0002, passwdlen, password_encoded);
+	aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
+	aim_tlvlist_add_raw(&tlvlist, 0x0002, passwdlen, password_encoded);
 
 	if (ci->clientstring)
-		aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring);
-	aim_tlvlist_add_16(&tl, 0x0016, (guint16)ci->clientid);
-	aim_tlvlist_add_16(&tl, 0x0017, (guint16)ci->major);
-	aim_tlvlist_add_16(&tl, 0x0018, (guint16)ci->minor);
-	aim_tlvlist_add_16(&tl, 0x0019, (guint16)ci->point);
-	aim_tlvlist_add_16(&tl, 0x001a, (guint16)ci->build);
-	aim_tlvlist_add_32(&tl, 0x0014, (guint32)ci->distrib); /* distribution chan */
-	aim_tlvlist_add_str(&tl, 0x000f, ci->lang);
-	aim_tlvlist_add_str(&tl, 0x000e, ci->country);
+		aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
+	aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid);
+	aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
+	aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
+	aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
+	aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
+	aim_tlvlist_add_32(&tlvlist, 0x0014, (guint32)ci->distrib); /* distribution chan */
+	aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang);
+	aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country);
 
-	aim_tlvlist_write(&frame->data, &tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
 
 	g_free(password_encoded);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -201,7 +201,7 @@
 aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key)
 {
 	FlapFrame *frame;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 	guint8 digest[16];
 	aim_snacid_t snacid;
 	size_t password_len;
@@ -220,7 +220,7 @@
 	snacid = aim_cachesnac(od, 0x0017, 0x0002, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x0017, 0x0002, 0x0000, snacid);
 
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
+	aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
 
 	/* Truncate ICQ and AOL passwords, if necessary */
 	password_len = strlen(password);
@@ -231,32 +231,32 @@
 
 	aim_encode_password_md5(password, password_len, key, digest);
 
-	aim_tlvlist_add_raw(&tl, 0x0025, 16, digest);
+	aim_tlvlist_add_raw(&tlvlist, 0x0025, 16, digest);
 
 #ifndef USE_OLD_MD5
-	aim_tlvlist_add_noval(&tl, 0x004c);
+	aim_tlvlist_add_noval(&tlvlist, 0x004c);
 #endif
 
 	if (ci->clientstring)
-		aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring);
-	aim_tlvlist_add_16(&tl, 0x0016, (guint16)ci->clientid);
-	aim_tlvlist_add_16(&tl, 0x0017, (guint16)ci->major);
-	aim_tlvlist_add_16(&tl, 0x0018, (guint16)ci->minor);
-	aim_tlvlist_add_16(&tl, 0x0019, (guint16)ci->point);
-	aim_tlvlist_add_16(&tl, 0x001a, (guint16)ci->build);
-	aim_tlvlist_add_32(&tl, 0x0014, (guint32)ci->distrib);
-	aim_tlvlist_add_str(&tl, 0x000f, ci->lang);
-	aim_tlvlist_add_str(&tl, 0x000e, ci->country);
+		aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
+	aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid);
+	aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
+	aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
+	aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
+	aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
+	aim_tlvlist_add_32(&tlvlist, 0x0014, (guint32)ci->distrib);
+	aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang);
+	aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country);
 
 	/*
 	 * If set, old-fashioned buddy lists will not work. You will need
 	 * to use SSI.
 	 */
-	aim_tlvlist_add_8(&tl, 0x004a, 0x01);
+	aim_tlvlist_add_8(&tlvlist, 0x004a, 0x01);
 
-	aim_tlvlist_write(&frame->data, &tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
 
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -274,7 +274,7 @@
 static int
 parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_rxcallback_t userfunc;
 	struct aim_authresp_info *info;
 	int ret = 0;
@@ -402,7 +402,7 @@
 	if ((userfunc = aim_callhandler(od, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
 		ret = userfunc(od, conn, frame, info);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -471,7 +471,7 @@
 {
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !conn || !sn)
 		return -EINVAL;
@@ -486,16 +486,16 @@
 	snacid = aim_cachesnac(od, 0x0017, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x0017, 0x0006, 0x0000, snacid);
 
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
+	aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
 
 	/* Tell the server we support SecurID logins. */
-	aim_tlvlist_add_noval(&tl, 0x004b);
+	aim_tlvlist_add_noval(&tlvlist, 0x004b);
 
 	/* Unknown.  Sent in recent WinAIM clients.*/
-	aim_tlvlist_add_noval(&tl, 0x005a);
+	aim_tlvlist_add_noval(&tlvlist, 0x005a);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -517,7 +517,7 @@
 	int keylen, ret = 1;
 	aim_rxcallback_t userfunc;
 	char *keystr;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	gboolean truncate_pass;
 
 	keylen = byte_stream_get16(bs);
@@ -539,7 +539,7 @@
 		ret = userfunc(od, conn, frame, keystr, (int)truncate_pass);
 
 	g_free(keystr);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_bos.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_bos.c	Fri Jun 01 23:53:05 2007 +0000
@@ -39,7 +39,7 @@
 static int rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	guint16 maxpermits = 0, maxdenies = 0;
 	int ret = 0;
 
@@ -63,7 +63,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, maxpermits, maxdenies);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_buddy.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Fri Jun 01 23:53:05 2007 +0000
@@ -47,7 +47,7 @@
 rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	guint16 maxbuddies = 0, maxwatchers = 0;
 	int ret = 0;
 
@@ -82,7 +82,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, maxbuddies, maxwatchers);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_chat.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_chat.c	Fri Jun 01 23:53:05 2007 +0000
@@ -159,7 +159,7 @@
 	char *roomname;
 	struct aim_chat_roominfo roominfo;
 	guint16 tlvcount = 0;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_tlv_t *tlv;
 	char *roomdesc;
 	guint16 flags;
@@ -309,7 +309,7 @@
 	g_free(userinfo);
 	g_free(roomname);
 	g_free(roomdesc);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -357,7 +357,7 @@
 	IcbmCookie *cookie;
 	aim_snacid_t snacid;
 	guint8 ckstr[8];
-	aim_tlvlist_t *tlvlist = NULL, *inner_tlvlist = NULL;
+	GSList *tlvlist = NULL, *inner_tlvlist = NULL;
 
 	if (!od || !conn || !msg || (msglen <= 0))
 		return 0;
@@ -430,8 +430,8 @@
 
 	aim_tlvlist_write(&frame->data, &tlvlist);
 
-	aim_tlvlist_free(&inner_tlvlist);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -471,7 +471,7 @@
 	aim_userinfo_t userinfo;
 	guint8 cookie[8];
 	guint16 channel;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	char *msg = NULL;
 	int len = 0;
 	char *encoding = NULL, *language = NULL;
@@ -536,7 +536,7 @@
 	tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1);
 	if (tlv != NULL)
 	{
-		aim_tlvlist_t *inner_tlvlist;
+		GSList *inner_tlvlist;
 		aim_tlv_t *inner_tlv;
 
 		byte_stream_init(&tbs, tlv->value, tlv->length);
@@ -562,7 +562,7 @@
 		 */
 		language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1);
 
-		aim_tlvlist_free(&inner_tlvlist);
+		aim_tlvlist_free(inner_tlvlist);
 	}
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
@@ -572,7 +572,7 @@
 	g_free(msg);
 	g_free(encoding);
 	g_free(language);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_chatnav.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_chatnav.c	Fri Jun 01 23:53:05 2007 +0000
@@ -50,7 +50,7 @@
 	static const char charset[] = {"us-ascii"};
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	frame = flap_frame_new(od, 0x02, 1152);
 
@@ -85,15 +85,15 @@
 	/* detail level */
 	byte_stream_put8(&frame->data, 0x01);
 
-	aim_tlvlist_add_str(&tl, 0x00d3, name);
-	aim_tlvlist_add_str(&tl, 0x00d6, charset);
-	aim_tlvlist_add_str(&tl, 0x00d7, lang);
+	aim_tlvlist_add_str(&tlvlist, 0x00d3, name);
+	aim_tlvlist_add_str(&tlvlist, 0x00d6, charset);
+	aim_tlvlist_add_str(&tlvlist, 0x00d7, lang);
 
 	/* tlvcount */
-	byte_stream_put16(&frame->data, aim_tlvlist_count(&tl));
-	aim_tlvlist_write(&frame->data, &tl);
+	byte_stream_put16(&frame->data, aim_tlvlist_count(tlvlist));
+	aim_tlvlist_write(&frame->data, &tlvlist);
 
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -109,7 +109,7 @@
 	int curexchange;
 	aim_tlv_t *exchangetlv;
 	guint8 maxrooms = 0;
-	aim_tlvlist_t *tlvlist, *innerlist;
+	GSList *tlvlist, *innerlist;
 
 	tlvlist = aim_tlvlist_read(bs);
 
@@ -290,7 +290,7 @@
 		}
 #endif
 
-		aim_tlvlist_free(&innerlist);
+		aim_tlvlist_free(innerlist);
 	}
 
 	/*
@@ -307,7 +307,7 @@
 		g_free(exchanges[curexchange].lang2);
 	}
 	g_free(exchanges);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -316,7 +316,7 @@
 parseinfo_create(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2)
 {
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist, *innerlist;
+	GSList *tlvlist, *innerlist;
 	char *ck = NULL, *fqcn = NULL, *name = NULL;
 	guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
 	guint32 createtime = 0;
@@ -330,7 +330,7 @@
 
 	if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
 		purple_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 		return 0;
 	}
 
@@ -344,7 +344,7 @@
 
 	if (detaillevel != 0x02) {
 		purple_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 		g_free(ck);
 		return 0;
 	}
@@ -381,8 +381,8 @@
 	g_free(ck);
 	g_free(name);
 	g_free(fqcn);
-	aim_tlvlist_free(&innerlist);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(innerlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_feedbag.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Fri Jun 01 23:53:05 2007 +0000
@@ -110,7 +110,7 @@
  * @param data The additional data for the new item.
  * @return A pointer to the newly created item.
  */
-static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, aim_tlvlist_t *data)
+static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
 {
 	gboolean exists;
 	struct aim_ssi_item *cur, *new;
@@ -214,7 +214,7 @@
 
 	/* Free the removed item */
 	g_free(del->name);
-	aim_tlvlist_free(&del->data);
+	aim_tlvlist_free(del->data);
 	g_free(del);
 
 	return 0;
@@ -610,7 +610,7 @@
 		del = cur;
 		cur = cur->next;
 		g_free(del->name);
-		aim_tlvlist_free(&del->data);
+		aim_tlvlist_free(del->data);
 		g_free(del);
 	}
 
@@ -619,7 +619,7 @@
 		del = cur;
 		cur = cur->next;
 		g_free(del->name);
-		aim_tlvlist_free(&del->data);
+		aim_tlvlist_free(del->data);
 		g_free(del);
 	}
 
@@ -670,7 +670,7 @@
 				aim_ssi_deldeny(od, NULL);
 		} else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(od->ssi.local, cur->gid, 0x0000)))) {
 			char *alias = aim_ssi_getalias(od->ssi.local, NULL, cur->name);
-			aim_ssi_addbuddy(od, cur->name, "orphans", alias, NULL, NULL, 0);
+			aim_ssi_addbuddy(od, cur->name, "orphans", NULL, alias, NULL, NULL, FALSE);
 			aim_ssi_delbuddy(od, cur->name, NULL);
 			g_free(alias);
 		}
@@ -721,15 +721,15 @@
  * @param od The oscar odion.
  * @param name The name of the item.
  * @param group The group of the item.
+ * @param data A TLV list to use as the additional data for this item.
  * @param alias The alias/nickname of the item, or NULL.
  * @param comment The buddy comment for the item, or NULL.
  * @param smsnum The locally assigned SMS number, or NULL.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
+int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *data, const char *alias, const char *comment, const char *smsnum, gboolean needauth)
 {
 	struct aim_ssi_item *parent;
-	aim_tlvlist_t *data = NULL;
 
 	if (!od || !name || !group)
 		return -EINVAL;
@@ -750,16 +750,16 @@
 	/* Create a TLV list for the new buddy */
 	if (needauth)
 		aim_tlvlist_add_noval(&data, 0x0066);
-	if (alias)
+	if (alias != NULL)
 		aim_tlvlist_add_str(&data, 0x0131, alias);
-	if (smsnum)
+	if (smsnum != NULL)
 		aim_tlvlist_add_str(&data, 0x013a, smsnum);
-	if (comment)
+	if (comment != NULL)
 		aim_tlvlist_add_str(&data, 0x013c, comment);
 
 	/* Add that bad boy */
 	aim_ssi_itemlist_add(&od->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
-	aim_tlvlist_free(&data);
+	aim_tlvlist_free(data);
 
 	/* Modify the parent group */
 	aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
@@ -920,16 +920,22 @@
  */
 int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *sn)
 {
-	char *alias;
-	gboolean waitingforauth;
+	struct aim_ssi_item *buddy;
+	GSList *data;
+
+	/* Find the buddy */
+	buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, sn, AIM_SSI_TYPE_BUDDY);
+	if (buddy == NULL)
+		return -EINVAL;
 
-	alias = aim_ssi_getalias(od->ssi.local, oldgn, sn);
-	waitingforauth = aim_ssi_waitingforauth(od->ssi.local, oldgn, sn);
+	/* Make a copy of the buddy's TLV list */
+	data = aim_tlvlist_copy(buddy->data);
 
+	/* Delete the old item */
 	aim_ssi_delbuddy(od, sn, oldgn);
-	aim_ssi_addbuddy(od, sn, newgn, alias, NULL, NULL, waitingforauth);
 
-	g_free(alias);
+	/* Add the new item using the EXACT SAME TLV list */
+	aim_ssi_addbuddy(od, sn, newgn, data, NULL, NULL, NULL, FALSE);
 
 	return 0;
 }
@@ -1172,7 +1178,7 @@
 {
 	int ret = 0, i;
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_tlv_t *tlv;
 	ByteStream bstream;
 	guint16 *maxitems;
@@ -1182,7 +1188,7 @@
 
 	/* TLV 0x0004 contains the maximum number of each item */
 	if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 		return 0;
 	}
 
@@ -1196,7 +1202,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, tlv->length/2, maxitems);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 	g_free(maxitems);
 
 	return ret;
@@ -1267,7 +1273,7 @@
 	guint8 fmtver; /* guess */
 	guint16 namelen, gid, bid, type;
 	char *name;
-	aim_tlvlist_t *data;
+	GSList *data;
 
 	fmtver = byte_stream_get8(bs); /* Version of ssi data.  Should be 0x00 */
 	od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */
@@ -1284,7 +1290,7 @@
 		data = aim_tlvlist_readlen(bs, byte_stream_get16(bs));
 		aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data);
 		g_free(name);
-		aim_tlvlist_free(&data);
+		aim_tlvlist_free(data);
 	}
 
 	/* Read in the timestamp */
@@ -1352,7 +1358,7 @@
 		if (cur->item->name)
 			snaclen += strlen(cur->item->name);
 		if (cur->item->data)
-			snaclen += aim_tlvlist_size(&cur->item->data);
+			snaclen += aim_tlvlist_size(cur->item->data);
 	}
 
 	frame = flap_frame_new(od, 0x02, snaclen);
@@ -1367,7 +1373,7 @@
 		byte_stream_put16(&frame->data, cur->item->gid);
 		byte_stream_put16(&frame->data, cur->item->bid);
 		byte_stream_put16(&frame->data, cur->item->type);
-		byte_stream_put16(&frame->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0);
+		byte_stream_put16(&frame->data, cur->item->data ? aim_tlvlist_size(cur->item->data) : 0);
 		if (cur->item->data)
 			aim_tlvlist_write(&frame->data, &cur->item->data);
 	}
@@ -1389,7 +1395,7 @@
 	aim_rxcallback_t userfunc;
 	char *name;
 	guint16 len, gid, bid, type;
-	aim_tlvlist_t *data;
+	GSList *data;
 
 	while (byte_stream_empty(bs)) {
 		if ((len = byte_stream_get16(bs)))
@@ -1406,7 +1412,7 @@
 
 		aim_ssi_itemlist_add(&od->ssi.local, name, gid, bid, type, data);
 		aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data);
-		aim_tlvlist_free(&data);
+		aim_tlvlist_free(data);
 
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 			ret = userfunc(od, conn, frame, type, name);
@@ -1428,7 +1434,7 @@
 	aim_rxcallback_t userfunc;
 	char *name;
 	guint16 len, gid, bid, type;
-	aim_tlvlist_t *data;
+	GSList *data;
 	struct aim_ssi_item *item;
 
 	while (byte_stream_empty(bs)) {
@@ -1453,7 +1459,7 @@
 				strcpy(item->name, name);
 			} else
 				item->name = NULL;
-			aim_tlvlist_free(&item->data);
+			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
 
@@ -1465,7 +1471,7 @@
 				strcpy(item->name, name);
 			} else
 				item->name = NULL;
-			aim_tlvlist_free(&item->data);
+			aim_tlvlist_free(item->data);
 			item->data = aim_tlvlist_copy(data);
 		}
 
@@ -1473,7 +1479,7 @@
 			ret = userfunc(od, conn, frame);
 
 		g_free(name);
-		aim_tlvlist_free(&data);
+		aim_tlvlist_free(data);
 	}
 
 	return ret;
@@ -1561,7 +1567,7 @@
 							strcpy(cur->item->name, cur1->name);
 						} else
 							cur->item->name = NULL;
-						aim_tlvlist_free(&cur->item->data);
+						aim_tlvlist_free(cur->item->data);
 						cur->item->data = aim_tlvlist_copy(cur1->data);
 					}
 				} else
@@ -1595,7 +1601,7 @@
 							strcpy(cur1->name, cur->item->name);
 						} else
 							cur1->name = NULL;
-						aim_tlvlist_free(&cur1->data);
+						aim_tlvlist_free(cur1->data);
 						cur1->data = aim_tlvlist_copy(cur->item->data);
 					}
 				} else
--- a/libpurple/protocols/oscar/family_icbm.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Fri Jun 01 23:53:05 2007 +0000
@@ -451,7 +451,7 @@
 	IcbmCookie *msgcookie;
 	struct aim_invite_priv *priv;
 	guchar cookie[8];
-	aim_tlvlist_t *otl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
@@ -498,19 +498,19 @@
 	byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
 
-	aim_tlvlist_add_16(&itl, 0x000a, 0x0001);
-	aim_tlvlist_add_noval(&itl, 0x000f);
-	aim_tlvlist_add_str(&itl, 0x000c, msg);
-	aim_tlvlist_add_chatroom(&itl, 0x2711, exchange, roomname, instance);
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&otl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg);
+	aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance);
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &otl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&otl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -689,7 +689,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
 	od = peer_conn->od;
@@ -705,7 +705,7 @@
 	/* ICBM header */
 	aim_im_puticbm(&frame->data, peer_conn->cookie, 0x0002, peer_conn->sn);
 
-	aim_tlvlist_add_noval(&tl, 0x0003);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
 	byte_stream_new(&hdrbs, 64);
 
@@ -714,16 +714,16 @@
 	byte_stream_putcaps(&hdrbs, peer_conn->type);
 
 	/* This TLV means "cancel!" */
-	aim_tlvlist_add_16(&itl, 0x000b, 0x0001);
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&tl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001);
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &tl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -775,7 +775,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
 	conn = flap_connection_findbygroup(od, 0x0004);
@@ -790,7 +790,7 @@
 	/* ICBM header */
 	aim_im_puticbm(&frame->data, cookie, 0x0002, sn);
 
-	aim_tlvlist_add_noval(&tl, 0x0003);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
 	byte_stream_new(&hdrbs, 128);
 
@@ -798,20 +798,20 @@
 	byte_stream_putraw(&hdrbs, cookie, 8);
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
 
-	aim_tlvlist_add_raw(&itl, 0x0002, 4, ip);
-	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
-	aim_tlvlist_add_16(&itl, 0x0005, port);
-	aim_tlvlist_add_16(&itl, 0x000a, requestnumber);
-	aim_tlvlist_add_noval(&itl, 0x000f);
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&tl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &tl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -826,7 +826,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 	guint8 ip_comp[4];
 
@@ -842,7 +842,7 @@
 	/* ICBM header */
 	aim_im_puticbm(&frame->data, cookie, 0x0002, sn);
 
-	aim_tlvlist_add_noval(&tl, 0x0003);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
 	byte_stream_new(&hdrbs, 128);
 
@@ -850,30 +850,30 @@
 	byte_stream_putraw(&hdrbs, cookie, 8);
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
 
-	aim_tlvlist_add_raw(&itl, 0x0002, 4, ip);
-	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
-	aim_tlvlist_add_16(&itl, 0x0005, pin);
-	aim_tlvlist_add_16(&itl, 0x000a, requestnumber);
-	aim_tlvlist_add_noval(&itl, 0x000f);
-	aim_tlvlist_add_noval(&itl, 0x0010);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
 
 	/* Send the bitwise complement of the port and ip.  As a check? */
 	ip_comp[0] = ~ip[0];
 	ip_comp[1] = ~ip[1];
 	ip_comp[2] = ~ip[2];
 	ip_comp[3] = ~ip[3];
-	aim_tlvlist_add_raw(&itl, 0x0016, 4, ip_comp);
-	aim_tlvlist_add_16(&itl, 0x0017, ~pin);
-
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&tl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
+
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &tl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -888,7 +888,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
 	conn = flap_connection_findbygroup(od, 0x0004);
@@ -903,7 +903,7 @@
 	/* ICBM header */
 	aim_im_puticbm(&frame->data, cookie, 0x0002, sn);
 
-	aim_tlvlist_add_noval(&tl, 0x0003);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
 	byte_stream_new(&hdrbs, 512);
 
@@ -911,11 +911,11 @@
 	byte_stream_putraw(&hdrbs, cookie, 8);
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
 
-	aim_tlvlist_add_raw(&itl, 0x0002, 4, ip);
-	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
-	aim_tlvlist_add_16(&itl, 0x0005, port);
-	aim_tlvlist_add_16(&itl, 0x000a, requestnumber);
-	aim_tlvlist_add_noval(&itl, 0x000f);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
 	/* TODO: Send 0x0016 and 0x0017 */
 
 #if 0
@@ -924,9 +924,9 @@
 	 *       redirect for a file receive (same conditions for
 	 *       sending 0x000f above)
 	 */
-	aim_tlvlist_add_raw(&itl, 0x000e, 2, "en");
-	aim_tlvlist_add_raw(&itl, 0x000d, 8, "us-ascii");
-	aim_tlvlist_add_raw(&itl, 0x000c, 24, "Please accept this file.");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000e, 2, "en");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000d, 8, "us-ascii");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, 24, "Please accept this file.");
 #endif
 
 	if (filename != NULL)
@@ -943,19 +943,19 @@
 		byte_stream_putstr(&bs, filename);
 		byte_stream_put8(&bs, 0x00);
 
-		aim_tlvlist_add_raw(&itl, 0x2711, bs.len, bs.data);
+		aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, bs.len, bs.data);
 		g_free(bs.data);
 		/* End TLV t(2711) */
 	}
 
-	aim_tlvlist_write(&hdrbs, &itl);
-	aim_tlvlist_add_raw(&tl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &tl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -970,7 +970,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 	guint8 ip_comp[4];
 
@@ -986,7 +986,7 @@
 	/* ICBM header */
 	aim_im_puticbm(&frame->data, cookie, 0x0002, sn);
 
-	aim_tlvlist_add_noval(&tl, 0x0003);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
 	byte_stream_new(&hdrbs, 512);
 
@@ -994,20 +994,20 @@
 	byte_stream_putraw(&hdrbs, cookie, 8);
 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
 
-	aim_tlvlist_add_raw(&itl, 0x0002, 4, ip);
-	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
-	aim_tlvlist_add_16(&itl, 0x0005, pin);
-	aim_tlvlist_add_16(&itl, 0x000a, requestnumber);
-	aim_tlvlist_add_noval(&itl, 0x000f);
-	aim_tlvlist_add_noval(&itl, 0x0010);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
 
 	/* Send the bitwise complement of the port and ip.  As a check? */
 	ip_comp[0] = ~ip[0];
 	ip_comp[1] = ~ip[1];
 	ip_comp[2] = ~ip[2];
 	ip_comp[3] = ~ip[3];
-	aim_tlvlist_add_raw(&itl, 0x0016, 4, ip_comp);
-	aim_tlvlist_add_16(&itl, 0x0017, ~pin);
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
+	aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
 
 #if 0
 	/* TODO: If the following is ever enabled, ensure that it is
@@ -1015,9 +1015,9 @@
 	 *       redirect for a file receive (same conditions for
 	 *       sending 0x000f above)
 	 */
-	aim_tlvlist_add_raw(&itl, 0x000e, 2, "en");
-	aim_tlvlist_add_raw(&itl, 0x000d, 8, "us-ascii");
-	aim_tlvlist_add_raw(&itl, 0x000c, 24, "Please accept this file.");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000e, 2, "en");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000d, 8, "us-ascii");
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x000c, 24, "Please accept this file.");
 #endif
 
 	if (filename != NULL)
@@ -1034,20 +1034,20 @@
 		byte_stream_putstr(&bs, filename);
 		byte_stream_put8(&bs, 0x00);
 
-		aim_tlvlist_add_raw(&itl, 0x2711, bs.len, bs.data);
+		aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, bs.len, bs.data);
 		g_free(bs.data);
 		/* End TLV t(2711) */
 	}
 
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&tl, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
+	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
+
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
 	g_free(hdrbs.data);
 
-	aim_tlvlist_write(&frame->data, &tl);
-
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &outer_tlvlist);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -1226,7 +1226,7 @@
 	aim_rxcallback_t userfunc;
 	guchar cookie[8];
 	guint16 channel;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	char *sn;
 	int snlen;
 	guint16 icbmflags = 0;
@@ -1281,7 +1281,7 @@
 
 	g_free(sn);
 	g_free(msg);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -1874,11 +1874,11 @@
 
 typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);
 
-static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
 {
 	aim_rxcallback_t userfunc;
 	aim_tlv_t *block1, *servdatatlv;
-	aim_tlvlist_t *list2;
+	GSList *list2;
 	aim_tlv_t *tlv;
 	IcbmArgsCh2 args;
 	ByteStream bbs, sdbs, *sdbsptr = NULL;
@@ -2078,12 +2078,12 @@
 	g_free((char *)args.encoding);
 	g_free((char *)args.language);
 
-	aim_tlvlist_free(&list2);
+	aim_tlvlist_free(list2);
 
 	return ret;
 }
 
-static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
 {
 	ByteStream meat;
 	aim_rxcallback_t userfunc;
@@ -2186,7 +2186,7 @@
 		ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
 
 	} else if (channel == 2) {
-		aim_tlvlist_t *tlvlist;
+		GSList *tlvlist;
 
 		/*
 		 * Read block of TLVs (not including the userinfo data).  All
@@ -2196,14 +2196,14 @@
 
 		ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
 
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 
 	} else if (channel == 4) {
-		aim_tlvlist_t *tlvlist;
+		GSList *tlvlist;
 
 		tlvlist = aim_tlvlist_read(bs);
 		ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 
 	} else {
 		purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
@@ -2284,7 +2284,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
 		return -EINVAL;
@@ -2300,9 +2300,9 @@
 	byte_stream_put8(&frame->data, strlen(sn));
 	byte_stream_putstr(&frame->data, sn);
 
-	aim_tlvlist_add_16(&tl, 0x0003, code);
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_add_16(&tlvlist, 0x0003, code);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
--- a/libpurple/protocols/oscar/family_icq.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Fri Jun 01 23:53:05 2007 +0000
@@ -474,14 +474,14 @@
 icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
 	int ret = 0;
-	aim_tlvlist_t *tl;
+	GSList *tlvlist;
 	aim_tlv_t *datatlv;
 	ByteStream qbs;
 	guint32 ouruin;
 	guint16 cmdlen, cmd, reqid;
 
-	if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) {
-		aim_tlvlist_free(&tl);
+	if (!(tlvlist = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tlvlist, 0x0001, 1))) {
+		aim_tlvlist_free(tlvlist);
 		purple_debug_misc("oscar", "corrupt ICQ response\n");
 		return 0;
 	}
@@ -661,7 +661,7 @@
 		}
 	}
 
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_locate.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Fri Jun 01 23:53:05 2007 +0000
@@ -930,7 +930,7 @@
 int
 aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info)
 {
-	aim_tlvlist_t *tlvlist = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!bs || !info)
 		return -EINVAL;
@@ -965,9 +965,9 @@
 	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
 
-	byte_stream_put16(bs, aim_tlvlist_count(&tlvlist));
+	byte_stream_put16(bs, aim_tlvlist_count(tlvlist));
 	aim_tlvlist_write(bs, &tlvlist);
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return 0;
 }
@@ -1050,7 +1050,7 @@
 static int
 rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
 	guint16 maxsiglen = 0;
@@ -1063,7 +1063,7 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, maxsiglen);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -1096,7 +1096,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 	char *encoding;
 	static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
 
@@ -1115,8 +1115,8 @@
 		/* no + 1 here because of %s */
 		encoding = g_malloc(strlen(defencoding) + strlen(profile_encoding));
 		snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
-		aim_tlvlist_add_str(&tl, 0x0001, encoding);
-		aim_tlvlist_add_raw(&tl, 0x0002, profile_len, (const guchar *)profile);
+		aim_tlvlist_add_str(&tlvlist, 0x0001, encoding);
+		aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile);
 		g_free(encoding);
 	}
 
@@ -1132,20 +1132,20 @@
 		if (awaymsg_len) {
 			encoding = g_malloc(strlen(defencoding) + strlen(awaymsg_encoding));
 			snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
-			aim_tlvlist_add_str(&tl, 0x0003, encoding);
-			aim_tlvlist_add_raw(&tl, 0x0004, awaymsg_len, (const guchar *)awaymsg);
+			aim_tlvlist_add_str(&tlvlist, 0x0003, encoding);
+			aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg);
 			g_free(encoding);
 		} else
-			aim_tlvlist_add_noval(&tl, 0x0004);
+			aim_tlvlist_add_noval(&tlvlist, 0x0004);
 	}
 
-	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(tlvlist));
 
 	snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x0002, 0x004, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -1161,20 +1161,20 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
-	aim_tlvlist_add_caps(&tl, 0x0005, caps);
+	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps);
 
-	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(tlvlist));
 
 	snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x0002, 0x004, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -1221,7 +1221,7 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	aim_userinfo_t *userinfo, *userinfo2;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_tlv_t *tlv = NULL;
 	int was_explicit;
 
@@ -1252,7 +1252,7 @@
 		userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
 		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
 	}
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	aim_locate_adduserinfo(od, userinfo);
 	userinfo2 = aim_locate_finduserinfo(od, userinfo->sn);
@@ -1284,42 +1284,42 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
-	aim_tlvlist_add_16(&tl, 0x000a, privacy);
+	aim_tlvlist_add_16(&tlvlist, 0x000a, privacy);
 
 	if (first)
-		aim_tlvlist_add_str(&tl, 0x0001, first);
+		aim_tlvlist_add_str(&tlvlist, 0x0001, first);
 	if (last)
-		aim_tlvlist_add_str(&tl, 0x0002, last);
+		aim_tlvlist_add_str(&tlvlist, 0x0002, last);
 	if (middle)
-		aim_tlvlist_add_str(&tl, 0x0003, middle);
+		aim_tlvlist_add_str(&tlvlist, 0x0003, middle);
 	if (maiden)
-		aim_tlvlist_add_str(&tl, 0x0004, maiden);
+		aim_tlvlist_add_str(&tlvlist, 0x0004, maiden);
 
 	if (state)
-		aim_tlvlist_add_str(&tl, 0x0007, state);
+		aim_tlvlist_add_str(&tlvlist, 0x0007, state);
 	if (city)
-		aim_tlvlist_add_str(&tl, 0x0008, city);
+		aim_tlvlist_add_str(&tlvlist, 0x0008, city);
 
 	if (nickname)
-		aim_tlvlist_add_str(&tl, 0x000c, nickname);
+		aim_tlvlist_add_str(&tlvlist, 0x000c, nickname);
 	if (zip)
-		aim_tlvlist_add_str(&tl, 0x000d, zip);
+		aim_tlvlist_add_str(&tlvlist, 0x000d, zip);
 
 	if (street)
-		aim_tlvlist_add_str(&tl, 0x0021, street);
+		aim_tlvlist_add_str(&tlvlist, 0x0021, street);
 
-	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(tlvlist));
 
 	snacid = aim_cachesnac(od, 0x0002, 0x0009, 0x0000, NULL, 0);
 
 	aim_putsnac(&frame->data, 0x0002, 0x0009, 0x0000, snacid);
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -1365,32 +1365,32 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
 	/* ?? privacy ?? */
-	aim_tlvlist_add_16(&tl, 0x000a, privacy);
+	aim_tlvlist_add_16(&tlvlist, 0x000a, privacy);
 
 	if (interest1)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest1);
+		aim_tlvlist_add_str(&tlvlist, 0x0000b, interest1);
 	if (interest2)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest2);
+		aim_tlvlist_add_str(&tlvlist, 0x0000b, interest2);
 	if (interest3)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest3);
+		aim_tlvlist_add_str(&tlvlist, 0x0000b, interest3);
 	if (interest4)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest4);
+		aim_tlvlist_add_str(&tlvlist, 0x0000b, interest4);
 	if (interest5)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest5);
+		aim_tlvlist_add_str(&tlvlist, 0x0000b, interest5);
 
-	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(tlvlist));
 
 	snacid = aim_cachesnac(od, 0x0002, 0x000f, 0x0000, NULL, 0);
 
 	aim_putsnac(&frame->data, 0x0002, 0x000f, 0x0000, 0);
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
--- a/libpurple/protocols/oscar/family_odir.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_odir.c	Fri Jun 01 23:53:05 2007 +0000
@@ -43,22 +43,22 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region || !email)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
-	aim_tlvlist_add_str(&tl, 0x001c, region);
-	aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */
-	aim_tlvlist_add_str(&tl, 0x0005, email);
+	aim_tlvlist_add_str(&tlvlist, 0x001c, region);
+	aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0001); /* Type of search */
+	aim_tlvlist_add_str(&tlvlist, 0x0005, email);
 
-	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(tlvlist));
 	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x000f, 0x0002, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -91,41 +91,41 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
-	aim_tlvlist_add_str(&tl, 0x001c, region);
-	aim_tlvlist_add_16(&tl, 0x000a, 0x0000); /* Type of search */
+	aim_tlvlist_add_str(&tlvlist, 0x001c, region);
+	aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0000); /* Type of search */
 	if (first)
-		aim_tlvlist_add_str(&tl, 0x0001, first);
+		aim_tlvlist_add_str(&tlvlist, 0x0001, first);
 	if (last)
-		aim_tlvlist_add_str(&tl, 0x0002, last);
+		aim_tlvlist_add_str(&tlvlist, 0x0002, last);
 	if (middle)
-		aim_tlvlist_add_str(&tl, 0x0003, middle);
+		aim_tlvlist_add_str(&tlvlist, 0x0003, middle);
 	if (maiden)
-		aim_tlvlist_add_str(&tl, 0x0004, maiden);
+		aim_tlvlist_add_str(&tlvlist, 0x0004, maiden);
 	if (country)
-		aim_tlvlist_add_str(&tl, 0x0006, country);
+		aim_tlvlist_add_str(&tlvlist, 0x0006, country);
 	if (state)
-		aim_tlvlist_add_str(&tl, 0x0007, state);
+		aim_tlvlist_add_str(&tlvlist, 0x0007, state);
 	if (city)
-		aim_tlvlist_add_str(&tl, 0x0008, city);
+		aim_tlvlist_add_str(&tlvlist, 0x0008, city);
 	if (nick)
-		aim_tlvlist_add_str(&tl, 0x000c, nick);
+		aim_tlvlist_add_str(&tlvlist, 0x000c, nick);
 	if (zip)
-		aim_tlvlist_add_str(&tl, 0x000d, zip);
+		aim_tlvlist_add_str(&tlvlist, 0x000d, zip);
 	if (address)
-		aim_tlvlist_add_str(&tl, 0x0021, address);
+		aim_tlvlist_add_str(&tlvlist, 0x0021, address);
 
-	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(tlvlist));
 	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x000f, 0x0002, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -145,23 +145,23 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
-	aim_tlvlist_add_str(&tl, 0x001c, region);
-	aim_tlvlist_add_16(&tl, 0x000a, 0x0001); /* Type of search */
+	aim_tlvlist_add_str(&tlvlist, 0x001c, region);
+	aim_tlvlist_add_16(&tlvlist, 0x000a, 0x0001); /* Type of search */
 	if (interest)
-		aim_tlvlist_add_str(&tl, 0x0001, interest);
+		aim_tlvlist_add_str(&tlvlist, 0x0001, interest);
 
-	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10+aim_tlvlist_size(tlvlist));
 	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x000f, 0x0002, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -189,22 +189,22 @@
 	/* Allocate a linked list, 1 node per result */
 	while (numresults) {
 		struct aim_odir *new;
-		aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, byte_stream_get16(bs));
+		GSList *tlvlist = aim_tlvlist_readnum(bs, byte_stream_get16(bs));
 		new = (struct aim_odir *)g_malloc(sizeof(struct aim_odir));
-		new->first = aim_tlv_getstr(tl, 0x0001, 1);
-		new->last = aim_tlv_getstr(tl, 0x0002, 1);
-		new->middle = aim_tlv_getstr(tl, 0x0003, 1);
-		new->maiden = aim_tlv_getstr(tl, 0x0004, 1);
-		new->email = aim_tlv_getstr(tl, 0x0005, 1);
-		new->country = aim_tlv_getstr(tl, 0x0006, 1);
-		new->state = aim_tlv_getstr(tl, 0x0007, 1);
-		new->city = aim_tlv_getstr(tl, 0x0008, 1);
-		new->sn = aim_tlv_getstr(tl, 0x0009, 1);
-		new->interest = aim_tlv_getstr(tl, 0x000b, 1);
-		new->nick = aim_tlv_getstr(tl, 0x000c, 1);
-		new->zip = aim_tlv_getstr(tl, 0x000d, 1);
-		new->region = aim_tlv_getstr(tl, 0x001c, 1);
-		new->address = aim_tlv_getstr(tl, 0x0021, 1);
+		new->first = aim_tlv_getstr(tlvlist, 0x0001, 1);
+		new->last = aim_tlv_getstr(tlvlist, 0x0002, 1);
+		new->middle = aim_tlv_getstr(tlvlist, 0x0003, 1);
+		new->maiden = aim_tlv_getstr(tlvlist, 0x0004, 1);
+		new->email = aim_tlv_getstr(tlvlist, 0x0005, 1);
+		new->country = aim_tlv_getstr(tlvlist, 0x0006, 1);
+		new->state = aim_tlv_getstr(tlvlist, 0x0007, 1);
+		new->city = aim_tlv_getstr(tlvlist, 0x0008, 1);
+		new->sn = aim_tlv_getstr(tlvlist, 0x0009, 1);
+		new->interest = aim_tlv_getstr(tlvlist, 0x000b, 1);
+		new->nick = aim_tlv_getstr(tlvlist, 0x000c, 1);
+		new->zip = aim_tlv_getstr(tlvlist, 0x000d, 1);
+		new->region = aim_tlv_getstr(tlvlist, 0x001c, 1);
+		new->address = aim_tlv_getstr(tlvlist, 0x0021, 1);
 		new->next = results;
 		results = new;
 		numresults--;
--- a/libpurple/protocols/oscar/family_oservice.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Fri Jun 01 23:53:05 2007 +0000
@@ -123,7 +123,7 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 	struct chatsnacinfo csi;
 
 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
@@ -145,9 +145,9 @@
 	 */
 	byte_stream_put16(&frame->data, 0x000e);
 
-	aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance);
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -160,7 +160,7 @@
 {
 	struct aim_redirect_data redir;
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	aim_snac_t *origsnac = NULL;
 	int ret = 0;
 
@@ -171,7 +171,7 @@
 	if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
 			!aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
 			!aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
-		aim_tlvlist_free(&tlvlist);
+		aim_tlvlist_free(tlvlist);
 		return 0;
 	}
 
@@ -201,7 +201,7 @@
 		g_free(origsnac->data);
 	g_free(origsnac);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -606,7 +606,7 @@
 	aim_rxcallback_t userfunc;
 	int ret = 0;
 	guint16 groupcount, i;
-	aim_tlvlist_t *tl;
+	GSList *tlvlist;
 	char *ip = NULL;
 	aim_tlv_t *cktlv;
 
@@ -630,17 +630,17 @@
 		purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
 	}
 
-	tl = aim_tlvlist_read(bs);
+	tlvlist = aim_tlvlist_read(bs);
 
-	if (aim_tlv_gettlv(tl, 0x0005, 1))
-		ip = aim_tlv_getstr(tl, 0x0005, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
+		ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
 
-	cktlv = aim_tlv_gettlv(tl, 0x0006, 1);
+	cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
 
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 	g_free(ip);
 
 	return ret;
@@ -653,7 +653,7 @@
 	aim_rxcallback_t userfunc;
 	char *msg = NULL;
 	int ret = 0;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	guint16 id;
 
 	/*
@@ -681,7 +681,7 @@
 
 	g_free(msg);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
@@ -805,24 +805,24 @@
 	FlapConnection *conn;
 	FlapFrame *frame;
 	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (seticqstatus)
 	{
-		aim_tlvlist_add_32(&tl, 0x0006, icqstatus |
+		aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
 				AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
 	}
 
 #if 0
 	if (other_stuff_that_isnt_implemented)
 	{
-		aim_tlvlist_add_raw(&tl, 0x000c, 0x0025,
+		aim_tlvlist_add_raw(&tlvlist, 0x000c, 0x0025,
 				chunk_of_x25_bytes_with_ip_address_etc);
-		aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41);
-		aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00);
+		aim_tlvlist_add_raw(&tlvlist, 0x0011, 0x0005, unknown 0x01 61 10 f6 41);
+		aim_tlvlist_add_16(&tlvlist, 0x0012, unknown 0x00 00);
 	}
 #endif
 
@@ -851,18 +851,18 @@
 			byte_stream_putstr(&tmpbs, itmsurl);
 		byte_stream_put16(&tmpbs, 0x0000);
 
-		aim_tlvlist_add_raw(&tl, 0x001d,
+		aim_tlvlist_add_raw(&tlvlist, 0x001d,
 				byte_stream_curpos(&tmpbs), tmpbs.data);
 		g_free(tmpbs.data);
 	}
 
-	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(&tl));
+	frame = flap_frame_new(od, 0x02, 10 + aim_tlvlist_size(tlvlist));
 
 	snacid = aim_cachesnac(od, 0x0001, 0x001e, 0x0000, NULL, 0);
 	aim_putsnac(&frame->data, 0x0001, 0x001e, 0x0000, snacid);
 
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 
@@ -913,14 +913,14 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint32 offset, len;
-	aim_tlvlist_t *list;
+	GSList *tlvlist;
 	char *modname;
 
 	offset = byte_stream_get32(bs);
 	len = byte_stream_get32(bs);
-	list = aim_tlvlist_read(bs);
+	tlvlist = aim_tlvlist_read(bs);
 
-	modname = aim_tlv_getstr(list, 0x0001, 1);
+	modname = aim_tlv_getstr(tlvlist, 0x0001, 1);
 
 	purple_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
 
@@ -928,7 +928,7 @@
 		ret = userfunc(od, conn, frame, offset, len, modname);
 
 	g_free(modname);
-	aim_tlvlist_free(&list);
+	aim_tlvlist_free(tlvlist);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_popup.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_popup.c	Fri Jun 01 23:53:05 2007 +0000
@@ -37,23 +37,23 @@
 parsepopup(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
 	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tl;
+	GSList *tlvlist;
 	int ret = 0;
 	char *msg, *url;
 	guint16 width, height, delay;
 
-	tl = aim_tlvlist_read(bs);
+	tlvlist = aim_tlvlist_read(bs);
 
-	msg = aim_tlv_getstr(tl, 0x0001, 1);
-	url = aim_tlv_getstr(tl, 0x0002, 1);
-	width = aim_tlv_get16(tl, 0x0003, 1);
-	height = aim_tlv_get16(tl, 0x0004, 1);
-	delay = aim_tlv_get16(tl, 0x0005, 1);
+	msg = aim_tlv_getstr(tlvlist, 0x0001, 1);
+	url = aim_tlv_getstr(tlvlist, 0x0002, 1);
+	width = aim_tlv_get16(tlvlist, 0x0003, 1);
+	height = aim_tlv_get16(tlvlist, 0x0004, 1);
+	delay = aim_tlv_get16(tlvlist, 0x0005, 1);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, msg, url, width, height, delay);
 
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_free(tlvlist);
 	g_free(msg);
 	g_free(url);
 
--- a/libpurple/protocols/oscar/family_userlookup.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/family_userlookup.c	Fri Jun 01 23:53:05 2007 +0000
@@ -89,7 +89,7 @@
 static int reply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
 	int j = 0, m, ret = 0;
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	char *cur = NULL, *buf = NULL;
 	aim_rxcallback_t userfunc;
 	aim_snac_t *snac2;
@@ -99,7 +99,7 @@
 		searchaddr = (const char *)snac2->data;
 
 	tlvlist = aim_tlvlist_read(bs);
-	m = aim_tlvlist_count(&tlvlist);
+	m = aim_tlvlist_count(tlvlist);
 
 	/* XXX uhm.
 	 * This is the only place that uses something other than 1 for the 3rd
@@ -116,7 +116,7 @@
 	}
 	g_free(cur);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, searchaddr, j, buf);
--- a/libpurple/protocols/oscar/flap_connection.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Fri Jun 01 23:53:05 2007 +0000
@@ -61,13 +61,13 @@
 flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
 {
 	FlapFrame *frame;
-	aim_tlvlist_t *tl = NULL;
+	GSList *tlvlist = NULL;
 
 	frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length);
 	byte_stream_put32(&frame->data, 0x00000001);
-	aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy);
-	aim_tlvlist_write(&frame->data, &tl);
-	aim_tlvlist_free(&tl);
+	aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
+	aim_tlvlist_write(&frame->data, &tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	flap_connection_send(conn, frame);
 }
@@ -693,7 +693,7 @@
 static void
 parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
 {
-	aim_tlvlist_t *tlvlist;
+	GSList *tlvlist;
 	char *msg = NULL;
 	guint16 code = 0;
 	aim_rxcallback_t userfunc;
@@ -721,7 +721,7 @@
 	if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
 		userfunc(od, conn, frame, code, msg);
 
-	aim_tlvlist_free(&tlvlist);
+	aim_tlvlist_free(tlvlist);
 
 	g_free(msg);
 }
--- a/libpurple/protocols/oscar/oscar.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Jun 01 23:53:05 2007 +0000
@@ -2202,7 +2202,7 @@
 				   buddy->name, group->name);
 		aim_ssi_sendauthrequest(od, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
 		if (!aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))
-			aim_ssi_addbuddy(od, buddy->name, group->name, purple_buddy_get_alias_only(buddy), NULL, NULL, 1);
+			aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
 	}
 }
 
@@ -4592,7 +4592,7 @@
 	if ((od->ssi.received_data) && !(aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))) {
 		purple_debug_info("oscar",
 				   "ssi: adding buddy %s to group %s\n", buddy->name, group->name);
-		aim_ssi_addbuddy(od, buddy->name, group->name, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
+		aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
 	}
 
 	/* XXX - Should this be done from AIM accounts, as well? */
--- a/libpurple/protocols/oscar/oscar.h	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Fri Jun 01 23:53:05 2007 +0000
@@ -1172,7 +1172,7 @@
 	guint16 gid;
 	guint16 bid;
 	guint16 type;
-	struct aim_tlvlist_s *data;
+	GSList *data;
 	struct aim_ssi_item *next;
 };
 
@@ -1208,7 +1208,7 @@
 gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn);
 
 /* Client functions for changing SSI data */
-int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth);
+int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth);
 int aim_ssi_addpermit(OscarData *od, const char *name);
 int aim_ssi_adddeny(OscarData *od, const char *name);
 int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group);
@@ -1357,54 +1357,47 @@
 	guint8 *value;
 } aim_tlv_t;
 
-/* TLV List structure */
-typedef struct aim_tlvlist_s
-{
-	aim_tlv_t *tlv;
-	struct aim_tlvlist_s *next;
-} aim_tlvlist_t;
-
 /* TLV handling functions */
 char *aim_tlv_getvalue_as_string(aim_tlv_t *tlv);
 
-aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, guint16 type, const int nth);
-int aim_tlv_getlength(aim_tlvlist_t *list, guint16 type, const int nth);
-char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth);
-guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth);
-guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth);
-guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth);
+aim_tlv_t *aim_tlv_gettlv(GSList *list, guint16 type, const int nth);
+int aim_tlv_getlength(GSList *list, guint16 type, const int nth);
+char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth);
+guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth);
+guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth);
+guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth);
 
 /* TLV list handling functions */
-aim_tlvlist_t *aim_tlvlist_read(ByteStream *bs);
-aim_tlvlist_t *aim_tlvlist_readnum(ByteStream *bs, guint16 num);
-aim_tlvlist_t *aim_tlvlist_readlen(ByteStream *bs, guint16 len);
-aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig);
+GSList *aim_tlvlist_read(ByteStream *bs);
+GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num);
+GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len);
+GSList *aim_tlvlist_copy(GSList *orig);
 
-int aim_tlvlist_count(aim_tlvlist_t **list);
-int aim_tlvlist_size(aim_tlvlist_t **list);
-int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two);
-int aim_tlvlist_write(ByteStream *bs, aim_tlvlist_t **list);
-void aim_tlvlist_free(aim_tlvlist_t **list);
+int aim_tlvlist_count(GSList *list);
+int aim_tlvlist_size(GSList *list);
+int aim_tlvlist_cmp(GSList *one, GSList *two);
+int aim_tlvlist_write(ByteStream *bs, GSList **list);
+void aim_tlvlist_free(GSList *list);
 
-int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value);
-int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type);
-int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value);
-int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value);
-int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value);
-int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value);
-int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps);
-int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo);
-int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
-int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl);
+int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value);
+int aim_tlvlist_add_noval(GSList **list, const guint16 type);
+int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value);
+int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value);
+int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value);
+int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value);
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps);
+int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo);
+int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
+int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
 
-int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 lenth, const guint8 *value);
-int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str);
-int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type);
-int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value);
-int aim_tlvlist_replace_16(aim_tlvlist_t **list, const guint16 type, const guint16 value);
-int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value);
+int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 lenth, const guint8 *value);
+int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str);
+int aim_tlvlist_replace_noval(GSList **list, const guint16 type);
+int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value);
+int aim_tlvlist_replace_16(GSList **list, const guint16 type, const guint16 value);
+int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value);
 
-void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type);
+void aim_tlvlist_remove(GSList **list, const guint16 type);
 
 
 
--- a/libpurple/protocols/oscar/tlv.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/oscar/tlv.c	Fri Jun 01 23:53:05 2007 +0000
@@ -18,7 +18,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-
 #include "oscar.h"
 
 static aim_tlv_t *
@@ -35,17 +34,57 @@
 }
 
 static void
-freetlv(aim_tlv_t **oldtlv)
+freetlv(aim_tlv_t *oldtlv)
 {
+	g_free(oldtlv->value);
+	g_free(oldtlv);
+}
 
-	if (!oldtlv || !*oldtlv)
-		return;
+static GSList *
+aim_tlv_read(GSList *list, ByteStream *bs)
+{
+	guint16 type, length;
+	aim_tlv_t *tlv;
+
+	type = byte_stream_get16(bs);
+	length = byte_stream_get16(bs);
 
-	g_free((*oldtlv)->value);
-	g_free(*oldtlv);
-	*oldtlv = NULL;
+#if 0
+	/*
+	 * This code hasn't been needed in years.  It's been commented
+	 * out since 2003, at the latest.  It seems likely that it was
+	 * just a bug in their server code that has since been fixed.
+	 * In any case, here's the orignal comment, kept for historical
+	 * purposes:
+	 *
+	 * Okay, so now AOL has decided that any TLV of
+	 * type 0x0013 can only be two bytes, despite
+	 * what the actual given length is.  So here
+	 * we dump any invalid TLVs of that sort.  Hopefully
+	 * there's no special cases to this special case.
+	 *   - mid (30jun2000)
+	 */
+	if ((type == 0x0013) && (length != 0x0002)) {
+		length = 0x0002;
+		return list;
+	}
+#endif
+	if (length > byte_stream_empty(bs)) {
+		aim_tlvlist_free(list);
+		return NULL;
+	}
 
-	return;
+	tlv = createtlv(type, length, NULL);
+	if (tlv->length > 0) {
+		tlv->value = byte_stream_getraw(bs, length);
+		if (!tlv->value) {
+			freetlv(tlv);
+			aim_tlvlist_free(list);
+			return NULL;
+		}
+	}
+
+	return g_slist_prepend(list, tlv);
 }
 
 /**
@@ -56,7 +95,7 @@
  * routines.  When done with a TLV chain, aim_tlvlist_free() should
  * be called to free the dynamic substructures.
  *
- * XXX There should be a flag setable here to have the tlvlist contain
+ * TODO: There should be a flag setable here to have the tlvlist contain
  * bstream references, so that at least the ->value portion of each
  * element doesn't need to be malloc/memcpy'd.  This could prove to be
  * just as efficient as the in-place TLV parsing used in a couple places
@@ -65,56 +104,17 @@
  * @param bs Input bstream
  * @return Return the TLV chain read
  */
-aim_tlvlist_t *aim_tlvlist_read(ByteStream *bs)
+GSList *aim_tlvlist_read(ByteStream *bs)
 {
-	aim_tlvlist_t *list = NULL, *cur;
+	GSList *list = NULL;
 
 	while (byte_stream_empty(bs) > 0) {
-		guint16 type, length;
-
-		type = byte_stream_get16(bs);
-		length = byte_stream_get16(bs);
-
-#if 0 /* temporarily disabled until I know if they're still doing it or not */
-		/*
-		 * Okay, so now AOL has decided that any TLV of
-		 * type 0x0013 can only be two bytes, despite
-		 * what the actual given length is.  So here
-		 * we dump any invalid TLVs of that sort.  Hopefully
-		 * there's no special cases to this special case.
-		 *   - mid (30jun2000)
-		 */
-		if ((type == 0x0013) && (length != 0x0002))
-			length = 0x0002;
-#else
-		if (0)
-			;
-#endif
-		else {
-
-			if (length > byte_stream_empty(bs)) {
-				aim_tlvlist_free(&list);
-				return NULL;
-			}
-
-			cur = g_new0(aim_tlvlist_t, 1);
-			cur->tlv = createtlv(type, length, NULL);
-			if (cur->tlv->length > 0) {
-				cur->tlv->value = byte_stream_getraw(bs, length);
-				if (!cur->tlv->value) {
-					freetlv(&cur->tlv);
-					g_free(cur);
-					aim_tlvlist_free(&list);
-					return NULL;
-				}
-			}
-
-			cur->next = list;
-			list = cur;
-		}
+		list = aim_tlv_read(list, bs);
+		if (list == NULL)
+			return NULL;
 	}
 
-	return list;
+	return g_slist_reverse(list);
 }
 
 /**
@@ -125,7 +125,7 @@
  * routines.  When done with a TLV chain, aim_tlvlist_free() should
  * be called to free the dynamic substructures.
  *
- * XXX There should be a flag setable here to have the tlvlist contain
+ * TODO: There should be a flag setable here to have the tlvlist contain
  * bstream references, so that at least the ->value portion of each
  * element doesn't need to be malloc/memcpy'd.  This could prove to be
  * just as efficient as the in-place TLV parsing used in a couple places
@@ -138,40 +138,18 @@
  *        preceded by the number of TLVs.  So you can limit that with this.
  * @return Return the TLV chain read
  */
-aim_tlvlist_t *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
+GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
 {
-	aim_tlvlist_t *list = NULL, *cur;
+	GSList *list = NULL;
 
 	while ((byte_stream_empty(bs) > 0) && (num != 0)) {
-		guint16 type, length;
-
-		type = byte_stream_get16(bs);
-		length = byte_stream_get16(bs);
-
-		if (length > byte_stream_empty(bs)) {
-			aim_tlvlist_free(&list);
+		list = aim_tlv_read(list, bs);
+		if (list == NULL)
 			return NULL;
-		}
-
-		cur = g_new0(aim_tlvlist_t, 1);
-		cur->tlv = createtlv(type, length, NULL);
-		if (cur->tlv->length > 0) {
-			cur->tlv->value = byte_stream_getraw(bs, length);
-			if (!cur->tlv->value) {
-				freetlv(&cur->tlv);
-				g_free(cur);
-				aim_tlvlist_free(&list);
-				return NULL;
-			}
-		}
-
-		if (num > 0)
-			num--;
-		cur->next = list;
-		list = cur;
+		num--;
 	}
 
-	return list;
+	return g_slist_reverse(list);
 }
 
 /**
@@ -182,7 +160,7 @@
  * routines.  When done with a TLV chain, aim_tlvlist_free() should
  * be called to free the dynamic substructures.
  *
- * XXX There should be a flag setable here to have the tlvlist contain
+ * TODO: There should be a flag setable here to have the tlvlist contain
  * bstream references, so that at least the ->value portion of each
  * element doesn't need to be malloc/memcpy'd.  This could prove to be
  * just as efficient as the in-place TLV parsing used in a couple places
@@ -195,39 +173,19 @@
  *        preceded by the length of the TLVs.  So you can limit that with this.
  * @return Return the TLV chain read
  */
-aim_tlvlist_t *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
+GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
 {
-	aim_tlvlist_t *list = NULL, *cur;
+	GSList *list = NULL;
 
 	while ((byte_stream_empty(bs) > 0) && (len > 0)) {
-		guint16 type, length;
-
-		type = byte_stream_get16(bs);
-		length = byte_stream_get16(bs);
-
-		if (length > byte_stream_empty(bs)) {
-			aim_tlvlist_free(&list);
+		list = aim_tlv_read(list, bs);
+		if (list == NULL)
 			return NULL;
-		}
 
-		cur = g_new0(aim_tlvlist_t, 1);
-		cur->tlv = createtlv(type, length, NULL);
-		if (cur->tlv->length > 0) {
-			cur->tlv->value = byte_stream_getraw(bs, length);
-			if (!cur->tlv->value) {
-				freetlv(&cur->tlv);
-				g_free(cur);
-				aim_tlvlist_free(&list);
-				return NULL;
-			}
-		}
-
-		len -= aim_tlvlist_size(&cur);
-		cur->next = list;
-		list = cur;
+		len -= 2 + 2 + ((aim_tlv_t *)list->data)->length;
 	}
 
-	return list;
+	return g_slist_reverse(list);
 }
 
 /**
@@ -237,12 +195,14 @@
  * @param orig The TLV chain you want to make a copy of.
  * @return A newly allocated TLV chain.
  */
-aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig)
+GSList *aim_tlvlist_copy(GSList *orig)
 {
-	aim_tlvlist_t *new = NULL;
+	GSList *new = NULL;
+	aim_tlv_t *tlv;
 
-	while (orig) {
-		aim_tlvlist_add_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value);
+	while (orig != NULL) {
+		tlv = orig->data;
+		aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value);
 		orig = orig->next;
 	}
 
@@ -257,15 +217,15 @@
  * @param two The other TLV chain to compare.
  * @return Return 0 if the lists are the same, return 1 if they are different.
  */
-int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two)
+int aim_tlvlist_cmp(GSList *one, GSList *two)
 {
 	ByteStream bs1, bs2;
 
-	if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two))
+	if (aim_tlvlist_size(one) != aim_tlvlist_size(two))
 		return 1;
 
-	byte_stream_new(&bs1, aim_tlvlist_size(&one));
-	byte_stream_new(&bs2, aim_tlvlist_size(&two));
+	byte_stream_new(&bs1, aim_tlvlist_size(one));
+	byte_stream_new(&bs2, aim_tlvlist_size(two));
 
 	aim_tlvlist_write(&bs1, &one);
 	aim_tlvlist_write(&bs2, &two);
@@ -291,26 +251,13 @@
  *
  * @param list Chain to be freed
  */
-void aim_tlvlist_free(aim_tlvlist_t **list)
+void aim_tlvlist_free(GSList *list)
 {
-	aim_tlvlist_t *cur;
-
-	if (!list || !*list)
-		return;
-
-	for (cur = *list; cur; ) {
-		aim_tlvlist_t *tmp;
-
-		freetlv(&cur->tlv);
-
-		tmp = cur->next;
-		g_free(cur);
-		cur = tmp;
+	while (list != NULL)
+	{
+		freetlv(list->data);
+		list = g_slist_delete_link(list, list);
 	}
-
-	list = NULL;
-
-	return;
 }
 
 /**
@@ -319,15 +266,15 @@
  * @param list Chain to be counted.
  * @return The number of TLVs stored in the passed chain.
  */
-int aim_tlvlist_count(aim_tlvlist_t **list)
+int aim_tlvlist_count(GSList *list)
 {
-	aim_tlvlist_t *cur;
+	GSList *cur;
 	int count;
 
-	if (!list || !*list)
+	if (list == NULL)
 		return 0;
 
-	for (cur = *list, count = 0; cur; cur = cur->next)
+	for (cur = list, count = 0; cur; cur = cur->next)
 		count++;
 
 	return count;
@@ -340,16 +287,16 @@
  * @return The number of bytes that would be needed to
  *         write the passed TLV chain to a data buffer.
  */
-int aim_tlvlist_size(aim_tlvlist_t **list)
+int aim_tlvlist_size(GSList *list)
 {
-	aim_tlvlist_t *cur;
+	GSList *cur;
 	int size;
 
-	if (!list || !*list)
+	if (list == NULL)
 		return 0;
 
-	for (cur = *list, size = 0; cur; cur = cur->next)
-		size += (4 + cur->tlv->length);
+	for (cur = list, size = 0; cur; cur = cur->next)
+		size += (4 + ((aim_tlv_t *)cur->data)->length);
 
 	return size;
 }
@@ -364,27 +311,20 @@
  * @param value String to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
+int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
 {
-	aim_tlvlist_t *newtlv, *cur;
+	aim_tlv_t *tlv;
 
 	if (list == NULL)
 		return 0;
 
-	newtlv = g_new0(aim_tlvlist_t, 1);
-	newtlv->tlv = createtlv(type, length, NULL);
-	if (newtlv->tlv->length > 0)
-		newtlv->tlv->value = g_memdup(value, length);
+	tlv = createtlv(type, length, NULL);
+	if (tlv->length > 0)
+		tlv->value = g_memdup(value, length);
 
-	if (!*list)
-		*list = newtlv;
-	else {
-		for(cur = *list; cur->next; cur = cur->next)
-			;
-		cur->next = newtlv;
-	}
+	*list = g_slist_append(*list, tlv);
 
-	return newtlv->tlv->length;
+	return tlv->length;
 }
 
 /**
@@ -395,7 +335,7 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
+int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value)
 {
 	guint8 v8[1];
 
@@ -412,7 +352,7 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value)
+int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value)
 {
 	guint8 v16[2];
 
@@ -429,7 +369,7 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
+int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value)
 {
 	guint8 v32[4];
 
@@ -446,7 +386,7 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value)
+int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
 {
 	return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
 }
@@ -467,12 +407,12 @@
  * @param caps Bitfield of capability flags to send
  * @return The size of the value added.
  */
-int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps)
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps)
 {
-	guint8 buf[16*16]; /* XXX icky fixed length buffer */
+	guint8 buf[256]; /* TODO: Don't use a fixed length buffer */
 	ByteStream bs;
 
-	if (!caps)
+	if (caps == 0)
 		return 0; /* nothing there anyway */
 
 	byte_stream_init(&bs, buf, sizeof(buf));
@@ -489,9 +429,9 @@
  * @param type TLV type to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo)
+int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo)
 {
-	guint8 buf[1024]; /* bleh */
+	guint8 buf[1024]; /* TODO: Don't use a fixed length buffer */
 	ByteStream bs;
 
 	byte_stream_init(&bs, buf, sizeof(buf));
@@ -510,7 +450,7 @@
  * @param instance The instance.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
+int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
 {
 	int len;
 	ByteStream bs;
@@ -536,7 +476,7 @@
  * @param type TLV type to add.
  * @return The size of the value added.
  */
-int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type)
+int aim_tlvlist_add_noval(GSList **list, const guint16 type)
 {
 	return aim_tlvlist_add_raw(list, type, 0, NULL);
 }
@@ -546,7 +486,7 @@
  * it is written using this.  Or rather, it can be, but updates won't be
  * made to this.
  *
- * XXX should probably support sublists for real.
+ * TODO: Should probably support sublists for real.
  *
  * This is so neat.
  *
@@ -557,19 +497,19 @@
  *         0 is returned if there was an error or if the destination
  *         TLV chain has length 0.
  */
-int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
+int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist)
 {
 	int buflen;
 	ByteStream bs;
 
-	buflen = aim_tlvlist_size(tl);
+	buflen = aim_tlvlist_size(*tlvlist);
 
 	if (buflen <= 0)
 		return 0;
 
 	byte_stream_new(&bs, buflen);
 
-	aim_tlvlist_write(&bs, tl);
+	aim_tlvlist_write(&bs, tlvlist);
 
 	aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
 
@@ -589,25 +529,33 @@
  * @param value String to add.
  * @return The length of the TLV.
  */
-int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
+int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
 {
-	aim_tlvlist_t *cur;
+	GSList *cur;
+	aim_tlv_t *tlv;
 
 	if (list == NULL)
 		return 0;
 
-	for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next);
+	for (cur = *list; cur != NULL; cur = cur->next)
+	{
+		tlv = cur->data;
+		if (tlv->type == type)
+			break;
+	}
+
 	if (cur == NULL)
+		/* TLV does not exist, so add a new one */
 		return aim_tlvlist_add_raw(list, type, length, value);
 
-	g_free(cur->tlv->value);
-	cur->tlv->length = length;
-	if (cur->tlv->length > 0) {
-		cur->tlv->value = g_memdup(value, length);
+	g_free(tlv->value);
+	tlv->length = length;
+	if (tlv->length > 0) {
+		tlv->value = g_memdup(value, length);
 	} else
-		cur->tlv->value = NULL;
+		tlv->value = NULL;
 
-	return cur->tlv->length;
+	return tlv->length;
 }
 
 /**
@@ -620,7 +568,7 @@
  * @param str String to add.
  * @return The length of the TLV.
  */
-int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str)
+int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str)
 {
 	return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
 }
@@ -634,7 +582,7 @@
  * @param type TLV type.
  * @return The length of the TLV.
  */
-int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type)
+int aim_tlvlist_replace_noval(GSList **list, const guint16 type)
 {
 	return aim_tlvlist_replace_raw(list, type, 0, NULL);
 }
@@ -649,7 +597,7 @@
  * @param value 8 bit value to add.
  * @return The length of the TLV.
  */
-int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
+int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value)
 {
 	guint8 v8[1];
 
@@ -668,7 +616,7 @@
  * @param value 32 bit value to add.
  * @return The length of the TLV.
  */
-int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
+int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value)
 {
 	guint8 v32[4];
 
@@ -678,36 +626,36 @@
 }
 
 /**
- * Remove a TLV of a given type.  If you attempt to remove a TLV that
- * does not exist, nothing happens.
+ * Remove all TLVs of a given type.  If you attempt to remove a TLV
+ * that does not exist, nothing happens.
  *
  * @param list Desination chain (%NULL pointer if empty).
  * @param type TLV type.
  */
-void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type)
+void aim_tlvlist_remove(GSList **list, const guint16 type)
 {
-	aim_tlvlist_t *del;
+	GSList *cur, *next;
+	aim_tlv_t *tlv;
 
-	if (!list || !(*list))
+	if (list == NULL || *list == NULL)
 		return;
 
-	/* Remove the item from the list */
-	if ((*list)->tlv->type == type) {
-		del = *list;
-		*list = (*list)->next;
-	} else {
-		aim_tlvlist_t *cur;
-		for (cur=*list; (cur->next && (cur->next->tlv->type!=type)); cur=cur->next);
-		if (!cur->next)
-			return;
-		del = cur->next;
-		cur->next = del->next;
+	cur = *list;
+	while (cur != NULL)
+	{
+		tlv = cur->data;
+		next = cur->next;
+
+		if (tlv->type == type)
+		{
+			/* Delete this TLV */
+			*list = g_slist_delete_link(*list, cur);
+			g_free(tlv->value);
+			g_free(tlv);
+		}
+
+		cur = next;
 	}
-
-	/* Free the removed item */
-	g_free(del->tlv->value);
-	g_free(del->tlv);
-	g_free(del);
 }
 
 /**
@@ -718,32 +666,34 @@
  * aim_tlvlist_free() must still be called to free up the memory used
  * by the chain structures.
  *
- * XXX clean this up, make better use of bstreams
+ * TODO: Clean this up, make better use of bstreams
  *
  * @param bs Input bstream
  * @param list Source TLV chain
  * @return Return 0 if the destination bstream is too small.
  */
-int aim_tlvlist_write(ByteStream *bs, aim_tlvlist_t **list)
+int aim_tlvlist_write(ByteStream *bs, GSList **list)
 {
 	int goodbuflen;
-	aim_tlvlist_t *cur;
+	GSList *cur;
+	aim_tlv_t *tlv;
 
 	/* do an initial run to test total length */
-	goodbuflen = aim_tlvlist_size(list);
+	goodbuflen = aim_tlvlist_size(*list);
 
 	if (goodbuflen > byte_stream_empty(bs))
 		return 0; /* not enough buffer */
 
 	/* do the real write-out */
 	for (cur = *list; cur; cur = cur->next) {
-		byte_stream_put16(bs, cur->tlv->type);
-		byte_stream_put16(bs, cur->tlv->length);
-		if (cur->tlv->length)
-			byte_stream_putraw(bs, cur->tlv->value, cur->tlv->length);
+		tlv = cur->data;
+		byte_stream_put16(bs, tlv->type);
+		byte_stream_put16(bs, tlv->length);
+		if (tlv->length > 0)
+			byte_stream_putraw(bs, tlv->value, tlv->length);
 	}
 
-	return 1; /* XXX this is a nonsensical return */
+	return 1; /* TODO: This is a nonsensical return */
 }
 
 
@@ -760,17 +710,19 @@
  * @param nth Index of TLV of type to get.
  * @return The TLV you were looking for, or NULL if one could not be found.
  */
-aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth)
+aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth)
 {
-	aim_tlvlist_t *cur;
+	GSList *cur;
+	aim_tlv_t *tlv;
 	int i;
 
-	for (cur = list, i = 0; cur; cur = cur->next) {
-		if (cur && cur->tlv) {
-			if (cur->tlv->type == type)
+	for (cur = list, i = 0; cur != NULL; cur = cur->next) {
+		tlv = cur->data;
+		if (tlv != NULL) { /* TODO: This NULL check shouldn't be needed */
+			if (tlv->type == type)
 				i++;
 			if (i >= nth)
-				return cur->tlv;
+				return tlv;
 		}
 	}
 
@@ -786,21 +738,15 @@
  * @return The length of the data in this TLV, or -1 if the TLV could not be
  *         found.  Unless -1 is returned, this value will be 2 bytes.
  */
-int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth)
+int aim_tlv_getlength(GSList *list, const guint16 type, const int nth)
 {
-	aim_tlvlist_t *cur;
-	int i;
+	aim_tlv_t *tlv;
 
-	for (cur = list, i = 0; cur; cur = cur->next) {
-		if (cur && cur->tlv) {
-			if (cur->tlv->type == type)
-				i++;
-			if (i >= nth)
-				return cur->tlv->length;
-		}
-	}
+	tlv = aim_tlv_gettlv(list, type, nth);
+	if (tlv == NULL)
+		return -1;
 
-	return -1;
+	return tlv->length;
 }
 
 char *
@@ -825,11 +771,12 @@
  *         not be found.  This is a dynamic buffer and must be freed by the
  *         caller.
  */
-char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth)
+char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
-	if (!(tlv = aim_tlv_gettlv(list, type, nth)))
+	tlv = aim_tlv_gettlv(list, type, nth);
+	if (tlv == NULL)
 		return NULL;
 
 	return aim_tlv_getvalue_as_string(tlv);
@@ -845,12 +792,14 @@
  * @return The value the TLV you were looking for, or 0 if one could
  *         not be found.
  */
-guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth)
+guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
-	if (!(tlv = aim_tlv_gettlv(list, type, nth)))
+	tlv = aim_tlv_gettlv(list, type, nth);
+	if (tlv == NULL)
 		return 0; /* erm */
+
 	return aimutil_get8(tlv->value);
 }
 
@@ -864,12 +813,14 @@
  * @return The value the TLV you were looking for, or 0 if one could
  *         not be found.
  */
-guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth)
+guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
-	if (!(tlv = aim_tlv_gettlv(list, type, nth)))
+	tlv = aim_tlv_gettlv(list, type, nth);
+	if (tlv == NULL)
 		return 0; /* erm */
+
 	return aimutil_get16(tlv->value);
 }
 
@@ -883,11 +834,13 @@
  * @return The value the TLV you were looking for, or 0 if one could
  *         not be found.
  */
-guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth)
+guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
-	if (!(tlv = aim_tlv_gettlv(list, type, nth)))
+	tlv = aim_tlv_gettlv(list, type, nth);
+	if (tlv == NULL)
 		return 0; /* erm */
+
 	return aimutil_get32(tlv->value);
 }
--- a/libpurple/protocols/silc/silc.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Fri Jun 01 23:53:05 2007 +0000
@@ -391,7 +391,7 @@
 
 	/* Send QUIT */
 	silc_client_command_call(sg->client, sg->conn, NULL,
-				 "QUIT", "Download Purple: " PURPLE_WEBSITE, NULL);
+				 "QUIT", "Download this: " PURPLE_WEBSITE, NULL);
 
 	if (sg->conn)
 		silc_client_close_connection(sg->client, sg->conn);
@@ -1552,7 +1552,7 @@
 		return PURPLE_CMD_RET_FAILED;
 
 	silc_client_command_call(sg->client, sg->conn, NULL,
-				 "QUIT", (args && args[0]) ? args[0] : "Download Purple: " PURPLE_WEBSITE, NULL);
+				 "QUIT", (args && args[0]) ? args[0] : "Download this: " PURPLE_WEBSITE, NULL);
 
 	return PURPLE_CMD_RET_OK;
 }
--- a/libpurple/protocols/zephyr/zephyr.h	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/protocols/zephyr/zephyr.h	Fri Jun 01 23:53:05 2007 +0000
@@ -109,7 +109,7 @@
     ZChecksum_t		z_checksum;
     int			z_num_other_fields;
     char		*z_other_fields[Z_MAXOTHERFIELDS];
-    void		*z_message;
+    caddr_t		z_message;
     int			z_message_len;
 } ZNotice_t;
 
--- a/libpurple/proxy.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/proxy.c	Fri Jun 01 23:53:05 2007 +0000
@@ -1059,6 +1059,22 @@
 	}
 }
 
+static gboolean
+s5_ensure_buffer_length(PurpleProxyConnectData *connect_data, int len)
+{
+	if(connect_data->read_len < len) {
+		if(connect_data->read_buf_len < len) {
+			/* it's not just that we haven't read enough, it's that we haven't tried to read enough yet */
+			purple_debug_info("s5", "reallocing from %d to %d\n", connect_data->read_buf_len, len);
+			connect_data->read_buf_len = len;
+			connect_data->read_buffer = g_realloc(connect_data->read_buffer, connect_data->read_buf_len);
+		}
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static void
 s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
 {
@@ -1067,7 +1083,7 @@
 	int len;
 
 	if (connect_data->read_buffer == NULL) {
-		connect_data->read_buf_len = 512;
+		connect_data->read_buf_len = 4;
 		connect_data->read_buffer = g_malloc(connect_data->read_buf_len);
 		connect_data->read_len = 0;
 	}
@@ -1075,8 +1091,6 @@
 	dest = connect_data->read_buffer + connect_data->read_len;
 	buf = connect_data->read_buffer;
 
-	purple_debug_info("socks5 proxy", "Able to read again.\n");
-
 	len = read(connect_data->fd, dest, (connect_data->read_buf_len - connect_data->read_len));
 
 	if (len == 0)
@@ -1119,33 +1133,31 @@
 	/* Skip past BND.ADDR */
 	switch(buf[3]) {
 		case 0x01: /* the address is a version-4 IP address, with a length of 4 octets */
-			if(connect_data->read_len < 4 + 4)
+			if(!s5_ensure_buffer_length(connect_data, 4 + 4))
 				return;
 			buf += 4 + 4;
 			break;
 		case 0x03: /* the address field contains a fully-qualified domain name.  The first
 					  octet of the address field contains the number of octets of name that
 					  follow, there is no terminating NUL octet. */
-			if(connect_data->read_len < 4 + 1)
+			if(!s5_ensure_buffer_length(connect_data, 4 + 1))
 				return;
-			buf += 4 + 1;
-			if(connect_data->read_len < 4 + 1 + buf[0])
+			buf += 4;
+			if(!s5_ensure_buffer_length(connect_data, 4 + 1 + buf[0]))
 				return;
-			buf += buf[0];
+			buf += buf[0] + 1;
 			break;
 		case 0x04: /* the address is a version-6 IP address, with a length of 16 octets */
-			if(connect_data->read_len < 4 + 16)
+			if(!s5_ensure_buffer_length(connect_data, 4 + 16))
 				return;
 			buf += 4 + 16;
 			break;
 	}
 
-	if(connect_data->read_len < (buf - connect_data->read_buffer) + 2)
+	/* Skip past BND.PORT */
+	if(!s5_ensure_buffer_length(connect_data, (buf - connect_data->read_buffer) + 2))
 		return;
 
-	/* Skip past BND.PORT */
-	buf += 2;
-
 	purple_proxy_connect_data_connected(connect_data);
 }
 
--- a/libpurple/purple-client.c	Thu May 31 00:40:46 2007 +0000
+++ b/libpurple/purple-client.c	Fri Jun 01 23:53:05 2007 +0000
@@ -5,7 +5,7 @@
 #include <stdlib.h>
 
 #include "dbus-purple.h"
-#include "purple-client-bindings.h"
+#include "purple-client.h"
 
 static DBusGConnection *bus;
 static DBusGProxy *purple_proxy;
--- a/pidgin/Makefile.mingw	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/Makefile.mingw	Fri Jun 01 23:53:05 2007 +0000
@@ -106,26 +106,22 @@
 ##
 ## LIBRARIES
 ##
-LIBPURPLES =	\
-		-lpurple \
-		-lglib-2.0 \
-		-lgthread-2.0 \
-		-lgobject-2.0 \
-		-lgmodule-2.0 \
+
+PIDGIN_LIBS =	\
 		-lintl \
-		-lws2_32 \
-		-lwinmm \
-		-lz
-
-GTKPURPLES =	\
-		$(LIBPURPLES) \
+		-lglib-2.0 \
+		-lgobject-2.0 \
+		-lgthread-2.0 \
+		-lpurple \
+		-lz \
 		-lidletrack \
 		-lgtk-win32-2.0 \
 		-latk-1.0 \
 		-lpango-1.0 \
 		-lgdk-win32-2.0 \
 		-lgdk_pixbuf-2.0 \
-		-lgdi32
+		-lgdi32 \
+		-lwinmm
 
 include $(PIDGIN_COMMON_RULES)
 
@@ -159,7 +155,7 @@
 $(EXE_OBJECTS) $(PIDGIN_OBJECTS): $(PIDGIN_CONFIG_H)
 
 $(PIDGIN_TARGET).dll $(PIDGIN_TARGET).dll.a: $(PURPLE_DLL).a $(PIDGIN_IDLETRACK_DLL).a $(PIDGIN_OBJECTS)
-	$(CC) -shared $(PIDGIN_OBJECTS) $(LIB_PATHS) $(GTKPURPLES) $(DLL_LD_FLAGS) -Wl,--output-def,$(PIDGIN_TARGET).def,--out-implib,$(PIDGIN_TARGET).dll.a -o $(PIDGIN_TARGET).dll
+	$(CC) -shared $(PIDGIN_OBJECTS) $(LIB_PATHS) $(PIDGIN_LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(PIDGIN_TARGET).def,--out-implib,$(PIDGIN_TARGET).dll.a -o $(PIDGIN_TARGET).dll
 
 $(EXE_TARGET).exe: $(PIDGIN_CONFIG_H) $(PIDGIN_DLL).a $(PIDGIN_IDLETRACK_DLL).a $(EXE_OBJECTS)
 	$(CC) $(LDFLAGS) $(EXE_OBJECTS) -o $(EXE_TARGET).exe
--- a/pidgin/gtkaccount.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkaccount.c	Fri Jun 01 23:53:05 2007 +0000
@@ -498,8 +498,8 @@
 		menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
 		item = gtk_menu_get_active(GTK_MENU(menu));
 		protocol = g_object_get_data(G_OBJECT(item), "protocol");
-		if (value == NULL && !strcmp(protocol, "prpl-fake") && 
-			!strcmp(purple_account_user_split_get_text(split), _("Domain")))
+		if (value == NULL && protocol != NULL && !strcmp(protocol, "prpl-fake") &&
+				!strcmp(purple_account_user_split_get_text(split), _("Domain")))
 			value = "gmail.com";
 
 		if (value != NULL)
@@ -827,9 +827,9 @@
 				menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(dialog->protocol_menu));
 				item = gtk_menu_get_active(GTK_MENU(menu));
 				protocol = g_object_get_data(G_OBJECT(item), "protocol");
-				if (str_value == NULL && !strcmp(protocol, "prpl-fake") &&
+				if (str_value == NULL && protocol != NULL && !strcmp(protocol, "prpl-fake") &&
 					!strcmp(_("Connect server"),  purple_account_option_get_text(option)))
-					str_value = "talk.google.com";	
+					str_value = "talk.google.com";
 		
 				if (str_value != NULL)
 					gtk_entry_set_text(GTK_ENTRY(entry), str_value);
@@ -2025,19 +2025,26 @@
 {
 	GdkPixbuf *pixbuf, *buddyicon = NULL;
 	PurpleStoredImage *img = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info = NULL;
 
 	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 	if ((pixbuf != NULL) && purple_account_is_disconnected(account))
 		gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
 
-	if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) {
-		if (global_buddyicon != NULL)
-			buddyicon = g_object_ref(G_OBJECT(global_buddyicon));
-		/* This is for when set_account() is called for a single account */
-		else
+	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+	if (prpl != NULL)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	if (prpl_info != NULL && prpl_info->icon_spec.format != NULL) {
+		if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) {
+			if (global_buddyicon != NULL)
+				buddyicon = g_object_ref(G_OBJECT(global_buddyicon));
+			/* This is for when set_account() is called for a single account */
+			else
+				img = purple_buddy_icons_find_account_icon(account);
+		} else {
 			img = purple_buddy_icons_find_account_icon(account);
-	} else {
-		img = purple_buddy_icons_find_account_icon(account);
+		}
 	}
 
 	if (img != NULL) {
--- a/pidgin/gtkblist.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkblist.c	Fri Jun 01 23:53:05 2007 +0000
@@ -2721,6 +2721,7 @@
 {
 	GtkTreePath *path;
 	int delay;
+	GdkRectangle rect;
 
 	/*
 	 * When dragging a buddy into a contact, this is the delay before
@@ -2736,7 +2737,17 @@
 	}
 
 	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), x, y, &path, NULL, NULL, NULL);
-	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->tip_rect);
+	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect);
+
+	/* Only autoexpand when in the middle of the cell to avoid annoying un-intended expands */
+	if (y < rect.y + (rect.height / 3) ||
+	    y > rect.y + (2 * (rect.height /3)))
+		return FALSE;
+	
+	rect.height = rect.height / 3;
+	rect.y += rect.height;
+
+	gtkblist->tip_rect = rect;
 
 	if (path)
 		gtk_tree_path_free(path);
@@ -3235,6 +3246,9 @@
 		else if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL)
 			ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE_I,
 					icon_size, "GtkTreeView");
+		else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
+			ret = gtk_widget_render_icon(GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_INVISIBLE,
+					icon_size, "GtkTreeView");
 		else
 			ret = gtk_widget_render_icon(GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE,
 					icon_size, "GtkTreeView");
--- a/pidgin/gtkconv.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkconv.c	Fri Jun 01 23:53:05 2007 +0000
@@ -235,6 +235,10 @@
 		return FALSE;
 	}
 
+	if (gdk_window_get_state(gtkconv->win->window->window) & GDK_WINDOW_STATE_MAXIMIZED) {
+		return FALSE;
+	}
+
 	/* I find that I resize the window when it has a bunch of conversations in it, mostly so that the
 	 * tab bar will fit, but then I don't want new windows taking up the entire screen.  I check to see
 	 * if there is only one conversation in the window.  This way we'll be setting new windows to the
@@ -6951,6 +6955,9 @@
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP);
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000);
 
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font", TRUE);
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", "");
+
 	/* Conversations -> Chat */
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/chat");
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", 410);
--- a/pidgin/gtkimhtmltoolbar.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Fri Jun 01 23:53:05 2007 +0000
@@ -852,6 +852,64 @@
 	update_buttons(toolbar);
 }
 
+
+/* This comes from gtkmenutoolbutton.c from gtk+
+ * Copyright (C) 2003 Ricardo Fernandez Pascual
+ * Copyright (C) 2004 Paolo Borelli
+ */
+menu_position_func (GtkMenu           *menu,
+                    int               *x,
+                    int               *y,
+                    gboolean          *push_in,
+                    GtkWidget         *widget)
+{
+  GtkRequisition req;
+  GtkRequisition menu_req;
+  GtkOrientation orientation;
+  GtkTextDirection direction;
+  GdkRectangle monitor;
+  gint monitor_num;
+  GdkScreen *screen;
+
+  gtk_widget_size_request (GTK_WIDGET (widget), &menu_req);
+
+  direction = gtk_widget_get_direction (widget);
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+  monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
+  if (monitor_num < 0)
+    monitor_num = 0;
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+  gdk_window_get_origin (widget->window, x, y);
+  *x += widget->allocation.x;
+    *y += widget->allocation.y;
+
+      if (direction == GTK_TEXT_DIR_LTR)
+	*x += MAX (widget->allocation.width - menu_req.width, 0);
+      else if (menu_req.width > widget->allocation.width)
+        *x -= menu_req.width - widget->allocation.width;
+
+      if ((*y + widget->allocation.height + menu_req.height) <= monitor.y + monitor.height)
+	*y += widget->allocation.height;
+      else if ((*y - menu_req.height) >= monitor.y)
+	*y -= menu_req.height;
+      else if (monitor.y + monitor.height - (*y + widget->allocation.height) > *y)
+	*y += widget->allocation.height;
+      else
+	*y -= menu_req.height;
+  *push_in = FALSE;
+}
+
+static void pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu) {
+	gtk_widget_show_all(menu);
+	gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time());
+}
+
+static void pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button) {
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+}
+
 enum {
 	LAST_SIGNAL
 };
@@ -899,12 +957,91 @@
 	gobject_class->finalize = gtk_imhtmltoolbar_finalize;
 }
 
+static void gtk_imhtmltoolbar_create_old_buttons(GtkIMHtmlToolbar *toolbar)
+{
+	GtkWidget *button;
+	/* Bold */
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_bold), toolbar);
+	toolbar->bold = button;
+
+
+	/* Italic */
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_italic), toolbar);
+	toolbar->italic = button;
+
+	/* Underline */
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_underline), toolbar);
+	toolbar->underline = button;
+
+	/* Increase font size */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_LARGER);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_big), toolbar);
+	toolbar->larger_size = button;
+
+	/* Decrease font size */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_small), toolbar);
+	toolbar->smaller_size = button;
+
+	/* Font Face */
+
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FONT_FACE);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_font), toolbar);
+	toolbar->font = button;
+
+	/* Foreground Color */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FGCOLOR);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_fg_color), toolbar);
+	toolbar->fgcolor = button;
+
+	/* Background Color */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_BGCOLOR);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_bg_color), toolbar);
+	toolbar->bgcolor = button;
+
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_LINK);
+	g_signal_connect(G_OBJECT(button), "clicked",
+				 G_CALLBACK(insert_link_cb), toolbar);
+	toolbar->link = button;
+
+	/* Insert IM Image */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(insert_image_cb), toolbar);
+	toolbar->image = button;
+
+	/* Insert Smiley */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(insert_smiley_cb), toolbar);
+	toolbar->smiley = button;
+
+
+}
+
 static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar)
 {
 	GtkWidget *hbox = GTK_WIDGET(toolbar);
+	GtkWidget *bbox;
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkWidget *insert_button;
+	GtkWidget *font_button;
+	GtkWidget *font_menu;
+	GtkWidget *insert_menu;
 	GtkWidget *button;
 	GtkWidget *sep;
-	GtkSizeGroup *sg;
 
 	toolbar->imhtml = NULL;
 	toolbar->font_dialog = NULL;
@@ -917,164 +1054,116 @@
 	toolbar->tooltips = gtk_tooltips_new();
 
 	gtk_box_set_spacing(GTK_BOX(toolbar), 3);
-	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
+
+	gtk_imhtmltoolbar_create_old_buttons(toolbar);
 
 	/* Bold */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Bold"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_bold), toolbar);
+	font_button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(font_button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(font_button), bbox);
+	image = gtk_image_new_from_stock(GTK_STOCK_BOLD, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("_Font"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0);
+	gtk_widget_show_all(font_button);
 
-	toolbar->bold = button;
+	font_menu = gtk_menu_new();
+	
+	button = gtk_check_menu_item_new_with_mnemonic(_("_Bold"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->bold);
+	gtk_menu_shell_append(font_menu, button);
 
-	/* Italic */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Italic"), NULL);
+	button = gtk_check_menu_item_new_with_mnemonic(_("_Italic"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->italic);
+	gtk_menu_shell_append(font_menu, button);
+	
+	button = gtk_check_menu_item_new_with_mnemonic(_("_Underline"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->underline);
+	gtk_menu_shell_append(font_menu, button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_italic), toolbar);
+	button = gtk_menu_item_new_with_mnemonic(_("_Larger"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->larger_size);
+	gtk_menu_shell_append(font_menu, button);
 
-	toolbar->italic = button;
+	button = gtk_menu_item_new_with_mnemonic(_("_Normal"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->normal_size);
+	gtk_menu_shell_append(font_menu, button);
+
+	button = gtk_menu_item_new_with_mnemonic(_("_Smaller"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smaller_size);
+	gtk_menu_shell_append(font_menu, button);
 
-	/* Underline */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Underline"), NULL);
+	button = gtk_menu_item_new_with_mnemonic(_("_Font face"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->font);
+	gtk_menu_shell_append(font_menu, button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_underline), toolbar);
+	button = gtk_menu_item_new_with_mnemonic(_("_Foreground color"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->fgcolor);
+	gtk_menu_shell_append(font_menu, button);
 
-	toolbar->underline = button;
+	button = gtk_menu_item_new_with_mnemonic(_("_Background color"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->bgcolor);
+	gtk_menu_shell_append(font_menu, button);
+	
+	g_signal_connect(G_OBJECT(font_button), "clicked", G_CALLBACK(pidgin_menu_clicked), font_menu);
+	g_signal_connect(G_OBJECT(font_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), font_button);	
 
 	/* Sep */
 	sep = gtk_vseparator_new();
 	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
-
-	/* Increase font size */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_LARGER);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			     _("Larger font size"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_big), toolbar);
-
-	toolbar->larger_size = button;
-
-	/* Decrease font size */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			     _("Smaller font size"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_small), toolbar);
-
-	toolbar->smaller_size = button;
-
-	/* Sep */
-	sep = gtk_vseparator_new();
-	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
-
-	/* Font Face */
-
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FONT_FACE);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			_("Font face"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_font), toolbar);
-
-	toolbar->font = button;
-
-	/* Foreground Color */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FGCOLOR);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			     _("Foreground font color"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_fg_color), toolbar);
-
-	toolbar->fgcolor = button;
-
-	/* Background Color */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_BGCOLOR);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			     _("Background color"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_bg_color), toolbar);
-
-	toolbar->bgcolor = button;
-
-	/* Sep */
-	sep = gtk_vseparator_new();
-	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
+	gtk_widget_show_all(sep);
 
 	/* Reset Formatting */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_CLEAR);
-	gtk_size_group_add_widget(sg, button);
+	button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(button), bbox);
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_CLEAR, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("_Reset font"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button,
-			     _("Reset formatting"), NULL);
-
+	gtk_widget_show_all(button);
 	g_signal_connect(G_OBJECT(button), "clicked",
 			 G_CALLBACK(clear_formatting_cb), toolbar);
-
 	toolbar->clear = button;
 
 	/* Sep */
 	sep = gtk_vseparator_new();
 	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
+	gtk_widget_show_all(sep);
 
 	/* Insert Link */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_LINK);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert link"), NULL);
-	g_signal_connect(G_OBJECT(button), "clicked",
-				 G_CALLBACK(insert_link_cb), toolbar);
-
-	toolbar->link = button;
-
-	/* Insert IM Image */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert image"), NULL);
+	insert_button = gtk_toggle_button_new();
+	gtk_button_set_relief(GTK_BUTTON(insert_button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(insert_button), bbox);
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("_Insert"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), insert_button, FALSE, FALSE, 0);
+	gtk_widget_show_all(insert_button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(insert_image_cb), toolbar);
-
-	toolbar->image = button;
+	insert_menu = gtk_menu_new();
+	
+	button = gtk_menu_item_new_with_mnemonic(_("_Smiley"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
+	gtk_menu_shell_append(insert_menu, button);
+	
+	button = gtk_menu_item_new_with_mnemonic(_("_Image"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image);
+	gtk_menu_shell_append(insert_menu, button);
 
-	/* Insert Smiley */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY);
-	gtk_size_group_add_widget(sg, button);
-	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert smiley"), NULL);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(insert_smiley_cb), toolbar);
-
-	toolbar->smiley = button;
-
+	button = gtk_menu_item_new_with_mnemonic(_("_Link"));
+	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link);
+	gtk_menu_shell_append(insert_menu, button);
+	
+	g_signal_connect(G_OBJECT(insert_button), "clicked", G_CALLBACK(pidgin_menu_clicked), insert_menu);
+	g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);	
 	toolbar->sml = NULL;
-	gtk_widget_show_all(hbox);
 }
 
 GtkWidget *gtk_imhtmltoolbar_new()
--- a/pidgin/gtkmain.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkmain.c	Fri Jun 01 23:53:05 2007 +0000
@@ -473,6 +473,9 @@
 #else
 	debug_enabled = FALSE;
 #endif
+
+	/* This is the first Glib function call. Make sure to initialize GThread bfeore then */
+	g_thread_init(NULL);
 	
 #ifdef ENABLE_NLS
 	bindtextdomain(PACKAGE, LOCALEDIR);
--- a/pidgin/gtkprefs.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkprefs.c	Fri Jun 01 23:53:05 2007 +0000
@@ -382,7 +382,7 @@
 	new_theme = current_smiley_theme;
 	description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
 								"<span size='smaller' foreground='white'>%s</span>",
-								new_theme->name, new_theme->author, new_theme->desc);
+								_(new_theme->name), _(new_theme->author), _(new_theme->desc));
 	gtk_list_store_set(smiley_theme_store, &iter, 1, description, -1);
 	g_free(description);
 
@@ -391,7 +391,7 @@
 		if (gtk_tree_model_get_iter(model, &iter, oldpath)) {
 			description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
 								"<span size='smaller' foreground='dim grey'>%s</span>",
-								old_theme->name, old_theme->author, old_theme->desc);
+								_(old_theme->name), _(old_theme->author), _(old_theme->desc));
 			gtk_list_store_set(smiley_theme_store, &iter, 1,
 				description, -1);
 			g_free(description);
@@ -426,7 +426,7 @@
 		struct smiley_theme *theme = themes->data;
 		char *description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
 						    "<span size='smaller' foreground='dim grey'>%s</span>",
-						    theme->name, theme->author, theme->desc);
+						    _(theme->name), _(theme->author), _(theme->desc));
 		gtk_list_store_append (smiley_theme_store, &iter);
 
 		/*
@@ -881,6 +881,15 @@
 	return ret;
 }
 
+#if GTK_CHECK_VERSION(2,4,0)
+static void
+pidgin_custom_font_set(GtkFontButton *font_button, gpointer nul)
+{
+	purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/custom_font",
+				gtk_font_button_get_font_name(font_button));
+}
+#endif
+
 static GtkWidget *
 conv_page()
 {
@@ -889,9 +898,17 @@
 	GtkWidget *toolbar;
 	GtkWidget *iconpref1;
 	GtkWidget *iconpref2;
+	GtkWidget *fontpref;
 	GtkWidget *imhtml;
 	GtkWidget *frame;
 
+#if GTK_CHECK_VERSION(2,4,0)
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *font_button;
+	const char *font_name;
+#endif
+
 	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
 	gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
 
@@ -922,6 +939,26 @@
 	pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox);
 #endif
 
+#if GTK_CHECK_VERSION(2,4,0)
+	vbox = pidgin_make_frame(ret, _("Font"));
+	if (purple_running_gnome())
+		fontpref = pidgin_prefs_checkbox(_("Use document font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
+	else
+		fontpref = pidgin_prefs_checkbox(_("Use font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
+	hbox = gtk_hbox_new(FALSE, 3);
+	label = gtk_label_new_with_mnemonic(_("Conversation _font:"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font");
+	font_button = gtk_font_button_new_with_font(purple_prefs_get_string(font_name ? font_name : NULL));
+	gtk_font_button_set_show_style(GTK_FONT_BUTTON(font_button), TRUE);
+	gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font"))
+		gtk_widget_set_sensitive(hbox, FALSE);
+	g_signal_connect(G_OBJECT(fontpref), "clicked", G_CALLBACK(pidgin_toggle_sensitive), hbox);
+	g_signal_connect(G_OBJECT(font_button), "font-set", G_CALLBACK(pidgin_custom_font_set), NULL);
+#endif
+
 	vbox = pidgin_make_frame(ret, _("Default Formatting"));
 
 	frame = pidgin_create_imhtml(TRUE, &imhtml, &toolbar, NULL);
--- a/pidgin/gtkstatusbox.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Fri Jun 01 23:53:05 2007 +0000
@@ -2029,6 +2029,10 @@
 	int w, h;
 	GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM);
 	gtk_icon_size_lookup(icon_size, &w, &h);
+	if (height > width)
+		w = width * h  / height;
+	else if (width > height)
+		h = height * w / width;
 	gdk_pixbuf_loader_set_size(loader, w, h);
 #endif
 }
--- a/pidgin/gtkthemes.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkthemes.c	Fri Jun 01 23:53:05 2007 +0000
@@ -150,7 +150,7 @@
 		} else if (!g_ascii_strncasecmp(i, "Name=", strlen("Name="))) {
 			int len;
 			g_free(theme->name);
-			theme->name = g_strdup(i+ strlen("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')
--- a/pidgin/gtkutils.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/gtkutils.c	Fri Jun 01 23:53:05 2007 +0000
@@ -94,6 +94,7 @@
 void
 pidgin_setup_imhtml(GtkWidget *imhtml)
 {
+	PangoFontDescription *desc = NULL;
 	g_return_if_fail(imhtml != NULL);
 	g_return_if_fail(GTK_IS_IMHTML(imhtml));
 
@@ -104,10 +105,12 @@
 
 	gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
 
-	/* Use the GNOME "document" font, if applicable */
-	if (purple_running_gnome()) {
+	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font")) {
+		const char *font = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font");
+		desc = pango_font_description_from_string(font);
+	} else if (purple_running_gnome()) {
+		/* Use the GNOME "document" font, if applicable */
 		char *path, *font;
-		PangoFontDescription *desc = NULL;
 
 		if ((path = g_find_program_in_path("gconftool-2"))) {
 			g_free(path);
@@ -118,11 +121,11 @@
 		}
 		desc = pango_font_description_from_string(font);
 		g_free(font);
-
-		if (desc) {
-			gtk_widget_modify_font(imhtml, desc);
-			pango_font_description_free(desc);
-		}
+	}
+	
+	if (desc) {
+		gtk_widget_modify_font(imhtml, desc);
+		pango_font_description_free(desc);
 	}
 }
 
@@ -1454,8 +1457,7 @@
 			else
 				purple_request_choice(NULL, NULL,
 						    _("You have dragged an image"),
-						    (ft ? _("You can send this image as a file transfer or "
-							   "embed it into this message, or use it as the buddy icon for this user.") :
+						    (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,
--- a/pidgin/pidginstock.c	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/pidginstock.c	Fri Jun 01 23:53:05 2007 +0000
@@ -151,6 +151,7 @@
 	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
--- a/pidgin/pidginstock.h	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/pidginstock.h	Fri Jun 01 23:53:05 2007 +0000
@@ -116,6 +116,7 @@
 #define PIDGIN_STOCK_TOOLBAR_FONT_FACE	  "pidgin-font-face"
 #define PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER "pidgin-text-smaller"
 #define PIDGIN_STOCK_TOOLBAR_TEXT_LARGER  "pidgin-text-larger"
+#define PIDGIN_STOCK_TOOLBAR_INSERT       "pidgin-insert"
 #define PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE "pidgin-insert-image"
 #define PIDGIN_STOCK_TOOLBAR_INSERT_LINK  "pidgin-insert-link"
 #define PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW  "pidgin-message-new"
--- a/pidgin/pixmaps/emotes/default/22/Makefile.am	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/pixmaps/emotes/default/22/Makefile.am	Fri Jun 01 23:53:05 2007 +0000
@@ -173,8 +173,15 @@
 		yin-yang.png
 
 pidginsmileypixdir = $(datadir)/pixmaps/pidgin/emotes/default
+pidginsmileypix_in_files = default.theme.in
 pidginsmileypix_DATA = \
         $(SMILEYS) \
         theme
 
-EXTRA_DIST = $(pidginsmileypix_DATA)
+theme: default.theme.in
+	sed -e 's/^_Name=/Name=/' \
+	    -e 's/^_Description=/Description=/' \
+	    -e 's/^_Author=/Author=/' \
+	$< > $@
+
+EXTRA_DIST = $(pidginsmileypix_DATA) $(pidginsmileypix_in_files)
--- a/pidgin/pixmaps/emotes/default/22/Makefile.mingw	Thu May 31 00:40:46 2007 +0000
+++ b/pidgin/pixmaps/emotes/default/22/Makefile.mingw	Fri Jun 01 23:53:05 2007 +0000
@@ -10,11 +10,13 @@
 datadir = $(PIDGIN_INSTALL_DIR)
 include ./Makefile.am
 
-.PHONY: install
+.PHONY: install clean
 
-install:
+install: theme
 	if test '$(pidginsmileypix_DATA)'; then \
 	  mkdir -p $(pidginsmileypixdir); \
 	  cp $(pidginsmileypix_DATA) $(pidginsmileypixdir); \
 	fi;
 
+clean:
+	rm -f theme
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/emotes/default/22/default.theme.in	Fri Jun 01 23:53:05 2007 +0000
@@ -0,0 +1,357 @@
+_Name=Default
+_Description=Pidgin smileys
+Icon=smile.png
+Author=Hylke Bons
+
+# default smileys
+[default]
+smile.png           :)      :-)
+smile-big.png       :-D     :-d     :D      :d
+sad.png             :-(     :(
+wink.png            ;-)     ;)
+tongue.png          :P      :-P     :-p     :p
+shock.png           =-O     =-o
+kiss.png            :-*
+glasses-cool.png    8-)
+embarrassed.png     :-[
+crying.png          :'(
+thinking.png        :-/     :-\\
+angel.png           O:-)    o:-)
+shut-mouth.png      :-X
+moneymouth.png      :-$
+foot-in-mouth.png   :-!
+shout.png           >:o     >:O
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Following AIM 6.1
+[AIM]
+smile.png           :-)     :)
+wink.png            ;-)     ;)
+sad.png             :-(     :(
+tongue.png          :-P     :P
+shock.png           =-O
+kiss.png            :-*
+shout.png           >:o
+smile-big.png       :-D     :D
+moneymouth.png      :-$
+foot-in-mouth.png   :-!
+embarrassed.png     :-[
+angel.png           O:-)
+thinking.png        :-\\    :-/
+crying.png          :'(
+shut-mouth.png      :-X
+glasses-cool.png    8-)
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Following Windows Live Messenger 8.1
+[MSN]
+smile.png           :)      :-)
+smile-big.png       :D      :d :-D :-d
+wink.png            ;)      ;-)
+shock.png           :-O     :-o :O :o
+tongue.png          :P      :p :-P :-p
+glasses-cool.png    (H)     (h)
+angry.png           :@      :-@
+embarrassed.png     :$      :-$
+confused.png        :S      :s :-S :-s
+sad.png             :(      :-(
+crying.png          :'(
+neutral.png         :|      :-|
+devil.png           (6)
+angel.png           (A)     (a)
+love.png            (L)     (l)
+love-over.png       (U)     (u)
+msn.png             (M)     (m)
+cat.png             (@)
+dog.png             (&)
+moon.png            (S)
+star.png            (*)
+film.png            (~)
+musical-note.png    (8)
+mail.png            (E)     (e)
+rose.png            (F)     (f)
+rose-dead.png       (W)     (w)
+clock.png           (O)     (o)
+kiss.png            (K)     (k)
+present.png         (G)     (g)
+cake.png            (^)
+camera.png          (P)     (p)
+lamp.png            (I)     (i)
+coffee.png          (C)     (c)
+phone.png           (T)     (t)
+hug-left.png        ({)
+hug-right.png       (})
+beer.png            (B)     (b)
+drink.png           (D)     (d)
+boy.png             (Z)     (z)
+girl.png            (X)     (x)
+good.png            (Y)     (y)
+bad.png             (N)     (n)
+vampire.png         :[      :-[
+goat.png            (nah)
+sun.png             (#)
+rainbow.png         (R)     (r)
+quiet.png           :-#
+teeth.png           8o|
+glasses-nerdy.png   8-|
+sarcastic.png       ^o)
+secret.png          :-*
+sick.png            +o(
+snail.png           (sn)
+turtle.png          (tu)
+plate.png           (pl)
+bowl.png            (||)
+pizza.png           (pi)
+soccerball.png      (so)
+car.png             (au)
+airplane.png        (ap)
+umbrella.png        (um)
+island.png          (ip)
+computer.png        (co)
+mobile.png          (mp)
+brb.png             (brb)
+rain.png            (st)
+highfive.png        (h5)
+coins.png           (mo)
+sheep.png           (bah)
+dont-know.png       :^)
+thinking.png        *-)
+thunder.png         (li)
+party.png           <:o)
+eyeroll.png         8-)
+yawn.png            |-) 
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Hidden MSN emotes
+sigarette.png      	(ci)    (CI)
+handcuffs.png       (%)
+console.png			(xx)    (XX)
+fingers-crossed.png	(yn)    (YN)
+
+### Following QQ 2006
+[QQ]
+shock.png           /惊讶   /:O      /jy       /surprised
+curl-lip.png        /撇嘴   /:~      /pz       /curl_lip
+desire.png          /色     /:*      /se       /desire
+dazed.png           /发呆   /:|      /dazed
+party.png           /得意   /8-)     /dy       /revel
+crying.png          /流泪   /:<      /ll       /cry
+bashful.png         /害羞   /:$      /hx       /bashful
+shut-mouth.png      /闭嘴   /:X      /bz       /shut_mouth
+sleepy.png          /睡     /:Z      /shui     /sleep
+weep.png            /大哭   /:'(     /dk       /weep
+embarrassed.png     /尴尬   /:-|     /gg       /embarassed
+pissed-off.png      /发怒   /:@      /fn       /pissed_off
+act-up.png          /调皮   /:P      /tp       /act_up
+smile-big.png       /呲牙   /:D      /cy       /toothy_smile
+smile.png           /微笑   /:)      /wx       /small_smile
+sad.png             /难过   /:(      /ng       /sad
+glasses-cool.png    /酷     /:+      /kuk      /cool
+doctor.png          /非典   /:#      /feid     /SARS
+silly.png           /抓狂   /:Q      /zk       /crazy
+sick.png            /吐     /:T      /tu       /vomit
+snicker.png         /偷笑   /;p      /tx       /titter
+cute.png            /可爱   /;-D     /ka       /cute
+disdain.png         /白眼   /;d      /by       /disdain
+arrogant.png        /傲慢   /;o      /am       /arrogant
+starving.png        /饥饿   /:g      /jie      /starving
+yawn.png            /困     /|-)     /kun      /sleepy
+terror.png          /惊恐   /:!      /jk       /terror
+sweat.png           /流汗   /:L      /sweat
+smirk.png           /憨笑   /:>      /hanx     /smirk
+soldier.png         /大兵   /:;      /db       /soldier
+struggle.png        /奋斗   /;f      /fendou   /struggle
+curse.png           /咒骂   /:-S     /zhm      /curse
+question.png        /疑问   /?       /yiw      /question
+quiet.png           /嘘...  /;x      /xu       /shh
+hypnotized.png      /晕     /;@      /yun      /dizzy
+excruciating.png    /折磨   /:8      /zhem     /excrutiating
+freaked-out.png     /衰     /;!      /shuai    /freaked_out
+skeleton.png        /骷髅   /!!!     /kl       /skeleton
+hammer.png          /敲打   /xx      /qiao     /hammer
+bye.png             /再见   /bye     /zj       /bye
+go-away.png         /闪人   /go      /shan     /go
+tremble.png         /发抖   /shake   /fad      /shake
+in-love.png         /爱情   /love    /aiq      /love
+jump.png            /跳     /jump    /tiao     /jump
+search.png          /找     /find    /zhao     /search
+lashes.png          /美眉   /&       /mm       /beautiful_eyebrows
+pig.png             /猪头   /pig     /zt       /pig
+cat.png             /猫咪   /cat     /mm       /cat
+dog.png             /小狗   /dog     /xg       /dog
+hug-left.png        /拥抱   /hug     /yb       /hug
+coins.png           /钱     /$       /qianc    /money
+lamp.png            /灯泡   /!       /dp       /lightbulb
+bowl.png            /酒杯   /cup     /bei      /cup
+cake.png            /蛋糕   /cake    /dg       /cake
+thunder.png         /闪电   /li      /shd      /lightning
+bomb.png            /炸弹   /bome    /zhd      /bomb
+knife.png           /刀     /kn      /dao      /knife
+soccerball.png      /足球   /footb   /zq       /soccer
+musical-note.png    /音乐   /music   /yy       /music
+poop.png            /便便   /shit    /bb       /shit
+coffee.png          /咖啡   /coffee  /kf       /coffee
+eat.png             /饭     /eat     /fan      /eat
+pill.png            /药丸   /pill    /yw       /pill
+rose.png            /玫瑰   /rose    /mg       /rose
+wilt.png            /凋谢   /fade    /dx       /wilt
+kiss.png            /吻     /kiss    /wen      /kiss
+love.png            /爱心   /heart   /xin      /heart
+love-over.png       /心碎   /break   /xs       /broken_heart
+meeting.png         /会议   /meeting /hy       /meeting
+present.png         /礼物   /gift    /lw       /gift
+phone.png           /电话   /phone   /dh       /phone
+clock.png           /时间   /time    /sj       /time
+mail.png            /邮件   /email   /yj       /email
+tv.png              /电视   /TV      /ds       /TV
+sun.png             /太阳   /sun     /ty       /sun
+moon.png            /月亮   /moon    /yl       /moon
+good.png            /强     /strong  /qiang    /thumbs_up
+bad.png             /弱     /weak    /ruo      /thumbs_down
+handshake.png       /握手   /share   /ws       /handshake
+victory.png         /胜利   /v       /shl      /victory
+beauty.png          /美女   /<J>     /mn       /beauty
+qq.png              /Q仔    /<QQ>    /qz       /qq
+blowkiss.png        /飞吻   /<L>     /fw       /blow_kiss
+angry.png           /怄火   /<O>     /oh       /angry
+liquor.png          /白酒   /<B>     /bj       /baijiu
+can.png             /汽水   /<U>     /qsh      /soda
+watermelon.png      /西瓜   /<W>     /xigua    /watermelon
+rain.png            /下雨   /<!!>    /xy       /rain
+cloudy.png          /多云   /<~>     /duoy     /cloudy
+snowman.png         /雪人   /<Z>     /xr       /snowman
+star.png            /星星   /<*>     /xixing   /star
+girl.png            /女     /<00>    /nv       /woman
+boy.png             /男     /<11>    /nan      /man
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Following ICQ 5.1
+[ICQ]
+smile.png           :-)     :)
+sad.png             :-(     :(
+wink.png            ;-)     ;)
+tongue.png          :-P     :P
+laugh.png           *JOKINGLY*
+crying.png          :'(
+#*KISSED*
+kiss.png            :-*
+embarrassed.png     :-[
+angel.png           O:-)
+shut-mouth.png      :-X     :X
+thinking.png        :-\\    :-/
+shout.png           >:o     >:O
+smile-big.png       :-D     :D
+moneymouth.png      :-$
+shock.png           =-O
+glasses-cool.png    8-)
+#[:-}
+sleepy.png          *TIRED*
+sick.png            :-!
+#*STOP*
+#*KISSING* 
+devil.png           ]:->
+rose.png            @}->--
+bomb.png            @=
+good.png            *THUMBS\ UP*
+beer.png            *DRINK*
+in-love.png         *IN\ LOVE*
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Following Yahoo! Messenger 8.1
+[Yahoo]
+smile.png           :)      :-)
+question.png        :-/     :-\\
+shock.png           :-O     :O      :-o     :o 
+devil.png           >:)
+angel.png           O:-)    o:-)    0:-)
+sick.png            :-&
+yawn.png            (:|
+hypnotized.png      @-)
+#on_the_phone       :)]     MISSING: "Wait,I'm phoning!" (not phone.png)    => smily showing phone and hand ("stop")
+sad.png             :(      :-(
+in-love.png         :x      :-x     :X      :-X
+angry.png           X-(     x-(     X(      x(
+crying.png          :((
+glasses-nerdy.png   :-B     :-b
+quiet.png           :-$
+drool.png           =P~     =p~
+lying.png           :^O     :^o
+call-me.png         :-c
+wink.png            ;)      ;-)
+embarrassed.png     :">
+mean.png            :->     :>
+laugh.png           :))     :-))
+bye.png             =;
+arrogant.png        [-(
+thinking.png        :-?
+waiting.png         :-w     :-W
+#at_wits_end        ~x(     ~X(     MISSING: "Why.Does.This.Not.Work.AAAAARGH!!"    => angry smily shouting and pulling hair off
+smile-big.png       :D      :-D     :d      :-d
+tongue.png          :-P     :P      :-p     :p
+glasses-cool.png    B-)     b-)
+neutral.png         :|      :-|
+sleepy.png          I-)     i-)     |-)
+clown.png           :o)     :O)
+doh.png             #-o     #-O
+weep.png            :-<
+go-away.png         :-h
+lashes.png          ;;)
+kiss.png            :-*     :*
+confused.png        :-S     :-s
+sarcastic.png       /:)
+eyeroll.png         8-|
+silly.png           8-}
+clap.png            =D>     =d>
+mad-tongue.png      >:P     >:p
+#timeout            :-t     :-T     MISSING: "Let's have a break."
+hug-left.png        >:D<    >:d<
+love-over.png       =((
+sweat.png           #:-S    #:-s
+rotfl.png           =))    
+#loser              L-)     l-)     MISSING/YAHOO 6: "Loser!"
+party.png           <:-P    <:-p
+nailbiting.png      :-SS	:-Ss	:-sS	:-ss
+cowboy.png          <):)
+desire.png          8->
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)
+
+### Hidden Yahoo emotes
+alien.png           =:)     >-)
+beat-up.png         b-(     B-(
+chicken.png         ~:>
+coffee.png          ~o)     ~O)
+cow.png             3:-O    3:-o
+dance.png           \\:D/   \\:d/
+rose.png            @};-
+dont-know.png       :-L     :-l
+skeleton.png        8-X     8-x
+lamp.png            *-:)
+monkey.png          :(|)
+coins.png           $-)
+peace.png           :)>-
+pig.png             :@)
+pray.png            [-o<    [-O<
+pumpkin.png         (~~)
+shame.png           [-X     [-x
+flag.png            **==
+clover.png          %%-
+musical-note.png    :-"
+giggle.png          ;))
+worship.png         ^:)^
+star.png            (*)
+#waving.png         >:/
+#talktohand.png     :-@
+#youkiddingme.png   :-j :-J
+
+### These only work in a certain IMvironment
+#malefighter1.png   o->     O->
+#malefighter2.png   o=>     O=>
+#femalefighter.png  o-+     O-+
+yin-yang.png        (%)
+
--- a/pidgin/pixmaps/emotes/default/22/theme	Thu May 31 00:40:46 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,357 +0,0 @@
-Name=Default
-Description=Pidgin smileys
-Icon=smile.png
-Author=Hylke Bons
-
-# default smileys
-[default]
-smile.png           :)      :-)
-smile-big.png       :-D     :-d     :D      :d
-sad.png             :-(     :(
-wink.png            ;-)     ;)
-tongue.png          :P      :-P     :-p     :p
-shock.png           =-O     =-o
-kiss.png            :-*
-glasses-cool.png    8-)
-embarrassed.png     :-[
-crying.png          :'(
-thinking.png        :-/     :-\\
-angel.png           O:-)    o:-)
-shut-mouth.png      :-X
-moneymouth.png      :-$
-foot-in-mouth.png   :-!
-shout.png           >:o     >:O
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)
-
-### Following AIM 6.1
-[AIM]
-smile.png           :-)     :)
-wink.png            ;-)     ;)
-sad.png             :-(     :(
-tongue.png          :-P     :P
-shock.png           =-O
-kiss.png            :-*
-shout.png           >:o
-smile-big.png       :-D     :D
-moneymouth.png      :-$
-foot-in-mouth.png   :-!
-embarrassed.png     :-[
-angel.png           O:-)
-thinking.png        :-\\    :-/
-crying.png          :'(
-shut-mouth.png      :-X
-glasses-cool.png    8-)
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)
-
-### Following Windows Live Messenger 8.1
-[MSN]
-smile.png           :)      :-)
-smile-big.png       :D      :d :-D :-d
-wink.png            ;)      ;-)
-shock.png           :-O     :-o :O :o
-tongue.png          :P      :p :-P :-p
-glasses-cool.png    (H)     (h)
-angry.png           :@      :-@
-embarrassed.png     :$      :-$
-confused.png        :S      :s :-S :-s
-sad.png             :(      :-(
-crying.png          :'(
-neutral.png         :|      :-|
-devil.png           (6)
-angel.png           (A)     (a)
-love.png            (L)     (l)
-love-over.png       (U)     (u)
-msn.png             (M)     (m)
-cat.png             (@)
-dog.png             (&)
-moon.png            (S)
-star.png            (*)
-film.png            (~)
-musical-note.png    (8)
-mail.png            (E)     (e)
-rose.png            (F)     (f)
-rose-dead.png       (W)     (w)
-clock.png           (O)     (o)
-kiss.png            (K)     (k)
-present.png         (G)     (g)
-cake.png            (^)
-camera.png          (P)     (p)
-lamp.png            (I)     (i)
-coffee.png          (C)     (c)
-phone.png           (T)     (t)
-hug-left.png        ({)
-hug-right.png       (})
-beer.png            (B)     (b)
-drink.png           (D)     (d)
-boy.png             (Z)     (z)
-girl.png            (X)     (x)
-good.png            (Y)     (y)
-bad.png             (N)     (n)
-vampire.png         :[      :-[
-goat.png            (nah)
-sun.png             (#)
-rainbow.png         (R)     (r)
-quiet.png           :-#
-teeth.png           8o|
-glasses-nerdy.png   8-|
-sarcastic.png       ^o)
-secret.png          :-*
-sick.png            +o(
-snail.png           (sn)
-turtle.png          (tu)
-plate.png           (pl)
-bowl.png            (||)
-pizza.png           (pi)
-soccerball.png      (so)
-car.png             (au)
-airplane.png        (ap)
-umbrella.png        (um)
-island.png          (ip)
-computer.png        (co)
-mobile.png          (mp)
-brb.png             (brb)
-rain.png            (st)
-highfive.png        (h5)
-coins.png           (mo)
-sheep.png           (bah)
-dont-know.png       :^)
-thinking.png        *-)
-thunder.png         (li)
-party.png           <:o)
-eyeroll.png         8-)
-yawn.png            |-) 
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)
-
-### Hidden MSN emotes
-sigarette.png      	(ci)    (CI)
-handcuffs.png       (%)
-console.png			(xx)    (XX)
-fingers-crossed.png	(yn)    (YN)
-
-### Following QQ 2006
-[QQ]
-shock.png           /惊讶   /:O      /jy       /surprised
-curl-lip.png        /撇嘴   /:~      /pz       /curl_lip
-desire.png          /色     /:*      /se       /desire
-dazed.png           /发呆   /:|      /dazed
-party.png           /得意   /8-)     /dy       /revel
-crying.png          /流泪   /:<      /ll       /cry
-bashful.png         /害羞   /:$      /hx       /bashful
-shut-mouth.png      /闭嘴   /:X      /bz       /shut_mouth
-sleepy.png          /睡     /:Z      /shui     /sleep
-weep.png            /大哭   /:'(     /dk       /weep
-embarrassed.png     /尴尬   /:-|     /gg       /embarassed
-pissed-off.png      /发怒   /:@      /fn       /pissed_off
-act-up.png          /调皮   /:P      /tp       /act_up
-smile-big.png       /呲牙   /:D      /cy       /toothy_smile
-smile.png           /微笑   /:)      /wx       /small_smile
-sad.png             /难过   /:(      /ng       /sad
-glasses-cool.png    /酷     /:+      /kuk      /cool
-doctor.png          /非典   /:#      /feid     /SARS
-silly.png           /抓狂   /:Q      /zk       /crazy
-sick.png            /吐     /:T      /tu       /vomit
-snicker.png         /偷笑   /;p      /tx       /titter