changeset 13234:f2431a7e33aa

[gaim-migrate @ 15600] Massive oscar shuffling. No change in functionality. I renamed each of the files that contains stuff for a SNAC family. I started splitting the file transfer/direct connect stuff into peer.c and peer.h. I stopped using fu8_t, fu16_t and fu32_t and switched to guint8, guint16 and guint32 instead. I changed the SNAC family and subtype defines so they are more meaningful. Added LGPL copyright header to each file. Added myself to the AUTHORS file. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sat, 11 Feb 2006 21:45:18 +0000
parents f09c6e8df82c
children 2f143ea42129
files src/protocols/oscar/AUTHORS src/protocols/oscar/Makefile.am src/protocols/oscar/Makefile.mingw src/protocols/oscar/admin.c src/protocols/oscar/adverts.c src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/aim_internal.h src/protocols/oscar/auth.c src/protocols/oscar/bart.c src/protocols/oscar/bos.c src/protocols/oscar/bstream.c src/protocols/oscar/buddylist.c src/protocols/oscar/chat.c src/protocols/oscar/chatnav.c src/protocols/oscar/conn.c src/protocols/oscar/email.c src/protocols/oscar/family_admin.c src/protocols/oscar/family_advert.c src/protocols/oscar/family_alert.c src/protocols/oscar/family_auth.c src/protocols/oscar/family_bart.c src/protocols/oscar/family_bos.c src/protocols/oscar/family_buddy.c src/protocols/oscar/family_chat.c src/protocols/oscar/family_chatnav.c src/protocols/oscar/family_feedbag.c src/protocols/oscar/family_icbm.c src/protocols/oscar/family_icq.c src/protocols/oscar/family_invite.c src/protocols/oscar/family_locate.c src/protocols/oscar/family_odir.c src/protocols/oscar/family_oservice.c src/protocols/oscar/family_popup.c src/protocols/oscar/family_stats.c src/protocols/oscar/family_translate.c src/protocols/oscar/family_userlookup.c src/protocols/oscar/ft.c src/protocols/oscar/icq.c src/protocols/oscar/im.c src/protocols/oscar/invite.c src/protocols/oscar/locate.c src/protocols/oscar/misc.c src/protocols/oscar/msgcookie.c src/protocols/oscar/odir.c src/protocols/oscar/oscar.c src/protocols/oscar/oscar.h src/protocols/oscar/oscar_internal.h src/protocols/oscar/peer.c src/protocols/oscar/peer.h src/protocols/oscar/popups.c src/protocols/oscar/rxhandlers.c src/protocols/oscar/rxqueue.c src/protocols/oscar/search.c src/protocols/oscar/service.c src/protocols/oscar/snac.c src/protocols/oscar/snactypes.h src/protocols/oscar/ssi.c src/protocols/oscar/stats.c src/protocols/oscar/tlv.c src/protocols/oscar/translate.c src/protocols/oscar/txqueue.c src/protocols/oscar/util.c
diffstat 63 files changed, 15406 insertions(+), 14729 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/oscar/AUTHORS	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/AUTHORS	Sat Feb 11 21:45:18 2006 +0000
@@ -3,29 +3,40 @@
 
 ---
 
+N: Mark Doliner
+T: 2001-2006
+H: markdoliner
+E: mark a.t kingant d.o.t net
+W: http://kingant.net/
+
 N: Adam Fritzler
+T: 1998-2001
 H: mid
-E: mid@auk.cx
+E: mid a.t auk d.o.t cx
 W: http://www.auk.cx/~mid,http://www.auk.cx/faim
 D: Wrote most of the wap of crap that you see before you.
 
 N: Josh Myer
-E: josh@joshisanerd.com
+T: 1998-2001
+E: josh a.t joshisanerd d.o.t com
 D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings.
 
 N: Daniel Reed
+T: 1998-2001
 H: n, linuxkitty
-E: n@ml.org
+E: n a.t ml d.o.t org
 W: http://users.n.ml.org/n/
 D: Fixed aim_snac.c
 
 N: Eric Warmenhoven
-E: warmenhoven@linux.com
+T: 1998-2001
+E: warmenhoven a.t linux d.o.t com
 D: Some OFT info, author of the faim interface for gaim
 
 N: Brock Wilcox
+T: 1998-2001
 H: awwaiid
-E: awwaiid@auk.cx
+E: awwaiid a.t auk d.o.t cx
 D: Figured out original password roasting
 
 
--- a/src/protocols/oscar/Makefile.am	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/Makefile.am	Sat Feb 11 21:45:18 2006 +0000
@@ -6,41 +6,42 @@
 pkgdir = $(libdir)/gaim
 
 OSCARSOURCES = \
-	admin.c        \
-	adverts.c      \
-	aim.h          \
-	aim_cbtypes.h  \
-	aim_internal.h \
-	auth.c         \
-	bart.c         \
-	bos.c          \
-	bstream.c      \
-	buddylist.c    \
-	chat.c         \
-	chatnav.c      \
-	conn.c         \
-	email.c        \
-	ft.c           \
-	icq.c          \
-	im.c           \
-	invite.c       \
-	locate.c       \
+	bstream.c           \
+	conn.c              \
+	family_admin.c      \
+	family_advert.c     \
+	family_alert.c      \
+	family_auth.c       \
+	family_bart.c       \
+	family_bos.c        \
+	family_buddy.c      \
+	family_chat.c       \
+	family_chatnav.c    \
+	family_icq.c        \
+	family_icbm.c       \
+	family_invite.c     \
+	family_locate.c     \
+	family_odir.c       \
+	family_oservice.c   \
+	family_popup.c      \
+	family_feedbag.c    \
+	family_stats.c      \
+	family_translate.c  \
+	family_userlookup.c \
 	misc.c         \
-	msgcookie.c    \
-	odir.c         \
-	popups.c       \
-	rxhandlers.c   \
-	rxqueue.c      \
-	search.c       \
-	service.c      \
-	snac.c         \
-	ssi.c          \
-	stats.c        \
-	tlv.c          \
-	translate.c    \
-	txqueue.c      \
-	util.c         \
-	oscar.c
+	msgcookie.c         \
+	oscar.c             \
+	oscar.h             \
+	oscar_internal.h    \
+	peer.c              \
+	peer.h              \
+	rxhandlers.c        \
+	rxqueue.c           \
+	snac.c              \
+	snactypes.h         \
+	tlv.c               \
+	txqueue.c           \
+	util.c
 
 AM_CFLAGS = $(st)
 
--- a/src/protocols/oscar/Makefile.mingw	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/Makefile.mingw	Sat Feb 11 21:45:18 2006 +0000
@@ -67,38 +67,39 @@
 ##  SOURCES, OBJECTS
 ##
 
-C_SRC =		admin.c		\
-			adverts.c	\
-			auth.c		\
-			bart.c		\
-			bos.c		\
-			bstream.c	\
-			buddylist.c	\
-			chat.c		\
-			chatnav.c	\
-			conn.c		\
-			email.c		\
-			ft.c		\
-			icq.c		\
-			im.c		\
-			invite.c	\
-			locate.c	\
-			misc.c		\
-			msgcookie.c	\
-			odir.c		\
-			popups.c	\
-			rxhandlers.c	\
-			rxqueue.c	\
-			search.c	\
-			service.c	\
-			snac.c		\
-			ssi.c		\
-			stats.c		\
-			tlv.c		\
-			translate.c	\
-			txqueue.c	\
-			util.c		\
-			oscar.c
+C_SRC = \
+	bstream.c			\
+	conn.c				\
+	family_admin.c		\
+	family_advert.c		\
+	family_alert.c		\
+	family_auth.c		\
+	family_bart.c		\
+	family_bos.c		\
+	family_buddylist.c	\
+	family_chat.c		\
+	family_chatnav.c	\
+	family_icq.c		\
+	family_icbm.c		\
+	family_invite.c		\
+	family_locate.c		\
+	family_odir.c		\
+	family_popup.c		\
+	family_oservice.c	\
+	family_feedbag.c	\
+	family_stats.c		\
+	family_translate.c	\
+	family_userlookup.c	\
+	misc.c		\
+	msgcookie.c			\
+	oscar.c				\
+	peer.c				\
+	rxhandlers.c		\
+	rxqueue.c			\
+	snac.c				\
+	tlv.c				\
+	txqueue.c			\
+	util.c
 
 
 OBJECTS = $(C_SRC:%.c=%.o)
--- a/src/protocols/oscar/admin.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * Family 0x0007 - Account Administration.
- *
- * Used for stuff like changing the formating of your screen name, changing your 
- * email address, requesting an account confirmation email, getting account info, 
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/*
- * Subtype 0x0002 - Request a bit of account info.
- *
- * Info should be one of the following:
- * 0x0001 - Screen name formatting
- * 0x0011 - Email address
- * 0x0013 - Unknown
- *
- */
-faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid);
-
-	aimbs_put16(&fr->data, info);
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtypes 0x0003 and 0x0005 - Parse account info.
- *
- * Called in reply to both an information request (subtype 0x0002) and 
- * an information change (subtype 0x0004).
- *
- */
-static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	char *url=NULL, *sn=NULL, *email=NULL;
-	fu16_t perms, tlvcount, err=0;
-
-	perms = aimbs_get16(bs);
-	tlvcount = aimbs_get16(bs);
-
-	while (tlvcount && aim_bstream_empty(bs)) {
-		fu16_t type, length;
-
-		type = aimbs_get16(bs);
-		length = aimbs_get16(bs);
-
-		switch (type) {
-			case 0x0001: {
-				sn = aimbs_getstr(bs, length);
-			} break;
-
-			case 0x0004: {
-				url = aimbs_getstr(bs, length);
-			} break;
-
-			case 0x0008: {
-				err = aimbs_get16(bs);
-			} break;
-
-			case 0x0011: {
-				if (length == 0) {
-					email = (char*)malloc(13*sizeof(char));
-					strcpy(email, "*suppressed*");
-				} else
-					email = aimbs_getstr(bs, length);
-			} break;
-		}
-
-		tlvcount--;
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email);
-
-	if (sn) free(sn);
-	if (url) free(url);
-	if (email) free(email);
-
-	return 1;
-}
-
-/*
- * Subtype 0x0004 - Set screenname formatting.
- *
- */
-faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
-
-	aim_tlvlist_add_str(&tl, 0x0001, newnick);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-
-	return 0;
-}
-
-/*
- * Subtype 0x0004 - Change password.
- *
- */
-faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
-{
-	aim_frame_t *fr;
-	aim_tlvlist_t *tl = NULL;
-	aim_snacid_t snacid;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
-
-	/* new password TLV t(0002) */
-	aim_tlvlist_add_str(&tl, 0x0002, newpw);
-
-	/* current password TLV t(0012) */
-	aim_tlvlist_add_str(&tl, 0x0012, curpw);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0004 - Change email address.
- *
- */
-faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
-
-	aim_tlvlist_add_str(&tl, 0x0011, newemail);
-	
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-	
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0006 - Request account confirmation.
- *
- * This will cause an email to be sent to the address associated with
- * the account.  By following the instructions in the mail, you can
- * get the TRIAL flag removed from your account.
- *
- */
-faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
-}
-
-/*
- * Subtype 0x0007 - Account confirmation request acknowledgement.
- *
- */
-static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t status;
-	aim_tlvlist_t *tl;
-
-	status = aimbs_get16(bs);
-	/* This is 0x0013 if unable to confirm at this time */
-
-	tl = aim_tlvlist_read(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, status);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005))
-		return infochange(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0007)
-		return accountconfirm(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0007;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "admin", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/adverts.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * Family 0x0005 - Advertisements.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	return 0;
-}
-
-faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0005;
-	mod->version = 0x0001;
-	mod->toolid = 0x0001;
-	mod->toolversion = 0x0001;
-	mod->flags = 0;
-	strncpy(mod->name, "adverts", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/aim.h	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1582 +0,0 @@
-/*
- * Main libfaim header.  Must be included in client for prototypes/macros.
- *
- * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
- *                                                -- Josh Myer
- *
- */
-
-#ifndef __AIM_H__
-#define __AIM_H__
-
-#include "aim_cbtypes.h"
-
-#include "debug.h"
-#include "internal.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <time.h>
-
-#ifndef _WIN32
-#include <sys/time.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#else
-#include "libc_interface.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* TODO: Remove these three typedefs and use guints everywhere. */
-typedef guint8  fu8_t;
-typedef guint16 fu16_t;
-typedef guint32 fu32_t;
-
-typedef guint32 aim_snacid_t;
-typedef guint16 flap_seqnum_t;
-
-#define WIN32_STATIC
-#if defined(_WIN32) && !defined(WIN32_STATIC)
-/*
- * For a win32 DLL, we define WIN32_INDLL if this file
- * is included while compiling the DLL. If it's not
- * defined (it's included in a client app), the symbols
- * will be imported instead of exported.
- */
-#ifdef WIN32_INDLL
-#define faim_export __declspec(dllexport)
-#else
-#define faim_export __declspec(dllimport)
-#endif /* WIN32_INDLL */
-#define faim_internal
-#else
-/*
- * Nothing normally needed for unix...
- */
-#define faim_export
-#define faim_internal
-#endif
-
-#ifndef FALSE
-#define FALSE (0)
-#endif
-
-#ifndef TRUE
-#define TRUE (!FALSE)
-#endif
-
-#define FAIM_SNAC_HASH_SIZE 16
-
-/*
- * Current Maximum Length for Screen Names (not including NULL)
- *
- * Currently only names up to 16 characters can be registered
- * however it is apparently legal for them to be larger.
- */
-#define MAXSNLEN 97
-
-/*
- * Current Maximum Length for Instant Messages
- *
- * This was found basically by experiment, but not wholly
- * accurate experiment.  It should not be regarded
- * as completely correct.  But its a decent approximation.
- *
- * Note that although we can send this much, its impossible
- * for WinAIM clients (up through the latest (4.0.1957)) to
- * send any more than 1kb.  Amaze all your windows friends
- * with utterly oversized instant messages!
- *
- * XXX: the real limit is the total SNAC size at 8192. Fix this.
- *
- */
-#define MAXMSGLEN 7987
-
-/*
- * Maximum size of a Buddy Icon.
- */
-#define MAXICONLEN 7168
-#define AIM_ICONIDENT "AVT1picture.id"
-
-/*
- * Current Maximum Length for Chat Room Messages
- *
- * This is actually defined by the protocol to be
- * dynamic, but I have yet to see due cause to 
- * define it dynamically here.  Maybe later.
- *
- */
-#define MAXCHATMSGLEN 512
-
-/**
- * Maximum length for the password of an ICQ account
- */
-#define MAXICQPASSLEN 8
-
-#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
-
-/*
- * Client info.  Filled in by the client and passed in to 
- * aim_send_login().  The information ends up getting passed to OSCAR
- * through the initial login command.
- *
- */
-struct client_info_s {
-	const char *clientstring;
-	fu16_t clientid;
-	fu16_t major;
-	fu16_t minor;
-	fu16_t point;
-	fu16_t build;
-	fu32_t distrib;
-	const char *country; /* two-letter abbrev */
-	const char *lang; /* two-letter abbrev */
-};
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_3_5_1670 { \
-	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
-	0x0004, \
-	0x0003, 0x0005, \
-	0x0000, 0x0686, \
-	0x0000002a, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-/* Latest winaim without ssi */
-#define CLIENTINFO_AIM_4_1_2010 { \
-	"AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
-	0x0004, \
-	0x0004, 0x0001, \
-	0x0000, 0x07da, \
-	0x0000004b, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_4_3_2188 { \
-	"AOL Instant Messenger (SM), version 4.3.2188/WIN32", \
-	0x0109, \
-	0x0400, 0x0003, \
-	0x0000, 0x088c, \
-	0x00000086, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_4_8_2540 { \
-	"AOL Instant Messenger (SM), version 4.8.2540/WIN32", \
-	0x0109, \
-	0x0004, 0x0008, \
-	0x0000, 0x09ec, \
-	0x000000af, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_AIM_5_0_2938 { \
-	"AOL Instant Messenger, version 5.0.2938/WIN32", \
-	0x0109, \
-	0x0005, 0x0000, \
-	0x0000, 0x0b7a, \
-	0x00000000, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_1_3036 { \
-	"AOL Instant Messenger, version 5.1.3036/WIN32", \
-	0x0109, \
-	0x0005, 0x0001, \
-	0x0000, 0x0bdc, \
-	0x000000d2, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_5_3415 { \
-	"AOL Instant Messenger, version 5.5.3415/WIN32", \
-	0x0109, \
-	0x0005, 0x0005, \
-	0x0000, 0x0057, \
-	0x000000ef, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_AIM_5_9_3702 { \
-	"AOL Instant Messenger, version 5.9.3702/WIN32", \
-	0x0109, \
-	0x0005, 0x0009, \
-	0x0000, 0x0e76, \
-	0x00000111, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_ICHAT_1_0 { \
-	"Apple iChat", \
-	0x311a, \
-	0x0001, 0x0000, \
-	0x0000, 0x003c, \
-	0x000000c6, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_ICQ_4_65_3281 { \
-	"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \
-	0x010a, \
-	0x0004, 0x0041, \
-	0x0001, 0x0cd1, \
-	0x00000055, \
-	"us", "en", \
-}
-
-/* Needs to be checked */
-#define CLIENTINFO_ICQ_5_34_3728 { \
-	"ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \
-	0x010a, \
-	0x0005, 0x0022, \
-	0x0001, 0x0e8f, \
-	0x00000055, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_ICQ_5_45_3777 { \
-	"ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \
-	0x010a, \
-	0x0005, 0x002d, \
-	0x0001, 0x0ec1, \
-	0x00000055, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_ICQBASIC_14_3_1068 { \
-	"ICQBasic", \
-	0x010a, \
-	0x0014, 0x0003, \
-	0x0000, 0x042c, \
-	0x0000043d, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_NETSCAPE_7_0_1 { \
-	"Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
-	0x1d0d, \
-	0x0007, 0x0000, \
-	0x0001, 0x0000, \
-	0x00000058, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_GAIM { \
-	"Gaim/" VERSION, \
-	0x0109, \
-	0x0005, 0x0001, \
-	0x0000, 0x0bdc, \
-	0x000000d2, \
-	"us", "en", \
-}
-
-#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
-#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777
-
-/*
- * These could be arbitrary, but its easier to use the actual AIM values
- */
-#define AIM_CONN_TYPE_BOS		0x0002
-#define AIM_CONN_TYPE_ADS		0x0005
-#define AIM_CONN_TYPE_AUTH		0x0007
-#define AIM_CONN_TYPE_CHATNAV	0x000d
-#define AIM_CONN_TYPE_CHAT		0x000e
-#define AIM_CONN_TYPE_SEARCH	0x000f
-#define AIM_CONN_TYPE_ICON		0x0010
-#define AIM_CONN_TYPE_EMAIL		0x0018
-
-/* they start getting arbitrary for rendezvous stuff =) */
-#define AIM_CONN_TYPE_RENDEZVOUS_PROXY	0xfffd /* these speak a strange language */
-#define AIM_CONN_TYPE_RENDEZVOUS	0xfffe /* these do not speak FLAP! */
-#define AIM_CONN_TYPE_LISTENER		0xffff /* socket waiting for accept() */
-
-/* Command types for doing a rendezvous proxy login
- * Thanks to Keith Lea and the Joust project for documenting these commands well */
-#define AIM_RV_PROXY_PACKETVER_DFLT	0x044a
-#define AIM_RV_PROXY_ERROR		0x0001
-#define AIM_RV_PROXY_INIT_SEND		0x0002 /* First command sent when creating a connection */
-#define AIM_RV_PROXY_INIT_RECV		0x0004 /* First command sent when receiving existing connection */
-#define AIM_RV_PROXY_ACK		0x0003
-#define AIM_RV_PROXY_READY		0x0005
-
-/* Number of bytes expected in each of the above packets, including the 2 bytes specifying length */
-#define AIM_RV_PROXY_ERROR_LEN		14
-#define AIM_RV_PROXY_INIT_SEND_LEN	55
-#define AIM_RV_PROXY_INIT_RECV_LEN	57
-#define AIM_RV_PROXY_ACK_LEN		18
-#define AIM_RV_PROXY_READY_LEN		12
-#define AIM_RV_PROXY_HDR_LEN		12	/* Bytes in just the header alone */
-
-/* Default values for unknown/unused values in rendezvous proxy negotiation packets */
-#define AIM_RV_PROXY_SERVER_FLAGS	0x0220		/* Default flags sent by proxy server */
-#define AIM_RV_PROXY_CLIENT_FLAGS	0x0000		/* Default flags sent by client */
-#define AIM_RV_PROXY_UNKNOWNA_DFLT	0x00000000	/* Default value for an unknown block */
-#define AIM_RV_PROXY_SERVER_URL		"ars.oscar.aol.com"
-#define AIM_RV_PROXY_CONNECT_PORT	5190		/* The port we should always connect to */
-
-/* What is the purpose of this transfer? (Who will end up with a new file?)
- * These values are used in oft_info->send_or_recv */
-#define AIM_XFER_SEND			0x0001
-#define AIM_XFER_RECV			0x0002
-
-/* Via what method is the data getting routed?
- * These values are used in oft_info->method */
-#define AIM_XFER_DIRECT			0x0001	/* Direct connection; receiver connects to sender */
-#define AIM_XFER_REDIR			0x0002	/* Redirected connection; sender connects to receiver */
-#define AIM_XFER_PROXY			0x0003	/* Proxied connection */
-
-/* Who requested the proxy?
- * The difference between a stage 2 and stage 3 proxied transfer is that the receiver does the
- * initial login for a stage 2, but the sender must do it for a stage 3.
- * These values are used in oft_info->stage */
-#define AIM_XFER_PROXY_NONE		0x0001
-#define AIM_XFER_PROXY_STG1		0x0002	/* Sender requested a proxy be used (stage1) */
-#define AIM_XFER_PROXY_STG2		0x0003	/* Receiver requested a proxy be used (stage2) */
-#define AIM_XFER_PROXY_STG3		0x0004	/* Receiver requested a proxy be used (stage3) */
-
-/*
- * Subtypes, we need these for OFT stuff.
- */
-#define AIM_CONN_SUBTYPE_OFT_DIRECTIM	0x0001
-#define AIM_CONN_SUBTYPE_OFT_GETFILE	0x0002
-#define AIM_CONN_SUBTYPE_OFT_SENDFILE	0x0003
-#define AIM_CONN_SUBTYPE_OFT_BUDDYICON	0x0004
-#define AIM_CONN_SUBTYPE_OFT_VOICE	0x0005
-
-/*
- * Status values returned from aim_conn_new().  ORed together.
- */
-#define AIM_CONN_STATUS_READY		0x0001
-#define AIM_CONN_STATUS_INTERNALERR	0x0002
-#define AIM_CONN_STATUS_RESOLVERR	0x0040
-#define AIM_CONN_STATUS_CONNERR		0x0080
-#define AIM_CONN_STATUS_INPROGRESS	0x0100
-
-#define AIM_FRAMETYPE_FLAP		0x0000
-#define AIM_FRAMETYPE_OFT		0x0001
-
-typedef struct aim_conn_s {
-	int fd;
-	fu16_t type;
-	fu16_t subtype;
-	flap_seqnum_t seqnum;
-	fu32_t status;
-	void *priv; /* misc data the client may want to store */
-	void *internal; /* internal conn-specific libfaim data */
-	time_t lastactivity; /* time of last transmit */
-	int forcedlatency; 
-	void *handlerlist;
-	void *sessv; /* pointer to parent session */
-	void *inside; /* only accessible from inside libfaim */
-	struct aim_conn_s *next;
-} aim_conn_t;
-
-/*
- * Byte Stream type. Sort of.
- *
- * Use of this type serves a couple purposes:
- *   - Buffer/buflen pairs are passed all around everywhere. This turns
- *     that into one value, as well as abstracting it slightly.
- *   - Through the abstraction, it is possible to enable bounds checking
- *     for robustness at the cost of performance.  But a clean failure on
- *     weird packets is much better than a segfault.
- *   - I like having variables named "bs".
- *
- * Don't touch the insides of this struct.  Or I'll have to kill you.
- *
- */
-typedef struct aim_bstream_s {
-	fu8_t *data;
-	fu32_t len;
-	fu32_t offset;
-} aim_bstream_t;
-
-typedef struct aim_frame_s {
-	fu8_t hdrtype; /* defines which piece of the union to use */
-	union {
-		struct { 
-			fu8_t channel;
-			flap_seqnum_t seqnum;
-		} flap;
-		struct {
-			fu8_t magic[4];	/* ODC2 or OFT2 */
-			fu16_t hdrlen;
-			fu16_t type;
-		} rend;
-	} hdr;
-	aim_bstream_t data;		/* payload stream */
-	aim_conn_t *conn;		/* the connection it came in on/is going out on */
-	fu8_t handled;			/* 0 = new, !0 = been handled */
-	struct aim_frame_s *next;
-} aim_frame_t;
-
-typedef struct aim_msgcookie_s {
-	guchar cookie[8];
-	int type;
-	void *data;
-	time_t addtime;
-	struct aim_msgcookie_s *next;
-} aim_msgcookie_t;
-
-/*
- * AIM Session: The main client-data interface.
- *
- */
-typedef struct aim_session_s {
-
-	/* ---- Client Accessible ------------------------ */
-
-	/* Our screen name. */
-	char sn[MAXSNLEN+1];
-
-	/*
-	 * Pointer to anything the client wants to 
-	 * explicitly associate with this session.
-	 *
-	 * This is for use in the callbacks mainly. In any
-	 * callback, you can access this with sess->aux_data.
-	 *
-	 */
-	void *aux_data;
-
-	/* ---- Internal Use Only ------------------------ */
-
-	/* Connection information */
-	aim_conn_t *connlist;
-
-	/*
-	 * Transmit/receive queues.
-	 *
-	 * These are only used when you don't use your own lowlevel
-	 * I/O.  I don't suggest that you use libfaim's internal I/O.
-	 * Its really bad and the API/event model is quirky at best.
-	 *  
-	 */
-	aim_frame_t *queue_outgoing;
-	aim_frame_t *queue_incoming;
-
-	/*
-	 * Tx Enqueuing function.
-	 *
-	 * This is how you override the transmit direction of libfaim's
-	 * internal I/O.  This function will be called whenever it needs
-	 * to send something.
-	 *
-	 */
-	int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *);
-
-	void *modlistv;
-
-	struct {
-		char server[128];
-		char username[128];
-		char password[128];
-	} socksproxy;
-
-	fu8_t nonblocking;
-
-	/*
-	 * Outstanding snac handling
-	 *
-	 * XXX: Should these be per-connection? -mid
-	 */
-	void *snac_hash[FAIM_SNAC_HASH_SIZE];
-	aim_snacid_t snacid_next;
-
-	aim_msgcookie_t *msgcookies;
-	struct aim_icq_info *icq_info;
-	struct aim_oft_info *oft_info;
-	struct aim_authresp_info *authinfo;
-	struct aim_emailinfo *emailinfo;
-
-	struct {
-		struct aim_userinfo_s *userinfo;
-		struct userinfo_node *torequest;
-		struct userinfo_node *requested;
-		int waiting_for_response;
-	} locate;
-
-	/* Server-stored information (ssi) */
-	struct {
-		int received_data;
-		fu16_t numitems;
-		struct aim_ssi_item *official;
-		struct aim_ssi_item *local;
-		struct aim_ssi_tmp *pending;
-		time_t timestamp;
-		int waiting_for_ack;
-	} ssi;
-} aim_session_t;
-
-/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
-#define AIM_ICQ_STATE_NORMAL		0x00000000
-#define AIM_ICQ_STATE_AWAY		0x00000001
-#define AIM_ICQ_STATE_DND		0x00000002
-#define AIM_ICQ_STATE_OUT		0x00000004
-#define AIM_ICQ_STATE_BUSY		0x00000010
-#define AIM_ICQ_STATE_CHAT		0x00000020
-#define AIM_ICQ_STATE_INVISIBLE		0x00000100
-#define AIM_ICQ_STATE_WEBAWARE		0x00010000
-#define AIM_ICQ_STATE_HIDEIP		0x00020000
-#define AIM_ICQ_STATE_BIRTHDAY		0x00080000
-#define AIM_ICQ_STATE_DIRECTDISABLED	0x00100000
-#define AIM_ICQ_STATE_ICQHOMEPAGE	0x00200000
-#define AIM_ICQ_STATE_DIRECTREQUIREAUTH	0x10000000
-#define AIM_ICQ_STATE_DIRECTCONTACTLIST	0x20000000
-
-/*
- * Get command from connections
- *
- * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction.
- * XXX Make this easily overridable.
- *
- */
-faim_export int aim_get_command(aim_session_t *, aim_conn_t *);
-
-/*
- * Dispatch commands that are in the rx queue.
- */
-faim_export void aim_rxdispatch(aim_session_t *);
-
-faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn);
-
-faim_export int aim_logoff(aim_session_t *);
-
-#if !defined(FAIM_INTERNAL)
-/* the library should never call aim_conn_kill */
-faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
-#endif
-
-typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...);
-
-
-/* auth.c */
-struct aim_clientrelease {
-	char *name;
-	fu32_t build;
-	char *url;
-	char *info;
-};
-
-struct aim_authresp_info {
-	char *sn;
-	fu16_t errorcode;
-	char *errorurl;
-	fu16_t regstatus;
-	char *email;
-	char *bosip;
-	fu16_t cookielen;
-	fu8_t *cookie;
-	char *chpassurl;
-	struct aim_clientrelease latestrelease;
-	struct aim_clientrelease latestbeta;
-};
-
-/* Callback data for redirect. */
-struct aim_redirect_data {
-	fu16_t group;
-	const char *ip;
-	fu16_t cookielen;
-	const fu8_t *cookie;
-	struct { /* group == AIM_CONN_TYPE_CHAT */
-		fu16_t exchange;
-		const char *room;
-		fu16_t instance;
-	} chat;
-};
-
-faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn);
-faim_export int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key);
-/* 0x000b */ faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid);
-
-faim_export void aim_purge_rxqueue(aim_session_t *);
-faim_export void aim_cleansnacs(aim_session_t *, int maxage);
-
-#define AIM_TX_QUEUED    0 /* default */
-#define AIM_TX_IMMEDIATE 1
-#define AIM_TX_USER      2
-faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *));
-
-faim_export int aim_tx_flushqueue(aim_session_t *);
-faim_export void aim_tx_purgequeue(aim_session_t *);
-
-faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval);
-
-faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags);
-faim_export int aim_clearhandlers(aim_conn_t *conn);
-
-faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group);
-faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn);
-faim_export void aim_conn_close(aim_conn_t *deadconn);
-faim_export aim_conn_t *aim_newconn(aim_session_t *, int type);
-faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_conn_isready(aim_conn_t *);
-faim_export int aim_conn_setstatus(aim_conn_t *, int);
-faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_conn_isconnecting(aim_conn_t *conn);
-
-faim_export void aim_session_init(aim_session_t *, fu8_t nonblocking);
-faim_export void aim_session_kill(aim_session_t *);
-faim_export aim_conn_t *aim_getconn_type(aim_session_t *, int type);
-faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *, int type);
-faim_export aim_conn_t *aim_getconn_fd(aim_session_t *, int fd);
-
-/* 0x0001 - service.c */
-faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg);
-faim_export int aim_srv_setidle(aim_session_t *sess, fu32_t idletime);
-
-/* misc.c */
-
-#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
-#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
-#define AIM_VISIBILITYCHANGE_DENYADD      0x07
-#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
-
-#define AIM_PRIVFLAGS_ALLOWIDLE           0x01
-#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE    0x02
-
-#define AIM_WARN_ANON                     0x01
-
-faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_nop(aim_session_t *, aim_conn_t *);
-faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *);
-faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, fu32_t mask);
-faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, fu32_t);
-faim_export int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *);
-faim_export int aim_reqservice(aim_session_t *, aim_conn_t *, fu16_t);
-faim_export int aim_bos_reqrights(aim_session_t *, aim_conn_t *);
-faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status);
-
-#define AIM_CLIENTTYPE_UNKNOWN  0x0000
-#define AIM_CLIENTTYPE_MC       0x0001
-#define AIM_CLIENTTYPE_WINAIM   0x0002
-#define AIM_CLIENTTYPE_WINAIM41 0x0003
-#define AIM_CLIENTTYPE_AOL_TOC  0x0004
-faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len);
-
-#define AIM_RATE_CODE_CHANGE     0x0001
-#define AIM_RATE_CODE_WARNING    0x0002
-#define AIM_RATE_CODE_LIMIT      0x0003
-#define AIM_RATE_CODE_CLEARLIMIT 0x0004
-faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
-
-
-
-/* im.c */
-#define AIM_OFT_SUBTYPE_SEND_FILE	0x0001
-#define AIM_OFT_SUBTYPE_SEND_DIR	0x0002
-#define AIM_OFT_SUBTYPE_GET_FILE	0x0011
-#define AIM_OPT_SUBTYPE_GET_LIST	0x0012
-
-#define AIM_TRANSFER_DENY_NOTSUPPORTED	0x0000
-#define AIM_TRANSFER_DENY_DECLINE	0x0001
-#define AIM_TRANSFER_DENY_NOTACCEPTING	0x0002
-
-#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
-#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
-
-/* This is what the server will give you if you don't set them yourself. */
-#define AIM_IMPARAM_DEFAULTS { \
-	0, \
-	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
-	512, /* !! Note how small this is. */ \
-	(99.9)*10, (99.9)*10, \
-	1000 /* !! And how large this is. */ \
-}
-
-/* This is what most AIM versions use. */
-#define AIM_IMPARAM_REASONABLE { \
-	0, \
-	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
-	8000, \
-	(99.9)*10, (99.9)*10, \
-	0 \
-}
-
-struct aim_icbmparameters {
-	fu16_t maxchan;
-	fu32_t flags; /* AIM_IMPARAM_FLAG_ */
-	fu16_t maxmsglen; /* message size that you will accept */
-	fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */
-	fu16_t maxrecverwarn;
-	fu32_t minmsginterval; /* in milliseconds? */
-};
-
-struct aim_chat_roominfo {
-	fu16_t exchange;
-	char *name;
-	fu16_t instance;
-};
-
-#define AIM_IMFLAGS_AWAY				0x0001 /* mark as an autoreply */
-#define AIM_IMFLAGS_ACK					0x0002 /* request a receipt notice */
-#define AIM_IMFLAGS_BUDDYREQ			0x0010 /* buddy icon requested */
-#define AIM_IMFLAGS_HASICON				0x0020 /* already has icon */
-#define AIM_IMFLAGS_SUBENC_MACINTOSH	0x0040 /* damn that Steve Jobs! */
-#define AIM_IMFLAGS_CUSTOMFEATURES 		0x0080 /* features field present */
-#define AIM_IMFLAGS_EXTDATA				0x0100
-#define AIM_IMFLAGS_X					0x0200
-#define AIM_IMFLAGS_MULTIPART			0x0400 /* ->mpmsg section valid */
-#define AIM_IMFLAGS_OFFLINE				0x0800 /* send to offline user */
-#define AIM_IMFLAGS_TYPINGNOT			0x1000 /* typing notification */
-
-#define AIM_CHARSET_ASCII		0x0000
-#define AIM_CHARSET_UNICODE	0x0002 /* UCS-2BE */
-#define AIM_CHARSET_CUSTOM	0x0003
-
-/*
- * Multipart message structures.
- */
-typedef struct aim_mpmsg_section_s {
-	fu16_t charset;
-	fu16_t charsubset;
-	gchar *data;
-	fu16_t datalen;
-	struct aim_mpmsg_section_s *next;
-} aim_mpmsg_section_t;
-
-typedef struct aim_mpmsg_s {
-	unsigned int numparts;
-	aim_mpmsg_section_t *parts;
-} aim_mpmsg_t;
-
-faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm);
-faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const gchar *data, fu16_t datalen);
-faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii);
-faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen);
-faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm);
-
-/*
- * Arguments to aim_send_im_ext().
- *
- * This is really complicated.  But immensely versatile.
- *
- */
-struct aim_sendimext_args {
-
-	/* These are _required_ */
-	const char *destsn;
-	fu32_t flags; /* often 0 */
-
-	/* Only required if not using multipart messages */
-	const char *msg;
-	int msglen;
-
-	/* Required if ->msg is not provided */
-	aim_mpmsg_t *mpmsg;
-
-	/* Only used if AIM_IMFLAGS_HASICON is set */
-	fu32_t iconlen;
-	time_t iconstamp;
-	fu32_t iconsum;
-
-	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
-	fu16_t featureslen;
-	fu8_t *features;
-
-	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
-	fu16_t charset;
-	fu16_t charsubset;
-};
-
-/*
- * Arguments to aim_send_rtfmsg().
- */
-struct aim_sendrtfmsg_args {
-	const char *destsn;
-	fu32_t fgcolor;
-	fu32_t bgcolor;
-	const char *rtfmsg; /* must be in RTF */
-};
-
-/*
- * This information is provided in the Incoming ICBM callback for
- * Channel 1 ICBM's.  
- *
- * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
- * are optional, both are always set by the current libfaim code.
- * That may or may not change in the future.  It is mainly for
- * consistency with aim_sendimext_args.
- *
- * Multipart messages require some explanation. If you want to use them,
- * I suggest you read all the comments in im.c.
- *
- */
-struct aim_incomingim_ch1_args {
-
-	/* Always provided */
-	aim_mpmsg_t mpmsg;
-	fu32_t icbmflags; /* some flags apply only to ->msg, not all mpmsg */
-	
-	/* Only provided if message has a human-readable section */
-	gchar *msg;
-	int msglen;
-
-	/* Only provided if AIM_IMFLAGS_HASICON is set */
-	time_t iconstamp;
-	fu32_t iconlen;
-	fu16_t iconsum;
-
-	/* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
-	fu8_t *features;
-	fu8_t featureslen;
-
-	/* Only provided if AIM_IMFLAGS_EXTDATA is set */
-	fu8_t extdatalen;
-	fu8_t *extdata;
-
-	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
-	fu16_t charset;
-	fu16_t charsubset;
-};
-
-/* Valid values for channel 2 args->status */
-#define AIM_RENDEZVOUS_PROPOSE	0x0000
-#define AIM_RENDEZVOUS_CANCEL	0x0001
-#define AIM_RENDEZVOUS_ACCEPT	0x0002
-
-struct aim_incomingim_ch2_args {
-	fu16_t status;
-	guchar cookie[8];
-	int reqclass;
-	const char *proxyip;
-	const char *clientip;
-	const char *verifiedip;
-	fu16_t port;
-	fu16_t errorcode;
-	const char *msg; /* invite message or file description */
-	fu16_t msglen;
-	const char *encoding;
-	const char *language;
-	union {
-		struct {
-			fu32_t checksum;
-			fu32_t length;
-			time_t timestamp;
-			fu8_t *icon;
-		} icon;
-		struct {
-			struct aim_chat_roominfo roominfo;
-		} chat;
-		struct {
-			fu16_t msgtype;
-			fu32_t fgcolor;
-			fu32_t bgcolor;
-			const char *rtfmsg;
-		} rtfmsg;
-		struct {
-			fu16_t subtype;
-			fu16_t totfiles;
-			fu32_t totsize;
-			char *filename;
-			/* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */
-			fu16_t reqnum;
-			fu8_t use_proxy; /* Did the user request that we use a rv proxy? */
-		} sendfile;
-	} info;
-	void *destructor; /* used internally only */
-};
-
-/* Valid values for channel 4 args->type */
-#define AIM_ICQMSG_AUTHREQUEST	0x0006
-#define AIM_ICQMSG_AUTHDENIED	0x0007
-#define AIM_ICQMSG_AUTHGRANTED	0x0008
-
-struct aim_incomingim_ch4_args {
-	fu32_t uin; /* Of the sender of the ICBM */
-	fu8_t type;
-	fu8_t flags;
-	gchar *msg; /* Reason for auth request, deny, or accept */
-	int msglen;
-};
-
-/* SNAC sending functions */
-/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params);
-/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess);
-/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args);
-/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, fu16_t flags, const char *msg);
-/* 0x0006 */ faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance);
-/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum);
-/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
-/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *cookie, gboolean usecookie, const char *sn, const fu8_t *ip, fu16_t port);
-/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info);
-/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info);
-/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info);
-/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type);
-/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, fu16_t type, const char *message);
-/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
-/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, fu16_t code);
-/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2);
-faim_export void aim_icbm_makecookie(guchar* cookie);
-
-
-/* ft.c */
-struct aim_fileheader_t {
-#if 0
-	char magic[4];		/* 0 */
-	fu16_t hdrlen;		/* 4 */
-	fu16_t hdrtype;		/* 6 */
-#endif
-	guchar bcookie[8];	/* 8 */
-	fu16_t encrypt;		/* 16 */
-	fu16_t compress;	/* 18 */
-	fu16_t totfiles;	/* 20 */
-	fu16_t filesleft;	/* 22 */
-	fu16_t totparts;	/* 24 */
-	fu16_t partsleft;	/* 26 */
-	fu32_t totsize;		/* 28 */
-	fu32_t size;		/* 32 */
-	fu32_t modtime;		/* 36 */
-	fu32_t checksum;	/* 40 */
-	fu32_t rfrcsum;		/* 44 */
-	fu32_t rfsize;		/* 48 */
-	fu32_t cretime;		/* 52 */
-	fu32_t rfcsum;		/* 56 */
-	fu32_t nrecvd;		/* 60 */
-	fu32_t recvcsum;	/* 64 */
-	char idstring[32];	/* 68 */
-	fu8_t flags;		/* 100 */
-	fu8_t lnameoffset;	/* 101 */
-	fu8_t lsizeoffset;	/* 102 */
-	char dummy[69];		/* 103 */
-	char macfileinfo[16];	/* 172 */
-	fu16_t nencode;		/* 188 */
-	fu16_t nlanguage;	/* 190 */
-	char name[64];		/* 192 */
-				/* 256 */
-};
-
-struct aim_rv_proxy_info {
-	fu16_t packet_ver;
-	fu16_t cmd_type;
-	fu16_t flags;
-	char* ip; /* IP address sent along with this packet */
-	fu16_t port; /* This is NOT the port we should use to connect. Always connect to 5190 */
-	guchar cookie[8];
-	fu32_t unknownA;
-	fu16_t err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */
-	aim_conn_t *conn;
-	aim_session_t *sess;
-};
-
-struct aim_oft_info {
-	guchar cookie[8];
-	char *sn;
-	char *proxyip;
-	char *clientip;
-	char *verifiedip;
-	fu16_t port;
-
-	int send_or_recv; /* Send or receive */
-	int method; /* What method is being used to transfer this file? DIRECT, REDIR, or PROXY */
-	int stage; /* At what stage was a proxy requested? NONE, STG1, STG2*/
-	int xfer_reffed; /* There are many timers, but we should only ref the xfer once */
-	int redir_attempted; /* Have we previously attempted to redirect the connection? */
-	fu32_t res_bytes; /* The bytes already received for resuming a transfer */
-
-	aim_conn_t *conn;
-	aim_session_t *sess;
-	int success; /* Was the connection successful? Used for timing out the transfer. */
-	struct aim_fileheader_t fh;
-	struct aim_oft_info *next;
-	struct aim_rv_proxy_info *proxy_info;
-};
-
-faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck);
-faim_export fu32_t aim_oft_checksum_file(char *filename);
-faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
-faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
-faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg);
-faim_export const char *aim_odc_getsn(aim_conn_t *conn);
-faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn);
-faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn);
-faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd,
-                                         const fu8_t *localip, fu16_t port, const guchar *mycookie);
-faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guchar *cookie);
-
-faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guchar *cookie, const char *sn,
-	const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv,
-	int method, int stage);
-faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info);
-faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guchar *cookie, fu16_t port);
-faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
-faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info);
-
-faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info);
-faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info);
-
-faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
-faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info);
-faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn);
-
-/* 0x0002 - locate.c */
-/*
- * AIM User Info, Standard Form.
- */
-#define AIM_FLAG_UNCONFIRMED	0x0001 /* "damned transients" */
-#define AIM_FLAG_ADMINISTRATOR	0x0002
-#define AIM_FLAG_AOL			0x0004
-#define AIM_FLAG_OSCAR_PAY		0x0008
-#define AIM_FLAG_FREE 			0x0010
-#define AIM_FLAG_AWAY			0x0020
-#define AIM_FLAG_ICQ			0x0040
-#define AIM_FLAG_WIRELESS		0x0080
-#define AIM_FLAG_UNKNOWN100		0x0100
-#define AIM_FLAG_UNKNOWN200		0x0200
-#define AIM_FLAG_ACTIVEBUDDY	0x0400
-#define AIM_FLAG_UNKNOWN800		0x0800
-#define AIM_FLAG_ABINTERNAL		0x1000
-#define AIM_FLAG_ALLUSERS		0x001f
-
-#define AIM_USERINFO_PRESENT_FLAGS        0x00000001
-#define AIM_USERINFO_PRESENT_MEMBERSINCE  0x00000002
-#define AIM_USERINFO_PRESENT_ONLINESINCE  0x00000004
-#define AIM_USERINFO_PRESENT_IDLE         0x00000008
-#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
-#define AIM_USERINFO_PRESENT_ICQIPADDR    0x00000020
-#define AIM_USERINFO_PRESENT_ICQDATA      0x00000040
-#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
-#define AIM_USERINFO_PRESENT_SESSIONLEN   0x00000100
-#define AIM_USERINFO_PRESENT_CREATETIME   0x00000200
-
-struct userinfo_node {
-	char *sn;
-	struct userinfo_node *next;
-};
-
-typedef struct aim_userinfo_s {
-	char *sn;
-	fu16_t warnlevel; /* evil percent * 10 (999 = 99.9%) */
-	fu16_t idletime; /* in seconds */
-	fu16_t flags;
-	fu32_t createtime; /* time_t */
-	fu32_t membersince; /* time_t */
-	fu32_t onlinesince; /* time_t */
-	fu32_t sessionlen;  /* in seconds */
-	fu32_t capabilities;
-	struct {
-		fu32_t status;
-		fu32_t ipaddr;
-		fu8_t crap[0x25]; /* until we figure it out... */
-	} icqinfo;
-	fu32_t present;
-
-	fu8_t iconcsumtype;
-	fu16_t iconcsumlen;
-	fu8_t *iconcsum;
-
-	char *info;
-	char *info_encoding;
-	fu16_t info_len;
-
-	char *status;
-	char *status_encoding;
-	fu16_t status_len;
-
-	char *away;
-	char *away_encoding;
-	fu16_t away_len;
-
-	struct aim_userinfo_s *next;
-} aim_userinfo_t;
-
-#define AIM_CAPS_BUDDYICON		0x00000001
-#define AIM_CAPS_TALK			0x00000002
-#define AIM_CAPS_DIRECTIM		0x00000004
-#define AIM_CAPS_CHAT			0x00000008
-#define AIM_CAPS_GETFILE		0x00000010
-#define AIM_CAPS_SENDFILE		0x00000020
-#define AIM_CAPS_GAMES			0x00000040
-#define AIM_CAPS_ADDINS			0x00000080
-#define AIM_CAPS_SENDBUDDYLIST	0x00000100
-#define AIM_CAPS_GAMES2			0x00000200
-#define AIM_CAPS_ICQ_DIRECT		0x00000400
-#define AIM_CAPS_APINFO			0x00000800
-#define AIM_CAPS_ICQRTF			0x00001000
-#define AIM_CAPS_EMPTY			0x00002000
-#define AIM_CAPS_ICQSERVERRELAY	0x00004000
-#define AIM_CAPS_ICQUTF8OLD		0x00008000
-#define AIM_CAPS_TRILLIANCRYPT	0x00010000
-#define AIM_CAPS_ICQUTF8		0x00020000
-#define AIM_CAPS_INTEROPERATE	0x00040000
-#define AIM_CAPS_ICHAT			0x00080000
-#define AIM_CAPS_HIPTOP			0x00100000
-#define AIM_CAPS_SECUREIM		0x00200000
-#define AIM_CAPS_SMS			0x00400000
-#define AIM_CAPS_GENERICUNKNOWN	0x00800000
-#define AIM_CAPS_VIDEO			0x01000000
-#define AIM_CAPS_ICHATAV		0x02000000
-#define AIM_CAPS_LIVEVIDEO		0x04000000
-#define AIM_CAPS_CAMERA			0x08000000
-#define AIM_CAPS_LAST			0x10000000
-
-#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST  0
-#define AIM_SENDMEMBLOCK_FLAG_ISHASH     1
-
-faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag);
-
-struct aim_invite_priv {
-	char *sn;
-	char *roomname;
-	fu16_t exchange;
-	fu16_t instance;
-};
-
-#define AIM_COOKIETYPE_UNKNOWN  0x00
-#define AIM_COOKIETYPE_ICBM     0x01
-#define AIM_COOKIETYPE_ADS      0x02
-#define AIM_COOKIETYPE_BOS      0x03
-#define AIM_COOKIETYPE_IM       0x04
-#define AIM_COOKIETYPE_CHAT     0x05
-#define AIM_COOKIETYPE_CHATNAV  0x06
-#define AIM_COOKIETYPE_INVITE   0x07
-/* we'll move OFT up a bit to give breathing room.  not like it really
- * matters. */
-#define AIM_COOKIETYPE_OFTIM    0x10
-#define AIM_COOKIETYPE_OFTGET   0x11
-#define AIM_COOKIETYPE_OFTSEND  0x12
-#define AIM_COOKIETYPE_OFTVOICE 0x13
-#define AIM_COOKIETYPE_OFTIMAGE 0x14
-#define AIM_COOKIETYPE_OFTICON  0x15
-
-faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn);
-faim_export void aim_locate_dorequest(aim_session_t *sess);
-
-/* 0x0002 */ faim_export int aim_locate_reqrights(aim_session_t *sess);
-/* 0x0004 */ faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps);
-/* 0x0004 */ faim_export int aim_locate_setprofile(aim_session_t *sess, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
-/* 0x0005 */ faim_export int aim_locate_getinfo(aim_session_t *sess, const char *, fu16_t);
-/* 0x0009 */ faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy);
-/* 0x000b */ faim_export int aim_locate_000b(aim_session_t *sess, const char *sn);
-/* 0x000f */ faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy);
-/* 0x0015 */ faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags);
-
-
-
-/* 0x0003 - buddylist.c */
-/* 0x0002 */ faim_export int aim_buddylist_reqrights(aim_session_t *, aim_conn_t *);
-/* 0x0004 */ faim_export int aim_buddylist_set(aim_session_t *, aim_conn_t *, const char *);
-/* 0x0004 */ faim_export int aim_buddylist_addbuddy(aim_session_t *, aim_conn_t *, const char *);
-/* 0x0005 */ faim_export int aim_buddylist_removebuddy(aim_session_t *, aim_conn_t *, const char *);
-/* 0x000b */ faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info);
-/* 0x000c */ faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
-
-
-
-/* 0x000a - search.c */
-faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *);
-
-
-
-/* 0x000d - chatnav.c */
-/* 0x000e - chat.c */
-/* These apply to exchanges as well. */
-#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
-#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
-#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
-#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
-
-struct aim_chat_exchangeinfo {
-	fu16_t number;
-	fu16_t flags;
-	char *name;
-	char *charset1;
-	char *lang1;
-	char *charset2;
-	char *lang2;
-};
-
-#define AIM_CHATFLAGS_NOREFLECT 0x0001
-#define AIM_CHATFLAGS_AWAY      0x0002
-faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const gchar *msg, int msglen, const char *encoding, const char *language);
-faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance);
-faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance);
-faim_export char *aim_chat_getname(aim_conn_t *conn);
-faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name);
-
-faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn);
-
-faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange);
-faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name);
-
-
-
-/* 0x000f - odir.c */
-struct aim_odir {
-	char *first;
-	char *last;
-	char *middle;
-	char *maiden;
-	char *email;
-	char *country;
-	char *state;
-	char *city;
-	char *sn;
-	char *interest;
-	char *nick;
-	char *zip;
-	char *region;
-	char *address;
-	struct aim_odir *next;
-};
-
-faim_export int aim_odir_email(aim_session_t *, const char *, const char *);
-faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *);
-faim_export int aim_odir_interest(aim_session_t *, const char *, const char *);
-
-
-
-/* 0x0010 - icon.c */
-faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen);
-faim_export int aim_bart_request(aim_session_t *sess, const char *sn, fu8_t iconcsumtype, const fu8_t *iconstr, fu16_t iconstrlen);
-
-
-
-/* 0x0013 - ssi.c */
-#define AIM_SSI_TYPE_BUDDY		0x0000
-#define AIM_SSI_TYPE_GROUP		0x0001
-#define AIM_SSI_TYPE_PERMIT		0x0002
-#define AIM_SSI_TYPE_DENY		0x0003
-#define AIM_SSI_TYPE_PDINFO		0x0004
-#define AIM_SSI_TYPE_PRESENCEPREFS	0x0005
-#define AIM_SSI_TYPE_ICONINFO		0x0014
-
-#define AIM_SSI_ACK_SUCCESS		0x0000
-#define AIM_SSI_ACK_ITEMNOTFOUND	0x0002
-#define AIM_SSI_ACK_IDNUMINUSE		0x000a
-#define AIM_SSI_ACK_ATMAX		0x000c
-#define AIM_SSI_ACK_INVALIDNAME		0x000d
-#define AIM_SSI_ACK_AUTHREQUIRED	0x000e
-
-/* These flags are set in the 0x00c9 TLV of SSI teyp 0x0005 */
-#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE        0x00000400
-#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000
-
-struct aim_ssi_item {
-	char *name;
-	fu16_t gid;
-	fu16_t bid;
-	fu16_t type;
-	struct aim_tlvlist_s *data;
-	struct aim_ssi_item *next;
-};
-
-struct aim_ssi_tmp {
-	fu16_t action;
-	fu16_t ack;
-	char *name;
-	struct aim_ssi_item *item;
-	struct aim_ssi_tmp *next;
-};
-
-/* These build the actual SNACs and queue them to be sent */
-/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess);
-/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess);
-/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, fu16_t localrev);
-/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess);
-/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess);
-/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess);
-/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess);
-/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg);
-/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg);
-/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, const char *msg);
-
-/* Client functions for retrieving SSI data */
-faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid);
-faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type);
-faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn);
-faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn);
-faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list);
-faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list);
-faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn);
-faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn);
-faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn);
-
-/* Client functions for changing SSI data */
-faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth);
-faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name);
-faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name);
-faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group);
-faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name);
-faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name);
-faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn);
-faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias);
-faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *alias);
-faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn);
-faim_export int aim_ssi_cleanlist(aim_session_t *sess);
-faim_export int aim_ssi_deletelist(aim_session_t *sess);
-faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask);
-faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence);
-faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen);
-faim_export int aim_ssi_delicon(aim_session_t *sess);
-
-
-
-/* 0x0015 - icq.c */
-#define AIM_ICQ_INFO_SIMPLE	0x001
-#define AIM_ICQ_INFO_SUMMARY	0x002
-#define AIM_ICQ_INFO_EMAIL	0x004
-#define AIM_ICQ_INFO_PERSONAL	0x008
-#define AIM_ICQ_INFO_ADDITIONAL	0x010
-#define AIM_ICQ_INFO_WORK	0x020
-#define AIM_ICQ_INFO_INTERESTS	0x040
-#define AIM_ICQ_INFO_ORGS	0x080
-#define AIM_ICQ_INFO_UNKNOWN	0x100
-#define AIM_ICQ_INFO_HAVEALL	0x1ff
-
-struct aim_icq_offlinemsg {
-	fu32_t sender;
-	fu16_t year;
-	fu8_t month, day, hour, minute;
-	fu8_t type;
-	fu8_t flags;
-	char *msg;
-	int msglen;
-};
-
-struct aim_icq_info {
-	fu16_t reqid;
-
-	/* simple */
-	fu32_t uin;
-
-	/* general and "home" information (0x00c8) */
-	char *nick;
-	char *first;
-	char *last;
-	char *email;
-	char *homecity;
-	char *homestate;
-	char *homephone;
-	char *homefax;
-	char *homeaddr;
-	char *mobile;
-	char *homezip;
-	fu16_t homecountry;
-/*	fu8_t timezone;
-	fu8_t hideemail; */
-
-	/* personal (0x00dc) */
-	fu8_t age;
-	fu8_t unknown;
-	fu8_t gender;
-	char *personalwebpage;
-	fu16_t birthyear;
-	fu8_t birthmonth;
-	fu8_t birthday;
-	fu8_t language1;
-	fu8_t language2;
-	fu8_t language3;
-
-	/* work (0x00d2) */
-	char *workcity;
-	char *workstate;
-	char *workphone;
-	char *workfax;
-	char *workaddr;
-	char *workzip;
-	fu16_t workcountry;
-	char *workcompany;
-	char *workdivision;
-	char *workposition;
-	char *workwebpage;
-
-	/* additional personal information (0x00e6) */
-	char *info;
-
-	/* email (0x00eb) */
-	fu16_t numaddresses;
-	char **email2;
-
-	/* we keep track of these in a linked list because we're 1337 */
-	struct aim_icq_info *next;
-};
-
-faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess);
-faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess);
-faim_export int aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware);
-faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd);
-faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
-faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin);
-faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin);
-
-
-
-/* 0x0017 - auth.c */
-faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const fu16_t length, const fu8_t *);
-faim_export int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw);
-faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info);
-faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
-faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick);
-
-
-
-/* 0x0018 - email.c */
-struct aim_emailinfo {
-	fu8_t *cookie16;
-	fu8_t *cookie8;
-	char *url;
-	fu16_t nummsgs;
-	fu8_t unread;
-	char *domain;
-	fu16_t flag;
-	struct aim_emailinfo *next;
-};
-
-faim_export int aim_email_sendcookies(aim_session_t *sess);
-faim_export int aim_email_activate(aim_session_t *sess);
-
-
-
-#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV)
-/* tlv.c - TLV handling */
-
-/* TLV structure */
-typedef struct aim_tlv_s {
-	fu16_t type;
-	fu16_t length;
-	fu8_t *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 */
-faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, fu16_t type, const int nth);
-faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, fu16_t type, const int nth);
-faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth);
-faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth);
-faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth);
-faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth);
-
-/* TLV list handling functions */
-faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs);
-faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num);
-faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len);
-faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig);
-
-faim_internal int aim_tlvlist_count(aim_tlvlist_t **list);
-faim_internal int aim_tlvlist_size(aim_tlvlist_t **list);
-faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two);
-faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list);
-faim_internal void aim_tlvlist_free(aim_tlvlist_t **list);
-
-faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value);
-faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type);
-faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value);
-faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value);
-faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value);
-faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const fu16_t type, const char *value);
-faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps);
-faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo);
-faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance);
-faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl);
-
-faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t lenth, const fu8_t *value);
-faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const fu16_t type, const char *str);
-faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type);
-faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value);
-faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value);
-faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value);
-
-faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type);
-#endif /* FAIM_INTERNAL */
-
-
-
-/* util.c */
-/*
- * These are really ugly.  You'd think this was LISP.  I wish it was.
- *
- * XXX With the advent of bstream's, these should be removed to enforce
- * their use.
- *
- */
-#define aimutil_put8(buf, data) ((*(buf) = (fu8_t)(data)&0xff),1)
-#define aimutil_get8(buf) ((*(buf))&0xff)
-#define aimutil_put16(buf, data) ( \
-		(*(buf) = (fu8_t)((data)>>8)&0xff), \
-		(*((buf)+1) = (fu8_t)(data)&0xff),  \
-		2)
-#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
-#define aimutil_put32(buf, data) ( \
-		(*((buf)) = (fu8_t)((data)>>24)&0xff), \
-		(*((buf)+1) = (fu8_t)((data)>>16)&0xff), \
-		(*((buf)+2) = (fu8_t)((data)>>8)&0xff), \
-		(*((buf)+3) = (fu8_t)(data)&0xff), \
-		4)
-#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
-		(((*((buf)+1))<<16)&0x00ff0000) + \
-		(((*((buf)+2))<< 8)&0x0000ff00) + \
-		(((*((buf)+3)    )&0x000000ff)))
-
-/* Little-endian versions (damn ICQ) */
-#define aimutil_putle8(buf, data) ( \
-		(*(buf) = (fu8_t)(data) & 0xff), \
-		1)
-#define aimutil_getle8(buf) ( \
-		(*(buf)) & 0xff \
-		)
-#define aimutil_putle16(buf, data) ( \
-		(*((buf)+0) = (fu8_t)((data) >> 0) & 0xff),  \
-		(*((buf)+1) = (fu8_t)((data) >> 8) & 0xff), \
-		2)
-#define aimutil_getle16(buf) ( \
-		(((*((buf)+0)) << 0) & 0x00ff) + \
-		(((*((buf)+1)) << 8) & 0xff00) \
-		)
-#define aimutil_putle32(buf, data) ( \
-		(*((buf)+0) = (fu8_t)((data) >>  0) & 0xff), \
-		(*((buf)+1) = (fu8_t)((data) >>  8) & 0xff), \
-		(*((buf)+2) = (fu8_t)((data) >> 16) & 0xff), \
-		(*((buf)+3) = (fu8_t)((data) >> 24) & 0xff), \
-		4)
-#define aimutil_getle32(buf) ( \
-		(((*((buf)+0)) <<  0) & 0x000000ff) + \
-		(((*((buf)+1)) <<  8) & 0x0000ff00) + \
-		(((*((buf)+2)) << 16) & 0x00ff0000) + \
-		(((*((buf)+3)) << 24) & 0xff000000))
-
-
-faim_export int aimutil_putstr(char *, const char *, int);
-faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen);
-faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl);
-faim_export int aimutil_itemcnt(char *toSearch, char dl);
-faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl);
-
-faim_export int aim_snvalid(const char *sn);
-faim_export int aim_sn_is_icq(const char *sn);
-faim_export int aim_sn_is_sms(const char *sn);
-faim_export int aim_snlen(const char *sn);
-faim_export int aim_sncmp(const char *sn1, const char *sn2);
-
-#include <aim_internal.h>
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __AIM_H__ */
--- a/src/protocols/oscar/aim_cbtypes.h	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,314 +0,0 @@
-/*
- * AIM Callback Types
- *
- */
-#ifndef __AIM_CBTYPES_H__
-#define __AIM_CBTYPES_H__
-
-/*
- * SNAC Families.
- */
-#define AIM_CB_FAM_ACK 0x0000
-#define AIM_CB_FAM_GEN 0x0001
-#define AIM_CB_FAM_LOC 0x0002
-#define AIM_CB_FAM_BUD 0x0003
-#define AIM_CB_FAM_MSG 0x0004
-#define AIM_CB_FAM_ADS 0x0005
-#define AIM_CB_FAM_INV 0x0006
-#define AIM_CB_FAM_ADM 0x0007
-#define AIM_CB_FAM_POP 0x0008
-#define AIM_CB_FAM_BOS 0x0009
-#define AIM_CB_FAM_LOK 0x000a
-#define AIM_CB_FAM_STS 0x000b
-#define AIM_CB_FAM_TRN 0x000c
-#define AIM_CB_FAM_CTN 0x000d /* ChatNav */
-#define AIM_CB_FAM_CHT 0x000e /* Chat */
-#define AIM_CB_FAM_SCH 0x000f /* "New" search */
-#define AIM_CB_FAM_ICO 0x0010 /* Used for uploading buddy icons */
-#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
-#define AIM_CB_FAM_ICQ 0x0015
-#define AIM_CB_FAM_ATH 0x0017
-#define AIM_CB_FAM_EML 0x0018
-#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
-#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
-
-/*
- * SNAC Family: Ack.
- * 
- * Not really a family, but treating it as one really
- * helps it fit into the libfaim callback structure better.
- *
- */
-#define AIM_CB_ACK_ACK 0x0001
-
-/*
- * SNAC Family: General.
- */
-#define AIM_CB_GEN_ERROR 0x0001
-#define AIM_CB_GEN_CLIENTREADY 0x0002
-#define AIM_CB_GEN_SERVERREADY 0x0003
-#define AIM_CB_GEN_SERVICEREQ 0x0004
-#define AIM_CB_GEN_REDIRECT 0x0005
-#define AIM_CB_GEN_RATEINFOREQ 0x0006
-#define AIM_CB_GEN_RATEINFO 0x0007
-#define AIM_CB_GEN_RATEINFOACK 0x0008
-#define AIM_CB_GEN_RATECHANGE 0x000a
-#define AIM_CB_GEN_SERVERPAUSE 0x000b
-#define AIM_CB_GEN_SERVERRESUME 0x000d
-#define AIM_CB_GEN_REQSELFINFO 0x000e
-#define AIM_CB_GEN_SELFINFO 0x000f
-#define AIM_CB_GEN_EVIL 0x0010
-#define AIM_CB_GEN_SETIDLE 0x0011
-#define AIM_CB_GEN_MIGRATIONREQ 0x0012
-#define AIM_CB_GEN_MOTD 0x0013
-#define AIM_CB_GEN_SETPRIVFLAGS 0x0014
-#define AIM_CB_GEN_WELLKNOWNURL 0x0015
-#define AIM_CB_GEN_NOP 0x0016
-#define AIM_CB_GEN_DEFAULT 0xffff
-
-/*
- * SNAC Family: Location Services.
- */
-#define AIM_CB_LOC_ERROR 0x0001
-#define AIM_CB_LOC_REQRIGHTS 0x0002
-#define AIM_CB_LOC_RIGHTSINFO 0x0003
-#define AIM_CB_LOC_SETUSERINFO 0x0004
-#define AIM_CB_LOC_REQUSERINFO 0x0005
-#define AIM_CB_LOC_USERINFO 0x0006
-#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
-#define AIM_CB_LOC_WATCHERNOT 0x0008
-#define AIM_CB_LOC_GOTINFOBLOCK 0xfffd
-#define AIM_CB_LOC_REQUESTINFOTIMEOUT 0xfffe
-#define AIM_CB_LOC_DEFAULT 0xffff
-
-/*
- * SNAC Family: Buddy List Management Services.
- */
-#define AIM_CB_BUD_ERROR 0x0001
-#define AIM_CB_BUD_REQRIGHTS 0x0002
-#define AIM_CB_BUD_RIGHTSINFO 0x0003
-#define AIM_CB_BUD_ADDBUDDY 0x0004
-#define AIM_CB_BUD_REMBUDDY 0x0005
-#define AIM_CB_BUD_REJECT 0x000a
-#define AIM_CB_BUD_ONCOMING 0x000b
-#define AIM_CB_BUD_OFFGOING 0x000c
-#define AIM_CB_BUD_DEFAULT 0xffff
-
-/*
- * SNAC Family: Messaging Services.
- */
-#define AIM_CB_MSG_ERROR 0x0001
-#define AIM_CB_MSG_PARAMINFO 0x0005
-#define AIM_CB_MSG_INCOMING 0x0007
-#define AIM_CB_MSG_EVIL 0x0009
-#define AIM_CB_MSG_MISSEDCALL 0x000a
-#define AIM_CB_MSG_CLIENTAUTORESP 0x000b
-#define AIM_CB_MSG_ACK 0x000c
-#define AIM_CB_MSG_MTN 0x0014
-#define AIM_CB_MSG_DEFAULT 0xffff
-
-/*
- * SNAC Family: Advertisement Services
- */
-#define AIM_CB_ADS_ERROR 0x0001
-#define AIM_CB_ADS_DEFAULT 0xffff
-
-/*
- * SNAC Family: Invitation Services.
- */
-#define AIM_CB_INV_ERROR 0x0001
-#define AIM_CB_INV_DEFAULT 0xffff
-
-/*
- * SNAC Family: Administrative Services.
- */
-#define AIM_CB_ADM_ERROR 0x0001
-#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005
-#define AIM_CB_ADM_DEFAULT 0xffff
-
-/*
- * SNAC Family: Popup Messages
- */
-#define AIM_CB_POP_ERROR 0x0001
-#define AIM_CB_POP_DEFAULT 0xffff
-
-/*
- * SNAC Family: Misc BOS Services.
- */
-#define AIM_CB_BOS_ERROR 0x0001
-#define AIM_CB_BOS_RIGHTSQUERY 0x0002
-#define AIM_CB_BOS_RIGHTS 0x0003
-#define AIM_CB_BOS_DEFAULT 0xffff
-
-/*
- * SNAC Family: User Lookup Services
- */
-#define AIM_CB_LOK_ERROR 0x0001
-#define AIM_CB_LOK_DEFAULT 0xffff
-
-/*
- * SNAC Family: User Status Services
- */
-#define AIM_CB_STS_ERROR 0x0001
-#define AIM_CB_STS_SETREPORTINTERVAL 0x0002
-#define AIM_CB_STS_REPORTACK 0x0004
-#define AIM_CB_STS_DEFAULT 0xffff
-
-/*
- * SNAC Family: Translation Services
- */
-#define AIM_CB_TRN_ERROR 0x0001
-#define AIM_CB_TRN_DEFAULT 0xffff
-
-/*
- * SNAC Family: Chat Navigation Services
- */
-#define AIM_CB_CTN_ERROR 0x0001
-#define AIM_CB_CTN_CREATE 0x0008
-#define AIM_CB_CTN_INFO 0x0009
-#define AIM_CB_CTN_DEFAULT 0xffff
-
-/*
- * SNAC Family: Chat Services
- */
-#define AIM_CB_CHT_ERROR 0x0001
-#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002
-#define AIM_CB_CHT_USERJOIN 0x0003
-#define AIM_CB_CHT_USERLEAVE 0x0004
-#define AIM_CB_CHT_OUTGOINGMSG 0x0005
-#define AIM_CB_CHT_INCOMINGMSG 0x0006
-#define AIM_CB_CHT_DEFAULT 0xffff
-
-/*
- * SNAC Family: "New" Search
- */
-#define AIM_CB_SCH_ERROR 0x0001
-#define AIM_CB_SCH_SEARCH 0x0002
-#define AIM_CB_SCH_RESULTS 0x0003
-
-/*
- * SNAC Family: Buddy icons
- */
-#define AIM_CB_ICO_ERROR 0x0001
-#define AIM_CB_ICO_REQUEST 0x0004
-#define AIM_CB_ICO_RESPONSE 0x0005
-
-/*
- * SNAC Family: ICQ
- *
- * Most of these are actually special.
- */
-#define AIM_CB_ICQ_ERROR 0x0001
-#define AIM_CB_ICQ_OFFLINEMSG 0x00f0
-#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1
-#define AIM_CB_ICQ_INFO 0x00f2
-#define AIM_CB_ICQ_ALIAS 0x00f3
-#define AIM_CB_ICQ_DEFAULT 0xffff
-
-/*
- * SNAC Family: Server-Stored Buddy Lists
- */
-#define AIM_CB_SSI_ERROR 0x0001
-#define AIM_CB_SSI_REQRIGHTS 0x0002
-#define AIM_CB_SSI_RIGHTSINFO 0x0003
-#define AIM_CB_SSI_REQDATA 0x0004
-#define AIM_CB_SSI_REQIFCHANGED 0x0005
-#define AIM_CB_SSI_LIST 0x0006
-#define AIM_CB_SSI_ACTIVATE 0x0007
-#define AIM_CB_SSI_ADD 0x0008
-#define AIM_CB_SSI_MOD 0x0009
-#define AIM_CB_SSI_DEL 0x000A
-#define AIM_CB_SSI_SRVACK 0x000E
-#define AIM_CB_SSI_NOLIST 0x000F
-#define AIM_CB_SSI_EDITSTART 0x0011
-#define AIM_CB_SSI_EDITSTOP 0x0012
-#define AIM_CB_SSI_SENDAUTH 0x0014
-#define AIM_CB_SSI_RECVAUTH 0x0015
-#define AIM_CB_SSI_SENDAUTHREQ 0x0018
-#define AIM_CB_SSI_RECVAUTHREQ 0x0019
-#define AIM_CB_SSI_SENDAUTHREP 0x001a
-#define AIM_CB_SSI_RECVAUTHREP 0x001b
-#define AIM_CB_SSI_ADDED 0x001c
-
-/*
- * SNAC Family: Authorizer
- *
- * Used only in protocol versions three and above.
- *
- */
-#define AIM_CB_ATH_ERROR 0x0001
-#define AIM_CB_ATH_LOGINREQEST 0x0002
-#define AIM_CB_ATH_LOGINRESPONSE 0x0003
-#define AIM_CB_ATH_AUTHREQ 0x0006
-#define AIM_CB_ATH_AUTHRESPONSE 0x0007
-#define AIM_CB_ATH_SECURID_REQUEST 0x000a
-#define AIM_CB_ATH_SECURID_RESPONSE 0x000b
-
-/*
- * SNAC Family: Email
- *
- * Used for getting information on the email address
- * associated with your screen name.
- *
- */
-#define AIM_CB_EML_ERROR 0x0001
-#define AIM_CB_EML_SENDCOOKIES 0x0006
-#define AIM_CB_EML_MAILSTATUS 0x0007
-#define AIM_CB_EML_INIT 0x0016
-
-/*
- * OFT Services
- *
- * For all of the above #defines, the number is the subtype 
- * of the SNAC.  For OFT #defines, the number is the 
- * "hdrtype" which comes after the magic string and OFT 
- * packet length.
- *
- * I'm pretty sure the ODC ones are arbitrary right now, 
- * that should be changed.
- */
-#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001	/* connect request -- actually an OSCAR CAP */
-#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
-#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
-#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
-#define AIM_CB_OFT_DIRECTIM_ESTABLISHED 0x0005
-
-#define AIM_CB_OFT_PROMPT 0x0101		/* "I am going to send you this file, is that ok?" */
-#define AIM_CB_OFT_RESUMESOMETHING 0x0106	/* I really don't know */
-#define AIM_CB_OFT_ACK 0x0202			/* "Yes, it is ok for you to send me that file" */
-#define AIM_CB_OFT_DONE 0x0204			/* "I received that file with no problems, thanks a bunch" */
-#define AIM_CB_OFT_RESUME 0x0205		/* Resume transferring, sent by whoever paused? */
-#define AIM_CB_OFT_RESUMEACK 0x0207		/* Not really sure */
-
-#define AIM_CB_OFT_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */
-#define AIM_CB_OFT_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */
-#define AIM_CB_OFT_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */
-#define AIM_CB_OFT_GETFILE_ACKLISTING 0x120b	/* "I received the listing.txt file successfully" */
-#define AIM_CB_OFT_GETFILE_REQUESTFILE 0x120c	/* "Please send me this file" */
-
-#define AIM_CB_OFT_ESTABLISHED 0xFFFF		/* connection to buddy initiated */
-
-/*
- * SNAC Family: Internal Messages
- *
- * This isn't truly a SNAC family either, but using
- * these, we can integrated non-SNAC services into
- * the SNAC-centered libfaim callback structure.
- *
- */
-#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
-#define AIM_CB_SPECIAL_AUTHOTHER 0x0002
-#define AIM_CB_SPECIAL_CONNERR 0x0003
-#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
-#define AIM_CB_SPECIAL_FLAPVER 0x0005
-#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
-#define AIM_CB_SPECIAL_IMAGETRANSFER 0x0007
-#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008
-#define AIM_CB_SPECIAL_CONNDEAD 0x0009
-#define AIM_CB_SPECIAL_UNKNOWN 0xffff
-#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
-
-/* SNAC flags */
-#define AIM_SNACFLAGS_DESTRUCTOR 0x0001
-
-#endif/*__AIM_CBTYPES_H__ */
--- a/src/protocols/oscar/aim_internal.h	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-/*
- * aim_internal.h -- prototypes/structs for the guts of libfaim
- *
- */
-
-#ifdef FAIM_INTERNAL
-#ifndef __AIM_INTERNAL_H__
-#define __AIM_INTERNAL_H__ 1
-
-typedef struct {
-	fu16_t family;
-	fu16_t subtype;
-	fu16_t flags;
-	fu32_t id;
-} aim_modsnac_t;
-
-#define AIM_MODULENAME_MAXLEN 16
-#define AIM_MODFLAG_MULTIFAMILY 0x0001
-typedef struct aim_module_s {
-	fu16_t family;
-	fu16_t version;
-	fu16_t toolid;
-	fu16_t toolversion;
-	fu16_t flags;
-	char name[AIM_MODULENAME_MAXLEN+1];
-	int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs);
-
-	void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod);
-	void *priv;
-	struct aim_module_s *next;
-} aim_module_t;
-
-faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *));
-faim_internal void aim__shutdownmodules(aim_session_t *sess);
-faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group);
-faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name);
-
-faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod);
-faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod);
-
-faim_internal int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype);
-faim_internal int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype);
-faim_internal int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *);
-faim_internal int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *);
-
-#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset)
-
-/* bstream.c */
-faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len);
-faim_internal int aim_bstream_empty(aim_bstream_t *bs);
-faim_internal int aim_bstream_curpos(aim_bstream_t *bs);
-faim_internal int aim_bstream_setpos(aim_bstream_t *bs, unsigned int off);
-faim_internal void aim_bstream_rewind(aim_bstream_t *bs);
-faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n);
-faim_internal fu8_t aimbs_get8(aim_bstream_t *bs);
-faim_internal fu16_t aimbs_get16(aim_bstream_t *bs);
-faim_internal fu32_t aimbs_get32(aim_bstream_t *bs);
-faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs);
-faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs);
-faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs);
-faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len);
-faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len);
-faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len);
-faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v);
-faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v);
-faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v);
-faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v);
-faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v);
-faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v);
-faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len);
-faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str);
-faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len);
-faim_internal int aimbs_putcaps(aim_bstream_t *bs, fu32_t caps);
-
-/* conn.c */
-faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
-
-/* ft.c */
-faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr);
-
-/* rxhandlers.c */
-faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type);
-faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_frame_t *ptr);
-faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
-faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
-
-/* rxqueue.c */
-faim_internal int aim_recv(int fd, void *buf, size_t count);
-faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count);
-faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
-faim_internal void aim_frame_destroy(aim_frame_t *);
-
-/* txqueue.c */
-faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen);
-faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
-faim_internal int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count);
-faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
-faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);
-
-/*
- * Generic SNAC structure.  Rarely if ever used.
- */
-typedef struct aim_snac_s {
-	aim_snacid_t id;
-	fu16_t family;
-	fu16_t type;
-	fu16_t flags;
-	void *data;
-	time_t issuetime;
-	struct aim_snac_s *next;
-} aim_snac_t;
-
-/* snac.c */
-faim_internal void aim_initsnachash(aim_session_t *sess);
-faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac);
-faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen);
-faim_internal aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id);
-faim_internal int aim_putsnac(aim_bstream_t *, fu16_t family, fu16_t type, fu16_t flags, aim_snacid_t id);
-
-/* Stored in ->priv of the service request SNAC for chats. */
-struct chatsnacinfo {
-	fu16_t exchange;
-	char name[128];
-	fu16_t instance;
-};
-
-/*
- * In SNACland, the terms 'family' and 'group' are synonymous -- the former
- * is my term, the latter is AOL's.
- */
-struct snacgroup {
-	fu16_t group;
-	struct snacgroup *next;
-};
-
-#ifdef FAIM_NEED_CONN_INTERNAL
-struct snacpair {
-	fu16_t group;
-	fu16_t subtype;
-	struct snacpair *next;
-};
-
-struct rateclass {
-	fu16_t classid;
-	fu32_t windowsize;
-	fu32_t clear;
-	fu32_t alert;
-	fu32_t limit;
-	fu32_t disconnect;
-	fu32_t current;
-	fu32_t max;
-	fu8_t unknown[5]; /* only present in versions >= 3 */
-	struct snacpair *members;
-	struct rateclass *next;
-};
-#endif /* FAIM_NEED_CONN_INTERNAL */
-
-/*
- * This is inside every connection.  But it is a void * to anything
- * outside of libfaim.  It should remain that way.  It's called data
- * abstraction.  Maybe you've heard of it.  (Probably not if you're a
- * libfaim user.)
- *
- */
-typedef struct aim_conn_inside_s {
-	struct snacgroup *groups;
-	struct rateclass *rates;
-} aim_conn_inside_t;
-
-faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group);
-
-faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
-faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type);
-faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *, int, void *);
-faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *, const unsigned char *, const int);
-faim_internal int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
-faim_internal int aim_msgcookie_gettype(int reqclass);
-faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie);
-
-/* 0x0002 - locate.c */
-faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn);
-faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len);
-faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len);
-faim_internal void aim_info_free(aim_userinfo_t *);
-faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *);
-faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info);
-
-faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo);
-
-faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
-
-/* These are all handled internally now. */
-faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
-faim_internal int aim_reqrates(aim_session_t *, aim_conn_t *);
-faim_internal int aim_rates_addparam(aim_session_t *, aim_conn_t *);
-faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *);
-
-#endif /* __AIM_INTERNAL_H__ */
-#endif /* FAIM_INTERNAL */
--- a/src/protocols/oscar/auth.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,654 +0,0 @@
-/*
- * Family 0x0017 - Authentication.
- *
- * Deals with the authorizer for SNAC-based login, and also old-style
- * non-SNAC login.
- *
- */
-
-#define FAIM_INTERNAL
-#include "aim.h"
-
-#include "cipher.h"
-
-#include <ctype.h>
-
-#define USE_XOR_FOR_ICQ
-
-#ifdef USE_XOR_FOR_ICQ
-/**
- * Encode a password using old XOR method
- *
- * This takes a const pointer to a (null terminated) string
- * containing the unencoded password.  It also gets passed
- * an already allocated buffer to store the encoded password.
- * This buffer should be the exact length of the password without
- * the null.  The encoded password buffer /is not %NULL terminated/.
- *
- * The encoding_table seems to be a fixed set of values.  We'll
- * hope it doesn't change over time!
- *
- * This is only used for the XOR method, not the better MD5 method.
- *
- * @param password Incoming password.
- * @param encoded Buffer to put encoded password.
- */
-static int aim_encode_password(const char *password, fu8_t *encoded)
-{
-	fu8_t encoding_table[] = {
-#if 0 /* old v1 table */
-		0xf3, 0xb3, 0x6c, 0x99,
-		0x95, 0x3f, 0xac, 0xb6,
-		0xc5, 0xfa, 0x6b, 0x63,
-		0x69, 0x6c, 0xc3, 0x9f
-#else /* v2.1 table, also works for ICQ */
-		0xf3, 0x26, 0x81, 0xc4,
-		0x39, 0x86, 0xdb, 0x92,
-		0x71, 0xa3, 0xb9, 0xe6,
-		0x53, 0x7a, 0x95, 0x7c
-#endif
-	};
-	unsigned int i;
-
-	for (i = 0; i < strlen(password); i++)
-		encoded[i] = (password[i] ^ encoding_table[i]);
-
-	return 0;
-}
-#endif
-
-#ifdef USE_OLD_MD5
-static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest)
-{
-	GaimCipher *cipher;
-	GaimCipherContext *context;
-
-	cipher = gaim_ciphers_find_cipher("md5");
-
-	context = gaim_cipher_context_new(cipher, NULL);
-	gaim_cipher_context_append(context, (const guchar *)key, strlen(key));
-	gaim_cipher_context_append(context, (const guchar *)password, strlen(password));
-	gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
-	gaim_cipher_context_digest(context, 16, digest, NULL);
-	gaim_cipher_context_destroy(context);
-
-	return 0;
-}
-#else
-static int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest)
-{
-	GaimCipher *cipher;
-	GaimCipherContext *context;
-	guchar passdigest[16];
-
-	cipher = gaim_ciphers_find_cipher("md5");
-
-	context = gaim_cipher_context_new(cipher, NULL);
-	gaim_cipher_context_append(context, (const guchar *)password, strlen(password));
-	gaim_cipher_context_digest(context, 16, passdigest, NULL);
-	gaim_cipher_context_destroy(context);
-
-	context = gaim_cipher_context_new(cipher, NULL);
-	gaim_cipher_context_append(context, (const guchar *)key, strlen(key));
-	gaim_cipher_context_append(context, passdigest, 16);
-	gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
-	gaim_cipher_context_digest(context, 16, digest, NULL);
-	gaim_cipher_context_destroy(context);
-
-	return 0;
-}
-#endif
-
-/*
- * The FLAP version is sent by itself at the beginning of authorization
- * connections.  The FLAP version is also sent before the cookie when connecting
- * for other services (BOS, chatnav, chat, etc.).
- */
-faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_frame_t *fr;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4)))
-		return -ENOMEM;
-
-	aimbs_put32(&fr->data, 0x00000001);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * This just pushes the passed cookie onto the passed connection, without
- * the SNAC header or any of that.
- *
- * Very commonly used, as every connection except auth will require this to
- * be the first thing you send.
- *
- */
-faim_export int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const fu16_t length, const fu8_t *chipsahoy)
-{
-	aim_frame_t *fr;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4+2+2+length)))
-		return -ENOMEM;
-
-	aimbs_put32(&fr->data, 0x00000001);
-	aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy);
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-#ifdef USE_XOR_FOR_ICQ
-/*
- * Part two of the ICQ hack.  Note the ignoring of the key.
- */
-static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci)
-{
-	aim_frame_t *fr;
-	aim_tlvlist_t *tl = NULL;
-	int passwdlen;
-	fu8_t *password_encoded;
-
-	passwdlen = strlen(password);
-	if (!(password_encoded = (fu8_t *)malloc(passwdlen+1)))
-		return -ENOMEM;
-	if (passwdlen > MAXICQPASSLEN)
-		passwdlen = MAXICQPASSLEN;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) {
-		free(password_encoded);
-		return -ENOMEM;
-	}
-
-	aim_encode_password(password, password_encoded);
-
-	aimbs_put32(&fr->data, 0x00000001); /* FLAP Version */
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
-	aim_tlvlist_add_raw(&tl, 0x0002, passwdlen, password_encoded);
-
-	if (ci->clientstring)
-		aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring);
-	aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid);
-	aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major);
-	aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor);
-	aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point);
-	aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build);
-	aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib); /* distribution chan */
-	aim_tlvlist_add_str(&tl, 0x000f, ci->lang);
-	aim_tlvlist_add_str(&tl, 0x000e, ci->country);
-
-	aim_tlvlist_write(&fr->data, &tl);
-
-	free(password_encoded);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-#endif
-
-/*
- * Subtype 0x0002
- *
- * This is the initial login request packet.
- *
- * NOTE!! If you want/need to make use of the aim_sendmemblock() function,
- * then the client information you send here must exactly match the
- * executable that you're pulling the data from.
- *
- * Java AIM 1.1.19:
- *   clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
- *   clientid = 0x0001
- *   major  = 0x0001
- *   minor  = 0x0001
- *   point = (not sent)
- *   build  = 0x0013
- *   unknown= (not sent)
- *   
- * AIM for Linux 1.1.112:
- *   clientstring = "AOL Instant Messenger (SM)"
- *   clientid = 0x1d09
- *   major  = 0x0001
- *   minor  = 0x0001
- *   point = 0x0001
- *   build  = 0x0070
- *   unknown= 0x0000008b
- *   serverstore = 0x01
- *
- */
-faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key)
-{
-	aim_frame_t *fr;
-	aim_tlvlist_t *tl = NULL;
-	fu8_t digest[16];
-	aim_snacid_t snacid;
-
-	if (!ci || !sn || !password)
-		return -EINVAL;
-
-#ifdef USE_XOR_FOR_ICQ
-	/* If we're signing on an ICQ account then use the older, XOR login method */
-	if (isdigit(sn[0]))
-		return goddamnicq2(sess, conn, sn, password, ci);
-#endif
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
-
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
-
-	/* Truncate ICQ passwords, if necessary */
-	if (isdigit(sn[0]) && (strlen(password) > MAXICQPASSLEN))
-	{
-		char truncated[MAXICQPASSLEN + 1];
-		strncpy(truncated, password, MAXICQPASSLEN);
-		truncated[MAXICQPASSLEN] = 0;
-		aim_encode_password_md5(truncated, key, digest);
-	}
-	else
-	{
-		aim_encode_password_md5(password, key, digest);
-	}
-
-	aim_tlvlist_add_raw(&tl, 0x0025, 16, digest);
-
-#ifndef USE_OLD_MD5
-	aim_tlvlist_add_noval(&tl, 0x004c);
-#endif
-
-	if (ci->clientstring)
-		aim_tlvlist_add_str(&tl, 0x0003, ci->clientstring);
-	aim_tlvlist_add_16(&tl, 0x0016, (fu16_t)ci->clientid);
-	aim_tlvlist_add_16(&tl, 0x0017, (fu16_t)ci->major);
-	aim_tlvlist_add_16(&tl, 0x0018, (fu16_t)ci->minor);
-	aim_tlvlist_add_16(&tl, 0x0019, (fu16_t)ci->point);
-	aim_tlvlist_add_16(&tl, 0x001a, (fu16_t)ci->build);
-	aim_tlvlist_add_32(&tl, 0x0014, (fu32_t)ci->distrib);
-	aim_tlvlist_add_str(&tl, 0x000f, ci->lang);
-	aim_tlvlist_add_str(&tl, 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_write(&fr->data, &tl);
-
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * This is sent back as a general response to the login command.
- * It can be either an error or a success, depending on the
- * presence of certain TLVs.  
- *
- * The client should check the value passed as errorcode. If
- * its nonzero, there was an error.
- */
-static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_tlvlist_t *tlvlist;
-	aim_rxcallback_t userfunc;
-	struct aim_authresp_info *info;
-	int ret = 0;
-
-	info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info));
-	memset(info, 0, sizeof(struct aim_authresp_info));
-
-	/*
-	 * Read block of TLVs.  All further data is derived
-	 * from what is parsed here.
-	 */
-	tlvlist = aim_tlvlist_read(bs);
-
-	/*
-	 * No matter what, we should have a screen name.
-	 */
-	memset(sess->sn, 0, sizeof(sess->sn));
-	if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
-		info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1);
-		strncpy(sess->sn, info->sn, sizeof(sess->sn));
-	}
-
-	/*
-	 * Check for an error code.  If so, we should also
-	 * have an error url.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0008, 1))
-		info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
-		info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1);
-
-	/*
-	 * BOS server address.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
-		info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
-
-	/*
-	 * Authorization cookie.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
-		aim_tlv_t *tmptlv;
-
-		tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
-
-		info->cookielen = tmptlv->length;
-		info->cookie = tmptlv->value;
-	}
-
-	/*
-	 * The email address attached to this account
-	 *   Not available for ICQ or @mac.com logins.
-	 *   If you receive this TLV, then you are allowed to use 
-	 *   family 0x0018 to check the status of your email.
-	 * XXX - Not really true!
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0011, 1))
-		info->email = aim_tlv_getstr(tlvlist, 0x0011, 1);
-
-	/*
-	 * The registration status.  (Not real sure what it means.)
-	 *   Not available for ICQ or @mac.com logins.
-	 *
-	 *   1 = No disclosure
-	 *   2 = Limited disclosure
-	 *   3 = Full disclosure
-	 *
-	 * This has to do with whether your email address is available
-	 * to other users or not.  AFAIK, this feature is no longer used.
-	 *
-	 * Means you can use the admin family? (0x0007)
-	 *
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0013, 1))
-		info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1);
-
-	if (aim_tlv_gettlv(tlvlist, 0x0040, 1))
-		info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0041, 1))
-		info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0042, 1))
-		info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0043, 1))
-		info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1);
-
-#if 0
-	if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) {
-		/* beta serial */
-	}
-#endif
-
-	if (aim_tlv_gettlv(tlvlist, 0x0044, 1))
-		info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0045, 1))
-		info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0046, 1))
-		info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1);
-	if (aim_tlv_gettlv(tlvlist, 0x0047, 1))
-		info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1);
-
-#if 0
-	if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) {
-		/* lastest release serial */
-	}
-#endif
-
-	/*
-	 * URL to change password.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0054, 1))
-		info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1);
-
-#if 0
-	/*
-	 * Unknown.  Seen on an @mac.com screen name with value of 0x003f
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) {
-		/* Unhandled */
-	}
-#endif
-
-	sess->authinfo = info;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
-		ret = userfunc(sess, rx, info);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-#ifdef USE_XOR_FOR_ICQ
-/*
- * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client
- *
- * This is a bit confusing.
- *
- * Normal SNAC login goes like this:
- *   - connect
- *   - server sends flap version
- *   - client sends flap version
- *   - client sends screen name (17/6)
- *   - server sends hash key (17/7)
- *   - client sends auth request (17/2 -- aim_send_login)
- *   - server yells
- *
- * XOR login (for ICQ) goes like this:
- *   - connect
- *   - server sends flap version
- *   - client sends auth request which contains flap version (aim_send_login)
- *   - server yells
- *
- * For the client API, we make them implement the most complicated version,
- * and for the simpler version, we fake it and make it look like the more
- * complicated process.
- *
- * This is done by giving the client a faked key, just so we can convince
- * them to call aim_send_login right away, which will detect the session
- * flag that says this is XOR login and ignore the key, sending an ICQ
- * login request instead of the normal SNAC one.
- *
- * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
- *
- * XXX This may cause problems if the client relies on callbacks only
- * being called from the context of aim_rxdispatch()...
- *
- */
-static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t fr;
-	aim_rxcallback_t userfunc;
-
-	fr.conn = conn;
-
-	if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007)))
-		userfunc(sess, &fr, "");
-
-	return 0;
-}
-#endif
-
-/*
- * Subtype 0x0006
- *
- * In AIM 3.5 protocol, the first stage of login is to request login from the 
- * Authorizer, passing it the screen name for verification.  If the name is 
- * invalid, a 0017/0003 is spit back, with the standard error contents.  If 
- * valid, a 0017/0007 comes back, which is the signal to send it the main 
- * login command (0017/0002). 
- *
- */
-faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !conn || !sn)
-		return -EINVAL;
-
-#ifdef USE_XOR_FOR_ICQ
-	if (isdigit(sn[0]))
-		return goddamnicq(sess, conn, sn);
-#endif
-
-	aim_sendflapver(sess, conn);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)+8 )))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid);
-
-	aim_tlvlist_add_str(&tl, 0x0001, sn);
-
-	/* Tell the server we support SecurID logins. */
-	aim_tlvlist_add_noval(&tl, 0x004b);
-
-	/* Unknown.  Sent in recent WinAIM clients.*/
-	aim_tlvlist_add_noval(&tl, 0x005a);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0007
- *
- * Middle handler for 0017/0007 SNACs.  Contains the auth key prefixed
- * by only its length in a two byte word.
- *
- * Calls the client, which should then use the value to call aim_send_login.
- *
- */
-static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int keylen, ret = 1;
-	aim_rxcallback_t userfunc;
-	char *keystr;
-
-	keylen = aimbs_get16(bs);
-	keystr = aimbs_getstr(bs, keylen);
-
-	/* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register 
-	 * for the netscape network.  This SNAC had a type 0x0058 TLV with length 10.  
-	 * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, keystr);
-
-	free(keystr);
-
-	return ret;
-}
-
-/**
- * Subtype 0x000a
- *
- * Receive SecurID request.
- */
-static int got_securid_request(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx);
-
-	return ret;
-}
-
-/**
- * Subtype 0x000b
- *
- * Send SecurID response.
- */
-faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int len;
-
-	if (!sess || !(conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH)) || !securid)
-		return -EINVAL;
-
-	len = strlen(securid);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+len)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_RESPONSE, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_RESPONSE, 0x0000, 0);
-
-	aimbs_put16(&fr->data, len);
-	aimbs_putstr(&fr->data, securid);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-static void auth_shutdown(aim_session_t *sess, aim_module_t *mod)
-{
-	if (sess->authinfo) {
-		free(sess->authinfo->sn);
-		free(sess->authinfo->bosip);
-		free(sess->authinfo->errorurl);
-		free(sess->authinfo->email);
-		free(sess->authinfo->chpassurl);
-		free(sess->authinfo->latestrelease.name);
-		free(sess->authinfo->latestrelease.url);
-		free(sess->authinfo->latestrelease.info);
-		free(sess->authinfo->latestbeta.name);
-		free(sess->authinfo->latestbeta.url);
-		free(sess->authinfo->latestbeta.info);
-		free(sess->authinfo);
-	}
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return parse(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0007)
-		return keyparse(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000a)
-		return got_securid_request(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0017;
-	mod->version = 0x0000;
-	mod->flags = 0;
-	strncpy(mod->name, "auth", sizeof(mod->name));
-	mod->snachandler = snachandler;
-	mod->shutdown = auth_shutdown;
-
-	return 0;
-}
--- a/src/protocols/oscar/bart.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * Family 0x0010 - Server stored buddy art
- *
- * Used for storing and retrieving your cute little buddy icon 
- * from the AIM servers.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/**
- * Subtype 0x0002 - Upload your icon.
- *
- * @param sess The oscar session.
- * @param conn The icon connection for this session.
- * @param icon The raw data of the icon image file.
- * @param iconlen Length of the raw data of the icon image file.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen)))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid);
-
-	/* The reference number for the icon */
-	aimbs_put16(&fr->data, 1);
-
-	/* The icon */
-	aimbs_put16(&fr->data, iconlen);
-	aimbs_putraw(&fr->data, icon, iconlen);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0003 - Acknowledgement for uploading a buddy icon.
- *
- * You get this honky after you upload a buddy icon.
- */
-static int uploadack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t something, somethingelse;
-	fu8_t onemorething;
-
-	something = aimbs_get16(bs);
-	somethingelse = aimbs_get16(bs);
-	onemorething = aimbs_get8(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx);
-
-	return ret;
-}
-
-/**
- * Subtype 0x0004 - Request someone's icon.
- *
- * @param sess The oscar session.
- * @param conn The icon connection for this session.
- * @param sn The screen name of the person who's icon you are requesting.
- * @param iconcsum The MD5 checksum of the icon you are requesting.
- * @param iconcsumlen Length of the MD5 checksum given above.  Should be 10 bytes.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_bart_request(aim_session_t *sess, const char *sn, fu8_t iconcsumtype, const fu8_t *iconcsum, fu16_t iconcsumlen)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen)))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid);
-
-	/* Screen name */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	/* Some numbers.  You like numbers, right? */
-	aimbs_put8(&fr->data, 0x01);
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put8(&fr->data, iconcsumtype);
-
-	/* Icon string */
-	aimbs_put8(&fr->data, iconcsumlen);
-	aimbs_putraw(&fr->data, iconcsum, iconcsumlen);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0005 - Receive a buddy icon.
- *
- * This is sent in response to a buddy icon request.
- */
-static int parseicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	char *sn;
-	fu16_t flags, iconlen;
-	fu8_t iconcsumtype, iconcsumlen, *iconcsum, *icon;
-
-	sn = aimbs_getstr(bs, aimbs_get8(bs));
-	flags = aimbs_get16(bs);
-	iconcsumtype = aimbs_get8(bs);
-	iconcsumlen = aimbs_get8(bs);
-	iconcsum = aimbs_getraw(bs, iconcsumlen);
-	iconlen = aimbs_get16(bs);
-	icon = aimbs_getraw(bs, iconlen);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen);
-
-	free(sn);
-	free(iconcsum);
-	free(icon);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return uploadack(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0005)
-		return parseicon(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0010;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "bart", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/bos.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-/*
- * Family 0x0009 - Basic Oscar Service.
- *
- * The functionality of this family has been replaced by SSI.
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-#include <string.h>
-
-/* Subtype 0x0002 - Request BOS rights. */
-faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n_snacid(sess, conn, 0x0009, 0x0002);
-}
-
-/* Subtype 0x0003 - BOS Rights. */
-static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
-	fu16_t maxpermits = 0, maxdenies = 0;
-	int ret = 0;
-
-	/* 
-	 * TLVs follow 
-	 */
-	tlvlist = aim_tlvlist_read(bs);
-
-	/*
-	 * TLV type 0x0001: Maximum number of buddies on permit list.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
-		maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1);
-
-	/*
-	 * TLV type 0x0002: Maximum number of buddies on deny list.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) 
-		maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, maxpermits, maxdenies);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;  
-}
-
-/* 
- * Subtype 0x0004 - Set group permission mask.
- *
- * Normally 0x1f (all classes).
- *
- * The group permission mask allows you to keep users of a certain
- * class or classes from talking to you.  The mask should be
- * a bitwise OR of all the user classes you want to see you.
- *
- */
-faim_export int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, fu32_t mask)
-{
-	return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
-}
-
-/*
- * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists.
- *
- * Changes your visibility depending on changetype:
- *
- *  AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
- *  AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
- *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
- *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
- *
- * list should be a list of 
- * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
- *
- * Equivelents to options in WinAIM:
- *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
- *      with only your name on it.
- *   - Allow only users on my Buddy List: Send an 
- *      AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
- *      buddy list
- *   - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD 
- *      with everyone listed that you want to see you.
- *   - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only 
- *      yourself in the list
- *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
- *      the list of users to be blocked
- *
- * XXX ye gods.
- */
-faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist)
-{
-	aim_frame_t *fr;
-	int packlen = 0;
-	fu16_t subtype;
-	char *localcpy = NULL, *tmpptr = NULL;
-	int i;
-	int listcount;
-	aim_snacid_t snacid;
-
-	if (!denylist)
-		return -EINVAL;
-
-	if (changetype == AIM_VISIBILITYCHANGE_PERMITADD)
-		subtype = 0x05;
-	else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE)
-		subtype = 0x06;
-	else if (changetype == AIM_VISIBILITYCHANGE_DENYADD)
-		subtype = 0x07;
-	else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE)
-		subtype = 0x08;
-	else
-		return -EINVAL;
-
-	localcpy = strdup(denylist);
-
-	listcount = aimutil_itemcnt(localcpy, '&');
-	packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) {
-		free(localcpy);
-		return -ENOMEM;
-	}
-
-	snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid);
-
-	for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
-		tmpptr = aimutil_itemindex(localcpy, i, '&');
-
-		aimbs_put8(&fr->data, strlen(tmpptr));
-		aimbs_putstr(&fr->data, tmpptr);
-
-		free(tmpptr);
-	}
-	free(localcpy);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return rights(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0009;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "bos", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/bstream.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/bstream.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,13 +1,30 @@
 /*
- * bstream.c
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
  *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
  * This file contains all functions needed to use bstreams.
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
 
-faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len)
+faim_internal int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len)
 {
 
 	if (!bs)
@@ -65,7 +82,7 @@
 	return n;
 }
 
-faim_internal fu8_t aimbs_get8(aim_bstream_t *bs)
+faim_internal guint8 aimbs_get8(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 1)
@@ -76,7 +93,7 @@
 	return aimutil_get8(bs->data + bs->offset - 1);
 }
 
-faim_internal fu16_t aimbs_get16(aim_bstream_t *bs)
+faim_internal guint16 aimbs_get16(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 2)
@@ -87,7 +104,7 @@
 	return aimutil_get16(bs->data + bs->offset - 2);
 }
 
-faim_internal fu32_t aimbs_get32(aim_bstream_t *bs)
+faim_internal guint32 aimbs_get32(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 4)
@@ -98,7 +115,7 @@
 	return aimutil_get32(bs->data + bs->offset - 4);
 }
 
-faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs)
+faim_internal guint8 aimbs_getle8(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 1)
@@ -109,7 +126,7 @@
 	return aimutil_getle8(bs->data + bs->offset - 1);
 }
 
-faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs)
+faim_internal guint16 aimbs_getle16(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 2)
@@ -120,7 +137,7 @@
 	return aimutil_getle16(bs->data + bs->offset - 2);
 }
 
-faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs)
+faim_internal guint32 aimbs_getle32(aim_bstream_t *bs)
 {
 
 	if (aim_bstream_empty(bs) < 4)
@@ -131,7 +148,7 @@
 	return aimutil_getle32(bs->data + bs->offset - 4);
 }
 
-faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len)
+faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len)
 {
 
 	if (aim_bstream_empty(bs) < len)
@@ -143,9 +160,9 @@
 	return len;
 }
 
-faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len)
+faim_internal guint8 *aimbs_getraw(aim_bstream_t *bs, int len)
 {
-	fu8_t *ob;
+	guint8 *ob;
 
 	if (!(ob = malloc(len)))
 		return NULL;
@@ -165,7 +182,7 @@
 	if (!(ob = malloc(len + 1)))
 		return NULL;
 
-	if (aimbs_getrawbuf(bs, (fu8_t *)ob, len) < len) {
+	if (aimbs_getrawbuf(bs, (guint8 *)ob, len) < len) {
 		free(ob);
 		return NULL;
 	}
@@ -175,7 +192,7 @@
 	return ob;
 }
 
-faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v)
+faim_internal int aimbs_put8(aim_bstream_t *bs, guint8 v)
 {
 
 	if (aim_bstream_empty(bs) < 1)
@@ -186,7 +203,7 @@
 	return 1;
 }
 
-faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v)
+faim_internal int aimbs_put16(aim_bstream_t *bs, guint16 v)
 {
 
 	if (aim_bstream_empty(bs) < 2)
@@ -197,7 +214,7 @@
 	return 2;
 }
 
-faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v)
+faim_internal int aimbs_put32(aim_bstream_t *bs, guint32 v)
 {
 
 	if (aim_bstream_empty(bs) < 4)
@@ -208,7 +225,7 @@
 	return 1;
 }
 
-faim_internal int aimbs_putle8(aim_bstream_t *bs, fu8_t v)
+faim_internal int aimbs_putle8(aim_bstream_t *bs, guint8 v)
 {
 
 	if (aim_bstream_empty(bs) < 1)
@@ -219,7 +236,7 @@
 	return 1;
 }
 
-faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v)
+faim_internal int aimbs_putle16(aim_bstream_t *bs, guint16 v)
 {
 
 	if (aim_bstream_empty(bs) < 2)
@@ -230,7 +247,7 @@
 	return 2;
 }
 
-faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v)
+faim_internal int aimbs_putle32(aim_bstream_t *bs, guint32 v)
 {
 
 	if (aim_bstream_empty(bs) < 4)
@@ -242,7 +259,7 @@
 }
 
 
-faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len)
+faim_internal int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len)
 {
 
 	if (aim_bstream_empty(bs) < len)
@@ -256,7 +273,7 @@
 
 faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str)
 {
-	return aimbs_putraw(bs, (fu8_t *)str, strlen(str));
+	return aimbs_putraw(bs, (guint8 *)str, strlen(str));
 }
 
 faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len)
--- a/src/protocols/oscar/buddylist.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-/*
- * Family 0x0003 - Old-style Buddylist Management (non-SSI).
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-#include <string.h>
-
-/*
- * Subtype 0x0002 - Request rights.
- *
- * Request Buddy List rights.
- *
- */
-faim_export int aim_buddylist_reqrights(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n_snacid(sess, conn, 0x0003, 0x0002);
-}
-
-/*
- * Subtype 0x0003 - Rights.
- *
- */
-static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
-	fu16_t maxbuddies = 0, maxwatchers = 0;
-	int ret = 0;
-
-	/* 
-	 * TLVs follow 
-	 */
-	tlvlist = aim_tlvlist_read(bs);
-
-	/*
-	 * TLV type 0x0001: Maximum number of buddies.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
-		maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1);
-
-	/*
-	 * TLV type 0x0002: Maximum number of watchers.
-	 *
-	 * Watchers are other users who have you on their buddy
-	 * list.  (This is called the "reverse list" by a certain
-	 * other IM protocol.)
-	 * 
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
-		maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1);
-
-	/*
-	 * TLV type 0x0003: Unknown.
-	 *
-	 * ICQ only?
-	 */
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, maxbuddies, maxwatchers);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0004 - Add buddy to list.
- *
- * Adds a single buddy to your buddy list after login.
- * XXX This should just be an extension of setbuddylist()
- *
- */
-faim_export int aim_buddylist_addbuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sn || !strlen(sn))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
-	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
-
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0004 - Add multiple buddies to your buddy list.
- *
- * This just builds the "set buddy list" command then queues it.
- *
- * buddy_list = "Screen Name One&ScreenNameTwo&";
- *
- * XXX Clean this up.  
- *
- */
-faim_export int aim_buddylist_set(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int len = 0;
-	char *localcpy = NULL;
-	char *tmpptr = NULL;
-
-	if (!buddy_list || !(localcpy = strdup(buddy_list))) 
-		return -EINVAL;
-
-	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
-		gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
-		len += 1 + strlen(tmpptr);
-		tmpptr = strtok(NULL, "&");
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
-
-	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
-
-	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
-
-		gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
-
-		aimbs_put8(&fr->data, strlen(tmpptr));
-		aimbs_putstr(&fr->data, tmpptr);
-		tmpptr = strtok(NULL, "&");
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	free(localcpy);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0005 - Remove buddy from list.
- *
- * XXX generalise to support removing multiple buddies (basically, its
- * the same as setbuddylist() but with a different snac subtype).
- *
- */
-faim_export int aim_buddylist_removebuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sn || !strlen(sn))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
-	aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid);
-
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* 
- * Subtype 0x000b
- *
- * XXX Why would we send this?
- *
- */
-faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !info)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
-	aim_putuserinfo(&fr->data, info);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* 
- * Subtype 0x000c
- *
- * XXX Why would we send this?
- *
- */
-faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtypes 0x000b and 0x000c - Change in buddy status
- *
- * Oncoming Buddy notifications contain a subset of the
- * user information structure.  It's close enough to run
- * through aim_info_extract() however.
- *
- * Although the offgoing notification contains no information,
- * it is still in a format parsable by aim_info_extract().
- *
- */
-static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_userinfo_t userinfo;
-	aim_rxcallback_t userfunc;
-
-	aim_info_extract(sess, bs, &userinfo);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, &userinfo);
-
-	if (snac->subtype == 0x000b)
-		aim_locate_requestuserinfo(sess, userinfo.sn);
-	aim_info_free(&userinfo);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return rights(sess, mod, rx, snac, bs);
-	else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
-		return buddychange(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0003;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "buddylist", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/chat.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,582 +0,0 @@
-/*
- * Family 0x000e - Routines for the Chat service.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-#include <string.h>
-
-/* Stored in the ->internal of chat connections */
-struct chatconnpriv {
-	fu16_t exchange;
-	char *name;
-	fu16_t instance;
-};
-
-faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
-{
-	struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
-
-	if (ccp)
-		free(ccp->name);
-	free(ccp);
-
-	return;
-}
-
-faim_export char *aim_chat_getname(aim_conn_t *conn)
-{
-	struct chatconnpriv *ccp;
-
-	if (!conn)
-		return NULL;
-
-	if (conn->type != AIM_CONN_TYPE_CHAT)
-		return NULL;
-
-	ccp = (struct chatconnpriv *)conn->internal;
-
-	return ccp->name;
-}
-
-/* XXX get this into conn.c -- evil!! */
-faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
-{
-	aim_conn_t *cur;
-
-	for (cur = sess->connlist; cur; cur = cur->next) {
-		struct chatconnpriv *ccp = (struct chatconnpriv *)cur->internal;
-
-		if (cur->type != AIM_CONN_TYPE_CHAT)
-			continue;
-		if (!cur->internal) {
-			gaim_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
-			continue;
-		}
-
-		if (strcmp(ccp->name, name) == 0)
-			break;
-	}
-
-	return cur;
-}
-
-faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
-{
-	struct chatconnpriv *ccp;
-
-	if (!conn || !roomname)
-		return -EINVAL;
-
-	if (conn->internal)
-		free(conn->internal);
-
-	if (!(ccp = malloc(sizeof(struct chatconnpriv))))
-		return -ENOMEM;
-
-	ccp->exchange = exchange;
-	ccp->name = strdup(roomname);
-	ccp->instance = instance;
-
-	conn->internal = (void *)ccp;
-
-	return 0;
-}
-
-faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
-{
-	int namelen;
-
-	if (!bs || !outinfo)
-		return 0;
-
-	outinfo->exchange = aimbs_get16(bs);
-	namelen = aimbs_get8(bs);
-	outinfo->name = aimbs_getstr(bs, namelen);
-	outinfo->instance = aimbs_get16(bs);
-
-	return 0;
-}
-
-faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name)
-{
-	aim_conn_t *conn;
-
-	if (!(conn = aim_chat_getconn(sess, name)))
-		return -ENOENT;
-
-	aim_conn_close(conn);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0002 - General room information.  Lots of stuff.
- *
- * Values I know are in here but I haven't attached
- * them to any of the 'Unknown's:
- *	- Language (English)
- *
- */
-static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_userinfo_t *userinfo = NULL;
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-	int usercount = 0;
-	fu8_t detaillevel = 0;
-	char *roomname = NULL;
-	struct aim_chat_roominfo roominfo;
-	fu16_t tlvcount = 0;
-	aim_tlvlist_t *tlvlist;
-	char *roomdesc = NULL;
-	fu16_t flags = 0;
-	fu32_t creationtime = 0;
-	fu16_t maxmsglen = 0, maxvisiblemsglen = 0;
-	fu16_t unknown_d2 = 0, unknown_d5 = 0;
-
-	aim_chat_readroominfo(bs, &roominfo);
-
-	detaillevel = aimbs_get8(bs);
-
-	if (detaillevel != 0x02) {
-		gaim_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
-		return 1;
-	}
-
-	tlvcount = aimbs_get16(bs);
-
-	/*
-	 * Everything else are TLVs.
-	 */ 
-	tlvlist = aim_tlvlist_read(bs);
-
-	/*
-	 * TLV type 0x006a is the room name in Human Readable Form.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x006a, 1))
-		roomname = aim_tlv_getstr(tlvlist, 0x006a, 1);
-
-	/*
-	 * Type 0x006f: Number of occupants.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x006f, 1))
-		usercount = aim_tlv_get16(tlvlist, 0x006f, 1);
-
-	/*
-	 * Type 0x0073:  Occupant list.
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) {	
-		int curoccupant = 0;
-		aim_tlv_t *tmptlv;
-		aim_bstream_t occbs;
-
-		tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1);
-
-		/* Allocate enough userinfo structs for all occupants */
-		userinfo = calloc(usercount, sizeof(aim_userinfo_t));
-
-		aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
-
-		while (curoccupant < usercount)
-			aim_info_extract(sess, &occbs, &userinfo[curoccupant++]);
-	}
-
-	/* 
-	 * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00c9, 1))
-		flags = aim_tlv_get16(tlvlist, 0x00c9, 1);
-
-	/* 
-	 * Type 0x00ca: Creation time (4 bytes)
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00ca, 1))
-		creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1);
-
-	/* 
-	 * Type 0x00d1: Maximum Message Length
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00d1, 1))
-		maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
-
-	/* 
-	 * Type 0x00d2: Unknown. (2 bytes)
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00d2, 1))
-		unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1);
-
-	/* 
-	 * Type 0x00d3: Room Description
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00d3, 1))
-		roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1);
-
-#if 0
-	/*
-	 * Type 0x000d4: Unknown (flag only)
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) {
-		/* Unhandled */
-	}
-#endif
-
-	/* 
-	 * Type 0x00d5: Unknown. (1 byte)
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x00d5, 1))
-		unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1);
-
-#if 0
-	/*
-	 * Type 0x00d6: Encoding 1 ("us-ascii")
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) {
-		/* Unhandled */
-	}
-
-	/*
-	 * Type 0x00d7: Language 1 ("en")
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) {
-		/* Unhandled */
-	}
-
-	/*
-	 * Type 0x00d8: Encoding 2 ("us-ascii")
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) {
-		/* Unhandled */
-	}
-	
-	/*
-	 * Type 0x00d9: Language 2 ("en")
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) {
-		/* Unhandled */
-	}
-#endif
-
-	/*
-	 * Type 0x00da: Maximum visible message length
-	 */
-	if (aim_tlv_gettlv(tlvlist, 0x000da, 1))
-		maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
-		ret = userfunc(sess,
-				rx,
-				&roominfo,
-				roomname,
-				usercount,
-				userinfo,
-				roomdesc,
-				flags,
-				creationtime,
-				maxmsglen,
-				unknown_d2,
-				unknown_d5,
-				maxvisiblemsglen);
-	}
-
-	free(roominfo.name);
-
-	while (usercount > 0)
-		aim_info_free(&userinfo[--usercount]);
-
-	free(userinfo);
-	free(roomname);
-	free(roomdesc);
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/* Subtypes 0x0003 and 0x0004 */
-static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_userinfo_t *userinfo = NULL;
-	aim_rxcallback_t userfunc;
-	int curcount = 0, ret = 0;
-
-	while (aim_bstream_empty(bs)) {
-		curcount++;
-		userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t));
-		aim_info_extract(sess, bs, &userinfo[curcount-1]);
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, curcount, userinfo);
-
-	aim_info_free(userinfo);
-	free(userinfo);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0005 - Send a Chat Message.
- *
- * Possible flags:
- *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
- *                                 should be sent to their sender.
- *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
- *                                 (Note that WinAIM does not honor this,
- *                                 and displays the message as normal.)
- *
- * XXX convert this to use tlvchains 
- */
-faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const gchar *msg, int msglen, const char *encoding, const char *language)
-{
-	int i;
-	aim_frame_t *fr;
-	aim_msgcookie_t *cookie;
-	aim_snacid_t snacid;
-	fu8_t ckstr[8];
-	aim_tlvlist_t *otl = NULL, *itl = NULL;
-
-	if (!sess || !conn || !msg || (msglen <= 0))
-		return 0;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
-
-	/*
-	 * Cookie
-	 *
-	 * XXX mkcookie should generate the cookie and cache it in one
-	 * operation to preserve uniqueness.
-	 */
-	for (i = 0; i < 8; i++)
-		ckstr[i] = (fu8_t)rand();
-
-	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
-	cookie->data = NULL; /* XXX store something useful here */
-
-	aim_cachecookie(sess, cookie);
-
-	/* ICBM Header */
-	aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
-	aimbs_put16(&fr->data, 0x0003); /* Channel */
-
-	/*
-	 * Type 1: Flag meaning this message is destined to the room.
-	 */
-	aim_tlvlist_add_noval(&otl, 0x0001);
-
-	/*
-	 * Type 6: Reflect
-	 */
-	if (!(flags & AIM_CHATFLAGS_NOREFLECT))
-		aim_tlvlist_add_noval(&otl, 0x0006);
-
-	/*
-	 * Type 7: Autoresponse
-	 */
-	if (flags & AIM_CHATFLAGS_AWAY)
-		aim_tlvlist_add_noval(&otl, 0x0007);
-
-	/*
-	 * SubTLV: Type 1: Message
-	 */
-	aim_tlvlist_add_raw(&itl, 0x0001, msglen, (guchar *)msg);
-
-	/*
-	 * SubTLV: Type 2: Encoding
-	 */
-	if (encoding != NULL)
-		aim_tlvlist_add_str(&itl, 0x0002, encoding);
-
-	/*
-	 * SubTLV: Type 3: Language
-	 */
-	if (language != NULL)
-		aim_tlvlist_add_str(&itl, 0x0003, language);
-
-	/*
-	 * Type 5: Message block.  Contains more TLVs.
-	 *
-	 * This could include other information... We just
-	 * put in a message TLV however.  
-	 * 
-	 */
-	aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl);
-
-	aim_tlvlist_write(&fr->data, &otl);
-	
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&otl);
-	
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0006
- *
- * We could probably include this in the normal ICBM parsing 
- * code as channel 0x0003, however, since only the start
- * would be the same, we might as well do it here.
- *
- * General outline of this SNAC:
- *   snac
- *   cookie
- *   channel id
- *   tlvlist
- *     unknown
- *     source user info
- *       name
- *       evility
- *       userinfo tlvs
- *         online time
- *         etc
- *     message metatlv
- *       message tlv
- *         message string
- *       possibly others
- *  
- */
-static int incomingim_ch3(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0, i;
-	aim_rxcallback_t userfunc;	
-	aim_userinfo_t userinfo;
-	fu8_t cookie[8];
-	fu16_t channel;
-	aim_tlvlist_t *otl;
-	char *msg = NULL;
-	int len = 0;
-	char *encoding = NULL, *language = NULL;
-	aim_msgcookie_t *ck;
-
-	memset(&userinfo, 0, sizeof(aim_userinfo_t));
-
-	/*
-	 * Read ICBM Cookie.
-	 */
-	for (i = 0; i < 8; i++)
-		cookie[i] = aimbs_get8(bs);
-
-	if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
-		free(ck->data);
-		free(ck);
-	}
-
-	/*
-	 * Channel ID
-	 *
-	 * Channel 0x0003 is used for chat messages.
-	 *
-	 */
-	channel = aimbs_get16(bs);
-
-	if (channel != 0x0003) {
-		gaim_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
-		return 0;
-	}
-
-	/*
-	 * Start parsing TLVs right away. 
-	 */
-	otl = aim_tlvlist_read(bs);
-
-	/*
-	 * Type 0x0003: Source User Information
-	 */
-	if (aim_tlv_gettlv(otl, 0x0003, 1)) {
-		aim_tlv_t *userinfotlv;
-		aim_bstream_t tbs;
-
-		userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1);
-
-		aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
-		aim_info_extract(sess, &tbs, &userinfo);
-	}
-
-#if 0
-	/*
-	 * Type 0x0001: If present, it means it was a message to the 
-	 * room (as opposed to a whisper).
-	 */
-	if (aim_tlv_gettlv(otl, 0x0001, 1)) {
-		/* Unhandled */
-	}
-#endif
-
-	/*
-	 * Type 0x0005: Message Block.  Conains more TLVs.
-	 */
-	if (aim_tlv_gettlv(otl, 0x0005, 1)) {
-		aim_tlvlist_t *itl;
-		aim_tlv_t *msgblock;
-		aim_bstream_t tbs;
-
-		msgblock = aim_tlv_gettlv(otl, 0x0005, 1);
-		aim_bstream_init(&tbs, msgblock->value, msgblock->length);
-		itl = aim_tlvlist_read(&tbs);
-
-		/* 
-		 * Type 0x0001: Message.
-		 */	
-		if (aim_tlv_gettlv(itl, 0x0001, 1)) {
-			msg = aim_tlv_getstr(itl, 0x0001, 1);
-			len = aim_tlv_gettlv(itl, 0x0001, 1)->length;
-		}
-
-		/*
-		 * Type 0x0002: Encoding.
-		 */	
-		if (aim_tlv_gettlv(itl, 0x0002, 1))
-			encoding = aim_tlv_getstr(itl, 0x0002, 1);
-
-		/*
-		 * Type 0x0003: Language.
-		 */	
-		if (aim_tlv_gettlv(itl, 0x0003, 1))
-			language = aim_tlv_getstr(itl, 0x0003, 1);
-
-		aim_tlvlist_free(&itl); 
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, &userinfo, len, msg, encoding, language);
-
-	aim_info_free(&userinfo);
-	free(msg);
-	aim_tlvlist_free(&otl);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0002)
-		return infoupdate(sess, mod, rx, snac, bs);
-	else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
-		return userlistchange(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0006)
-		return incomingim_ch3(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000e;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "chat", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/chatnav.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,451 +0,0 @@
-/*
- * Family 0x000d - Handle ChatNav.
- *
- * The ChatNav(igation) service does various things to keep chat
- * alive.  It provides room information, room searching and creating,
- * as well as giving users the right ("permission") to use chat.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/*
- * Subtype 0x0002
- *
- * conn must be a chatnav connection!
- *
- */
-faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
-}
-
-/*
- * Subtype 0x0008
- */
-faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange)
-{
-	static const char ck[] = {"create"};
-	static const char lang[] = {"en"};
-	static const char charset[] = {"us-ascii"};
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
-
-	/* exchange */
-	aimbs_put16(&fr->data, exchange);
-
-	/*
-	 * This looks to be a big hack.  You'll note that this entire
-	 * SNAC is just a room info structure, but the hard room name,
-	 * here, is set to "create".  
-	 *
-	 * Either this goes on the "list of questions concerning
-	 * why-the-hell-did-you-do-that", or this value is completely
-	 * ignored.  Without experimental evidence, but a good knowledge of
-	 * AOL style, I'm going to guess that it is the latter, and that
-	 * the value of the room name in create requests is ignored.
-	 */
-	aimbs_put8(&fr->data, strlen(ck));
-	aimbs_putstr(&fr->data, ck);
-
-	/* 
-	 * instance
-	 * 
-	 * Setting this to 0xffff apparently assigns the last instance.
-	 *
-	 */
-	aimbs_put16(&fr->data, 0xffff);
-
-	/* detail level */
-	aimbs_put8(&fr->data, 0x01);
-
-	aim_tlvlist_add_str(&tl, 0x00d3, name);
-	aim_tlvlist_add_str(&tl, 0x00d6, charset);
-	aim_tlvlist_add_str(&tl, 0x00d7, lang);
-
-	/* tlvcount */
-	aimbs_put16(&fr->data, aim_tlvlist_count(&tl));
-	aim_tlvlist_write(&fr->data, &tl);
-
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
-{
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-	struct aim_chat_exchangeinfo *exchanges = NULL;
-	int curexchange;
-	aim_tlv_t *exchangetlv;
-	fu8_t maxrooms = 0;
-	aim_tlvlist_t *tlvlist, *innerlist;
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	/* 
-	 * Type 0x0002: Maximum concurrent rooms.
-	 */ 
-	if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
-		maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1);
-
-	/* 
-	 * Type 0x0003: Exchange information
-	 *
-	 * There can be any number of these, each one
-	 * representing another exchange.  
-	 * 
-	 */
-	for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
-		aim_bstream_t tbs;
-
-		aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
-
-		curexchange++;
-
-		exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
-
-		/* exchange number */
-		exchanges[curexchange-1].number = aimbs_get16(&tbs);
-		innerlist = aim_tlvlist_read(&tbs);
-
-#if 0
-		/*
-		 * Type 0x000a: Unknown.
-		 *
-		 * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
-		 *
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x000a, 1)) {
-			/* Unhandled */
-		}
-
-		/*
-		 * Type 0x000d: Unknown.
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x000d, 1)) {
-			/* Unhandled */
-		}
-
-		/*
-		 * Type 0x0004: Unknown
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x0004, 1)) {
-			/* Unhandled */
-		}
-#endif
-
-		/*
-		 * Type 0x0002: Unknown
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x0002, 1)) {
-			fu16_t classperms;
-
-			classperms = aim_tlv_get16(innerlist, 0x0002, 1);
-
-			gaim_debug_misc("oscar", "faim: class permissions %x\n", classperms);
-		}
-
-		/*
-		 * Type 0x00c9: Flags
-		 *
-		 * 1 Evilable
-		 * 2 Nav Only
-		 * 4 Instancing Allowed
-		 * 8 Occupant Peek Allowed
-		 *
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
-			exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1);
-
-#if 0
-		/*
-		 * Type 0x00ca: Creation Date
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) {
-			/* Unhandled */
-		}
-
-		/*
-		 * Type 0x00d0: Mandatory Channels?
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) {
-			/* Unhandled */
-		}
-
-		/*
-		 * Type 0x00d1: Maximum Message length
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) {
-			/* Unhandled */
-		}
-
-		/*
-		 * Type 0x00d2: Maximum Occupancy?
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) {	
-			/* Unhandled */
-		}
-#endif
-
-		/*
-		 * Type 0x00d3: Exchange Description
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d3, 1))	
-			exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1);
-		else
-			exchanges[curexchange-1].name = NULL;
-
-#if 0
-		/*
-		 * Type 0x00d4: Exchange Description URL
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) {
-			/* Unhandled */
-		}
-#endif
-
-		/*
-		 * Type 0x00d5: Creation Permissions
-		 *
-		 * 0  Creation not allowed
-		 * 1  Room creation allowed
-		 * 2  Exchange creation allowed
-		 * 
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) {
-			fu8_t createperms;
-
-			createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
-		}
-
-		/*
-		 * Type 0x00d6: Character Set (First Time)
-		 */	      
-		if (aim_tlv_gettlv(innerlist, 0x00d6, 1))	
-			exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1);
-		else
-			exchanges[curexchange-1].charset1 = NULL;
-		      
-		/*
-		 * Type 0x00d7: Language (First Time)
-		 */	      
-		if (aim_tlv_gettlv(innerlist, 0x00d7, 1))	
-			exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1);
-		else
-			exchanges[curexchange-1].lang1 = NULL;
-
-		/*
-		 * Type 0x00d8: Character Set (Second Time)
-		 */	      
-		if (aim_tlv_gettlv(innerlist, 0x00d8, 1))	
-			exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1);
-		else
-			exchanges[curexchange-1].charset2 = NULL;
-
-		/*
-		 * Type 0x00d9: Language (Second Time)
-		 */	      
-		if (aim_tlv_gettlv(innerlist, 0x00d9, 1))	
-			exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1);
-		else
-			exchanges[curexchange-1].lang2 = NULL;
-
-#if 0
-		/*
-		 * Type 0x00da: Unknown
-		 */
-		if (aim_tlv_gettlv(innerlist, 0x00da, 1)) {
-			/* Unhandled */
-		}
-#endif
-
-		aim_tlvlist_free(&innerlist);
-	}
-
-	/*
-	 * Call client.
-	 */
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
-
-	for (curexchange--; curexchange >= 0; curexchange--) {
-		free(exchanges[curexchange].name);
-		free(exchanges[curexchange].charset1);
-		free(exchanges[curexchange].lang1);
-		free(exchanges[curexchange].charset2);
-		free(exchanges[curexchange].lang2);
-	}
-	free(exchanges);
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
-{
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist, *innerlist;
-	char *ck = NULL, *fqcn = NULL, *name = NULL;
-	fu16_t exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
-	fu32_t createtime = 0;
-	fu8_t createperms = 0, detaillevel;
-	int cklen;
-	aim_tlv_t *bigblock;
-	int ret = 0;
-	aim_bstream_t bbbs;
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
-		gaim_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
-		aim_tlvlist_free(&tlvlist);
-		return 0;
-	}
-
-	aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
-
-	exchange = aimbs_get16(&bbbs);
-	cklen = aimbs_get8(&bbbs);
-	ck = aimbs_getstr(&bbbs, cklen);
-	instance = aimbs_get16(&bbbs);
-	detaillevel = aimbs_get8(&bbbs);
-
-	if (detaillevel != 0x02) {
-		gaim_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
-		aim_tlvlist_free(&tlvlist);
-		free(ck);
-		return 0;
-	}
-
-	unknown = aimbs_get16(&bbbs);
-
-	innerlist = aim_tlvlist_read(&bbbs);
-
-	if (aim_tlv_gettlv(innerlist, 0x006a, 1))
-		fqcn = aim_tlv_getstr(innerlist, 0x006a, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
-		flags = aim_tlv_get16(innerlist, 0x00c9, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00ca, 1))
-		createtime = aim_tlv_get32(innerlist, 0x00ca, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00d1, 1))
-		maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00d2, 1))
-		maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
-		name = aim_tlv_getstr(innerlist, 0x00d3, 1);
-
-	if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
-		createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
-		ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
-	}
-
-	free(ck);
-	free(name);
-	free(fqcn);
-	aim_tlvlist_free(&innerlist);
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0009
- *
- * Since multiple things can trigger this callback, we must lookup the 
- * snacid to determine the original snac subtype that was called.
- *
- * XXX This isn't really how this works.  But this is:  Every d/9 response
- * has a 16bit value at the beginning. That matches to:
- *    Short Desc = 1
- *    Full Desc = 2
- *    Instance Info = 4
- *    Nav Short Desc = 8
- *    Nav Instance Info = 16
- * And then everything is really asynchronous.  There is no specific 
- * attachment of a response to a create room request, for example.  Creating
- * the room yields no different a response than requesting the room's info.
- *
- */
-static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_snac_t *snac2;
-	int ret = 0;
-
-	if (!(snac2 = aim_remsnac(sess, snac->id))) {
-		gaim_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id);
-		return 0;
-	}
-
-	if (snac2->family != 0x000d) {
-		gaim_debug_misc("oscar", "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family);
-		return 0;
-	}
-
-	/*
-	 * We now know what the original SNAC subtype was.
-	 */
-	if (snac2->type == 0x0002) /* request chat rights */
-		ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2);
-	else if (snac2->type == 0x0003) /* request exchange info */
-		gaim_debug_misc("oscar", "chatnav_parse_info: resposne to exchange info\n");
-	else if (snac2->type == 0x0004) /* request room info */
-		gaim_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
-	else if (snac2->type == 0x0005) /* request more room info */
-		gaim_debug_misc("oscar", "chatnav_parse_info: response to more room info\n");
-	else if (snac2->type == 0x0006) /* request occupant list */
-		gaim_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n");
-	else if (snac2->type == 0x0007) /* search for a room */
-		gaim_debug_misc("oscar", "chatnav_parse_info: search results\n");
-	else if (snac2->type == 0x0008) /* create room */
-		ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
-	else
-		gaim_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
-
-	if (snac2)
-		free(snac2->data);
-	free(snac2);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0009)
-		return parseinfo(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000d;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "chatnav", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/conn.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/conn.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /**
  * Low-level connection handling.
  *
@@ -5,11 +25,9 @@
  *
  */
 
-#define FAIM_INTERNAL
-#define FAIM_NEED_CONN_INTERNAL
-#include <aim.h> 
+#include "oscar.h"
 
-/* This is defined in aim.h, but only when !FAIM_INTERNAL, since the rest of
+/* This is defined in oscar.h, but only when !FAIM_INTERNAL, since the rest of
  * the library is not allowed to call it. */
 faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
 
@@ -78,7 +96,7 @@
  * about such inane things.
  *
  */
-faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group)
+faim_internal void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
 {
 	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
 	struct snacgroup *sg;
@@ -95,7 +113,7 @@
 	return;
 }
 
-faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group)
+faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
 {
 	aim_conn_t *cur;
 
@@ -519,7 +537,7 @@
  * @param sess Session to initialize.
  * @param nonblocking Set to true if you want connections to be non-blocking.
  */
-faim_export void aim_session_init(aim_session_t *sess, fu8_t nonblocking)
+faim_export void aim_session_init(aim_session_t *sess, guint8 nonblocking)
 {
 
 	if (!sess)
--- a/src/protocols/oscar/email.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-/*
- * Family 0x0018 - Email notification
- *
- * Used for being alerted when the email address(es) associated with 
- * your screen name get new electronic-m.  For normal AIM accounts, you 
- * get the email address screenname@netscape.net.  AOL accounts have 
- * screenname@aol.com, and can also activate a netscape.net account.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/**
- * Subtype 0x0006 - Request information about your email account
- *
- * @param sess The oscar session.
- * @param conn The email connection for this session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_email_sendcookies(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML)))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16+16)))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x0018, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0018, 0x0006, 0x0000, snacid);
-
-	/* Number of cookies to follow */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/* Cookie */
-	aimbs_put16(&fr->data, 0x5d5e);
-	aimbs_put16(&fr->data, 0x1708);
-	aimbs_put16(&fr->data, 0x55aa);
-	aimbs_put16(&fr->data, 0x11d3);
-	aimbs_put16(&fr->data, 0xb143);
-	aimbs_put16(&fr->data, 0x0060);
-	aimbs_put16(&fr->data, 0xb0fb);
-	aimbs_put16(&fr->data, 0x1ecb);
-
-	/* Cookie */
-	aimbs_put16(&fr->data, 0xb380);
-	aimbs_put16(&fr->data, 0x9ad8);
-	aimbs_put16(&fr->data, 0x0dba);
-	aimbs_put16(&fr->data, 0x11d5);
-	aimbs_put16(&fr->data, 0x9f8a);
-	aimbs_put16(&fr->data, 0x0060);
-	aimbs_put16(&fr->data, 0xb0ee);
-	aimbs_put16(&fr->data, 0x0631);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-
-/**
- * Subtype 0x0007 - Receive information about your email account
- *
- * So I don't even know if you can have multiple 16 byte keys, 
- * but this is coded so it will handle that, and handle it well.
- * This tells you if you have unread mail or not, the URL you 
- * should use to access that mail, and the domain name for the 
- * email account (screenname@domainname.com).  If this is the 
- * first 0x0007 SNAC you've received since you signed on, or if 
- * this is just a periodic status update, this will also contain 
- * the number of unread emails that you have.
- */
-static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	struct aim_emailinfo *new;
-	aim_tlvlist_t *tlvlist;
-	fu8_t *cookie8, *cookie16;
-	int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */
-
-	char *alertitle = NULL, *alerturl = NULL;
-
-	cookie8 = aimbs_getraw(bs, 8); /* Possibly the code used to log you in to mail? */
-	cookie16 = aimbs_getraw(bs, 16); /* Mail cookie sent above */
-
-	/* See if we already have some info associated with this cookie */
-	for (new = sess->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next);
-	if (new) {
-		/* Free some of the old info, if it exists */
-		free(new->cookie8);
-		free(new->cookie16);
-		free(new->url);
-		free(new->domain);
-	} else {
-		/* We don't already have info, so create a new struct for it */
-		if (!(new = malloc(sizeof(struct aim_emailinfo))))
-			return -ENOMEM;
-		memset(new, 0, sizeof(struct aim_emailinfo));
-		new->next = sess->emailinfo;
-		sess->emailinfo = new;
-	}
-
-	new->cookie8 = cookie8;
-	new->cookie16 = cookie16;
-
-	tlvlist = aim_tlvlist_readnum(bs, aimbs_get16(bs));
-
-	tmp = aim_tlv_get16(tlvlist, 0x0080, 1);
-	if (tmp) {
-		if (new->nummsgs < tmp)
-			havenewmail = 1;
-		new->nummsgs = tmp;
-	} else {
-		/* If they don't send a 0x0080 TLV, it means we definitely have new mail */
-		/* (ie. this is not just another status update) */
-		havenewmail = 1;
-		new->nummsgs++; /* We know we have at least 1 new email */
-	}
-	new->url = aim_tlv_getstr(tlvlist, 0x0007, 1);
-	if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) {
-		havenewmail = 0;
-		new->nummsgs = 0;
-	}
-	new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1);
-	new->flag = aim_tlv_get16(tlvlist, 0x0084, 1);
-
-	alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1);
-	alerturl  = aim_tlv_getstr(tlvlist, 0x000d, 1);
-	
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL));
-
-	aim_tlvlist_free(&tlvlist);
-
-	free(alertitle);
-	free(alerturl);
-
-	return ret;
-}
-
-/**
- * Subtype 0x0016 - Send something or other
- *
- * @param sess The oscar session.
- * @param conn The email connection for this session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_email_activate(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_EML)))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+16)))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x0018, 0x0016, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0018, 0x0016, 0x0000, snacid);
-
-	/* I would guess this tells AIM that you want updates for your mail accounts */
-	/* ...but I really have no idea */
-	aimbs_put8(&fr->data, 0x02);
-	aimbs_put32(&fr->data, 0x04000000);
-	aimbs_put32(&fr->data, 0x04000000);
-	aimbs_put32(&fr->data, 0x04000000);
-	aimbs_put32(&fr->data, 0x00000000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0007)
-		return parseinfo(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-static void email_shutdown(aim_session_t *sess, aim_module_t *mod)
-{
-	while (sess->emailinfo) {
-		struct aim_emailinfo *tmp = sess->emailinfo;
-		sess->emailinfo = sess->emailinfo->next;
-		free(tmp->cookie16);
-		free(tmp->cookie8);
-		free(tmp->url);
-		free(tmp->domain);
-		free(tmp);
-	}
-
-	return;
-}
-
-faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0018;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "email", sizeof(mod->name));
-	mod->snachandler = snachandler;
-	mod->shutdown = email_shutdown;
-
-	return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_admin.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,257 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0007 - Account Administration.
+ *
+ * Used for stuff like changing the formating of your screen name, changing your
+ * email address, requesting an account confirmation email, getting account info,
+ *
+ */
+
+#include "oscar.h"
+
+/*
+ * Subtype 0x0002 - Request a bit of account info.
+ *
+ * Info should be one of the following:
+ * 0x0001 - Screen name formatting
+ * 0x0011 - Email address
+ * 0x0013 - Unknown
+ *
+ */
+faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid);
+
+	aimbs_put16(&fr->data, info);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtypes 0x0003 and 0x0005 - Parse account info.
+ *
+ * Called in reply to both an information request (subtype 0x0002) and
+ * an information change (subtype 0x0004).
+ *
+ */
+static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	char *url=NULL, *sn=NULL, *email=NULL;
+	guint16 perms, tlvcount, err=0;
+
+	perms = aimbs_get16(bs);
+	tlvcount = aimbs_get16(bs);
+
+	while (tlvcount && aim_bstream_empty(bs)) {
+		guint16 type, length;
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		switch (type) {
+			case 0x0001: {
+				sn = aimbs_getstr(bs, length);
+			} break;
+
+			case 0x0004: {
+				url = aimbs_getstr(bs, length);
+			} break;
+
+			case 0x0008: {
+				err = aimbs_get16(bs);
+			} break;
+
+			case 0x0011: {
+				if (length == 0) {
+					email = (char*)malloc(13*sizeof(char));
+					strcpy(email, "*suppressed*");
+				} else
+					email = aimbs_getstr(bs, length);
+			} break;
+		}
+
+		tlvcount--;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email);
+
+	free(sn);
+	free(url);
+	free(email);
+
+	return 1;
+}
+
+/*
+ * Subtype 0x0004 - Set screenname formatting.
+ *
+ */
+faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	aim_tlvlist_add_str(&tl, 0x0001, newnick);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0004 - Change password.
+ *
+ */
+faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	/* new password TLV t(0002) */
+	aim_tlvlist_add_str(&tl, 0x0002, newpw);
+
+	/* current password TLV t(0012) */
+	aim_tlvlist_add_str(&tl, 0x0012, curpw);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0004 - Change email address.
+ *
+ */
+faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	aim_tlvlist_add_str(&tl, 0x0011, newemail);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0006 - Request account confirmation.
+ *
+ * This will cause an email to be sent to the address associated with
+ * the account.  By following the instructions in the mail, you can
+ * get the TRIAL flag removed from your account.
+ *
+ */
+faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
+}
+
+/*
+ * Subtype 0x0007 - Account confirmation request acknowledgement.
+ *
+ */
+static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 status;
+	aim_tlvlist_t *tl;
+
+	status = aimbs_get16(bs);
+	/* This is 0x0013 if unable to confirm at this time */
+
+	tl = aim_tlvlist_read(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, status);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005))
+		return infochange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return accountconfirm(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0007;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "admin", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_advert.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,50 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0005 - Advertisements.
+ *
+ */
+
+#include "oscar.h"
+
+faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	return 0;
+}
+
+faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0005;
+	mod->version = 0x0001;
+	mod->toolid = 0x0001;
+	mod->toolversion = 0x0001;
+	mod->flags = 0;
+	strncpy(mod->name, "advert", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_alert.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,235 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0018 - Email notification
+ *
+ * Used for being alerted when the email address(es) associated with
+ * your screen name get new electronic-m.  For normal AIM accounts, you
+ * get the email address screenname@netscape.net.  AOL accounts have
+ * screenname@aol.com, and can also activate a netscape.net account.
+ *
+ */
+
+#include "oscar.h"
+
+/**
+ * Subtype 0x0006 - Request information about your email account
+ *
+ * @param sess The oscar session.
+ * @param conn The email connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_email_sendcookies(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ALERT)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16+16)))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x0018, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0018, 0x0006, 0x0000, snacid);
+
+	/* Number of cookies to follow */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/* Cookie */
+	aimbs_put16(&fr->data, 0x5d5e);
+	aimbs_put16(&fr->data, 0x1708);
+	aimbs_put16(&fr->data, 0x55aa);
+	aimbs_put16(&fr->data, 0x11d3);
+	aimbs_put16(&fr->data, 0xb143);
+	aimbs_put16(&fr->data, 0x0060);
+	aimbs_put16(&fr->data, 0xb0fb);
+	aimbs_put16(&fr->data, 0x1ecb);
+
+	/* Cookie */
+	aimbs_put16(&fr->data, 0xb380);
+	aimbs_put16(&fr->data, 0x9ad8);
+	aimbs_put16(&fr->data, 0x0dba);
+	aimbs_put16(&fr->data, 0x11d5);
+	aimbs_put16(&fr->data, 0x9f8a);
+	aimbs_put16(&fr->data, 0x0060);
+	aimbs_put16(&fr->data, 0xb0ee);
+	aimbs_put16(&fr->data, 0x0631);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0007 - Receive information about your email account
+ *
+ * So I don't even know if you can have multiple 16 byte keys,
+ * but this is coded so it will handle that, and handle it well.
+ * This tells you if you have unread mail or not, the URL you
+ * should use to access that mail, and the domain name for the
+ * email account (screenname@domainname.com).  If this is the
+ * first 0x0007 SNAC you've received since you signed on, or if
+ * this is just a periodic status update, this will also contain
+ * the number of unread emails that you have.
+ */
+static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	struct aim_emailinfo *new;
+	aim_tlvlist_t *tlvlist;
+	guint8 *cookie8, *cookie16;
+	int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */
+
+	char *alertitle = NULL, *alerturl = NULL;
+
+	cookie8 = aimbs_getraw(bs, 8); /* Possibly the code used to log you in to mail? */
+	cookie16 = aimbs_getraw(bs, 16); /* Mail cookie sent above */
+
+	/* See if we already have some info associated with this cookie */
+	for (new = sess->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next);
+	if (new) {
+		/* Free some of the old info, if it exists */
+		free(new->cookie8);
+		free(new->cookie16);
+		free(new->url);
+		free(new->domain);
+	} else {
+		/* We don't already have info, so create a new struct for it */
+		if (!(new = malloc(sizeof(struct aim_emailinfo))))
+			return -ENOMEM;
+		memset(new, 0, sizeof(struct aim_emailinfo));
+		new->next = sess->emailinfo;
+		sess->emailinfo = new;
+	}
+
+	new->cookie8 = cookie8;
+	new->cookie16 = cookie16;
+
+	tlvlist = aim_tlvlist_readnum(bs, aimbs_get16(bs));
+
+	tmp = aim_tlv_get16(tlvlist, 0x0080, 1);
+	if (tmp) {
+		if (new->nummsgs < tmp)
+			havenewmail = 1;
+		new->nummsgs = tmp;
+	} else {
+		/* If they don't send a 0x0080 TLV, it means we definitely have new mail */
+		/* (ie. this is not just another status update) */
+		havenewmail = 1;
+		new->nummsgs++; /* We know we have at least 1 new email */
+	}
+	new->url = aim_tlv_getstr(tlvlist, 0x0007, 1);
+	if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) {
+		havenewmail = 0;
+		new->nummsgs = 0;
+	}
+	new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1);
+	new->flag = aim_tlv_get16(tlvlist, 0x0084, 1);
+
+	alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1);
+	alerturl  = aim_tlv_getstr(tlvlist, 0x000d, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL));
+
+	aim_tlvlist_free(&tlvlist);
+
+	free(alertitle);
+	free(alerturl);
+
+	return ret;
+}
+
+/**
+ * Subtype 0x0016 - Send something or other
+ *
+ * @param sess The oscar session.
+ * @param conn The email connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_email_activate(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ALERT)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+16)))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x0018, 0x0016, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0018, 0x0016, 0x0000, snacid);
+
+	/* I would guess this tells AIM that you want updates for your mail accounts */
+	/* ...but I really have no idea */
+	aimbs_put8(&fr->data, 0x02);
+	aimbs_put32(&fr->data, 0x04000000);
+	aimbs_put32(&fr->data, 0x04000000);
+	aimbs_put32(&fr->data, 0x04000000);
+	aimbs_put32(&fr->data, 0x00000000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0007)
+		return parseinfo(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+static void email_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	while (sess->emailinfo) {
+		struct aim_emailinfo *tmp = sess->emailinfo;
+		sess->emailinfo = sess->emailinfo->next;
+		free(tmp->cookie16);
+		free(tmp->cookie8);
+		free(tmp->url);
+		free(tmp->domain);
+		free(tmp);
+	}
+
+	return;
+}
+
+faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0018;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "alert", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = email_shutdown;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_auth.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,673 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0017 - Authentication.
+ *
+ * Deals with the authorizer for SNAC-based login, and also old-style
+ * non-SNAC login.
+ *
+ */
+
+#include "oscar.h"
+
+#include "cipher.h"
+
+#include <ctype.h>
+
+#define USE_XOR_FOR_ICQ
+
+#ifdef USE_XOR_FOR_ICQ
+/**
+ * Encode a password using old XOR method
+ *
+ * This takes a const pointer to a (null terminated) string
+ * containing the unencoded password.  It also gets passed
+ * an already allocated buffer to store the encoded password.
+ * This buffer should be the exact length of the password without
+ * the null.  The encoded password buffer /is not %NULL terminated/.
+ *
+ * The encoding_table seems to be a fixed set of values.  We'll
+ * hope it doesn't change over time!
+ *
+ * This is only used for the XOR method, not the better MD5 method.
+ *
+ * @param password Incoming password.
+ * @param encoded Buffer to put encoded password.
+ */
+static int aim_encode_password(const char *password, guint8 *encoded)
+{
+	guint8 encoding_table[] = {
+#if 0 /* old v1 table */
+		0xf3, 0xb3, 0x6c, 0x99,
+		0x95, 0x3f, 0xac, 0xb6,
+		0xc5, 0xfa, 0x6b, 0x63,
+		0x69, 0x6c, 0xc3, 0x9f
+#else /* v2.1 table, also works for ICQ */
+		0xf3, 0x26, 0x81, 0xc4,
+		0x39, 0x86, 0xdb, 0x92,
+		0x71, 0xa3, 0xb9, 0xe6,
+		0x53, 0x7a, 0x95, 0x7c
+#endif
+	};
+	unsigned int i;
+
+	for (i = 0; i < strlen(password); i++)
+		encoded[i] = (password[i] ^ encoding_table[i]);
+
+	return 0;
+}
+#endif
+
+#ifdef USE_OLD_MD5
+static int aim_encode_password_md5(const char *password, const char *key, guint8 *digest)
+{
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+
+	cipher = gaim_ciphers_find_cipher("md5");
+
+	context = gaim_cipher_context_new(cipher, NULL);
+	gaim_cipher_context_append(context, (const guchar *)key, strlen(key));
+	gaim_cipher_context_append(context, (const guchar *)password, strlen(password));
+	gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+	gaim_cipher_context_digest(context, 16, digest, NULL);
+	gaim_cipher_context_destroy(context);
+
+	return 0;
+}
+#else
+static int aim_encode_password_md5(const char *password, const char *key, guint8 *digest)
+{
+	GaimCipher *cipher;
+	GaimCipherContext *context;
+	guchar passdigest[16];
+
+	cipher = gaim_ciphers_find_cipher("md5");
+
+	context = gaim_cipher_context_new(cipher, NULL);
+	gaim_cipher_context_append(context, (const guchar *)password, strlen(password));
+	gaim_cipher_context_digest(context, 16, passdigest, NULL);
+	gaim_cipher_context_destroy(context);
+
+	context = gaim_cipher_context_new(cipher, NULL);
+	gaim_cipher_context_append(context, (const guchar *)key, strlen(key));
+	gaim_cipher_context_append(context, passdigest, 16);
+	gaim_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+	gaim_cipher_context_digest(context, 16, digest, NULL);
+	gaim_cipher_context_destroy(context);
+
+	return 0;
+}
+#endif
+
+/*
+ * The FLAP version is sent by itself at the beginning of authorization
+ * connections.  The FLAP version is also sent before the cookie when connecting
+ * for other services (BOS, chatnav, chat, etc.).
+ */
+faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4)))
+		return -ENOMEM;
+
+	aimbs_put32(&fr->data, 0x00000001);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * This just pushes the passed cookie onto the passed connection, without
+ * the SNAC header or any of that.
+ *
+ * Very commonly used, as every connection except auth will require this to
+ * be the first thing you send.
+ *
+ */
+faim_export int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const guint16 length, const guint8 *chipsahoy)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4+2+2+length)))
+		return -ENOMEM;
+
+	aimbs_put32(&fr->data, 0x00000001);
+	aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy);
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+#ifdef USE_XOR_FOR_ICQ
+/*
+ * Part two of the ICQ hack.  Note the ignoring of the key.
+ */
+static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	int passwdlen;
+	guint8 *password_encoded;
+
+	passwdlen = strlen(password);
+	if (!(password_encoded = (guint8 *)malloc(passwdlen+1)))
+		return -ENOMEM;
+	if (passwdlen > MAXICQPASSLEN)
+		passwdlen = MAXICQPASSLEN;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) {
+		free(password_encoded);
+		return -ENOMEM;
+	}
+
+	aim_encode_password(password, password_encoded);
+
+	aimbs_put32(&fr->data, 0x00000001); /* FLAP Version */
+	aim_tlvlist_add_str(&tl, 0x0001, sn);
+	aim_tlvlist_add_raw(&tl, 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_write(&fr->data, &tl);
+
+	free(password_encoded);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+#endif
+
+/*
+ * Subtype 0x0002
+ *
+ * This is the initial login request packet.
+ *
+ * NOTE!! If you want/need to make use of the aim_sendmemblock() function,
+ * then the client information you send here must exactly match the
+ * executable that you're pulling the data from.
+ *
+ * Java AIM 1.1.19:
+ *   clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
+ *   clientid = 0x0001
+ *   major  = 0x0001
+ *   minor  = 0x0001
+ *   point = (not sent)
+ *   build  = 0x0013
+ *   unknown= (not sent)
+ *   
+ * AIM for Linux 1.1.112:
+ *   clientstring = "AOL Instant Messenger (SM)"
+ *   clientid = 0x1d09
+ *   major  = 0x0001
+ *   minor  = 0x0001
+ *   point = 0x0001
+ *   build  = 0x0070
+ *   unknown= 0x0000008b
+ *   serverstore = 0x01
+ *
+ */
+faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	guint8 digest[16];
+	aim_snacid_t snacid;
+
+	if (!ci || !sn || !password)
+		return -EINVAL;
+
+#ifdef USE_XOR_FOR_ICQ
+	/* If we're signing on an ICQ account then use the older, XOR login method */
+	if (isdigit(sn[0]))
+		return goddamnicq2(sess, conn, sn, password, ci);
+#endif
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
+
+	aim_tlvlist_add_str(&tl, 0x0001, sn);
+
+	/* Truncate ICQ passwords, if necessary */
+	if (isdigit(sn[0]) && (strlen(password) > MAXICQPASSLEN))
+	{
+		char truncated[MAXICQPASSLEN + 1];
+		strncpy(truncated, password, MAXICQPASSLEN);
+		truncated[MAXICQPASSLEN] = 0;
+		aim_encode_password_md5(truncated, key, digest);
+	}
+	else
+	{
+		aim_encode_password_md5(password, key, digest);
+	}
+
+	aim_tlvlist_add_raw(&tl, 0x0025, 16, digest);
+
+#ifndef USE_OLD_MD5
+	aim_tlvlist_add_noval(&tl, 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);
+
+	/*
+	 * If set, old-fashioned buddy lists will not work. You will need
+	 * to use SSI.
+	 */
+	aim_tlvlist_add_8(&tl, 0x004a, 0x01);
+
+	aim_tlvlist_write(&fr->data, &tl);
+
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * This is sent back as a general response to the login command.
+ * It can be either an error or a success, depending on the
+ * presence of certain TLVs.  
+ *
+ * The client should check the value passed as errorcode. If
+ * its nonzero, there was an error.
+ */
+static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_tlvlist_t *tlvlist;
+	aim_rxcallback_t userfunc;
+	struct aim_authresp_info *info;
+	int ret = 0;
+
+	info = (struct aim_authresp_info *)malloc(sizeof(struct aim_authresp_info));
+	memset(info, 0, sizeof(struct aim_authresp_info));
+
+	/*
+	 * Read block of TLVs.  All further data is derived
+	 * from what is parsed here.
+	 */
+	tlvlist = aim_tlvlist_read(bs);
+
+	/*
+	 * No matter what, we should have a screen name.
+	 */
+	memset(sess->sn, 0, sizeof(sess->sn));
+	if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
+		info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1);
+		strncpy(sess->sn, info->sn, sizeof(sess->sn));
+	}
+
+	/*
+	 * Check for an error code.  If so, we should also
+	 * have an error url.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0008, 1))
+		info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
+		info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1);
+
+	/*
+	 * BOS server address.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
+		info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
+
+	/*
+	 * Authorization cookie.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
+		aim_tlv_t *tmptlv;
+
+		tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
+
+		info->cookielen = tmptlv->length;
+		info->cookie = tmptlv->value;
+	}
+
+	/*
+	 * The email address attached to this account
+	 *   Not available for ICQ or @mac.com logins.
+	 *   If you receive this TLV, then you are allowed to use 
+	 *   family 0x0018 to check the status of your email.
+	 * XXX - Not really true!
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0011, 1))
+		info->email = aim_tlv_getstr(tlvlist, 0x0011, 1);
+
+	/*
+	 * The registration status.  (Not real sure what it means.)
+	 *   Not available for ICQ or @mac.com logins.
+	 *
+	 *   1 = No disclosure
+	 *   2 = Limited disclosure
+	 *   3 = Full disclosure
+	 *
+	 * This has to do with whether your email address is available
+	 * to other users or not.  AFAIK, this feature is no longer used.
+	 *
+	 * Means you can use the admin family? (0x0007)
+	 *
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0013, 1))
+		info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1);
+
+	if (aim_tlv_gettlv(tlvlist, 0x0040, 1))
+		info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0041, 1))
+		info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0042, 1))
+		info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0043, 1))
+		info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1);
+
+#if 0
+	if (aim_tlv_gettlv(tlvlist, 0x0048, 1)) {
+		/* beta serial */
+	}
+#endif
+
+	if (aim_tlv_gettlv(tlvlist, 0x0044, 1))
+		info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0045, 1))
+		info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0046, 1))
+		info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1);
+	if (aim_tlv_gettlv(tlvlist, 0x0047, 1))
+		info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1);
+
+#if 0
+	if (aim_tlv_gettlv(tlvlist, 0x0049, 1)) {
+		/* lastest release serial */
+	}
+#endif
+
+	/*
+	 * URL to change password.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0054, 1))
+		info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1);
+
+#if 0
+	/*
+	 * Unknown.  Seen on an @mac.com screen name with value of 0x003f
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) {
+		/* Unhandled */
+	}
+#endif
+
+	sess->authinfo = info;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
+		ret = userfunc(sess, rx, info);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+#ifdef USE_XOR_FOR_ICQ
+/*
+ * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client
+ *
+ * This is a bit confusing.
+ *
+ * Normal SNAC login goes like this:
+ *   - connect
+ *   - server sends flap version
+ *   - client sends flap version
+ *   - client sends screen name (17/6)
+ *   - server sends hash key (17/7)
+ *   - client sends auth request (17/2 -- aim_send_login)
+ *   - server yells
+ *
+ * XOR login (for ICQ) goes like this:
+ *   - connect
+ *   - server sends flap version
+ *   - client sends auth request which contains flap version (aim_send_login)
+ *   - server yells
+ *
+ * For the client API, we make them implement the most complicated version,
+ * and for the simpler version, we fake it and make it look like the more
+ * complicated process.
+ *
+ * This is done by giving the client a faked key, just so we can convince
+ * them to call aim_send_login right away, which will detect the session
+ * flag that says this is XOR login and ignore the key, sending an ICQ
+ * login request instead of the normal SNAC one.
+ *
+ * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
+ *
+ * XXX This may cause problems if the client relies on callbacks only
+ * being called from the context of aim_rxdispatch()...
+ *
+ */
+static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t fr;
+	aim_rxcallback_t userfunc;
+
+	fr.conn = conn;
+
+	if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007)))
+		userfunc(sess, &fr, "");
+
+	return 0;
+}
+#endif
+
+/*
+ * Subtype 0x0006
+ *
+ * In AIM 3.5 protocol, the first stage of login is to request login from the 
+ * Authorizer, passing it the screen name for verification.  If the name is 
+ * invalid, a 0017/0003 is spit back, with the standard error contents.  If 
+ * valid, a 0017/0007 comes back, which is the signal to send it the main 
+ * login command (0017/0002). 
+ *
+ */
+faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+#ifdef USE_XOR_FOR_ICQ
+	if (isdigit(sn[0]))
+		return goddamnicq(sess, conn, sn);
+#endif
+
+	aim_sendflapver(sess, conn);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)+8 )))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid);
+
+	aim_tlvlist_add_str(&tl, 0x0001, sn);
+
+	/* Tell the server we support SecurID logins. */
+	aim_tlvlist_add_noval(&tl, 0x004b);
+
+	/* Unknown.  Sent in recent WinAIM clients.*/
+	aim_tlvlist_add_noval(&tl, 0x005a);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0007
+ *
+ * Middle handler for 0017/0007 SNACs.  Contains the auth key prefixed
+ * by only its length in a two byte word.
+ *
+ * Calls the client, which should then use the value to call aim_send_login.
+ *
+ */
+static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int keylen, ret = 1;
+	aim_rxcallback_t userfunc;
+	char *keystr;
+
+	keylen = aimbs_get16(bs);
+	keystr = aimbs_getstr(bs, keylen);
+
+	/* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register 
+	 * for the netscape network.  This SNAC had a type 0x0058 TLV with length 10.  
+	 * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, keystr);
+
+	free(keystr);
+
+	return ret;
+}
+
+/**
+ * Subtype 0x000a
+ *
+ * Receive SecurID request.
+ */
+static int got_securid_request(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/**
+ * Subtype 0x000b
+ *
+ * Send SecurID response.
+ */
+faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int len;
+
+	if (!sess || !(conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH)) || !securid)
+		return -EINVAL;
+
+	len = strlen(securid);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+len)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, 0);
+
+	aimbs_put16(&fr->data, len);
+	aimbs_putstr(&fr->data, securid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static void auth_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	if (sess->authinfo) {
+		free(sess->authinfo->sn);
+		free(sess->authinfo->bosip);
+		free(sess->authinfo->errorurl);
+		free(sess->authinfo->email);
+		free(sess->authinfo->chpassurl);
+		free(sess->authinfo->latestrelease.name);
+		free(sess->authinfo->latestrelease.url);
+		free(sess->authinfo->latestrelease.info);
+		free(sess->authinfo->latestbeta.name);
+		free(sess->authinfo->latestbeta.url);
+		free(sess->authinfo->latestbeta.info);
+		free(sess->authinfo);
+	}
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return parse(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return keyparse(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000a)
+		return got_securid_request(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0017;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "auth", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = auth_shutdown;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_bart.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,184 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0010 - Server stored buddy art
+ *
+ * Used for storing and retrieving your cute little buddy icon
+ * from the AIM servers.
+ *
+ */
+
+#include "oscar.h"
+
+/**
+ * Subtype 0x0002 - Upload your icon.
+ *
+ * @param sess The oscar session.
+ * @param conn The icon connection for this session.
+ * @param icon The raw data of the icon image file.
+ * @param iconlen Length of the raw data of the icon image file.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_bart_upload(aim_session_t *sess, const guint8 *icon, guint16 iconlen)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen)))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid);
+
+	/* The reference number for the icon */
+	aimbs_put16(&fr->data, 1);
+
+	/* The icon */
+	aimbs_put16(&fr->data, iconlen);
+	aimbs_putraw(&fr->data, icon, iconlen);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0003 - Acknowledgement for uploading a buddy icon.
+ *
+ * You get this honky after you upload a buddy icon.
+ */
+static int uploadack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 something, somethingelse;
+	guint8 onemorething;
+
+	something = aimbs_get16(bs);
+	somethingelse = aimbs_get16(bs);
+	onemorething = aimbs_get8(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/**
+ * Subtype 0x0004 - Request someone's icon.
+ *
+ * @param sess The oscar session.
+ * @param conn The icon connection for this session.
+ * @param sn The screen name of the person who's icon you are requesting.
+ * @param iconcsum The MD5 checksum of the icon you are requesting.
+ * @param iconcsumlen Length of the MD5 checksum given above.  Should be 10 bytes.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_bart_request(aim_session_t *sess, const char *sn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen)))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid);
+
+	/* Screen name */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	/* Some numbers.  You like numbers, right? */
+	aimbs_put8(&fr->data, 0x01);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put8(&fr->data, iconcsumtype);
+
+	/* Icon string */
+	aimbs_put8(&fr->data, iconcsumlen);
+	aimbs_putraw(&fr->data, iconcsum, iconcsumlen);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0005 - Receive a buddy icon.
+ *
+ * This is sent in response to a buddy icon request.
+ */
+static int parseicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	char *sn;
+	guint16 flags, iconlen;
+	guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon;
+
+	sn = aimbs_getstr(bs, aimbs_get8(bs));
+	flags = aimbs_get16(bs);
+	iconcsumtype = aimbs_get8(bs);
+	iconcsumlen = aimbs_get8(bs);
+	iconcsum = aimbs_getraw(bs, iconcsumlen);
+	iconlen = aimbs_get16(bs);
+	icon = aimbs_getraw(bs, iconlen);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen);
+
+	free(sn);
+	free(iconcsum);
+	free(icon);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return uploadack(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0005)
+		return parseicon(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0010;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "bart", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_bos.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,186 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0009 - Basic Oscar Service.
+ *
+ * The functionality of this family has been replaced by SSI.
+ */
+
+#include "oscar.h"
+
+#include <string.h>
+
+/* Subtype 0x0002 - Request BOS rights. */
+faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x0009, 0x0002);
+}
+
+/* Subtype 0x0003 - BOS Rights. */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	guint16 maxpermits = 0, maxdenies = 0;
+	int ret = 0;
+
+	/*
+	 * TLVs follow
+	 */
+	tlvlist = aim_tlvlist_read(bs);
+
+	/*
+	 * TLV type 0x0001: Maximum number of buddies on permit list.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
+		maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1);
+
+	/*
+	 * TLV type 0x0002: Maximum number of buddies on deny list.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
+		maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxpermits, maxdenies);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0004 - Set group permission mask.
+ *
+ * Normally 0x1f (all classes).
+ *
+ * The group permission mask allows you to keep users of a certain
+ * class or classes from talking to you.  The mask should be
+ * a bitwise OR of all the user classes you want to see you.
+ *
+ */
+faim_export int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, guint32 mask)
+{
+	return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
+}
+
+/*
+ * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists.
+ *
+ * Changes your visibility depending on changetype:
+ *
+ *  AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
+ *  AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
+ *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
+ *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
+ *
+ * list should be a list of
+ * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
+ *
+ * Equivelents to options in WinAIM:
+ *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
+ *      with only your name on it.
+ *   - Allow only users on my Buddy List: Send an
+ *      AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
+ *      buddy list
+ *   - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD
+ *      with everyone listed that you want to see you.
+ *   - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only
+ *      yourself in the list
+ *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
+ *      the list of users to be blocked
+ *
+ * XXX ye gods.
+ */
+faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist)
+{
+	aim_frame_t *fr;
+	int packlen = 0;
+	guint16 subtype;
+	char *localcpy = NULL, *tmpptr = NULL;
+	int i;
+	int listcount;
+	aim_snacid_t snacid;
+
+	if (!denylist)
+		return -EINVAL;
+
+	if (changetype == AIM_VISIBILITYCHANGE_PERMITADD)
+		subtype = 0x05;
+	else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE)
+		subtype = 0x06;
+	else if (changetype == AIM_VISIBILITYCHANGE_DENYADD)
+		subtype = 0x07;
+	else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE)
+		subtype = 0x08;
+	else
+		return -EINVAL;
+
+	localcpy = strdup(denylist);
+
+	listcount = aimutil_itemcnt(localcpy, '&');
+	packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) {
+		free(localcpy);
+		return -ENOMEM;
+	}
+
+	snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid);
+
+	for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
+		tmpptr = aimutil_itemindex(localcpy, i, '&');
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putstr(&fr->data, tmpptr);
+
+		free(tmpptr);
+	}
+	free(localcpy);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0009;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "bos", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_buddy.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,304 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0003 - Old-style Buddylist Management (non-SSI).
+ *
+ */
+
+#include "oscar.h"
+
+#include <string.h>
+
+/*
+ * Subtype 0x0002 - Request rights.
+ *
+ * Request Buddy List rights.
+ *
+ */
+faim_export int aim_buddylist_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x0003, 0x0002);
+}
+
+/*
+ * Subtype 0x0003 - Rights.
+ *
+ */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	guint16 maxbuddies = 0, maxwatchers = 0;
+	int ret = 0;
+
+	/*
+	 * TLVs follow
+	 */
+	tlvlist = aim_tlvlist_read(bs);
+
+	/*
+	 * TLV type 0x0001: Maximum number of buddies.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
+		maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1);
+
+	/*
+	 * TLV type 0x0002: Maximum number of watchers.
+	 *
+	 * Watchers are other users who have you on their buddy
+	 * list.  (This is called the "reverse list" by a certain
+	 * other IM protocol.)
+	 *
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
+		maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1);
+
+	/*
+	 * TLV type 0x0003: Unknown.
+	 *
+	 * ICQ only?
+	 */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxbuddies, maxwatchers);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0004 - Add buddy to list.
+ *
+ * Adds a single buddy to your buddy list after login.
+ * XXX This should just be an extension of setbuddylist()
+ *
+ */
+faim_export int aim_buddylist_addbuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sn || !strlen(sn))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0004 - Add multiple buddies to your buddy list.
+ *
+ * This just builds the "set buddy list" command then queues it.
+ *
+ * buddy_list = "Screen Name One&ScreenNameTwo&";
+ *
+ * XXX Clean this up.
+ *
+ */
+faim_export int aim_buddylist_set(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int len = 0;
+	char *localcpy = NULL;
+	char *tmpptr = NULL;
+
+	if (!buddy_list || !(localcpy = strdup(buddy_list)))
+		return -EINVAL;
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+		gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
+		len += 1 + strlen(tmpptr);
+		tmpptr = strtok(NULL, "&");
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+
+		gaim_debug_misc("oscar", "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putstr(&fr->data, tmpptr);
+		tmpptr = strtok(NULL, "&");
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	free(localcpy);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0005 - Remove buddy from list.
+ *
+ * XXX generalise to support removing multiple buddies (basically, its
+ * the same as setbuddylist() but with a different snac subtype).
+ *
+ */
+faim_export int aim_buddylist_removebuddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sn || !strlen(sn))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid);
+
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x000b
+ *
+ * XXX Why would we send this?
+ *
+ */
+faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
+	aim_putuserinfo(&fr->data, info);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* 
+ * Subtype 0x000c
+ *
+ * XXX Why would we send this?
+ *
+ */
+faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtypes 0x000b and 0x000c - Change in buddy status
+ *
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure.  It's close enough to run
+ * through aim_info_extract() however.
+ *
+ * Although the offgoing notification contains no information,
+ * it is still in a format parsable by aim_info_extract().
+ *
+ */
+static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_userinfo_t userinfo;
+	aim_rxcallback_t userfunc;
+
+	aim_info_extract(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo);
+
+	if (snac->subtype == 0x000b)
+		aim_locate_requestuserinfo(sess, userinfo.sn);
+	aim_info_free(&userinfo);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+	else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
+		return buddychange(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0003;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "buddy", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_chat.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,601 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000e - Routines for the Chat service.
+ *
+ */
+
+#include "oscar.h"
+
+#include <string.h>
+
+/* Stored in the ->internal of chat connections */
+struct chatconnpriv {
+	guint16 exchange;
+	char *name;
+	guint16 instance;
+};
+
+faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
+
+	if (ccp)
+		free(ccp->name);
+	free(ccp);
+
+	return;
+}
+
+faim_export char *aim_chat_getname(aim_conn_t *conn)
+{
+	struct chatconnpriv *ccp;
+
+	if (!conn)
+		return NULL;
+
+	if (conn->type != AIM_CONN_TYPE_CHAT)
+		return NULL;
+
+	ccp = (struct chatconnpriv *)conn->internal;
+
+	return ccp->name;
+}
+
+/* XXX get this into conn.c -- evil!! */
+faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
+{
+	aim_conn_t *cur;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		struct chatconnpriv *ccp = (struct chatconnpriv *)cur->internal;
+
+		if (cur->type != AIM_CONN_TYPE_CHAT)
+			continue;
+		if (!cur->internal) {
+			gaim_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
+			continue;
+		}
+
+		if (strcmp(ccp->name, name) == 0)
+			break;
+	}
+
+	return cur;
+}
+
+faim_export int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+	struct chatconnpriv *ccp;
+
+	if (!conn || !roomname)
+		return -EINVAL;
+
+	if (conn->internal)
+		free(conn->internal);
+
+	if (!(ccp = malloc(sizeof(struct chatconnpriv))))
+		return -ENOMEM;
+
+	ccp->exchange = exchange;
+	ccp->name = strdup(roomname);
+	ccp->instance = instance;
+
+	conn->internal = (void *)ccp;
+
+	return 0;
+}
+
+faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
+{
+	int namelen;
+
+	if (!bs || !outinfo)
+		return 0;
+
+	outinfo->exchange = aimbs_get16(bs);
+	namelen = aimbs_get8(bs);
+	outinfo->name = aimbs_getstr(bs, namelen);
+	outinfo->instance = aimbs_get16(bs);
+
+	return 0;
+}
+
+faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name)
+{
+	aim_conn_t *conn;
+
+	if (!(conn = aim_chat_getconn(sess, name)))
+		return -ENOENT;
+
+	aim_conn_close(conn);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0002 - General room information.  Lots of stuff.
+ *
+ * Values I know are in here but I haven't attached
+ * them to any of the 'Unknown's:
+ *	- Language (English)
+ *
+ */
+static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t *userinfo = NULL;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	int usercount = 0;
+	guint8 detaillevel = 0;
+	char *roomname = NULL;
+	struct aim_chat_roominfo roominfo;
+	guint16 tlvcount = 0;
+	aim_tlvlist_t *tlvlist;
+	char *roomdesc = NULL;
+	guint16 flags = 0;
+	guint32 creationtime = 0;
+	guint16 maxmsglen = 0, maxvisiblemsglen = 0;
+	guint16 unknown_d2 = 0, unknown_d5 = 0;
+
+	aim_chat_readroominfo(bs, &roominfo);
+
+	detaillevel = aimbs_get8(bs);
+
+	if (detaillevel != 0x02) {
+		gaim_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
+		return 1;
+	}
+
+	tlvcount = aimbs_get16(bs);
+
+	/*
+	 * Everything else are TLVs.
+	 */
+	tlvlist = aim_tlvlist_read(bs);
+
+	/*
+	 * TLV type 0x006a is the room name in Human Readable Form.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x006a, 1))
+		roomname = aim_tlv_getstr(tlvlist, 0x006a, 1);
+
+	/*
+	 * Type 0x006f: Number of occupants.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x006f, 1))
+		usercount = aim_tlv_get16(tlvlist, 0x006f, 1);
+
+	/*
+	 * Type 0x0073:  Occupant list.
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) {
+		int curoccupant = 0;
+		aim_tlv_t *tmptlv;
+		aim_bstream_t occbs;
+
+		tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1);
+
+		/* Allocate enough userinfo structs for all occupants */
+		userinfo = calloc(usercount, sizeof(aim_userinfo_t));
+
+		aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
+
+		while (curoccupant < usercount)
+			aim_info_extract(sess, &occbs, &userinfo[curoccupant++]);
+	}
+
+	/*
+	 * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00c9, 1))
+		flags = aim_tlv_get16(tlvlist, 0x00c9, 1);
+
+	/*
+	 * Type 0x00ca: Creation time (4 bytes)
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00ca, 1))
+		creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1);
+
+	/*
+	 * Type 0x00d1: Maximum Message Length
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00d1, 1))
+		maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
+
+	/*
+	 * Type 0x00d2: Unknown. (2 bytes)
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00d2, 1))
+		unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1);
+
+	/*
+	 * Type 0x00d3: Room Description
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00d3, 1))
+		roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1);
+
+#if 0
+	/*
+	 * Type 0x000d4: Unknown (flag only)
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000d4, 1)) {
+		/* Unhandled */
+	}
+#endif
+
+	/*
+	 * Type 0x00d5: Unknown. (1 byte)
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x00d5, 1))
+		unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1);
+
+#if 0
+	/*
+	 * Type 0x00d6: Encoding 1 ("us-ascii")
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000d6, 1)) {
+		/* Unhandled */
+	}
+
+	/*
+	 * Type 0x00d7: Language 1 ("en")
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000d7, 1)) {
+		/* Unhandled */
+	}
+
+	/*
+	 * Type 0x00d8: Encoding 2 ("us-ascii")
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000d8, 1)) {
+		/* Unhandled */
+	}
+
+	/*
+	 * Type 0x00d9: Language 2 ("en")
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000d9, 1)) {
+		/* Unhandled */
+	}
+#endif
+
+	/*
+	 * Type 0x00da: Maximum visible message length
+	 */
+	if (aim_tlv_gettlv(tlvlist, 0x000da, 1))
+		maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+		ret = userfunc(sess,
+				rx,
+				&roominfo,
+				roomname,
+				usercount,
+				userinfo,
+				roomdesc,
+				flags,
+				creationtime,
+				maxmsglen,
+				unknown_d2,
+				unknown_d5,
+				maxvisiblemsglen);
+	}
+
+	free(roominfo.name);
+
+	while (usercount > 0)
+		aim_info_free(&userinfo[--usercount]);
+
+	free(userinfo);
+	free(roomname);
+	free(roomdesc);
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/* Subtypes 0x0003 and 0x0004 */
+static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t *userinfo = NULL;
+	aim_rxcallback_t userfunc;
+	int curcount = 0, ret = 0;
+
+	while (aim_bstream_empty(bs)) {
+		curcount++;
+		userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t));
+		aim_info_extract(sess, bs, &userinfo[curcount-1]);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, curcount, userinfo);
+
+	aim_info_free(userinfo);
+	free(userinfo);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0005 - Send a Chat Message.
+ *
+ * Possible flags:
+ *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
+ *                                 should be sent to their sender.
+ *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
+ *                                 (Note that WinAIM does not honor this,
+ *                                 and displays the message as normal.)
+ *
+ * XXX convert this to use tlvchains
+ */
+faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language)
+{
+	int i;
+	aim_frame_t *fr;
+	aim_msgcookie_t *cookie;
+	aim_snacid_t snacid;
+	guint8 ckstr[8];
+	aim_tlvlist_t *otl = NULL, *itl = NULL;
+
+	if (!sess || !conn || !msg || (msglen <= 0))
+		return 0;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
+
+	/*
+	 * Cookie
+	 *
+	 * XXX mkcookie should generate the cookie and cache it in one
+	 * operation to preserve uniqueness.
+	 */
+	for (i = 0; i < 8; i++)
+		ckstr[i] = (guint8)rand();
+
+	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
+	cookie->data = NULL; /* XXX store something useful here */
+
+	aim_cachecookie(sess, cookie);
+
+	/* ICBM Header */
+	aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
+	aimbs_put16(&fr->data, 0x0003); /* Channel */
+
+	/*
+	 * Type 1: Flag meaning this message is destined to the room.
+	 */
+	aim_tlvlist_add_noval(&otl, 0x0001);
+
+	/*
+	 * Type 6: Reflect
+	 */
+	if (!(flags & AIM_CHATFLAGS_NOREFLECT))
+		aim_tlvlist_add_noval(&otl, 0x0006);
+
+	/*
+	 * Type 7: Autoresponse
+	 */
+	if (flags & AIM_CHATFLAGS_AWAY)
+		aim_tlvlist_add_noval(&otl, 0x0007);
+
+	/*
+	 * SubTLV: Type 1: Message
+	 */
+	aim_tlvlist_add_raw(&itl, 0x0001, msglen, (guchar *)msg);
+
+	/*
+	 * SubTLV: Type 2: Encoding
+	 */
+	if (encoding != NULL)
+		aim_tlvlist_add_str(&itl, 0x0002, encoding);
+
+	/*
+	 * SubTLV: Type 3: Language
+	 */
+	if (language != NULL)
+		aim_tlvlist_add_str(&itl, 0x0003, language);
+
+	/*
+	 * Type 5: Message block.  Contains more TLVs.
+	 *
+	 * This could include other information... We just
+	 * put in a message TLV however.
+	 *
+	 */
+	aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl);
+
+	aim_tlvlist_write(&fr->data, &otl);
+
+	aim_tlvlist_free(&itl);
+	aim_tlvlist_free(&otl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0006
+ *
+ * We could probably include this in the normal ICBM parsing
+ * code as channel 0x0003, however, since only the start
+ * would be the same, we might as well do it here.
+ *
+ * General outline of this SNAC:
+ *   snac
+ *   cookie
+ *   channel id
+ *   tlvlist
+ *     unknown
+ *     source user info
+ *       name
+ *       evility
+ *       userinfo tlvs
+ *         online time
+ *         etc
+ *     message metatlv
+ *       message tlv
+ *         message string
+ *       possibly others
+ *
+ */
+static int incomingim_ch3(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0, i;
+	aim_rxcallback_t userfunc;
+	aim_userinfo_t userinfo;
+	guint8 cookie[8];
+	guint16 channel;
+	aim_tlvlist_t *otl;
+	char *msg = NULL;
+	int len = 0;
+	char *encoding = NULL, *language = NULL;
+	aim_msgcookie_t *ck;
+
+	memset(&userinfo, 0, sizeof(aim_userinfo_t));
+
+	/*
+	 * Read ICBM Cookie.
+	 */
+	for (i = 0; i < 8; i++)
+		cookie[i] = aimbs_get8(bs);
+
+	if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+		free(ck->data);
+		free(ck);
+	}
+
+	/*
+	 * Channel ID
+	 *
+	 * Channel 0x0003 is used for chat messages.
+	 *
+	 */
+	channel = aimbs_get16(bs);
+
+	if (channel != 0x0003) {
+		gaim_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
+		return 0;
+	}
+
+	/*
+	 * Start parsing TLVs right away.
+	 */
+	otl = aim_tlvlist_read(bs);
+
+	/*
+	 * Type 0x0003: Source User Information
+	 */
+	if (aim_tlv_gettlv(otl, 0x0003, 1)) {
+		aim_tlv_t *userinfotlv;
+		aim_bstream_t tbs;
+
+		userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1);
+
+		aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
+		aim_info_extract(sess, &tbs, &userinfo);
+	}
+
+#if 0
+	/*
+	 * Type 0x0001: If present, it means it was a message to the
+	 * room (as opposed to a whisper).
+	 */
+	if (aim_tlv_gettlv(otl, 0x0001, 1)) {
+		/* Unhandled */
+	}
+#endif
+
+	/*
+	 * Type 0x0005: Message Block.  Conains more TLVs.
+	 */
+	if (aim_tlv_gettlv(otl, 0x0005, 1)) {
+		aim_tlvlist_t *itl;
+		aim_tlv_t *msgblock;
+		aim_bstream_t tbs;
+
+		msgblock = aim_tlv_gettlv(otl, 0x0005, 1);
+		aim_bstream_init(&tbs, msgblock->value, msgblock->length);
+		itl = aim_tlvlist_read(&tbs);
+
+		/*
+		 * Type 0x0001: Message.
+		 */
+		if (aim_tlv_gettlv(itl, 0x0001, 1)) {
+			msg = aim_tlv_getstr(itl, 0x0001, 1);
+			len = aim_tlv_gettlv(itl, 0x0001, 1)->length;
+		}
+
+		/*
+		 * Type 0x0002: Encoding.
+		 */
+		if (aim_tlv_gettlv(itl, 0x0002, 1))
+			encoding = aim_tlv_getstr(itl, 0x0002, 1);
+
+		/*
+		 * Type 0x0003: Language.
+		 */
+		if (aim_tlv_gettlv(itl, 0x0003, 1))
+			language = aim_tlv_getstr(itl, 0x0003, 1);
+
+		aim_tlvlist_free(&itl);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo, len, msg, encoding, language);
+
+	aim_info_free(&userinfo);
+	free(msg);
+	aim_tlvlist_free(&otl);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0002)
+		return infoupdate(sess, mod, rx, snac, bs);
+	else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
+		return userlistchange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return incomingim_ch3(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000e;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "chat", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_chatnav.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,470 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000d - Handle ChatNav.
+ *
+ * The ChatNav(igation) service does various things to keep chat
+ * alive.  It provides room information, room searching and creating,
+ * as well as giving users the right ("permission") to use chat.
+ *
+ */
+
+#include "oscar.h"
+
+/*
+ * Subtype 0x0002
+ *
+ * conn must be a chatnav connection!
+ *
+ */
+faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
+}
+
+/*
+ * Subtype 0x0008
+ */
+faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange)
+{
+	static const char ck[] = {"create"};
+	static const char lang[] = {"en"};
+	static const char charset[] = {"us-ascii"};
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
+
+	/* exchange */
+	aimbs_put16(&fr->data, exchange);
+
+	/*
+	 * This looks to be a big hack.  You'll note that this entire
+	 * SNAC is just a room info structure, but the hard room name,
+	 * here, is set to "create".  
+	 *
+	 * Either this goes on the "list of questions concerning
+	 * why-the-hell-did-you-do-that", or this value is completely
+	 * ignored.  Without experimental evidence, but a good knowledge of
+	 * AOL style, I'm going to guess that it is the latter, and that
+	 * the value of the room name in create requests is ignored.
+	 */
+	aimbs_put8(&fr->data, strlen(ck));
+	aimbs_putstr(&fr->data, ck);
+
+	/* 
+	 * instance
+	 * 
+	 * Setting this to 0xffff apparently assigns the last instance.
+	 *
+	 */
+	aimbs_put16(&fr->data, 0xffff);
+
+	/* detail level */
+	aimbs_put8(&fr->data, 0x01);
+
+	aim_tlvlist_add_str(&tl, 0x00d3, name);
+	aim_tlvlist_add_str(&tl, 0x00d6, charset);
+	aim_tlvlist_add_str(&tl, 0x00d7, lang);
+
+	/* tlvcount */
+	aimbs_put16(&fr->data, aim_tlvlist_count(&tl));
+	aim_tlvlist_write(&fr->data, &tl);
+
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	struct aim_chat_exchangeinfo *exchanges = NULL;
+	int curexchange;
+	aim_tlv_t *exchangetlv;
+	guint8 maxrooms = 0;
+	aim_tlvlist_t *tlvlist, *innerlist;
+
+	tlvlist = aim_tlvlist_read(bs);
+
+	/* 
+	 * Type 0x0002: Maximum concurrent rooms.
+	 */ 
+	if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
+		maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1);
+
+	/* 
+	 * Type 0x0003: Exchange information
+	 *
+	 * There can be any number of these, each one
+	 * representing another exchange.  
+	 * 
+	 */
+	for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
+		aim_bstream_t tbs;
+
+		aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
+
+		curexchange++;
+
+		exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
+
+		/* exchange number */
+		exchanges[curexchange-1].number = aimbs_get16(&tbs);
+		innerlist = aim_tlvlist_read(&tbs);
+
+#if 0
+		/*
+		 * Type 0x000a: Unknown.
+		 *
+		 * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
+		 *
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x000a, 1)) {
+			/* Unhandled */
+		}
+
+		/*
+		 * Type 0x000d: Unknown.
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x000d, 1)) {
+			/* Unhandled */
+		}
+
+		/*
+		 * Type 0x0004: Unknown
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x0004, 1)) {
+			/* Unhandled */
+		}
+#endif
+
+		/*
+		 * Type 0x0002: Unknown
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x0002, 1)) {
+			guint16 classperms;
+
+			classperms = aim_tlv_get16(innerlist, 0x0002, 1);
+
+			gaim_debug_misc("oscar", "faim: class permissions %x\n", classperms);
+		}
+
+		/*
+		 * Type 0x00c9: Flags
+		 *
+		 * 1 Evilable
+		 * 2 Nav Only
+		 * 4 Instancing Allowed
+		 * 8 Occupant Peek Allowed
+		 *
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
+			exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1);
+
+#if 0
+		/*
+		 * Type 0x00ca: Creation Date
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) {
+			/* Unhandled */
+		}
+
+		/*
+		 * Type 0x00d0: Mandatory Channels?
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d0, 1)) {
+			/* Unhandled */
+		}
+
+		/*
+		 * Type 0x00d1: Maximum Message length
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) {
+			/* Unhandled */
+		}
+
+		/*
+		 * Type 0x00d2: Maximum Occupancy?
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) {	
+			/* Unhandled */
+		}
+#endif
+
+		/*
+		 * Type 0x00d3: Exchange Description
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d3, 1))	
+			exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1);
+		else
+			exchanges[curexchange-1].name = NULL;
+
+#if 0
+		/*
+		 * Type 0x00d4: Exchange Description URL
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d4, 1)) {
+			/* Unhandled */
+		}
+#endif
+
+		/*
+		 * Type 0x00d5: Creation Permissions
+		 *
+		 * 0  Creation not allowed
+		 * 1  Room creation allowed
+		 * 2  Exchange creation allowed
+		 * 
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) {
+			guint8 createperms;
+
+			createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
+		}
+
+		/*
+		 * Type 0x00d6: Character Set (First Time)
+		 */	      
+		if (aim_tlv_gettlv(innerlist, 0x00d6, 1))	
+			exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1);
+		else
+			exchanges[curexchange-1].charset1 = NULL;
+		      
+		/*
+		 * Type 0x00d7: Language (First Time)
+		 */	      
+		if (aim_tlv_gettlv(innerlist, 0x00d7, 1))	
+			exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1);
+		else
+			exchanges[curexchange-1].lang1 = NULL;
+
+		/*
+		 * Type 0x00d8: Character Set (Second Time)
+		 */	      
+		if (aim_tlv_gettlv(innerlist, 0x00d8, 1))	
+			exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1);
+		else
+			exchanges[curexchange-1].charset2 = NULL;
+
+		/*
+		 * Type 0x00d9: Language (Second Time)
+		 */	      
+		if (aim_tlv_gettlv(innerlist, 0x00d9, 1))	
+			exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1);
+		else
+			exchanges[curexchange-1].lang2 = NULL;
+
+#if 0
+		/*
+		 * Type 0x00da: Unknown
+		 */
+		if (aim_tlv_gettlv(innerlist, 0x00da, 1)) {
+			/* Unhandled */
+		}
+#endif
+
+		aim_tlvlist_free(&innerlist);
+	}
+
+	/*
+	 * Call client.
+	 */
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
+
+	for (curexchange--; curexchange >= 0; curexchange--) {
+		free(exchanges[curexchange].name);
+		free(exchanges[curexchange].charset1);
+		free(exchanges[curexchange].lang1);
+		free(exchanges[curexchange].charset2);
+		free(exchanges[curexchange].lang2);
+	}
+	free(exchanges);
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *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;
+	guint8 createperms = 0, detaillevel;
+	int cklen;
+	aim_tlv_t *bigblock;
+	int ret = 0;
+	aim_bstream_t bbbs;
+
+	tlvlist = aim_tlvlist_read(bs);
+
+	if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
+		gaim_debug_misc("oscar", "no bigblock in top tlv in create room response\n");
+		aim_tlvlist_free(&tlvlist);
+		return 0;
+	}
+
+	aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
+
+	exchange = aimbs_get16(&bbbs);
+	cklen = aimbs_get8(&bbbs);
+	ck = aimbs_getstr(&bbbs, cklen);
+	instance = aimbs_get16(&bbbs);
+	detaillevel = aimbs_get8(&bbbs);
+
+	if (detaillevel != 0x02) {
+		gaim_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
+		aim_tlvlist_free(&tlvlist);
+		free(ck);
+		return 0;
+	}
+
+	unknown = aimbs_get16(&bbbs);
+
+	innerlist = aim_tlvlist_read(&bbbs);
+
+	if (aim_tlv_gettlv(innerlist, 0x006a, 1))
+		fqcn = aim_tlv_getstr(innerlist, 0x006a, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00c9, 1))
+		flags = aim_tlv_get16(innerlist, 0x00c9, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00ca, 1))
+		createtime = aim_tlv_get32(innerlist, 0x00ca, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00d1, 1))
+		maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00d2, 1))
+		maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00d3, 1))
+		name = aim_tlv_getstr(innerlist, 0x00d3, 1);
+
+	if (aim_tlv_gettlv(innerlist, 0x00d5, 1))
+		createperms = aim_tlv_get8(innerlist, 0x00d5, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+		ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
+	}
+
+	free(ck);
+	free(name);
+	free(fqcn);
+	aim_tlvlist_free(&innerlist);
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0009
+ *
+ * Since multiple things can trigger this callback, we must lookup the 
+ * snacid to determine the original snac subtype that was called.
+ *
+ * XXX This isn't really how this works.  But this is:  Every d/9 response
+ * has a 16bit value at the beginning. That matches to:
+ *    Short Desc = 1
+ *    Full Desc = 2
+ *    Instance Info = 4
+ *    Nav Short Desc = 8
+ *    Nav Instance Info = 16
+ * And then everything is really asynchronous.  There is no specific 
+ * attachment of a response to a create room request, for example.  Creating
+ * the room yields no different a response than requesting the room's info.
+ *
+ */
+static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_snac_t *snac2;
+	int ret = 0;
+
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		gaim_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id);
+		return 0;
+	}
+
+	if (snac2->family != 0x000d) {
+		gaim_debug_misc("oscar", "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family);
+		return 0;
+	}
+
+	/*
+	 * We now know what the original SNAC subtype was.
+	 */
+	if (snac2->type == 0x0002) /* request chat rights */
+		ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2);
+	else if (snac2->type == 0x0003) /* request exchange info */
+		gaim_debug_misc("oscar", "chatnav_parse_info: resposne to exchange info\n");
+	else if (snac2->type == 0x0004) /* request room info */
+		gaim_debug_misc("oscar", "chatnav_parse_info: response to room info\n");
+	else if (snac2->type == 0x0005) /* request more room info */
+		gaim_debug_misc("oscar", "chatnav_parse_info: response to more room info\n");
+	else if (snac2->type == 0x0006) /* request occupant list */
+		gaim_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n");
+	else if (snac2->type == 0x0007) /* search for a room */
+		gaim_debug_misc("oscar", "chatnav_parse_info: search results\n");
+	else if (snac2->type == 0x0008) /* create room */
+		ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
+	else
+		gaim_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
+
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0009)
+		return parseinfo(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000d;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "chatnav", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_feedbag.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,2007 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0013 - Server-Side/Stored Information.
+ *
+ * Relatively new facility that allows certain types of information, such as
+ * a user's buddy list, permit/deny list, and permit/deny preferences, to be
+ * stored on the server, so that they can be accessed from any client.
+ *
+ * We keep 2 copies of SSI data:
+ * 1) An exact copy of what is stored on the AIM servers.
+ * 2) A local copy that we make changes to, and then send diffs
+ *    between this and the exact copy to keep them in sync.
+ *
+ * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
+ * that is given to them (i.e. they don't send SNACs).
+ *
+ * The SNAC sending and receiving functions are lower down in the file, and
+ * they're simpler.  They are in the order of the subtypes they deal with,
+ * starting with the request rights function (subtype 0x0002), then parse
+ * rights (subtype 0x0003), then--well, you get the idea.
+ *
+ * This is entirely too complicated.
+ * You don't know the half of it.
+ *
+ */
+
+#include "oscar.h"
+
+/**
+ * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param name A null terminated string containing the group name, or NULL
+ *        if you want to modify the master group.
+ * @return Return a pointer to the modified item.
+ */
+static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
+{
+	int newlen;
+	struct aim_ssi_item *cur, *group;
+
+	if (!list)
+		return NULL;
+
+	/* Find the group */
+	if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
+		return NULL;
+
+	/* Find the length for the new additional data */
+	newlen = 0;
+	if (group->gid == 0x0000) {
+		for (cur=list; cur; cur=cur->next)
+			if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
+				newlen += 2;
+	} else {
+		for (cur=list; cur; cur=cur->next)
+			if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+				newlen += 2;
+	}
+
+	/* Build the new TLV list */
+	if (newlen > 0) {
+		guint8 *newdata;
+
+		if (!(newdata = (guint8 *)malloc((newlen)*sizeof(guint8))))
+			return NULL;
+		newlen = 0;
+		if (group->gid == 0x0000) {
+			for (cur=list; cur; cur=cur->next)
+				if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
+						newlen += aimutil_put16(newdata+newlen, cur->gid);
+		} else {
+			for (cur=list; cur; cur=cur->next)
+				if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+						newlen += aimutil_put16(newdata+newlen, cur->bid);
+		}
+		aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
+
+		free(newdata);
+	}
+
+	return group;
+}
+
+/**
+ * Locally add a new item to the given item list.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param name A null terminated string of the name of the new item, or NULL if the 
+ *        item should have no name.
+ * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
+ * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
+ * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
+ * @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)
+{
+	int i;
+	struct aim_ssi_item *cur, *new;
+
+	if (!list)
+		return NULL;
+
+	if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
+		return NULL;
+
+	/* Set the name */
+	if (name) {
+		new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
+		strcpy(new->name, name);
+	} else
+		new->name = NULL;
+
+	/* Set the group ID# and buddy ID# */
+	new->gid = gid;
+	new->bid = bid;
+	if (type == AIM_SSI_TYPE_GROUP) {
+		if ((new->gid == 0xFFFF) && name) {
+			do {
+				new->gid += 0x0001;
+				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+					if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
+						i=1;
+			} while (i);
+		}
+	} else {
+		if (new->bid == 0xFFFF) {
+			do {
+				new->bid += 0x0001;
+				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+					if ((cur->bid == new->bid) && (cur->gid == new->gid))
+						i=1;
+			} while (i);
+		}
+	}
+
+	/* Set the type */
+	new->type = type;
+
+	/* Set the TLV list */
+	new->data = aim_tlvlist_copy(data);
+
+	/* Add the item to the list in the correct numerical position.  Fancy, eh? */
+	if (*list) {
+		if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
+			new->next = *list;
+			*list = new;
+		} else {
+			struct aim_ssi_item *prev;
+			for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
+			new->next = prev->next;
+			prev->next = new;
+		}
+	} else {
+		new->next = *list;
+		*list = new;
+	}
+
+	return new;
+}
+
+/**
+ * Locally delete an item from the given item list.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param del A pointer to the item you want to remove from the list.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
+{
+	if (!list || !(*list) || !del)
+		return -EINVAL;
+
+	/* Remove the item from the list */
+	if (*list == del) {
+		*list = (*list)->next;
+	} else {
+		struct aim_ssi_item *cur;
+		for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
+		if (cur->next)
+			cur->next = del->next;
+	}
+
+	/* Free the removed item */
+	free(del->name);
+	aim_tlvlist_free(&del->data);
+	free(del);
+
+	return 0;
+}
+
+/**
+ * Compare two items to see if they have the same data.
+ *
+ * @param cur1 A pointer to a pointer to the first item.
+ * @param cur2 A pointer to a pointer to the second item.
+ * @return Return 0 if no differences, or a number if there are differences.
+ */
+static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
+{
+	if (!cur1 || !cur2)
+		return 1;
+
+	if (cur1->data && !cur2->data)
+		return 2;
+
+	if (!cur1->data && cur2->data)
+		return 3;
+
+	if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
+			return 4;
+
+	if (cur1->name && !cur2->name)
+		return 5;
+
+	if (!cur1->name && cur2->name)
+		return 6;
+
+	if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
+		return 7;
+
+	if (cur1->gid != cur2->gid)
+		return 8;
+
+	if (cur1->bid != cur2->bid)
+		return 9;
+
+	if (cur1->type != cur2->type)
+		return 10;
+
+	return 0;
+}
+
+static int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
+{
+	struct aim_ssi_item *cur;
+	for (cur=list; cur; cur=cur->next)
+		if (cur == item)
+			return 1;
+	return 0;
+}
+
+/**
+ * Locally find an item given a group ID# and a buddy ID#.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gid The group ID# of the desired item.
+ * @param bid The buddy ID# of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
+{
+	struct aim_ssi_item *cur;
+	for (cur=list; cur; cur=cur->next)
+		if ((cur->gid == gid) && (cur->bid == bid))
+			return cur;
+	return NULL;
+}
+
+/**
+ * Locally find an item given a group name, screen name, and type.  If group name 
+ * and screen name are null, then just return the first item of the given type.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group name of the desired item.
+ * @param bn The buddy name of the desired item.
+ * @param type The type of the desired item.
+ * @return Return a pointer to the item if found, else return NULL.
+ */
+faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type)
+{
+	struct aim_ssi_item *cur;
+	if (!list)
+		return NULL;
+
+	if (gn && sn) { /* For finding buddies in groups */
+		for (cur=list; cur; cur=cur->next)
+			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+				struct aim_ssi_item *curg;
+				for (curg=list; curg; curg=curg->next)
+					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
+						return cur;
+			}
+
+	} else if (gn) { /* For finding groups */
+		for (cur=list; cur; cur=cur->next) {
+			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
+				return cur;
+			}
+		}
+
+	} else if (sn) { /* For finding permits, denies, and ignores */
+		for (cur=list; cur; cur=cur->next) {
+			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+				return cur;
+			}
+		}
+
+	/* For stuff without names--permit deny setting, visibility mask, etc. */
+	} else for (cur=list; cur; cur=cur->next) {
+		if ((cur->type == type) && (!cur->name))
+			return cur;
+	}
+
+	return NULL;
+}
+
+/**
+ * Check if the given buddy exists in any group in the buddy list.
+ *
+ * @param list A pointer to the current list of items.
+ * @param sn The group name of the desired item.
+ * @return Return a pointer to the name of the item if found, else return NULL;
+ */
+faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
+{
+	struct aim_ssi_item *cur;
+	if (!list || !sn)
+		return NULL;
+	for (cur=list; cur; cur=cur->next)
+		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
+			return cur;
+	return NULL;
+}
+
+/**
+ * Locally find the parent item of the given buddy name.
+ *
+ * @param list A pointer to the current list of items.
+ * @param bn The buddy name of the desired item.
+ * @return Return a pointer to the name of the item if found, else return NULL;
+ */
+faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
+{
+	struct aim_ssi_item *cur, *curg;
+	if (!list || !sn)
+		return NULL;
+	if (!(cur = aim_ssi_itemlist_exists(list, sn)))
+		return NULL;
+	if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
+		return NULL;
+	return curg->name;
+}
+
+/**
+ * Locally find the permit/deny setting item, and return the setting.
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current SSI permit deny setting, or 0 if no setting was found.
+ */
+faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
+	if (cur) {
+		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
+		if (tlv && tlv->value)
+			return aimutil_get8(tlv->value);
+	}
+	return 0;
+}
+
+/**
+ * Locally find the presence flag item, and return the setting.  The returned setting is a 
+ * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines 
+ * in oscar.h
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current visibility mask.
+ */
+faim_export guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
+	if (cur) {
+		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
+		if (tlv && tlv->length)
+			return aimutil_get32(tlv->value);
+	}
+	return 0xFFFFFFFF;
+}
+
+/**
+ * Locally find the alias of the given buddy.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group of the buddy.
+ * @param sn The name of the buddy.
+ * @return A pointer to a NULL terminated string that is the buddy's 
+ *         alias, or NULL if the buddy has no alias.  You should free
+ *         this returned value!
+ */
+faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	if (cur) {
+		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
+		if (tlv && tlv->length) {
+			char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
+			strncpy(alias, (char *)tlv->value, tlv->length);
+			alias[tlv->length] = 0;
+			return alias;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Locally find the comment of the given buddy.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group of the buddy.
+ * @param sn The name of the buddy.
+ * @return A pointer to a NULL terminated string that is the buddy's 
+ *         comment, or NULL if the buddy has no comment.  You should free
+ *         this returned value!
+ */
+faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	if (cur) {
+		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
+		if (tlv && tlv->length) {
+			char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
+			strncpy(alias, (char *)tlv->value, tlv->length);
+			alias[tlv->length] = 0;
+			return alias;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Locally find if you are waiting for authorization for a buddy.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group of the buddy.
+ * @param sn The name of the buddy.
+ * @return 1 if you are waiting for authorization; 0 if you are not
+ */
+faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	if (cur) {
+		if (aim_tlv_gettlv(cur->data, 0x0066, 1))
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * If there are changes, then create temporary items and 
+ * call addmoddel.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_sync(aim_session_t *sess)
+{
+	struct aim_ssi_item *cur1, *cur2;
+	struct aim_ssi_tmp *cur, *new;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* If we're waiting for an ack, we shouldn't do anything else */
+	if (sess->ssi.waiting_for_ack)
+		return 0;
+
+	/*
+	 * Compare the 2 lists and create an aim_ssi_tmp for each difference.  
+	 * We should only send either additions, modifications, or deletions 
+	 * before waiting for an acknowledgement.  So first do deletions, then 
+	 * additions, then modifications.  Also, both the official and the local 
+	 * list should be in ascending numerical order for the group ID#s and the 
+	 * buddy ID#s, which makes things more efficient.  I think.
+	 */
+
+	/* Additions */
+	if (!sess->ssi.pending) {
+		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
+			if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) {
+				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
+				new->action = OSCAR_SUBTYPE_FEEDBAG_ADD;
+				new->ack = 0xffff;
+				new->name = NULL;
+				new->item = cur1;
+				new->next = NULL;
+				if (sess->ssi.pending) {
+					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
+					cur->next = new;
+				} else
+					sess->ssi.pending = new;
+			}
+		}
+	}
+
+	/* Deletions */
+	if (!sess->ssi.pending) {
+		for (cur1=sess->ssi.official; cur1; cur1=cur1->next) {
+			if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) {
+				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
+				new->action = OSCAR_SUBTYPE_FEEDBAG_DEL;
+				new->ack = 0xffff;
+				new->name = NULL;
+				new->item = cur1;
+				new->next = NULL;
+				if (sess->ssi.pending) {
+					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
+					cur->next = new;
+				} else
+					sess->ssi.pending = new;
+			}
+		}
+	}
+
+	/* Modifications */
+	if (!sess->ssi.pending) {
+		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
+			cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid);
+			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
+				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
+				new->action = OSCAR_SUBTYPE_FEEDBAG_MOD;
+				new->ack = 0xffff;
+				new->name = NULL;
+				new->item = cur1;
+				new->next = NULL;
+				if (sess->ssi.pending) {
+					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
+					cur->next = new;
+				} else
+					sess->ssi.pending = new;
+			}
+		}
+	}
+
+	/* We're out of stuff to do, so tell the AIM servers we're done and exit */
+	if (!sess->ssi.pending) {
+		aim_ssi_modend(sess);
+		return 0;
+	}
+
+	/* Make sure we don't send anything else between now 
+	 * and when we receive the ack for the following operation */
+	sess->ssi.waiting_for_ack = 1;
+
+	/* Now go mail off our data and wait 4 to 6 weeks */
+	aim_ssi_addmoddel(sess);
+
+	return 0;
+}
+
+/**
+ * Free all SSI data.
+ *
+ * This doesn't remove it from the server, that's different.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_freelist(aim_session_t *sess)
+{
+	struct aim_ssi_item *cur, *del;
+	struct aim_ssi_tmp *curtmp, *deltmp;
+
+	cur = sess->ssi.official;
+	while (cur) {
+		del = cur;
+		cur = cur->next;
+		free(del->name);
+		aim_tlvlist_free(&del->data);
+		free(del);
+	}
+
+	cur = sess->ssi.local;
+	while (cur) {
+		del = cur;
+		cur = cur->next;
+		free(del->name);
+		aim_tlvlist_free(&del->data);
+		free(del);
+	}
+
+	curtmp = sess->ssi.pending;
+	while (curtmp) {
+		deltmp = curtmp;
+		curtmp = curtmp->next;
+		free(deltmp);
+	}
+
+	sess->ssi.numitems = 0;
+	sess->ssi.official = NULL;
+	sess->ssi.local = NULL;
+	sess->ssi.pending = NULL;
+	sess->ssi.timestamp = (time_t)0;
+
+	return 0;
+}
+
+/**
+ * Delete all SSI data.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_deletelist(aim_session_t *sess)
+{
+	struct aim_ssi_item *cur, *del;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Free the local list */
+	cur = sess->ssi.local;
+	while (cur) {
+		del = cur;
+		cur = cur->next;
+		free(del->name);
+		aim_tlvlist_free(&del->data);
+		free(del);
+	}
+	sess->ssi.local = NULL;
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * This "cleans" the ssi list.  It does the following:
+ * 1) Makes sure all buddies, permits, and denies have names.
+ * 2) Makes sure that all buddies are in a group that exist.
+ * 3) Deletes any empty groups
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_cleanlist(aim_session_t *sess)
+{
+	struct aim_ssi_item *cur, *next;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Delete any buddies, permits, or denies with empty names. */
+	/* If there are any buddies directly in the master group, add them to a real group. */
+	/* DESTROY any buddies that are directly in the master group. */
+	/* Do the same for buddies that are in a non-existant group. */
+	/* This will kind of mess up if you hit the item limit, but this function isn't too critical */
+	cur = sess->ssi.local;
+	while (cur) {
+		next = cur->next;
+		if (!cur->name) {
+			if (cur->type == AIM_SSI_TYPE_BUDDY)
+				aim_ssi_delbuddy(sess, NULL, NULL);
+			else if (cur->type == AIM_SSI_TYPE_PERMIT)
+				aim_ssi_delpermit(sess, NULL);
+			else if (cur->type == AIM_SSI_TYPE_DENY)
+				aim_ssi_deldeny(sess, NULL);
+		} else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) {
+			char *alias = aim_ssi_getalias(sess->ssi.local, NULL, cur->name);
+			aim_ssi_addbuddy(sess, cur->name, "orphans", alias, NULL, NULL, 0);
+			aim_ssi_delbuddy(sess, cur->name, NULL);
+			free(alias);
+		}
+		cur = next;
+	}
+
+	/* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
+	cur = sess->ssi.local;
+	while (cur) {
+		if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
+		{
+			struct aim_ssi_item *cur2, *next2;
+			cur2 = cur->next;
+			while (cur2) {
+				next2 = cur2->next;
+				if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!strcmp(cur->name, cur2->name))) {
+					aim_ssi_itemlist_del(&sess->ssi.local, cur2);
+				}
+				cur2 = next2;
+			}
+		}
+		cur = cur->next;
+	}
+
+	/* Check if there are empty groups and delete them */
+	cur = sess->ssi.local;
+	while (cur) {
+		next = cur->next;
+		if (cur->type == AIM_SSI_TYPE_GROUP) {
+			aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c8, 1);
+			if (!tlv || !tlv->length)
+				aim_ssi_itemlist_del(&sess->ssi.local, cur);
+		}
+		cur = next;
+	}
+
+	/* Check if the master group is empty */
+	if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data))
+		aim_ssi_itemlist_del(&sess->ssi.local, cur);
+
+	/* If we've made any changes then sync our list with the server's */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Add a buddy to the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item.
+ * @param group The group of the 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.
+ */
+faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
+{
+	struct aim_ssi_item *parent;
+	aim_tlvlist_t *data = NULL;
+
+	if (!sess || !name || !group)
+		return -EINVAL;
+
+	/* Find the parent */
+	if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
+		/* Find the parent's parent (the master group) */
+		if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)))
+			if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
+				return -ENOMEM;
+		/* Add the parent */
+		if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
+			return -ENOMEM;
+
+		/* Modify the parent's parent (the master group) */
+		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
+	}
+
+	/* Create a TLV list for the new buddy */
+	if (needauth)
+		aim_tlvlist_add_noval(&data, 0x0066);
+	if (alias)
+		aim_tlvlist_add_str(&data, 0x0131, alias);
+	if (smsnum)
+		aim_tlvlist_add_str(&data, 0x013a, smsnum);
+	if (comment)
+		aim_tlvlist_add_str(&data, 0x013c, comment);
+
+	/* Add that bad boy */
+	aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
+	aim_tlvlist_free(&data);
+
+	/* Modify the parent group */
+	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Add a permit buddy to the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item..
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
+{
+
+	if (!sess || !name)
+		return -EINVAL;
+
+	/* Add that bad boy */
+	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Add a deny buddy to the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item..
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
+{
+
+	if (!sess || !name)
+		return -EINVAL;
+
+	/* Add that bad boy */
+	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Deletes a buddy from the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item, or NULL.
+ * @param group The group of the item, or NULL.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
+{
+	struct aim_ssi_item *del;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the buddy */
+	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
+		return -EINVAL;
+
+	/* Remove the item from the list */
+	aim_ssi_itemlist_del(&sess->ssi.local, del);
+
+	/* Modify the parent group */
+	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
+
+	/* Check if we should delete the parent group */
+	if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) {
+		aim_ssi_itemlist_del(&sess->ssi.local, del);
+
+		/* Modify the parent group */
+		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
+
+		/* Check if we should delete the parent's parent (the master group) */
+		if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) {
+			aim_ssi_itemlist_del(&sess->ssi.local, del);
+		}
+	}
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Deletes a permit buddy from the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item, or NULL.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
+{
+	struct aim_ssi_item *del;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the item */
+	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
+		return -EINVAL;
+
+	/* Remove the item from the list */
+	aim_ssi_itemlist_del(&sess->ssi.local, del);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Deletes a deny buddy from the list.
+ *
+ * @param sess The oscar session.
+ * @param name The name of the item, or NULL.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
+{
+	struct aim_ssi_item *del;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the item */
+	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
+		return -EINVAL;
+
+	/* Remove the item from the list */
+	aim_ssi_itemlist_del(&sess->ssi.local, del);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Move a buddy from one group to another group.  This basically just deletes the 
+ * buddy and re-adds it.
+ *
+ * @param sess The oscar session.
+ * @param oldgn The group that the buddy is currently in.
+ * @param newgn The group that the buddy should be moved in to.
+ * @param sn The name of the buddy to be moved.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn)
+{
+	char *alias = aim_ssi_getalias(sess->ssi.local, oldgn, sn);
+	aim_ssi_addbuddy(sess, sn, newgn, alias, NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn));
+	aim_ssi_delbuddy(sess, sn, oldgn);
+	free(alias);
+	return 0;
+}
+
+/**
+ * Change the alias stored on the server for a given buddy.
+ *
+ * @param sess The oscar session.
+ * @param gn The group that the buddy is currently in.
+ * @param sn The screen name of the buddy.
+ * @param alias The new alias for the buddy, or NULL if you want to remove 
+ *        a buddy's comment.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
+{
+	struct aim_ssi_item *tmp;
+
+	if (!sess || !gn || !sn)
+		return -EINVAL;
+
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
+		return -EINVAL;
+
+	/* Either add or remove the 0x0131 TLV from the TLV chain */
+	if ((alias != NULL) && (strlen(alias) > 0))
+		aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
+	else
+		aim_tlvlist_remove(&tmp->data, 0x0131);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Change the comment stored on the server for a given buddy.
+ *
+ * @param sess The oscar session.
+ * @param gn The group that the buddy is currently in.
+ * @param sn The screen name of the buddy.
+ * @param alias The new comment for the buddy, or NULL if you want to remove 
+ *        a buddy's comment.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *comment)
+{
+	struct aim_ssi_item *tmp;
+
+	if (!sess || !gn || !sn)
+		return -EINVAL;
+
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
+		return -EINVAL;
+
+	/* Either add or remove the 0x0131 TLV from the TLV chain */
+	if ((comment != NULL) && (strlen(comment) > 0))
+		aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
+	else
+		aim_tlvlist_remove(&tmp->data, 0x013c);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Rename a group.
+ *
+ * @param sess The oscar session.
+ * @param oldgn The old group name.
+ * @param newgn The new group name.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
+{
+	struct aim_ssi_item *group;
+
+	if (!sess || !oldgn || !newgn)
+		return -EINVAL;
+
+	if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
+		return -EINVAL;
+
+	free(group->name);
+	group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
+	strcpy(group->name, newgn);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Stores your permit/deny setting on the server, and starts using it.
+ *
+ * @param sess The oscar session.
+ * @param permdeny Your permit/deny setting.  Can be one of the following:
+ *        1 - Allow all users
+ *        2 - Block all users
+ *        3 - Allow only the users below
+ *        4 - Block only the users below
+ *        5 - Allow only users on my buddy list
+ * @param vismask A bitmask of the class of users to whom you want to be
+ *        visible.  See the AIM_FLAG_BLEH #defines in oscar.h
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_setpermdeny(aim_session_t *sess, guint8 permdeny, guint32 vismask)
+{
+	struct aim_ssi_item *tmp;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the PDINFO item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
+
+	/* Need to add the 0x00ca TLV to the TLV chain */
+	aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
+
+	/* Need to add the 0x00cb TLV to the TLV chain */
+	aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/**
+ * Set buddy icon information
+ *
+ * @param sess The oscar session.
+ * @param iconcsum The MD5 checksum of the icon you are using.
+ * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_seticon(aim_session_t *sess, guint8 *iconsum, guint16 iconsumlen)
+{
+	struct aim_ssi_item *tmp;
+	guint8 *csumdata;
+
+	if (!sess || !iconsum || !iconsumlen)
+		return -EINVAL;
+
+	/* Find the ICONINFO item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL);
+	}
+
+	/* Need to add the 0x00d5 TLV to the TLV chain */
+	if (!(csumdata = (guint8 *)malloc((iconsumlen+2)*sizeof(guint8))))
+		return -ENOMEM;
+	csumdata[0] = 0x00;
+	csumdata[1] = 0x10;
+	memcpy(&csumdata[2], iconsum, iconsumlen);
+	aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata);
+	free(csumdata);
+
+	/* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
+	aim_tlvlist_replace_noval(&tmp->data, 0x0131);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+	return 0;
+}
+
+/**
+ * Remove a reference to a server stored buddy icon.  This will make your 
+ * icon stop showing up to other people.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_delicon(aim_session_t *sess)
+{
+	struct aim_ssi_item *tmp;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the ICONINFO item and delete it if it exists*/
+	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO)))
+		aim_ssi_itemlist_del(&sess->ssi.local, tmp);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+	return 0;
+}
+
+/**
+ * Stores your setting for various SSI settings.  Whether you 
+ * should show up as idle or not, etc.
+ *
+ * @param sess The oscar session.
+ * @param presence I think it's a bitmask, but I only know what one of the bits is:
+ *        0x00000002 - Hide wireless?
+ *        0x00000400 - Allow others to see your idle time
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_ssi_setpresence(aim_session_t *sess, guint32 presence) {
+	struct aim_ssi_item *tmp;
+
+	if (!sess)
+		return -EINVAL;
+
+	/* Find the PRESENCEPREFS item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
+
+	/* Need to add the x00c9 TLV to the TLV chain */
+	aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
+
+	/* Sync our local list with the server list */
+	aim_ssi_sync(sess);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0002 - Request SSI Rights.
+ */
+faim_export int aim_ssi_reqrights(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQRIGHTS);
+}
+
+/*
+ * Subtype 0x0003 - SSI Rights Information.
+ */
+static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0, i;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	aim_tlv_t *tlv;
+	aim_bstream_t bstream;
+	guint16 *maxitems;
+
+	/* This SNAC is made up of a bunch of TLVs */
+	tlvlist = aim_tlvlist_read(bs);
+
+	/* TLV 0x0004 contains the maximum number of each item */
+	if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
+		aim_tlvlist_free(&tlvlist);
+		return 0;
+	}
+
+	aim_bstream_init(&bstream, tlv->value, tlv->length);
+
+	if (!(maxitems = (guint16 *)malloc((tlv->length/2)*sizeof(guint16)))) {
+		aim_tlvlist_free(&tlvlist);
+		return 0;
+	}
+
+	for (i=0; i<(tlv->length/2); i++)
+		maxitems[i] = aimbs_get16(&bstream);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, tlv->length/2, maxitems);
+
+	aim_tlvlist_free(&tlvlist);
+	free(maxitems);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and 
+ * revision number.
+ * 
+ */
+faim_export int aim_ssi_reqdata(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	/* Free any current data, just in case */
+	aim_ssi_freelist(sess);
+
+	return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQDATA);
+}
+
+/*
+ * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision 
+ * number.
+ *
+ * The data will only be sent if it is newer than the posted local
+ * timestamp and revision.
+ * 
+ * Note that the client should never increment the revision, only the server.
+ * 
+ */
+faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, guint16 numitems)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED, 0x0000, snacid);
+	aimbs_put32(&fr->data, timestamp);
+	aimbs_put16(&fr->data, numitems);
+
+	aim_tx_enqueue(sess, fr);
+
+	/* Free any current data, just in case */
+	aim_ssi_freelist(sess);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0006 - SSI Data.
+ */
+static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint8 fmtver; /* guess */
+	guint16 namelen, gid, bid, type;
+	char *name;
+	aim_tlvlist_t *data;
+
+	fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
+	sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */
+
+	/* Read in the list */
+	while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
+		if ((namelen = aimbs_get16(bs)))
+			name = aimbs_getstr(bs, namelen);
+		else
+			name = NULL;
+		gid = aimbs_get16(bs);
+		bid = aimbs_get16(bs);
+		type = aimbs_get16(bs);
+		data = aim_tlvlist_readlen(bs, aimbs_get16(bs));
+		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
+		free(name);
+		aim_tlvlist_free(&data);
+	}
+
+	/* Read in the timestamp */
+	sess->ssi.timestamp = aimbs_get32(bs);
+
+	if (!(snac->flags & 0x0001)) {
+		/* Make a copy of the list */
+		struct aim_ssi_item *cur;
+		for (cur=sess->ssi.official; cur; cur=cur->next)
+			aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
+
+		sess->ssi.received_data = 1;
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0007 - SSI Activate Data.
+ *
+ * Should be sent after receiving 13/6 or 13/f to tell the server you
+ * are ready to begin using the list.  It will promptly give you the
+ * presence information for everyone in your list and put your permit/deny
+ * settings into effect.
+ * 
+ */
+faim_export int aim_ssi_enable(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, 0x0007);
+}
+
+/*
+ * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
+ *
+ * Sends the SNAC to add, modify, or delete an item from the server-stored
+ * information.  These 3 SNACs all have an identical structure.  The only
+ * difference is the subtype that is set for the SNAC.
+ * 
+ */
+faim_export int aim_ssi_addmoddel(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int snaclen;
+	struct aim_ssi_tmp *cur;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sess->ssi.pending || !sess->ssi.pending->item)
+		return -EINVAL;
+
+	/* Calculate total SNAC size */
+	snaclen = 10; /* For family, subtype, flags, and SNAC ID */
+	for (cur=sess->ssi.pending; cur; cur=cur->next) {
+		snaclen += 10; /* For length, GID, BID, type, and length */
+		if (cur->item->name)
+			snaclen += strlen(cur->item->name);
+		if (cur->item->data)
+			snaclen += aim_tlvlist_size(&cur->item->data);
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, sess->ssi.pending->action, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, sess->ssi.pending->action, 0x0000, snacid);
+
+	for (cur=sess->ssi.pending; cur; cur=cur->next) {
+		aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0);
+		if (cur->item->name)
+			aimbs_putstr(&fr->data, cur->item->name);
+		aimbs_put16(&fr->data, cur->item->gid);
+		aimbs_put16(&fr->data, cur->item->bid);
+		aimbs_put16(&fr->data, cur->item->type);
+		aimbs_put16(&fr->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0);
+		if (cur->item->data)
+			aim_tlvlist_write(&fr->data, &cur->item->data);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0008 - Incoming SSI add.
+ *
+ * Sent by the server, for example, when someone is added to 
+ * your "Recent Buddies" group.
+ */
+static int parseadd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	char *name;
+	guint16 len, gid, bid, type;
+	aim_tlvlist_t *data;
+
+	while (aim_bstream_empty(bs)) {
+		if ((len = aimbs_get16(bs)))
+			name = aimbs_getstr(bs, len);
+		else
+			name = NULL;
+		gid = aimbs_get16(bs);
+		bid = aimbs_get16(bs);
+		type = aimbs_get16(bs);
+		if ((len = aimbs_get16(bs)))
+			data = aim_tlvlist_readlen(bs, len);
+		else
+			data = NULL;
+
+		aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data);
+		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
+		aim_tlvlist_free(&data);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx, type, name);
+
+		free(name);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0009 - Incoming SSI mod.
+ *
+ * XXX - It would probably be good for the client to actually do something when it gets this.
+ */
+static int parsemod(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	char *name;
+	guint16 len, gid, bid, type;
+	aim_tlvlist_t *data;
+	struct aim_ssi_item *item;
+
+	while (aim_bstream_empty(bs)) {
+		if ((len = aimbs_get16(bs)))
+			name = aimbs_getstr(bs, len);
+		else
+			name = NULL;
+		gid = aimbs_get16(bs);
+		bid = aimbs_get16(bs);
+		type = aimbs_get16(bs);
+		if ((len = aimbs_get16(bs)))
+			data = aim_tlvlist_readlen(bs, len);
+		else
+			data = NULL;
+
+		/* Replace the 2 local items with the given one */
+		if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) {
+			item->type = type;
+			free(item->name);
+			if (name) {
+				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
+				strcpy(item->name, name);
+			} else
+				item->name = NULL;
+			aim_tlvlist_free(&item->data);
+			item->data = aim_tlvlist_copy(data);
+		}
+
+		if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) {
+			item->type = type;
+			free(item->name);
+			if (name) {
+				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
+				strcpy(item->name, name);
+			} else
+				item->name = NULL;
+			aim_tlvlist_free(&item->data);
+			item->data = aim_tlvlist_copy(data);
+		}
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx);
+
+		free(name);
+		aim_tlvlist_free(&data);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000a - Incoming SSI del.
+ *
+ * XXX - It would probably be good for the client to actually do something when it gets this.
+ */
+static int parsedel(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 gid, bid;
+	struct aim_ssi_item *del;
+
+	while (aim_bstream_empty(bs)) {
+		aim_bstream_advance(bs, aimbs_get16(bs));
+		gid = aimbs_get16(bs);
+		bid = aimbs_get16(bs);
+		aimbs_get16(bs);
+		aim_bstream_advance(bs, aimbs_get16(bs));
+
+		if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid)))
+			aim_ssi_itemlist_del(&sess->ssi.local, del);
+		if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid)))
+			aim_ssi_itemlist_del(&sess->ssi.official, del);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000e - SSI Add/Mod/Del Ack.
+ *
+ * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
+ *
+ */
+static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	struct aim_ssi_tmp *cur, *del;
+
+	/* Read in the success/failure flags from the ack SNAC */
+	cur = sess->ssi.pending;
+	while (cur && (aim_bstream_empty(bs)>0)) {
+		cur->ack = aimbs_get16(bs);
+		cur = cur->next;
+	}
+
+	/*
+	 * If outcome is 0, then add the item to the item list, or replace the other item, 
+	 * or remove the old item.  If outcome is non-zero, then remove the item from the 
+	 * local list, or unmodify it, or add it.
+	 */
+	for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
+	if (cur->item) {
+		if (cur->ack) {
+			/* Our action was unsuccessful, so change the local list back to how it was */
+			if (cur->action == OSCAR_SUBTYPE_FEEDBAG_ADD) {
+				/* Remove the item from the local list */
+				/* Make sure cur->item is still valid memory */
+				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
+					if (cur->item->name) {
+						cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
+						strcpy(cur->name, cur->item->name);
+					}
+					aim_ssi_itemlist_del(&sess->ssi.local, cur->item);
+				}
+				cur->item = NULL;
+
+			} else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_MOD) {
+				/* Replace the local item with the item from the official list */
+				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
+					struct aim_ssi_item *cur1;
+					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
+						free(cur->item->name);
+						if (cur1->name) {
+							cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char));
+							strcpy(cur->item->name, cur1->name);
+						} else
+							cur->item->name = NULL;
+						aim_tlvlist_free(&cur->item->data);
+						cur->item->data = aim_tlvlist_copy(cur1->data);
+					}
+				} else
+					cur->item = NULL;
+
+			} else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_DEL) {
+				/* Add the item back into the local list */
+				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) {
+					aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
+				} else
+					cur->item = NULL;
+			}
+
+		} else {
+			/* Do the exact opposite */
+			if (cur->action == OSCAR_SUBTYPE_FEEDBAG_ADD) {
+			/* Add the local item to the official list */
+				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
+					aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
+				} else
+					cur->item = NULL;
+
+			} else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_MOD) {
+				/* Replace the official item with the item from the local list */
+				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
+					struct aim_ssi_item *cur1;
+					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
+						free(cur1->name);
+						if (cur->item->name) {
+							cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
+							strcpy(cur1->name, cur->item->name);
+						} else
+							cur1->name = NULL;
+						aim_tlvlist_free(&cur1->data);
+						cur1->data = aim_tlvlist_copy(cur->item->data);
+					}
+				} else
+					cur->item = NULL;
+
+			} else if (cur->action == OSCAR_SUBTYPE_FEEDBAG_DEL) {
+				/* Remove the item from the official list */
+				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item))
+					aim_ssi_itemlist_del(&sess->ssi.official, cur->item);
+				cur->item = NULL;
+			}
+
+		}
+	} /* End if (cur->item) */
+	} /* End for loop */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sess->ssi.pending);
+
+	/* Free all aim_ssi_tmp's with an outcome */
+	cur = sess->ssi.pending;
+	while (cur && (cur->ack != 0xffff)) {
+		del = cur;
+		cur = cur->next;
+		free(del->name);
+		free(del);
+	}
+	sess->ssi.pending = cur;
+
+	/* If we're not waiting for any more acks, then send more SNACs */
+	if (!sess->ssi.pending) {
+		sess->ssi.pending = NULL;
+		sess->ssi.waiting_for_ack = 0;
+		aim_ssi_sync(sess);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000f - SSI Data Unchanged.
+ *
+ * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
+ * posted local stamp/revision.
+ *
+ */
+static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	sess->ssi.received_data = 1;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0011 - SSI Begin Data Modification.
+ *
+ * Tells the server you're going to start modifying data.
+ * 
+ */
+faim_export int aim_ssi_modbegin(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_EDITSTART);
+}
+
+/*
+ * Subtype 0x0012 - SSI End Data Modification.
+ *
+ * Tells the server you're finished modifying data.
+ *
+ */
+faim_export int aim_ssi_modend(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)))
+		return -EINVAL;
+
+	return aim_genericreq_n(sess, conn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_EDITSTOP);
+}
+
+/*
+ * Subtype 0x0014 - Grant authorization
+ *
+ * Authorizes a contact so they can add you to their contact list.
+ *
+ */
+faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTH, 0x0000, snacid);
+
+	/* Screen name */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	/* Message (null terminated) */
+	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
+	if (msg) {
+		aimbs_putstr(&fr->data, msg);
+		aimbs_put8(&fr->data, 0x00);
+	}
+
+	/* Unknown */
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0015 - Receive an authorization grant
+ */
+static int receiveauthgrant(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 tmp;
+	char *sn, *msg;
+
+	/* Read screen name */
+	if ((tmp = aimbs_get8(bs)))
+		sn = aimbs_getstr(bs, tmp);
+	else
+		sn = NULL;
+
+	/* Read message (null terminated) */
+	if ((tmp = aimbs_get16(bs)))
+		msg = aimbs_getstr(bs, tmp);
+	else
+		msg = NULL;
+
+	/* Unknown */
+	tmp = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sn, msg);
+
+	free(sn);
+	free(msg);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0018 - Send authorization request
+ *
+ * Sends a request for authorization to the given contact.  The request will either be
+ * granted, denied, or dropped.
+ *
+ */
+faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, snacid);
+
+	/* Screen name */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	/* Message (null terminated) */
+	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
+	if (msg) {
+		aimbs_putstr(&fr->data, msg);
+		aimbs_put8(&fr->data, 0x00);
+	}
+
+	/* Unknown */
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0019 - Receive an authorization request
+ */
+static int receiveauthrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 tmp;
+	char *sn, *msg;
+
+	/* Read screen name */
+	if ((tmp = aimbs_get8(bs)))
+		sn = aimbs_getstr(bs, tmp);
+	else
+		sn = NULL;
+
+	/* Read message (null terminated) */
+	if ((tmp = aimbs_get16(bs)))
+		msg = aimbs_getstr(bs, tmp);
+	else
+		msg = NULL;
+
+	/* Unknown */
+	tmp = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sn, msg);
+
+	free(sn);
+	free(msg);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x001a - Send authorization reply
+ *
+ * Sends a reply to a request for authorization.  The reply can either 
+ * grant authorization or deny authorization.
+ *
+ * if reply=0x00 then deny
+ * if reply=0x01 then grant
+ *
+ */
+faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, guint8 reply, const char *msg)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_FEEDBAG)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, snacid);
+
+	/* Screen name */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	/* Grant or deny */
+	aimbs_put8(&fr->data, reply);
+
+	/* Message (null terminated) */
+	aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0);
+	if (msg) {
+		aimbs_putstr(&fr->data, msg);
+		aimbs_put8(&fr->data, 0x00);
+	}
+
+	/* Unknown */
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x001b - Receive an authorization reply
+ * You get this bad boy when other people respond to the authorization 
+ * request that you have previously sent them.
+ */
+static int receiveauthreply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 tmp;
+	guint8 reply;
+	char *sn, *msg;
+
+	/* Read screen name */
+	if ((tmp = aimbs_get8(bs)))
+		sn = aimbs_getstr(bs, tmp);
+	else
+		sn = NULL;
+
+	/* Read reply */
+	reply = aimbs_get8(bs);
+
+	/* Read message (null terminated) */
+	if ((tmp = aimbs_get16(bs)))
+		msg = aimbs_getstr(bs, tmp);
+	else
+		msg = NULL;
+
+	/* Unknown */
+	tmp = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sn, reply, msg);
+
+	free(sn);
+	free(msg);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x001c - Receive a message telling you someone added you to their list.
+ */
+static int receiveadded(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 tmp;
+	char *sn;
+
+	/* Read screen name */
+	if ((tmp = aimbs_get8(bs)))
+		sn = aimbs_getstr(bs, tmp);
+	else
+		sn = NULL;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, sn);
+
+	free(sn);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO)
+		return parserights(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_LIST)
+		return parsedata(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_ADD)
+		return parseadd(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_MOD)
+		return parsemod(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_DEL)
+		return parsedel(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_SRVACK)
+		return parseack(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_NOLIST)
+		return parsedataunchanged(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTH)
+		return receiveauthgrant(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ)
+		return receiveauthrequest(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP)
+		return receiveauthreply(sess, mod, rx, snac, bs);
+	else if (snac->subtype == OSCAR_SUBTYPE_FEEDBAG_ADDED)
+		return receiveadded(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	aim_ssi_freelist(sess);
+}
+
+faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = OSCAR_FAMILY_FEEDBAG;
+	mod->version = 0x0004;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "feedbag", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = ssi_shutdown;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_icbm.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,2500 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0004 - Routines for sending/receiving Instant Messages.
+ *
+ * Note the term ICBM (Inter-Client Basic Message) which blankets
+ * all types of generically routed through-server messages.  Within
+ * the ICBM types (family 4), a channel is defined.  Each channel
+ * represents a different type of message.  Channel 1 is used for
+ * what would commonly be called an "instant message".  Channel 2
+ * is used for negotiating "rendezvous".  These transactions end in
+ * something more complex happening, such as a chat invitation, or
+ * a file transfer.  Channel 3 is used for chat messages (not in
+ * the same family as these channels).  Channel 4 is used for
+ * various ICQ messages.  Examples are normal messages, URLs, and
+ * old-style authorization.
+ *
+ * In addition to the channel, every ICBM contains a cookie.  For
+ * standard IMs, these are only used for error messages.  However,
+ * the more complex rendezvous messages make suitably more complex
+ * use of this field.
+ *
+ * TODO: Split this up into an im.c file an an icbm.c file.  It
+ *       will be beautiful, you'll see.
+ *
+ *       Need to rename all the mpmsg messages to aim_im_bleh.
+ *
+ *       Make sure aim_conn_findbygroup is used by all functions.
+ */
+
+#include "oscar.h"
+#include "peer.h"
+
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+/**
+ * Add a standard ICBM header to the given bstream with the given
+ * information.
+ *
+ * @param bs The bstream to write the ICBM header to.
+ * @param c c is for cookie, and cookie is for me.
+ * @param channel The ICBM channel (1 through 4).
+ * @param sn Null-terminated scrizeen nizame.
+ * @return The number of bytes written.  It's really not useful.
+ */
+static int aim_im_puticbm(aim_bstream_t *bs, const guchar *c, guint16 channel, const char *sn)
+{
+	aimbs_putraw(bs, c, 8);
+	aimbs_put16(bs, channel);
+	aimbs_put8(bs, strlen(sn));
+	aimbs_putstr(bs, sn);
+	return 8+2+1+strlen(sn);
+}
+
+/*
+ * Extracted from aim_im_sendch2_sendfile_ask
+ * Generates a random ICBM cookie in a character array of length 8
+ * and copies it into the variable passed as cookie
+ */
+faim_export void aim_icbm_makecookie(guchar *cookie)
+{
+	int i;
+
+	/* Should be like "21CBF95" and null terminated */
+	for (i = 0; i < 7; i++)
+		cookie[i] = 0x30 + ((guchar)rand() % 10);
+	cookie[7] = '\0';
+}
+
+/*
+ * Takes a msghdr (and a length) and returns a client type
+ * code.  Note that this is *only a guess* and has a low likelihood
+ * of actually being accurate.
+ *
+ * Its based on experimental data, with the help of Eric Warmenhoven
+ * who seems to have collected a wide variety of different AIM clients.
+ *
+ *
+ * Heres the current collection:
+ *  0501 0003 0101 0101 01		AOL Mobile Communicator, WinAIM 1.0.414
+ *  0501 0003 0101 0201 01		WinAIM 2.0.847, 2.1.1187, 3.0.1464,
+ *					4.3.2229, 4.4.2286
+ *  0501 0004 0101 0102 0101		WinAIM 4.1.2010, libfaim (right here)
+ *  0501 0003 0101 02			WinAIM 5
+ *  0501 0001 01			iChat x.x, mobile buddies
+ *  0501 0001 0101 01			AOL v6.0, CompuServe 2000 v6.0, any TOC client
+ *  0501 0002 0106			WinICQ 5.45.1.3777.85
+ *
+ * Note that in this function, only the feature bytes are tested, since
+ * the rest will always be the same.
+ *
+ */
+faim_export guint16 aim_im_fingerprint(const guint8 *msghdr, int len)
+{
+	static const struct {
+		guint16 clientid;
+		int len;
+		guint8 data[10];
+	} fingerprints[] = {
+		/* AOL Mobile Communicator, WinAIM 1.0.414 */
+		{ AIM_CLIENTTYPE_MC,
+		  3, {0x01, 0x01, 0x01}},
+
+		/* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
+		{ AIM_CLIENTTYPE_WINAIM,
+		  3, {0x01, 0x01, 0x02}},
+
+		/* WinAIM 4.1.2010, libfaim */
+		{ AIM_CLIENTTYPE_WINAIM41,
+		  4, {0x01, 0x01, 0x01, 0x02}},
+
+		/* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
+		{ AIM_CLIENTTYPE_AOL_TOC,
+		  1, {0x01}},
+
+		{ 0, 0, {0x00}}
+	};
+	int i;
+
+	if (!msghdr || (len <= 0))
+		return AIM_CLIENTTYPE_UNKNOWN;
+
+	for (i = 0; fingerprints[i].len; i++) {
+		if (fingerprints[i].len != len)
+			continue;
+		if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
+			return fingerprints[i].clientid;
+	}
+
+	return AIM_CLIENTTYPE_UNKNOWN;
+}
+
+/**
+ * Subtype 0x0002 - Set ICBM parameters.
+ *
+ * I definitely recommend sending this.  If you don't, you'll be stuck
+ * with the rather unreasonable defaults.
+ *
+ */
+faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!params)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
+
+	/* This is read-only (see Parameter Reply). Must be set to zero here. */
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* These are all read-write */
+	aimbs_put32(&fr->data, params->flags);
+	aimbs_put16(&fr->data, params->maxmsglen);
+	aimbs_put16(&fr->data, params->maxsenderwarn);
+	aimbs_put16(&fr->data, params->maxrecverwarn);
+	aimbs_put32(&fr->data, params->minmsginterval);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0004 - Request ICBM parameter information.
+ *
+ */
+faim_export int aim_im_reqparams(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004);
+}
+
+/**
+ * Subtype 0x0005 - Receive parameter information.
+ *
+ */
+static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	struct aim_icbmparameters params;
+
+	params.maxchan = aimbs_get16(bs);
+	params.flags = aimbs_get32(bs);
+	params.maxmsglen = aimbs_get16(bs);
+	params.maxsenderwarn = aimbs_get16(bs);
+	params.maxrecverwarn = aimbs_get16(bs);
+	params.minmsginterval = aimbs_get32(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &params);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an ICBM (instant message).
+ *
+ *
+ * Possible flags:
+ *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
+ *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
+ *                        when the message is received (of type 0x0004/0x000c)
+ *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
+ *                        online (probably ICQ only).
+ *
+ * Generally, you should use the lowest encoding possible to send
+ * your message.  If you only use basic punctuation and the generic
+ * Latin alphabet, use ASCII7 (no flags).  If you happen to use non-ASCII7
+ * characters, but they are all clearly defined in ISO-8859-1, then
+ * use that.  Keep in mind that not all characters in the PC ASCII8
+ * character set are defined in the ISO standard. For those cases (most
+ * notably when the (r) symbol is used), you must use the full UNICODE
+ * encoding for your message.  In UNICODE mode, _all_ characters must
+ * occupy 16bits, including ones that are not special.  (Remember that
+ * the first 128 UNICODE symbols are equivalent to ASCII7, however they
+ * must be prefixed with a zero high order byte.)
+ *
+ * I strongly discourage the use of UNICODE mode, mainly because none
+ * of the clients I use can parse those messages (and besides that,
+ * wchars are difficult and non-portable to handle in most UNIX environments).
+ * If you really need to include special characters, use the HTML UNICODE
+ * entities.  These are of the form &#2026; where 2026 is the hex
+ * representation of the UNICODE index (in this case, UNICODE
+ * "Horizontal Ellipsis", or 133 in in ASCII8).
+ *
+ * Implementation note:  Since this is one of the most-used functions
+ * in all of libfaim, it is written with performance in mind.  As such,
+ * it is not as clear as it could be in respect to how this message is
+ * supposed to be layed out. Most obviously, tlvlists should be used
+ * instead of writing out the bytes manually.
+ *
+ * XXX - more precise verification that we never send SNACs larger than 8192
+ * XXX - check SNAC size for multipart
+ *
+ */
+faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+	int msgtlvlen;
+	static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!args)
+		return -EINVAL;
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		if (args->mpmsg->numparts == 0)
+			return -EINVAL;
+	} else {
+		if (!args->msg || (args->msglen <= 0))
+			return -EINVAL;
+
+		if (args->msglen >= MAXMSGLEN)
+			return -E2BIG;
+	}
+
+	/* Painfully calculate the size of the message TLV */
+	msgtlvlen = 1 + 1; /* 0501 */
+
+	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
+		msgtlvlen += 2 + args->featureslen;
+	else
+		msgtlvlen += 2 + sizeof(deffeatures);
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		aim_mpmsg_section_t *sec;
+
+		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+			msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+			msgtlvlen += 4 /* charset */ + sec->datalen;
+		}
+
+	} else {
+		msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+		msgtlvlen += 4 /* charset */ + args->msglen;
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
+		return -ENOMEM;
+
+	/* XXX - should be optional */
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* Generate an ICBM cookie */
+	aim_icbm_makecookie(cookie);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0001, args->destsn);
+
+	/* Message TLV (type 0x0002) */
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, msgtlvlen);
+
+	/* Features TLV (type 0x0501) */
+	aimbs_put16(&fr->data, 0x0501);
+	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
+		aimbs_put16(&fr->data, args->featureslen);
+		aimbs_putraw(&fr->data, args->features, args->featureslen);
+	} else {
+		aimbs_put16(&fr->data, sizeof(deffeatures));
+		aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
+	}
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		aim_mpmsg_section_t *sec;
+
+		/* Insert each message part in a TLV (type 0x0101) */
+		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+			aimbs_put16(&fr->data, 0x0101);
+			aimbs_put16(&fr->data, sec->datalen + 4);
+			aimbs_put16(&fr->data, sec->charset);
+			aimbs_put16(&fr->data, sec->charsubset);
+			aimbs_putraw(&fr->data, (guchar *)sec->data, sec->datalen);
+		}
+
+	} else {
+
+		/* Insert message text in a TLV (type 0x0101) */
+		aimbs_put16(&fr->data, 0x0101);
+
+		/* Message block length */
+		aimbs_put16(&fr->data, args->msglen + 0x04);
+
+		/* Character set */
+		aimbs_put16(&fr->data, args->charset);
+		aimbs_put16(&fr->data, args->charsubset);
+
+		/* Message.  Not terminated */
+		aimbs_putraw(&fr->data, (guchar *)args->msg, args->msglen);
+	}
+
+	/* Set the Autoresponse flag */
+	if (args->flags & AIM_IMFLAGS_AWAY) {
+		aimbs_put16(&fr->data, 0x0004);
+		aimbs_put16(&fr->data, 0x0000);
+	} else if (args->flags & AIM_IMFLAGS_ACK) {
+		/* Set the Request Acknowledge flag */
+		aimbs_put16(&fr->data, 0x0003);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	if (args->flags & AIM_IMFLAGS_OFFLINE) {
+		aimbs_put16(&fr->data, 0x0006);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	/*
+	 * Set the I HAVE A REALLY PURTY ICON flag.
+	 * XXX - This should really only be sent on initial
+	 * IMs and when you change your icon.
+	 */
+	if (args->flags & AIM_IMFLAGS_HASICON) {
+		aimbs_put16(&fr->data, 0x0008);
+		aimbs_put16(&fr->data, 0x000c);
+		aimbs_put32(&fr->data, args->iconlen);
+		aimbs_put16(&fr->data, 0x0001);
+		aimbs_put16(&fr->data, args->iconsum);
+		aimbs_put32(&fr->data, args->iconstamp);
+	}
+
+	/*
+	 * Set the Buddy Icon Requested flag.
+	 * XXX - Every time?  Surely not...
+	 */
+	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
+		aimbs_put16(&fr->data, 0x0009);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	/* clean out SNACs over 60sec old */
+	aim_cleansnacs(sess, 60);
+
+	return 0;
+}
+
+/*
+ * Simple wrapper for aim_im_sendch1_ext()
+ *
+ * You cannot use aim_send_im if you need the HASICON flag.  You must
+ * use aim_im_sendch1_ext directly for that.
+ *
+ * aim_send_im also cannot be used if you require UNICODE messages, because
+ * that requires an explicit message length.  Use aim_im_sendch1_ext().
+ *
+ */
+faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, guint16 flags, const char *msg)
+{
+	struct aim_sendimext_args args;
+
+	args.destsn = sn;
+	args.flags = flags;
+	args.msg = msg;
+	args.msglen = strlen(msg);
+	args.charset = 0x0000;
+	args.charsubset = 0x0000;
+
+	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
+	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
+
+	return aim_im_sendch1_ext(sess, &args);
+}
+
+/*
+ * Subtype 0x0006 - Send a chat invitation.
+ */
+faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_msgcookie_t *msgcookie;
+	struct aim_invite_priv *priv;
+	guchar cookie[8];
+	aim_tlvlist_t *otl = NULL, *itl = NULL;
+	guint8 *hdr;
+	int hdrlen;
+	aim_bstream_t hdrbs;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!sn || !msg || !roomname)
+		return -EINVAL;
+
+	aim_icbm_makecookie(cookie);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* XXX should be uncached by an unwritten 'invite accept' handler */
+	if ((priv = malloc(sizeof(struct aim_invite_priv)))) {
+		priv->sn = strdup(sn);
+		priv->roomname = strdup(roomname);
+		priv->exchange = exchange;
+		priv->instance = instance;
+	}
+
+	if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
+		aim_cachecookie(sess, msgcookie);
+	else
+		free(priv);
+
+	/* ICBM Header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Everything else is inside this TLV.
+	 *
+	 * Sigh.  AOL was rather inconsistent right here.  So we have
+	 * to play some minor tricks.  Right inside the type 5 is some
+	 * raw data, followed by a series of TLVs.
+	 *
+	 */
+	hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2;
+	hdr = malloc(hdrlen);
+	aim_bstream_init(&hdrbs, hdr, hdrlen);
+
+	aimbs_put16(&hdrbs, 0x0000); /* Unknown! */
+	aimbs_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
+	aimbs_putcaps(&hdrbs, AIM_CAPS_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, aim_bstream_curpos(&hdrbs), hdr);
+
+	aim_tlvlist_write(&fr->data, &otl);
+
+	free(hdr);
+	aim_tlvlist_free(&itl);
+	aim_tlvlist_free(&otl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send your icon to a given user.
+ *
+ * This is also performance sensitive. (If you can believe it...)
+ *
+ */
+faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
+		return -EINVAL;
+
+	aim_icbm_makecookie(cookie);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Encompasses everything below.
+	 */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, cookie, 8);
+	aimbs_putcaps(&fr->data, AIM_CAPS_BUDDYICON);
+
+	/* TLV t(000a) */
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+
+	/* TLV t(000f) */
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* TLV t(2711) */
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, iconsum);
+	aimbs_put32(&fr->data, iconlen);
+	aimbs_put32(&fr->data, stamp);
+	aimbs_putraw(&fr->data, icon, iconlen);
+	aimbs_putstr(&fr->data, AIM_ICONIDENT);
+
+	/* TLV t(0003) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0006 - Send a rich text message.
+ *
+ * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
+ * send it to clients advertising the RTF capability.  In fact, if you send
+ * it to a client that doesn't support that capability, the server will gladly
+ * bounce it back to you.
+ *
+ * You'd think this would be in icq.c, but, well, I'm trying to stick with
+ * the one-group-per-file scheme as much as possible.  This could easily
+ * be an exception, since Rendezvous IMs are external of the Oscar core,
+ * and therefore are undefined.  Really I just need to think of a good way to
+ * make an interface similar to what AOL actually uses.  But I'm not using COM.
+ *
+ */
+faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+	int servdatalen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!args || !args->destsn || !args->rtfmsg)
+		return -EINVAL;
+
+	servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
+
+	aim_icbm_makecookie(cookie);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, args->destsn);
+
+	/* TLV t(0005) - Encompasses everything below. */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, cookie, 8);
+	aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+	/* t(000a) l(0002) v(0001) */
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+
+	/* t(000f) l(0000) v() */
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* Service Data TLV */
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, servdatalen);
+
+	aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */);
+	aimbs_putle16(&fr->data, 9);
+	aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY);
+	aimbs_putle16(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle8(&fr->data, 0);
+	aimbs_putle16(&fr->data, 0x03ea); /* trid1 */
+
+	aimbs_putle16(&fr->data, 14);
+	aimbs_putle16(&fr->data, 0x03eb); /* trid2 */
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+
+	aimbs_putle16(&fr->data, 0x0001);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1);
+	aimbs_putraw(&fr->data, (const guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1);
+
+	aimbs_putle32(&fr->data, args->fgcolor);
+	aimbs_putle32(&fr->data, args->bgcolor);
+	aimbs_putle32(&fr->data, strlen(rtfcap)+1);
+	aimbs_putraw(&fr->data, (const guint8 *)rtfcap, strlen(rtfcap)+1);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an "I want to directly connect to you" message
+ *
+ */
+faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *usercookie, gboolean usecookie, const char *sn, const guint8 *ip, guint16 port)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	int hdrlen;
+	guint8 *hdr;
+	aim_bstream_t hdrbs;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/*
+	 * Generate a random message cookie
+	 *
+	 * This cookie needs to be alphanumeric and NULL-terminated to be
+	 * TOC-compatible.
+	 *
+	 * XXX - have I mentioned these should be generated in msgcookie.c?
+	 *
+	 */
+
+	if (usercookie && usecookie) /* allow user-specified cookie */
+		memcpy(cookie, usercookie, 8);
+	else
+		aim_icbm_makecookie(cookie);
+	cookie[7] = '\0';
+
+	if (usercookie && !usecookie)
+		memcpy(cookie, usercookie, 8);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
+
+	aim_tlvlist_add_noval(&tl, 0x0003);
+
+	hdrlen = 2+8+16+6+8+6+4;
+	hdr = malloc(hdrlen);
+	aim_bstream_init(&hdrbs, hdr, hdrlen);
+
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, cookie, 8);
+	aimbs_putcaps(&hdrbs, AIM_CAPS_DIRECTIM);
+
+	aim_tlvlist_add_16(&itl, 0x000a, 0x0001);
+	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
+	aim_tlvlist_add_16(&itl, 0x0005, port);
+	aim_tlvlist_add_noval(&itl, 0x000f);
+
+	aim_tlvlist_write(&hdrbs, &itl);
+
+	aim_tlvlist_add_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+	aim_tlvlist_write(&fr->data, &tl);
+
+	free(hdr);
+	aim_tlvlist_free(&itl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an "I want to send you this file" message
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl=NULL, *subtl=NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
+		return -EINVAL;
+
+	/* The cookie must already have been generated by this point */
+
+	{ /* Create the subTLV chain */
+		guint8 *buf;
+		int buflen;
+		aim_bstream_t bs;
+		guint8 ip[4];
+		guint8 ip_comp[4]; /* The bitwise complement of the ip */
+		char *nexttoken;
+		int i;
+
+		/* In a stage 2 proxied transfer & a transfer redirect, we send a second "reply request"
+		 * Being the second request for this transfer, its request number is 2
+		 * You can fill in the blank for a stage 3's request number... */
+		if ((oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG2)
+			|| (oft_info->send_or_recv == AIM_XFER_RECV
+				&& oft_info->stage == AIM_XFER_PROXY_STG3)
+			|| oft_info->method == AIM_XFER_REDIR)
+			aim_tlvlist_add_16(&subtl, 0x000a, 0x0002);
+		else if(oft_info->send_or_recv == AIM_XFER_SEND && oft_info->stage == AIM_XFER_PROXY_STG3)
+			aim_tlvlist_add_16(&subtl, 0x000a, 0x0003);
+		else
+			aim_tlvlist_add_16(&subtl, 0x000a, 0x0001);
+
+		/* This is usually necessary, but ruins a redirect and a stg3 proxy request */
+		if(!(oft_info->send_or_recv == AIM_XFER_RECV
+			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+			aim_tlvlist_add_noval(&subtl, 0x000f);
+		}
+
+		/* If the following is ever enabled, ensure that it is not sent with a receive redirect
+		 * or stage 3 proxy redirect for a file receive (same conditions for sending 0x000f above) */
+/*		aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en");
+		aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii");
+		aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */
+		/* XXX - Change oft_info->clientip to an array of 4 bytes */
+		if (oft_info->clientip) {
+			i = 0;
+			nexttoken = strtok(oft_info->clientip, ".");
+			while (nexttoken && i<4) {
+				ip[i] = atoi(nexttoken);
+				ip_comp[i] = ~ip[i];
+				nexttoken = strtok(NULL, ".");
+				i++;
+			}
+
+			/* If there is no proxyip, we must fill it in with the clientip */
+			if(!oft_info->proxyip) {
+				aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
+				aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); /* check? value */
+			}
+
+			aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip);
+		}
+
+		/* Don't send the proxyip & accompanying info during a receive redirect or stg3 proxy request */
+		if(!(oft_info->send_or_recv == AIM_XFER_RECV
+			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+			if (oft_info->proxyip) { /* Generate the proxyip */
+				i = 0;
+				nexttoken = strtok(oft_info->proxyip, ".");
+				while (nexttoken && i<4) {
+					ip[i] = atoi(nexttoken);
+					ip_comp[i] = ~ip[i];
+					nexttoken = strtok(NULL, ".");
+					i++;
+				}
+				aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
+				/* This zero-length TLV specifies a proxy will be used */
+				aim_tlvlist_add_noval(&subtl, 0x0010);
+
+				/* Proxied transfers fail without this next (check?) value */
+				aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp);
+			}
+		}
+
+		/* Don't send the port & its check during a stage 3 proxy request */
+		if(!(oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG3)) {
+			aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port);
+
+			/* Check value? Bitwise complement of the port */
+			aim_tlvlist_add_16(&subtl, 0x0017, ~(oft_info->port));
+		}
+
+		/* winAIM gets mad at us if we send too much info during a send redirect or stg3 proxy request */
+		if(!(oft_info->send_or_recv == AIM_XFER_RECV
+			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+			/* TLV t(2711) */
+			buflen = 2+2+4+strlen(oft_info->fh.name)+1;
+			buf = malloc(buflen);
+			aim_bstream_init(&bs, buf, buflen);
+			aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
+			aimbs_put16(&bs, oft_info->fh.totfiles);
+			aimbs_put32(&bs, oft_info->fh.totsize);
+
+			/* Filename - NULL terminated, for some odd reason */
+			aimbs_putstr(&bs, oft_info->fh.name);
+			aimbs_put8(&bs, 0x00);
+
+			aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data);
+			free(buf);
+		}
+	}
+
+	{ /* Create the main TLV chain */
+		guint8 *buf;
+		int buflen;
+		aim_bstream_t bs;
+
+		/* TLV t(0005) - Encompasses everything from above. Gee. */
+		buflen = 2+8+16+aim_tlvlist_size(&subtl);
+		buf = malloc(buflen);
+		aim_bstream_init(&bs, buf, buflen);
+		aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE);
+		aimbs_putraw(&bs, oft_info->cookie, 8);
+		aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);
+		aim_tlvlist_write(&bs, &subtl);
+		aim_tlvlist_free(&subtl);
+		aim_tlvlist_add_raw(&tl, 0x0005, bs.len, bs.data);
+		free(buf);
+
+		/* TLV t(0003) - Request an ack */
+		aim_tlvlist_add_noval(&tl, 0x0003);
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie));
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
+	/* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an "I will accept this file" message?
+ *
+ * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE)
+ */
+faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT);
+	aimbs_putraw(&fr->data, oft_info->cookie, 8);
+	aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send a "cancel this file transfer" message?
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL);
+	aimbs_putraw(&fr->data, (const guchar *)oft_info->cookie, 8);
+	aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Request the status message of the given ICQ user.
+ *
+ * @param sess The oscar session.
+ * @param sn The UIN of the user of whom you wish to request info.
+ * @param type The type of info you wish to request.  This should be the current
+ *        state of the user, as one of the AIM_ICQ_STATE_* defines.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
+		return -EINVAL;
+
+	aim_icbm_makecookie(cookie);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
+
+	/* TLV t(0005) - Encompasses almost everything below. */
+	aimbs_put16(&fr->data, 0x0005); /* T */
+	aimbs_put16(&fr->data, 0x005e); /* L */
+	{ /* V */
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* Cookie */
+		aimbs_putraw(&fr->data, cookie, 8);
+
+		/* Put the 16 byte server relay capability */
+		aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+		/* TLV t(000a) */
+		aimbs_put16(&fr->data, 0x000a);
+		aimbs_put16(&fr->data, 0x0002);
+		aimbs_put16(&fr->data, 0x0001);
+
+		/* TLV t(000f) */
+		aimbs_put16(&fr->data, 0x000f);
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* TLV t(2711) */
+		aimbs_put16(&fr->data, 0x2711);
+		aimbs_put16(&fr->data, 0x0036);
+		{ /* V */
+			aimbs_putle16(&fr->data, 0x001b); /* L */
+			aimbs_putle16(&fr->data, 0x0009); /* Protocol version */
+			aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY);
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle16(&fr->data, 0x0001); /* Client features? */
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle8(&fr->data, 0x00); /* Unkizown */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+
+			aimbs_putle16(&fr->data, 0x000e); /* L */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+
+			/* The type of status message being requested */
+			if (type & AIM_ICQ_STATE_CHAT)
+				aimbs_putle16(&fr->data, 0x03ec);
+			else if(type & AIM_ICQ_STATE_DND)
+				aimbs_putle16(&fr->data, 0x03eb);
+			else if(type & AIM_ICQ_STATE_OUT)
+				aimbs_putle16(&fr->data, 0x03ea);
+			else if(type & AIM_ICQ_STATE_BUSY)
+				aimbs_putle16(&fr->data, 0x03e9);
+			else if(type & AIM_ICQ_STATE_AWAY)
+				aimbs_putle16(&fr->data, 0x03e8);
+
+			aimbs_putle16(&fr->data, 0x0001); /* Status? */
+			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
+			aimbs_putle16(&fr->data, 0x0001); /* L */
+			aimbs_putle8(&fr->data, 0x00); /* String of length L */
+		} /* End TLV t(2711) */
+	} /* End TLV t(0005) */
+
+	/* TLV t(0003) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an ICQ-esque ICBM.
+ *
+ * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."
+ * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack,
+ * mostly because it's in network byte order.  Figuring this stuff out sometimes takes a while,
+ * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people
+ * were taking when they merged the two protocols.
+ *
+ * @param sn The destination screen name.
+ * @param type The type of message.  0x0007 for authorization denied.  0x0008 for authorization granted.
+ * @param message The message you want to send, it should be null terminated.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, guint16 type, const char *message)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
+		return -EINVAL;
+
+	if (!sn || !type || !message)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+3+strlen(sn)+12+strlen(message)+1+4)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	aim_icbm_makecookie(cookie);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0004, sn);
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * ICQ data (the UIN and the message).
+	 */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1);
+
+	/*
+	 * Your UIN
+	 */
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+
+	/*
+	 * TLV t(type) l(strlen(message)+1) v(message+NULL)
+	 */
+	aimbs_putle16(&fr->data, type);
+	aimbs_putle16(&fr->data, strlen(message)+1);
+	aimbs_putraw(&fr->data, (const guint8 *)message, strlen(message)+1);
+
+	/*
+	 * TLV t(0006) l(0000) v()
+	 */
+	aimbs_put16(&fr->data, 0x0006);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * XXX - I don't see when this would ever get called...
+ */
+static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guchar cookie[8];
+	guint16 channel;
+	aim_tlvlist_t *tlvlist;
+	char *sn;
+	int snlen;
+	guint16 icbmflags = 0;
+	guint8 flag1 = 0, flag2 = 0;
+	gchar *msg = NULL;
+	aim_tlv_t *msgblock;
+
+	/* ICBM Cookie. */
+	aim_icbm_makecookie(cookie);
+
+	/* Channel ID */
+	channel = aimbs_get16(bs);
+
+	if (channel != 0x01) {
+		gaim_debug_misc("oscar", "icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
+		return 0;
+	}
+
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+
+	tlvlist = aim_tlvlist_read(bs);
+
+	if (aim_tlv_gettlv(tlvlist, 0x0003, 1))
+		icbmflags |= AIM_IMFLAGS_ACK;
+	if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
+		icbmflags |= AIM_IMFLAGS_AWAY;
+
+	if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
+		aim_bstream_t mbs;
+		int featurelen, msglen;
+
+		aim_bstream_init(&mbs, msgblock->value, msgblock->length);
+
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
+			aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+
+		msglen = aimbs_get16(&mbs) - 4; /* final block length */
+
+		flag1 = aimbs_get16(&mbs);
+		flag2 = aimbs_get16(&mbs);
+
+		msg = aimbs_getstr(&mbs, msglen);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2);
+
+	free(sn);
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Ahh, the joys of nearly ridiculous over-engineering.
+ *
+ * Not only do AIM ICBM's support multiple channels.  Not only do they
+ * support multiple character sets.  But they support multiple character
+ * sets / encodings within the same ICBM.
+ *
+ * These multipart messages allow for complex space savings techniques, which
+ * seem utterly unnecessary by today's standards.  In fact, there is only
+ * one client still in popular use that still uses this method: AOL for the
+ * Macintosh, Version 5.0.  Obscure, yes, I know.
+ *
+ * In modern (non-"legacy") clients, if the user tries to send a character
+ * that is not ISO-8859-1 or ASCII, the client will send the entire message
+ * as UNICODE, meaning that every character in the message will occupy the
+ * full 16 bit UNICODE field, even if the high order byte would be zero.
+ * Multipart messages prevent this wasted space by allowing the client to
+ * only send the characters in UNICODE that need to be sent that way, and
+ * the rest of the message can be sent in whatever the native character
+ * set is (probably ASCII).
+ *
+ * An important note is that sections will be displayed in the order that
+ * they appear in the ICBM.  There is no facility for merging or rearranging
+ * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
+ * you must supply two ASCII sections with a UNICODE in the middle, and incur
+ * the associated overhead.
+ *
+ * Normally I would have laughed and given a firm 'no' to supporting this
+ * seldom-used feature, but something is attracting me to it.  In the future,
+ * it may be possible to abuse this to send mixed-media messages to other
+ * open source clients (like encryption or something) -- see faimtest for
+ * examples of how to do this.
+ *
+ * I would definitely recommend avoiding this feature unless you really
+ * know what you are doing, and/or you have something neat to do with it.
+ *
+ */
+faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+
+	memset(mpm, 0, sizeof(aim_mpmsg_t));
+
+	return 0;
+}
+
+static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, gchar *data, guint16 datalen)
+{
+	aim_mpmsg_section_t *sec;
+
+	if (!(sec = malloc(sizeof(aim_mpmsg_section_t))))
+		return -1;
+
+	sec->charset = charset;
+	sec->charsubset = charsubset;
+	sec->data = data;
+	sec->datalen = datalen;
+	sec->next = NULL;
+
+	if (!mpm->parts)
+		mpm->parts = sec;
+	else {
+		aim_mpmsg_section_t *cur;
+
+		for (cur = mpm->parts; cur->next; cur = cur->next)
+			;
+		cur->next = sec;
+	}
+
+	mpm->numparts++;
+
+	return 0;
+}
+
+faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen)
+{
+	gchar *dup;
+
+	if (!(dup = malloc(datalen)))
+		return -1;
+	memcpy(dup, data, datalen);
+
+	if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
+		free(dup);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* XXX - should provide a way of saying ISO-8859-1 specifically */
+faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
+{
+	gchar *dup;
+
+	if (!(dup = strdup(ascii)))
+		return -1;
+
+	if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) {
+		free(dup);
+		return -1;
+	}
+
+	return 0;
+}
+
+faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen)
+{
+	gchar *buf;
+	aim_bstream_t bs;
+	int i;
+
+	if (!(buf = malloc(unicodelen * 2)))
+		return -1;
+
+	aim_bstream_init(&bs, (guchar *)buf, unicodelen * 2);
+
+	/* We assume unicode is in /host/ byte order -- convert to network */
+	for (i = 0; i < unicodelen; i++)
+		aimbs_put16(&bs, unicode[i]);
+
+	if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
+		free(buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+	aim_mpmsg_section_t *cur;
+
+	for (cur = mpm->parts; cur; ) {
+		aim_mpmsg_section_t *tmp;
+
+		tmp = cur->next;
+		free(cur->data);
+		free(cur);
+		cur = tmp;
+	}
+
+	mpm->numparts = 0;
+	mpm->parts = NULL;
+
+	return;
+}
+
+/*
+ * Start by building the multipart structures, then pick the first
+ * human-readable section and stuff it into args->msg so no one gets
+ * suspicious.
+ *
+ */
+static int incomingim_ch1_parsemsgs(aim_session_t *sess, aim_userinfo_t *userinfo, guint8 *data, int len, struct aim_incomingim_ch1_args *args)
+{
+	/* Should this be ASCII -> UNICODE -> Custom */
+	static const guint16 charsetpri[] = {
+		AIM_CHARSET_ASCII, /* ASCII first */
+		AIM_CHARSET_CUSTOM, /* then ISO-8859-1 */
+		AIM_CHARSET_UNICODE, /* UNICODE as last resort */
+	};
+	static const int charsetpricount = 3;
+	int i;
+	aim_bstream_t mbs;
+	aim_mpmsg_section_t *sec;
+
+	aim_bstream_init(&mbs, data, len);
+
+	while (aim_bstream_empty(&mbs)) {
+		guint16 msglen, flag1, flag2;
+		gchar *msgbuf;
+
+		aimbs_get8(&mbs); /* 01 */
+		aimbs_get8(&mbs); /* 01 */
+
+		/* Message string length, including character set info. */
+		msglen = aimbs_get16(&mbs);
+		if (msglen > aim_bstream_empty(&mbs))
+		{
+			gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.", userinfo->sn);
+			break;
+		}
+
+		/* Character set info */
+		flag1 = aimbs_get16(&mbs);
+		flag2 = aimbs_get16(&mbs);
+
+		/* Message. */
+		msglen -= 4;
+
+		/*
+		 * For now, we don't care what the encoding is.  Just copy
+		 * it into a multipart struct and deal with it later. However,
+		 * always pad the ending with a NULL.  This makes it easier
+		 * to treat ASCII sections as strings.  It won't matter for
+		 * UNICODE or binary data, as you should never read past
+		 * the specified data length, which will not include the pad.
+		 *
+		 * XXX - There's an API bug here.  For sending, the UNICODE is
+		 * given in host byte order (aim_mpmsg_addunicode), but here
+		 * the received messages are given in network byte order.
+		 *
+		 */
+		msgbuf = (gchar *)aimbs_getraw(&mbs, msglen);
+		mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen);
+
+	} /* while */
+
+	args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
+
+	/*
+	 * Clients that support multiparts should never use args->msg, as it
+	 * will point to an arbitrary section.
+	 *
+	 * Here, we attempt to provide clients that do not support multipart
+	 * messages with something to look at -- hopefully a human-readable
+	 * string.  But, failing that, a UNICODE message, or nothing at all.
+	 *
+	 * Which means that even if args->msg is NULL, it does not mean the
+	 * message was blank.
+	 *
+	 */
+	for (i = 0; i < charsetpricount; i++) {
+		for (sec = args->mpmsg.parts; sec; sec = sec->next) {
+
+			if (sec->charset != charsetpri[i])
+				continue;
+
+			/* Great. We found one.  Fill it in. */
+			args->charset = sec->charset;
+			args->charsubset = sec->charsubset;
+
+			/* Set up the simple flags */
+			switch (args->charsubset)
+			{
+				case 0x0000:
+					/* standard subencoding? */
+					break;
+				case 0x000b:
+					args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
+					break;
+				case 0xffff:
+					/* no subencoding */
+					break;
+				default:
+					break;
+			}
+
+			args->msg = sec->data;
+			args->msglen = sec->datalen;
+
+			return 0;
+		}
+	}
+
+	/* No human-readable sections found.  Oh well. */
+	args->charset = args->charsubset = 0xffff;
+	args->msg = NULL;
+	args->msglen = 0;
+
+	return 0;
+}
+
+static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie)
+{
+	guint16 type, length;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	struct aim_incomingim_ch1_args args;
+	unsigned int endpos;
+
+	memset(&args, 0, sizeof(args));
+
+	aim_mpmsg_init(sess, &args.mpmsg);
+
+	/*
+	 * This used to be done using tlvchains.  For performance reasons,
+	 * I've changed it to process the TLVs in-place.  This avoids lots
+	 * of per-IM memory allocations.
+	 */
+	while (aim_bstream_empty(bs))
+	{
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		if (length > aim_bstream_empty(bs))
+		{
+			gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+			break;
+		}
+
+		endpos = aim_bstream_curpos(bs) + length;
+
+		if (type == 0x0002) { /* Message Block */
+
+			/*
+			 * This TLV consists of the following:
+			 *   - 0501 -- Unknown
+			 *   - Features: Don't know how to interpret these
+			 *   - 0101 -- Unknown
+			 *   - Message
+			 *
+			 */
+
+			aimbs_get8(bs); /* 05 */
+			aimbs_get8(bs); /* 01 */
+
+			args.featureslen = aimbs_get16(bs);
+			if (args.featureslen > aim_bstream_empty(bs))
+			{
+				gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
+			if (args.featureslen == 0)
+			{
+				args.features = NULL;
+			}
+			else
+			{
+				args.features = aimbs_getraw(bs, args.featureslen);
+				args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
+			}
+
+			/*
+			 * The rest of the TLV contains one or more message
+			 * blocks...
+			 */
+			incomingim_ch1_parsemsgs(sess, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
+
+		} else if (type == 0x0003) { /* Server Ack Requested */
+
+			args.icbmflags |= AIM_IMFLAGS_ACK;
+
+		} else if (type == 0x0004) { /* Message is Auto Response */
+
+			args.icbmflags |= AIM_IMFLAGS_AWAY;
+
+		} else if (type == 0x0006) { /* Message was received offline. */
+
+			/* XXX - not sure if this actually gets sent. */
+			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
+
+		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
+
+			args.iconlen = aimbs_get32(bs);
+			aimbs_get16(bs); /* 0x0001 */
+			args.iconsum = aimbs_get16(bs);
+			args.iconstamp = aimbs_get32(bs);
+
+			/*
+			 * This looks to be a client bug.  MacAIM 4.3 will
+			 * send this tag, but with all zero values, in the
+			 * first message of a conversation. This makes no
+			 * sense whatsoever, so I'm going to say its a bug.
+			 *
+			 * You really shouldn't advertise a zero-length icon
+			 * anyway.
+			 *
+			 */
+			if (args.iconlen)
+				args.icbmflags |= AIM_IMFLAGS_HASICON;
+
+		} else if (type == 0x0009) {
+
+			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
+
+		} else if (type == 0x000b) { /* Non-direct connect typing notification */
+
+			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
+
+		} else if (type == 0x0017) {
+
+			free(args.extdata);
+			args.extdatalen = length;
+			if (args.extdatalen > aim_bstream_empty(bs))
+			{
+				gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
+			if (args.extdatalen == 0)
+				args.extdata = NULL;
+			else
+				args.extdata = aimbs_getraw(bs, args.extdatalen);
+
+		} else {
+			gaim_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
+		}
+
+		/*
+		 * This is here to protect ourselves from ourselves.  That
+		 * is, if something above doesn't completely parse its value
+		 * section, or, worse, overparses it, this will set the
+		 * stream where it needs to be in order to land on the next
+		 * TLV when the loop continues.
+		 *
+		 */
+		aim_bstream_setpos(bs, endpos);
+	}
+
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+	aim_mpmsg_free(sess, &args.mpmsg);
+	free(args.features);
+	free(args.extdata);
+
+	return ret;
+}
+
+static void incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+
+	/*
+	 * This goes like this...
+	 *
+	 *   group name length
+	 *   group name
+	 *     num of buddies in group
+	 *     buddy name length
+	 *     buddy name
+	 *     buddy name length
+	 *     buddy name
+	 *     ...
+	 *   group name length
+	 *   group name
+	 *     num of buddies in group
+	 *     buddy name length
+	 *     buddy name
+	 *     ...
+	 *   ...
+	 */
+	while (servdata && aim_bstream_empty(servdata)) {
+		guint16 gnlen, numb;
+		int i;
+		char *gn;
+
+		gnlen = aimbs_get16(servdata);
+		gn = aimbs_getstr(servdata, gnlen);
+		numb = aimbs_get16(servdata);
+
+		for (i = 0; i < numb; i++) {
+			guint16 bnlen;
+			char *bn;
+
+			bnlen = aimbs_get16(servdata);
+			bn = aimbs_getstr(servdata, bnlen);
+
+			gaim_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
+
+			free(bn);
+		}
+
+		free(gn);
+	}
+
+	return;
+}
+
+static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+
+	free(args->info.icon.icon);
+
+	return;
+}
+
+static void incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+
+	if (servdata) {
+		args->info.icon.checksum = aimbs_get32(servdata);
+		args->info.icon.length = aimbs_get32(servdata);
+		args->info.icon.timestamp = aimbs_get32(servdata);
+		args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length);
+	}
+
+	args->destructor = (void *)incomingim_ch2_buddyicon_free;
+
+	return;
+}
+
+static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+
+	/* XXX - aim_chat_roominfo_free() */
+	free(args->info.chat.roominfo.name);
+
+	return;
+}
+
+static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+
+	/*
+	 * Chat room info.
+	 */
+	if (servdata)
+		aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
+
+	args->destructor = (void *)incomingim_ch2_chat_free;
+
+	return;
+}
+
+static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+
+	free((char *)args->info.rtfmsg.rtfmsg);
+
+	return;
+}
+
+/*
+ * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is
+ * kind of odd. This sends the client ICQRTF since that is all that I've seen
+ * SERVERRELAY used for.
+ *
+ * Note that this is all little-endian.  Cringe.
+ *
+ */
+static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+	guint16 hdrlen, anslen, msglen;
+
+	hdrlen = aimbs_getle16(servdata);
+	aim_bstream_advance(servdata, hdrlen);
+
+	hdrlen = aimbs_getle16(servdata);
+	aim_bstream_advance(servdata, hdrlen);
+
+	args->info.rtfmsg.msgtype = aimbs_getle16(servdata);
+
+	anslen = aimbs_getle32(servdata);
+	aim_bstream_advance(servdata, anslen);
+
+	msglen = aimbs_getle16(servdata);
+	args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen);
+
+	args->info.rtfmsg.fgcolor = aimbs_getle32(servdata);
+	args->info.rtfmsg.bgcolor = aimbs_getle32(servdata);
+
+	hdrlen = aimbs_getle32(servdata);
+	aim_bstream_advance(servdata, hdrlen);
+
+	args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
+
+	return;
+}
+
+static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+	free(args->info.sendfile.filename);
+}
+
+static void incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+
+	args->destructor = (void *)incomingim_ch2_sendfile_free;
+
+	/* Maybe there is a better way to tell what kind of sendfile
+	 * this is?  Maybe TLV t(000a)? */
+	if (servdata) { /* Someone is sending us a file */
+		int flen;
+
+		/* subtype is one of AIM_OFT_SUBTYPE_* */
+		args->info.sendfile.subtype = aimbs_get16(servdata);
+		args->info.sendfile.totfiles = aimbs_get16(servdata);
+		args->info.sendfile.totsize = aimbs_get32(servdata);
+
+		/*
+		 * I hope to God I'm right when I guess that there is a
+		 * 32 char max filename length for single files.  I think
+		 * OFT tends to do that.  Gotta love inconsistency.  I saw
+		 * a 26 byte filename?
+		 */
+		/* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */
+		/* Use an inelegant way of getting the null-terminated filename,
+		 * since there's no easy bstream routine. */
+		for (flen = 0; aimbs_get8(servdata); flen++);
+		aim_bstream_advance(servdata, -flen -1);
+		args->info.sendfile.filename = aimbs_getstr(servdata, flen);
+
+		/* There is sometimes more after the null-terminated filename,
+		 * but I'm unsure of its format. */
+		/* I don't believe him. */
+		/* There is sometimes a null byte inside a unicode filename,
+		 * but as far as I can tell the filename is the last
+		 * piece of data that will be in this message. --Jonathan */
+	}
+
+	return;
+}
+
+typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args);
+
+static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlv_t *block1, *servdatatlv;
+	aim_tlvlist_t *list2;
+	struct aim_incomingim_ch2_args args;
+	aim_bstream_t bbs, sdbs, *sdbsptr = NULL;
+	guint8 *cookie2;
+	int ret = 0;
+
+	char proxyip[30] = {""};
+	char clientip[30] = {""};
+	char verifiedip[30] = {""};
+
+	memset(&args, 0, sizeof(args));
+
+	/*
+	 * There's another block of TLVs embedded in the type 5 here.
+	 */
+	block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
+	aim_bstream_init(&bbs, block1->value, block1->length);
+
+	/*
+	 * First two bytes represent the status of the connection.
+	 *
+	 * 0 is a request, 1 is a cancel, 2 is an accept
+	 */
+	args.status = aimbs_get16(&bbs);
+
+	/*
+	 * Next comes the cookie.  Should match the ICBM cookie.
+	 */
+	cookie2 = aimbs_getraw(&bbs, 8);
+	if (memcmp(cookie, cookie2, 8) != 0)
+		gaim_debug_misc("oscar", "rend: warning cookies don't match!\n");
+	memcpy(args.cookie, cookie2, 8);
+	free(cookie2);
+
+	/*
+	 * The next 16bytes are a capability block so we can
+	 * identify what type of rendezvous this is.
+	 */
+	args.reqclass = aim_locate_getcaps(sess, &bbs, 0x10);
+
+	/*
+	 * What follows may be TLVs or nothing, depending on the
+	 * purpose of the message.
+	 *
+	 * Ack packets for instance have nothing more to them.
+	 */
+	list2 = aim_tlvlist_read(&bbs);
+
+	/*
+	 * IP address to proxy the file transfer through.
+	 *
+	 * XXX - I don't like this.  Maybe just read in an int?  Or inet_ntoa...
+	 */
+	if (aim_tlv_gettlv(list2, 0x0002, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_tlv_gettlv(list2, 0x0002, 1);
+		if (iptlv->length == 4)
+			snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
+	}
+
+	/*
+	 * IP address from the perspective of the client.
+	 */
+	if (aim_tlv_gettlv(list2, 0x0003, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_tlv_gettlv(list2, 0x0003, 1);
+		if (iptlv->length == 4)
+			snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
+	}
+
+	/*
+	 * Verified IP address (from the perspective of Oscar).
+	 *
+	 * This is added by the server.
+	 */
+	if (aim_tlv_gettlv(list2, 0x0004, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_tlv_gettlv(list2, 0x0004, 1);
+		if (iptlv->length == 4)
+			snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
+	}
+
+	/*
+	 * Port number for something.
+	 */
+	if (aim_tlv_gettlv(list2, 0x0005, 1))
+		args.port = aim_tlv_get16(list2, 0x0005, 1);
+
+	/*
+	 * Something to do with ft? -- two bytes
+	 * 0x0001 - "I want to send you this file"
+	 * 0x0002 - "I will accept this file from you"
+	 * 0x0002 - Also used in ICQ Lite Beta 4.0 URLs
+	 */
+	 /*
+	  * This is what I call the request number of the file transfer
+	  * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
+	  * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
+	  * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
+	  * -- Jonathan
+	  */
+	if (aim_tlv_gettlv(list2, 0x000a, 1))
+		args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1);
+
+	/*
+	 * Error code.
+	 */
+	if (aim_tlv_gettlv(list2, 0x000b, 1))
+		args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
+
+	/*
+	 * Invitation message / chat description.
+	 */
+	if (aim_tlv_gettlv(list2, 0x000c, 1)) {
+		args.msg = aim_tlv_getstr(list2, 0x000c, 1);
+		args.msglen = aim_tlv_getlength(list2, 0x000c, 1);
+	}
+
+	/*
+	 * Character set.
+	 */
+	if (aim_tlv_gettlv(list2, 0x000d, 1))
+		args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
+
+	/*
+	 * Language.
+	 */
+	if (aim_tlv_gettlv(list2, 0x000e, 1))
+		args.language = aim_tlv_getstr(list2, 0x000e, 1);
+
+#if 0
+	/*
+	 * Unknown -- no value
+	 *
+	 * Maybe means we should connect directly to transfer the file?
+	 * Also used in ICQ Lite Beta 4.0 URLs.  Also empty.
+	 */
+	 /* I don't think this indicates a direct transfer; this flag is
+	  * also present in a stage 1 proxied file send request -- Jonathan */
+	if (aim_tlv_gettlv(list2, 0x000f, 1)) {
+		/* Unhandled */
+	}
+#endif
+
+	/*
+	 * Flag meaning we should proxy the file transfer through an AIM server
+	 */
+	if (aim_tlv_gettlv(list2, 0x0010, 1))
+		args.info.sendfile.use_proxy = TRUE;
+	else
+		args.info.sendfile.use_proxy = FALSE;
+
+	if (strlen(proxyip))
+		args.proxyip = (char *)proxyip;
+	if (strlen(clientip))
+		args.clientip = (char *)clientip;
+	if (strlen(verifiedip))
+		args.verifiedip = (char *)verifiedip;
+
+	/*
+	 * This must be present in PROPOSALs, but will probably not
+	 * exist in CANCELs and ACCEPTs.  Also exists in ICQ Lite
+	 * Beta 4.0 URLs (AIM_CAPS_ICQSERVERRELAY).
+	 *
+	 * Service Data blocks are module-specific in format.
+	 */
+	if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
+
+		aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length);
+		sdbsptr = &sdbs;
+	}
+
+	/*
+	 * The rest of the handling depends on what type it is.
+	 *
+	 * Not all of them have special handling (yet).
+	 */
+	if (args.reqclass & AIM_CAPS_BUDDYICON)
+		incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+	else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST)
+		incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+	else if (args.reqclass & AIM_CAPS_CHAT)
+		incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+	else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)
+		incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+	else if (args.reqclass & AIM_CAPS_SENDFILE)
+		incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+
+	if (args.destructor)
+		((ch2_args_destructor_t)args.destructor)(sess, &args);
+
+	free((char *)args.msg);
+	free((char *)args.encoding);
+	free((char *)args.language);
+
+	aim_tlvlist_free(&list2);
+
+	return ret;
+}
+
+static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+	aim_bstream_t meat;
+	aim_rxcallback_t userfunc;
+	aim_tlv_t *block;
+	struct aim_incomingim_ch4_args args;
+	int ret = 0;
+
+	/*
+	 * Make a bstream for the meaty part.  Yum.  Meat.
+	 */
+	if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
+		return -1;
+	aim_bstream_init(&meat, block->value, block->length);
+
+	args.uin = aimbs_getle32(&meat);
+	args.type = aimbs_getle8(&meat);
+	args.flags = aimbs_getle8(&meat);
+	args.msglen = aimbs_getle16(&meat);
+	args.msg = (gchar *)aimbs_getraw(&meat, args.msglen);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+	free(args.msg);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0007
+ *
+ * It can easily be said that parsing ICBMs is THE single
+ * most difficult thing to do in the in AIM protocol.  In
+ * fact, I think I just did say that.
+ *
+ * Below is the best damned solution I've come up with
+ * over the past sixteen months of battling with it. This
+ * can parse both away and normal messages from every client
+ * I have access to.  Its not fast, its not clean.  But it works.
+ *
+ */
+static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	guchar *cookie;
+	guint16 channel;
+	aim_userinfo_t userinfo;
+
+	memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
+
+	/*
+	 * Read ICBM Cookie.
+	 */
+	cookie = aimbs_getraw(bs, 8);
+
+	/*
+	 * Channel ID.
+	 *
+	 * Channel 0x0001 is the message channel.  It is
+	 * used to send basic ICBMs.
+	 *
+	 * Channel 0x0002 is the Rendezvous channel, which
+	 * is where Chat Invitiations and various client-client
+	 * connection negotiations come from.
+	 *
+	 * Channel 0x0003 is used for chat messages.
+	 *
+	 * Channel 0x0004 is used for ICQ authorization, or
+	 * possibly any system notice.
+	 *
+	 */
+	channel = aimbs_get16(bs);
+
+	/*
+	 * Extract the standard user info block.
+	 *
+	 * Note that although this contains TLVs that appear contiguous
+	 * with the TLVs read below, they are two different pieces.  The
+	 * userinfo block contains the number of TLVs that contain user
+	 * information, the rest are not even though there is no separation.
+	 * You can start reading the message TLVs after aim_info_extract()
+	 * parses out the standard userinfo block.
+	 *
+	 * That also means that TLV types can be duplicated between the
+	 * userinfo block and the rest of the message, however there should
+	 * never be two TLVs of the same type in one block.
+	 *
+	 */
+	aim_info_extract(sess, bs, &userinfo);
+
+	/*
+	 * From here on, its depends on what channel we're on.
+	 *
+	 * Technically all channels have a TLV list have this, however,
+	 * for the common channel 1 case, in-place parsing is used for
+	 * performance reasons (less memory allocation).
+	 */
+	if (channel == 1) {
+
+		ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
+
+	} else if (channel == 2) {
+		aim_tlvlist_t *tlvlist;
+
+		/*
+		 * Read block of TLVs (not including the userinfo data).  All
+		 * further data is derived from what is parsed here.
+		 */
+		tlvlist = aim_tlvlist_read(bs);
+
+		ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+
+		aim_tlvlist_free(&tlvlist);
+
+	} else if (channel == 4) {
+		aim_tlvlist_t *tlvlist;
+
+		tlvlist = aim_tlvlist_read(bs);
+		ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+		aim_tlvlist_free(&tlvlist);
+
+	} else {
+		gaim_debug_misc("oscar", "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
+	}
+
+	aim_info_free(&userinfo);
+	free(cookie);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0008 - Send a warning to sn.
+ *
+ * Flags:
+ *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
+ *
+ * returns -1 on error (couldn't alloc packet), 0 on success.
+ *
+ */
+faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, guint32 flags)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
+
+	aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x000a */
+static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 channel, nummissed, reason;
+	aim_userinfo_t userinfo;
+
+	while (aim_bstream_empty(bs)) {
+
+		channel = aimbs_get16(bs);
+		aim_info_extract(sess, bs, &userinfo);
+		nummissed = aimbs_get16(bs);
+		reason = aimbs_get16(bs);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			 ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
+
+		aim_info_free(&userinfo);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000b
+ *
+ * Possible codes:
+ *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
+ *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
+ *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
+ *
+ */
+faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, guint16 code)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+
+	aimbs_putraw(&fr->data, cookie, 8);
+
+	aimbs_put16(&fr->data, 0x0002); /* channel */
+	aimbs_put8(&fr->data, strlen(sender));
+	aimbs_putstr(&fr->data, sender);
+
+	aim_tlvlist_add_16(&tl, 0x0003, code);
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x000b - Receive the response from an ICQ status message request.
+ *
+ * This contains the ICQ status message.  Go figure.
+ *
+ */
+static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 channel, reason;
+	char *sn;
+	guchar *cookie;
+	guint8 snlen;
+
+	cookie = aimbs_getraw(bs, 8);
+	channel = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+	reason = aimbs_get16(bs);
+
+	if (channel == 0x0002) { /* File transfer declined */
+		aimbs_get16(bs); /* Unknown */
+		aimbs_get16(bs); /* Unknown */
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx, channel, sn, reason, cookie);
+	} else if (channel == 0x0004) { /* ICQ message */
+		switch (reason) {
+			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
+				guint8 statusmsgtype, *msg;
+				guint16 len;
+				guint32 state;
+
+				len = aimbs_getle16(bs); /* Should be 0x001b */
+				aim_bstream_advance(bs, len); /* Unknown */
+
+				len = aimbs_getle16(bs); /* Should be 0x000e */
+				aim_bstream_advance(bs, len); /* Unknown */
+
+				statusmsgtype = aimbs_getle8(bs);
+				switch (statusmsgtype) {
+					case 0xe8:
+						state = AIM_ICQ_STATE_AWAY;
+						break;
+					case 0xe9:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
+						break;
+					case 0xea:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
+						break;
+					case 0xeb:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
+						break;
+					case 0xec:
+						state = AIM_ICQ_STATE_CHAT;
+						break;
+					default:
+						state = 0;
+						break;
+				}
+
+				aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
+				aimbs_getle16(bs); /* Unknown - 0x0000 */
+				aimbs_getle16(bs); /* Unknown - 0x0000 */
+
+				len = aimbs_getle16(bs);
+				msg = aimbs_getraw(bs, len);
+
+				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+					ret = userfunc(sess, rx, channel, sn, reason, state, msg);
+
+				free(msg);
+			} break;
+
+			default: {
+				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+					ret = userfunc(sess, rx, channel, sn, reason);
+			} break;
+		} /* end switch */
+	}
+
+	free(cookie);
+	free(sn);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000c - Receive an ack after sending an ICBM.
+ *
+ * You have to have send the message with the AIM_IMFLAGS_ACK flag set
+ * (TLV t(0003)).  The ack contains the ICBM header of the message you
+ * sent.
+ *
+ */
+static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint16 ch;
+	guchar *cookie;
+	char *sn;
+	int ret = 0;
+
+	cookie = aimbs_getraw(bs, 8);
+	ch = aimbs_get16(bs);
+	sn = aimbs_getstr(bs, aimbs_get8(bs));
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, ch, sn);
+
+	free(sn);
+	free(cookie);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
+ *
+ * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
+ * and Gaim 0.60 and newer.
+ *
+ */
+faim_export int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid);
+
+	/*
+	 * 8 days of light
+	 * Er, that is to say, 8 bytes of 0's
+	 */
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/*
+	 * Type 1 (should be 0x0001 for mtn)
+	 */
+	aimbs_put16(&fr->data, type1);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	/*
+	 * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn)
+	 */
+	aimbs_put16(&fr->data, type2);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
+ *
+ * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
+ * and Gaim 0.60 and newer.
+ *
+ */
+static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	char *sn;
+	guint8 snlen;
+	guint16 type1, type2;
+
+	aim_bstream_advance(bs, 8); /* Unknown - All 0's */
+	type1 = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+	type2 = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, type1, sn, type2);
+
+	free(sn);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0005)
+		return aim_im_paraminfo(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return outgoingim(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return incomingim(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000a)
+		return missedcall(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000b)
+		return clientautoresp(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000c)
+		return msgack(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0014)
+		return mtn_receive(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0004;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "messaging", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_icq.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,715 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0015 - Encapsulated ICQ.
+ *
+ */
+
+#include "oscar.h"
+
+faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x003c); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x003e); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+faim_export int
+aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x0c3a); /* shrug. */
+	aimbs_putle16(&fr->data, 0x030c);
+	aimbs_putle16(&fr->data, 0x0001);
+	aimbs_putle8(&fr->data, webaware);
+	aimbs_putle8(&fr->data, 0xf8);
+	aimbs_putle8(&fr->data, 0x02);
+	aimbs_putle8(&fr->data, 0x01);
+	aimbs_putle8(&fr->data, 0x00);
+	aimbs_putle8(&fr->data, !auth_required);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Change your ICQ password.
+ *
+ * @param sess The oscar session
+ * @param passwd The new password.  If this is longer than 8 characters it
+ *        will be truncated.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen, passwdlen;
+
+	if (!passwd)
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	passwdlen = strlen(passwd);
+	if (passwdlen > MAXICQPASSLEN)
+		passwdlen = MAXICQPASSLEN;
+	bslen = 2+4+2+2+2+2+passwdlen+1;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x042e); /* shrug. */
+	aimbs_putle16(&fr->data, passwdlen+1);
+	aimbs_putstr(&fr->data, passwd);
+	aimbs_putle8(&fr->data, '\0');
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+	struct aim_icq_info *info;
+
+	if (!uin || uin[0] < '0' || uin[0] > '9')
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
+	aimbs_putle32(&fr->data, atoi(uin));
+
+	aim_tx_enqueue(sess, fr);
+
+	/* Keep track of this request and the ICQ number and request ID */
+	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
+	info->reqid = snacid;
+	info->uin = atoi(uin);
+	info->next = sess->icq_info;
+	sess->icq_info = info;
+
+	return 0;
+}
+
+faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+	struct aim_icq_info *info;
+
+	if (!uin || uin[0] < '0' || uin[0] > '9')
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x04ba); /* shrug. */
+	aimbs_putle32(&fr->data, atoi(uin));
+
+	aim_tx_enqueue(sess, fr);
+
+	/* Keep track of this request and the ICQ number and request ID */
+	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
+	info->reqid = snacid;
+	info->uin = atoi(uin);
+	info->next = sess->icq_info;
+	sess->icq_info = info;
+
+	return 0;
+}
+
+faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!uin || uin[0] < '0' || uin[0] > '9')
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x051f); /* shrug. */
+	aimbs_putle32(&fr->data, atoi(uin));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+#if 0
+faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!xml || !strlen(xml))
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 10 + 2 + strlen(xml) + 1;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x0998); /* shrug. */
+	aimbs_putle16(&fr->data, strlen(xml) + 1);
+	aimbs_putraw(&fr->data, (guint8 *)xml, strlen(xml) + 1);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+#endif
+
+#if 0
+/*
+ * Send an SMS message.  This is the non-US way.  The US-way is to IM
+ * their cell phone number (+19195551234).
+ *
+ * We basically construct and send an XML message.  The format is:
+ * <icq_sms_message>
+ *   <destination>full_phone_without_leading_+</destination>
+ *   <text>message</text>
+ *   <codepage>1252</codepage>
+ *   <senders_UIN>self_uin</senders_UIN>
+ *   <senders_name>self_name</senders_name>
+ *   <delivery_receipt>Yes|No</delivery_receipt>
+ *   <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time>
+ * </icq_sms_message>
+ *
+ * Yeah hi Peter, whaaaat's happening.  If there's any way to use
+ * a codepage other than 1252 that would be great.  Thaaaanks.
+ */
+faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen, xmllen;
+	char *xml;
+	const char *timestr;
+	time_t t;
+	struct tm *tm;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	if (!name || !msg || !alias)
+		return -EINVAL;
+
+	time(&t);
+	tm = gmtime(&t);
+	timestr = gaim_utf8_strftime("%a, %d %b %Y %T %Z", tm);
+
+	/* The length of xml included the null terminating character */
+	xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1;
+
+	if (!(xml = (char *)malloc(xmllen*sizeof(char))))
+		return -ENOMEM;
+	snprintf(xml, xmllen, "<icq_sms_message>\n"
+		"\t<destination>%s</destination>\n"
+		"\t<text>%s</text>\n"
+		"\t<codepage>1252</codepage>\n"
+		"\t<senders_UIN>%s</senders_UIN>\n"
+		"\t<senders_name>%s</senders_name>\n"
+		"\t<delivery_receipt>Yes</delivery_receipt>\n"
+		"\t<time>%s</time>\n"
+		"</icq_sms_message>\n",
+		name, msg, sess->sn, alias, timestr);
+
+	bslen = 37 + xmllen;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) {
+		free(xml);
+		return -ENOMEM;
+	}
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+
+	/* From libicq200-0.3.2/src/SNAC-SRV.cpp */
+	aimbs_putle16(&fr->data, 0x8214);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0016);
+	aimbs_put32(&fr->data, 0x00000000);
+	aimbs_put32(&fr->data, 0x00000000);
+	aimbs_put32(&fr->data, 0x00000000);
+	aimbs_put32(&fr->data, 0x00000000);
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, xmllen);
+	aimbs_putstr(&fr->data, xml);
+
+	aim_tx_enqueue(sess, fr);
+
+	free(xml);
+
+	return 0;
+}
+#endif
+
+static void aim_icq_freeinfo(struct aim_icq_info *info) {
+	int i;
+
+	if (!info)
+		return;
+	free(info->nick);
+	free(info->first);
+	free(info->last);
+	free(info->email);
+	free(info->homecity);
+	free(info->homestate);
+	free(info->homephone);
+	free(info->homefax);
+	free(info->homeaddr);
+	free(info->mobile);
+	free(info->homezip);
+	free(info->personalwebpage);
+	if (info->email2)
+		for (i = 0; i < info->numaddresses; i++)
+			free(info->email2[i]);
+	free(info->email2);
+	free(info->workcity);
+	free(info->workstate);
+	free(info->workphone);
+	free(info->workfax);
+	free(info->workaddr);
+	free(info->workzip);
+	free(info->workcompany);
+	free(info->workdivision);
+	free(info->workposition);
+	free(info->workwebpage);
+	free(info->info);
+	free(info);
+}
+
+/**
+ * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
+ */
+static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_tlvlist_t *tl;
+	aim_tlv_t *datatlv;
+	aim_bstream_t qbs;
+	guint32 ouruin;
+	guint16 cmdlen, cmd, reqid;
+
+	if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) {
+		aim_tlvlist_free(&tl);
+		gaim_debug_misc("oscar", "corrupt ICQ response\n");
+		return 0;
+	}
+
+	aim_bstream_init(&qbs, datatlv->value, datatlv->length);
+
+	cmdlen = aimbs_getle16(&qbs);
+	ouruin = aimbs_getle32(&qbs);
+	cmd = aimbs_getle16(&qbs);
+	reqid = aimbs_getle16(&qbs);
+
+	gaim_debug_misc("oscar", "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid);
+
+	if (cmd == 0x0041) { /* offline message */
+		struct aim_icq_offlinemsg msg;
+		aim_rxcallback_t userfunc;
+
+		memset(&msg, 0, sizeof(msg));
+
+		msg.sender = aimbs_getle32(&qbs);
+		msg.year = aimbs_getle16(&qbs);
+		msg.month = aimbs_getle8(&qbs);
+		msg.day = aimbs_getle8(&qbs);
+		msg.hour = aimbs_getle8(&qbs);
+		msg.minute = aimbs_getle8(&qbs);
+		msg.type = aimbs_getle8(&qbs);
+		msg.flags = aimbs_getle8(&qbs);
+		msg.msglen = aimbs_getle16(&qbs);
+		msg.msg = aimbs_getstr(&qbs, msg.msglen);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSG)))
+			ret = userfunc(sess, rx, &msg);
+
+		free(msg.msg);
+
+	} else if (cmd == 0x0042) {
+		aim_rxcallback_t userfunc;
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE)))
+			ret = userfunc(sess, rx);
+
+	} else if (cmd == 0x07da) { /* information */
+		guint16 subtype;
+		struct aim_icq_info *info;
+		aim_rxcallback_t userfunc;
+
+		subtype = aimbs_getle16(&qbs);
+		aim_bstream_advance(&qbs, 1); /* 0x0a */
+
+		/* find other data from the same request */
+		for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next);
+		if (!info) {
+			info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
+			info->reqid = reqid;
+			info->next = sess->icq_info;
+			sess->icq_info = info;
+		}
+
+		switch (subtype) {
+		case 0x00a0: { /* hide ip status */
+			/* nothing */
+		} break;
+
+		case 0x00aa: { /* password change status */
+			/* nothing */
+		} break;
+
+		case 0x00c8: { /* general and "home" information */
+			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->homecountry = aimbs_getle16(&qbs);
+			/* 0x0a 00 02 00 */
+			/* 1 byte timezone? */
+			/* 1 byte hide email flag? */
+		} break;
+
+		case 0x00dc: { /* personal information */
+			info->age = aimbs_getle8(&qbs);
+			info->unknown = aimbs_getle8(&qbs);
+			info->gender = aimbs_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */
+			info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->birthyear = aimbs_getle16(&qbs);
+			info->birthmonth = aimbs_getle8(&qbs);
+			info->birthday = aimbs_getle8(&qbs);
+			info->language1 = aimbs_getle8(&qbs);
+			info->language2 = aimbs_getle8(&qbs);
+			info->language3 = aimbs_getle8(&qbs);
+			/* 0x00 00 01 00 00 01 00 00 00 00 00 */
+		} break;
+
+		case 0x00d2: { /* work information */
+			info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workcountry = aimbs_getle16(&qbs);
+			info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			aim_bstream_advance(&qbs, 2); /* 0x01 00 */
+			info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+		} break;
+
+		case 0x00e6: { /* additional personal information */
+			info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1);
+		} break;
+
+		case 0x00eb: { /* email address(es) */
+			int i;
+			info->numaddresses = aimbs_getle16(&qbs);
+			info->email2 = (char **)calloc(info->numaddresses, sizeof(char *));
+			for (i = 0; i < info->numaddresses; i++) {
+				info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+				if (i+1 != info->numaddresses)
+					aim_bstream_advance(&qbs, 1); /* 0x00 */
+			}
+		} break;
+
+		case 0x00f0: { /* personal interests */
+		} break;
+
+		case 0x00fa: { /* past background and current organizations */
+		} break;
+
+		case 0x0104: { /* alias info */
+			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */
+			/* Then 0x00 02 00 */
+		} break;
+
+		case 0x010e: { /* unknown */
+			/* 0x00 00 */
+		} break;
+
+		case 0x019a: { /* simple info */
+			aim_bstream_advance(&qbs, 2);
+			info->uin = aimbs_getle32(&qbs);
+			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+			/* Then 0x00 02 00 00 00 00 00 */
+		} break;
+		} /* End switch statement */
+
+		if (!(snac->flags & 0x0001)) {
+			if (subtype != 0x0104)
+				if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_INFO)))
+					ret = userfunc(sess, rx, info);
+
+			if (info->uin && info->nick)
+				if ((userfunc = aim_callhandler(sess, rx->conn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_ALIAS)))
+					ret = userfunc(sess, rx, info);
+
+			if (sess->icq_info == info) {
+				sess->icq_info = info->next;
+			} else {
+				struct aim_icq_info *cur;
+				for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
+				if (cur->next)
+					cur->next = cur->next->next;
+			}
+			aim_icq_freeinfo(info);
+		}
+	}
+
+	aim_tlvlist_free(&tl);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return icqresponse(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+static void icq_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	struct aim_icq_info *del;
+
+	while (sess->icq_info) {
+		del = sess->icq_info;
+		sess->icq_info = sess->icq_info->next;
+		aim_icq_freeinfo(del);
+	}
+
+	return;
+}
+
+faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0015;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x047c;
+	mod->flags = 0;
+	strncpy(mod->name, "icq", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = icq_shutdown;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_invite.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,53 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0006 - This isn't really ever used by anyone anymore.
+ *
+ * Once upon a time, there used to be a menu item in AIM clients that
+ * said something like "Invite a friend to use AIM..." and then it would
+ * ask for an email address and it would sent a mail to them saying
+ * how perfectly wonderful the AIM service is and why you should use it
+ * and click here if you hate the person who sent this to you and want to
+ * complain and yell at them in a small box with pretty fonts.
+ *
+ * I could've sworn libfaim had this implemented once, a long long time ago,
+ * but I can't find it.
+ *
+ * I'm mainly adding this so that I can keep advertising that we support
+ * group 6, even though we don't.
+ *
+ */
+
+#include "oscar.h"
+
+faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0006;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "invite", sizeof(mod->name));
+	mod->snachandler = NULL;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_locate.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,1409 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0002 - Locate.
+ *
+ * The functions here are responsible for requesting and parsing information-
+ * gathering SNACs.  Or something like that.  This family contains the SNACs
+ * for getting and setting info, away messages, directory profile thingy, etc.
+ */
+
+#include "oscar.h"
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+/*
+ * Capability blocks.
+ *
+ * These are CLSIDs. They should actually be of the form:
+ *
+ * {0x0946134b, 0x4c7f, 0x11d1,
+ *  {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
+ *
+ * But, eh.
+ */
+static const struct {
+	guint32 flag;
+	guint8 data[16];
+} aim_caps[] = {
+
+	/*
+	 * These are in ascending numerical order.
+	 */
+
+	/*
+	 * Perhaps better called AIM_CAPS_SHORTCAPS
+	 */
+	{AIM_CAPS_ICHAT,
+	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SECUREIM,
+	 {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_VIDEO,
+	 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* "Live Video" support in Windows AIM 5.5.3501 and newer */
+	{AIM_CAPS_LIVEVIDEO,
+	 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* "Camera" support in Windows AIM 5.5.3501 and newer */
+	{AIM_CAPS_CAMERA,
+	 {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* In Windows AIM 5.5.3501 and newer */
+	{AIM_CAPS_GENERICUNKNOWN,
+	 {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* In iChatAV (version numbers...?) */
+	{AIM_CAPS_ICHATAV,
+	 {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
+
+	/*
+	 * Not really sure about this one.  In an email from
+	 * 26 Sep 2003, Matthew Sachs suggested that, "this
+	 * is probably the capability for the SMS features."
+	 */
+	{AIM_CAPS_SMS,
+	 {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_GENERICUNKNOWN,
+	 {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_GENERICUNKNOWN,
+	 {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_GENERICUNKNOWN,
+	 {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_HIPTOP,
+	 {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_TALK,
+	 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SENDFILE,
+	 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICQ_DIRECT,
+	 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_DIRECTIM,
+	 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_BUDDYICON,
+	 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ADDINS,
+	 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_GETFILE,
+	 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICQSERVERRELAY,
+	 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	 * Indeed, there are two of these.  The former appears to be correct,
+	 * but in some versions of winaim, the second one is set.  Either they
+	 * forgot to fix endianness, or they made a typo. It really doesn't
+	 * matter which.
+	 */
+	{AIM_CAPS_GAMES,
+	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+	{AIM_CAPS_GAMES2,
+	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SENDBUDDYLIST,
+	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	 * Setting this lets AIM users receive messages from ICQ users, and ICQ
+	 * users receive messages from AIM users.  It also lets ICQ users show
+	 * up in buddy lists for AIM users, and AIM users show up in buddy lists
+	 * for ICQ users.  And ICQ privacy/invisibility acts like AIM privacy,
+	 * in that if you add a user to your deny list, you will not be able to
+	 * see them as online (previous you could still see them, but they
+	 * couldn't see you.
+	 */
+	{AIM_CAPS_INTEROPERATE,
+	 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICQUTF8,
+	 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICQUTF8OLD,
+	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
+	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
+
+	/*
+	 * Chat is oddball.
+	 */
+	{AIM_CAPS_CHAT,
+	 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	{AIM_CAPS_ICQ2GO,
+	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
+	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
+	*/
+
+	{AIM_CAPS_ICQRTF,
+	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
+	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
+
+	/* This is added by the servers and it only shows up for ourselves... */
+	{AIM_CAPS_GENERICUNKNOWN,
+	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
+	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}},
+
+	{AIM_CAPS_APINFO,
+	 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
+	  0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
+
+	{AIM_CAPS_TRILLIANCRYPT,
+	 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
+	  0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
+
+	{AIM_CAPS_EMPTY,
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+
+	{AIM_CAPS_LAST,
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+};
+
+/*
+ * Add the userinfo to our linked list.  If we already have userinfo
+ * for this buddy, then just overwrite parts of the old data.
+ *
+ * @param userinfo Contains the new information for the buddy.
+ */
+static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) {
+	aim_userinfo_t *cur;
+	aim_conn_t *conn;
+	aim_rxcallback_t userfunc;
+
+	cur = aim_locate_finduserinfo(sess, userinfo->sn);
+
+	if (cur == NULL) {
+		cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t));
+		cur->sn = strdup(userinfo->sn);
+		cur->next = sess->locate.userinfo;
+		sess->locate.userinfo = cur;
+	}
+
+	cur->warnlevel = userinfo->warnlevel;
+	cur->idletime = userinfo->idletime;
+	if (userinfo->flags != 0)
+		cur->flags = userinfo->flags;
+	if (userinfo->createtime != 0)
+		cur->createtime = userinfo->createtime;
+	if (userinfo->membersince != 0)
+		cur->membersince = userinfo->membersince;
+	if (userinfo->onlinesince != 0)
+		cur->onlinesince = userinfo->onlinesince;
+	if (userinfo->sessionlen != 0)
+		cur->sessionlen = userinfo->sessionlen;
+	if (userinfo->capabilities != 0)
+		cur->capabilities = userinfo->capabilities;
+	cur->present |= userinfo->present;
+
+	if (userinfo->iconcsumlen > 0) {
+		free(cur->iconcsum);
+		cur->iconcsum = (guint8 *)malloc(userinfo->iconcsumlen);
+		memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen);
+		cur->iconcsumlen = userinfo->iconcsumlen;
+	}
+
+	if (userinfo->info != NULL) {
+		free(cur->info);
+		free(cur->info_encoding);
+		if (userinfo->info_len > 0) {
+			cur->info = (char *)malloc(userinfo->info_len);
+			memcpy(cur->info, userinfo->info, userinfo->info_len);
+		} else
+			cur->info = NULL;
+		cur->info_encoding = strdup(userinfo->info_encoding);
+		cur->info_len = userinfo->info_len;
+	}
+
+	if (userinfo->status != NULL) {
+		free(cur->status);
+		free(cur->status_encoding);
+		if (userinfo->status_len > 0) {
+			cur->status = (char *)malloc(userinfo->status_len);
+			memcpy(cur->status, userinfo->status, userinfo->status_len);
+		} else
+			cur->status = NULL;
+		if (userinfo->status_encoding != NULL)
+			cur->status_encoding = strdup(userinfo->status_encoding);
+		else
+			cur->status_encoding = NULL;
+		cur->status_len = userinfo->status_len;
+	}
+
+	if (userinfo->away != NULL) {
+		free(cur->away);
+		free(cur->away_encoding);
+		if (userinfo->away_len > 0) {
+			cur->away = (char *)malloc(userinfo->away_len);
+			memcpy(cur->away, userinfo->away, userinfo->away_len);
+		} else
+			cur->away = NULL;
+		cur->away_encoding = strdup(userinfo->away_encoding);
+		cur->away_len = userinfo->away_len;
+	}
+
+	/*
+	 * This callback can be used by a client if they want to know whenever
+	 * info for a buddy is updated.  For example, if a client shows away
+	 * messages in its buddy list, then it would need to know if a user's
+	 * away message changes.
+	 */
+	conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE);
+	if ((userfunc = aim_callhandler(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK)))
+		userfunc(sess, NULL, cur);
+}
+
+faim_export void aim_locate_dorequest(aim_session_t *sess) {
+	struct userinfo_node *cur = sess->locate.torequest;
+
+	if (cur == NULL)
+		return;
+
+	if (sess->locate.waiting_for_response == TRUE)
+		return;
+
+	sess->locate.waiting_for_response = TRUE;
+	aim_locate_getinfoshort(sess, cur->sn, 0x00000003);
+
+	/* Move this node to the "requested" queue */
+	sess->locate.torequest = cur->next;
+	cur->next = sess->locate.requested;
+	sess->locate.requested = cur;
+}
+
+/**
+ * Remove this screen name from our queue.  If this info was requested
+ * by our info request queue, then pop the next element off of the queue.
+ *
+ * @param sess The aim session.
+ * @param sn Screen name of the info we just received.
+ * @return True if the request was explicit (client requested the info),
+ *         false if the request was implicit (libfaim request the info).
+ */
+static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) {
+	struct userinfo_node *cur, *del;
+	int was_explicit = TRUE;
+
+	while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) {
+		del = sess->locate.requested;
+		sess->locate.requested = del->next;
+		was_explicit = FALSE;
+		free(del->sn);
+		free(del);
+	}
+
+	cur = sess->locate.requested;
+	while ((cur != NULL) && (cur->next != NULL)) {
+		if (aim_sncmp(sn, cur->next->sn) == 0) {
+			del = cur->next;
+			cur->next = del->next;
+			was_explicit = FALSE;
+			free(del->sn);
+			free(del);
+		} else
+			cur = cur->next;
+	}
+
+	if (!was_explicit) {
+		aim_conn_t *conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE);
+		aim_rxcallback_t userfunc;
+
+		sess->locate.waiting_for_response = FALSE;
+
+		if ((userfunc = aim_callhandler(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT)))
+			userfunc(sess, NULL);
+		else
+			aim_locate_dorequest(sess);
+	}
+
+	return was_explicit;
+}
+
+faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) {
+	struct userinfo_node *cur;
+
+	/* Make sure we aren't already requesting info for this buddy */
+	cur = sess->locate.torequest;
+	while (cur != NULL) {
+		if (aim_sncmp(sn, cur->sn) == 0)
+			return;
+		cur = cur->next;
+	}
+
+	/* Add a new node to our request queue */
+	cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node));
+	cur->sn = strdup(sn);
+	cur->next = sess->locate.torequest;
+	sess->locate.torequest = cur;
+
+	/* Actually request some info up in this piece */
+	aim_locate_dorequest(sess);
+}
+
+faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) {
+	aim_userinfo_t *cur = NULL;
+
+	if (sn == NULL)
+		return NULL;
+
+	cur = sess->locate.userinfo;
+
+	while (cur != NULL) {
+		if (aim_sncmp(cur->sn, sn) == 0)
+			return cur;
+		cur = cur->next;
+	}
+
+	return NULL;
+}
+
+faim_internal guint32 aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len)
+{
+	guint32 flags = 0;
+	int offset;
+
+	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
+		guint8 *cap;
+		int i, identified;
+
+		cap = aimbs_getraw(bs, 0x10);
+
+		for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
+			if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
+				flags |= aim_caps[i].flag;
+				identified++;
+				break; /* should only match once... */
+			}
+		}
+
+		if (!identified)
+			gaim_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
+					cap[0], cap[1], cap[2], cap[3],
+					cap[4], cap[5],
+					cap[6], cap[7],
+					cap[8], cap[9],
+					cap[10], cap[11], cap[12], cap[13],
+					cap[14], cap[15]);
+
+		free(cap);
+	}
+
+	return flags;
+}
+
+faim_internal guint32 aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len)
+{
+	guint32 flags = 0;
+	int offset;
+
+	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) {
+		guint8 *cap;
+		int i, identified;
+
+		cap = aimbs_getraw(bs, 0x02);
+
+		for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
+			if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) {
+				flags |= aim_caps[i].flag;
+				identified++;
+				break; /* should only match once... */
+			}
+		}
+
+		if (!identified)
+			gaim_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
+
+		free(cap);
+	}
+
+	return flags;
+}
+
+faim_internal int aimbs_putcaps(aim_bstream_t *bs, guint32 caps)
+{
+	int i;
+
+	if (!bs)
+		return -EINVAL;
+
+	for (i = 0; aim_bstream_empty(bs); i++) {
+
+		if (aim_caps[i].flag == AIM_CAPS_LAST)
+			break;
+
+		if (caps & aim_caps[i].flag)
+			aimbs_putraw(bs, aim_caps[i].data, 0x10);
+
+	}
+
+	return 0;
+}
+
+static void dumptlv(aim_session_t *sess, guint16 type, aim_bstream_t *bs, guint8 len)
+{
+	int i;
+
+	if (!sess || !bs || !len)
+		return;
+
+	gaim_debug_misc("oscar", "userinfo:   type  =0x%04x\n", type);
+	gaim_debug_misc("oscar", "userinfo:   length=0x%04x\n", len);
+	gaim_debug_misc("oscar", "userinfo:   value:\n");
+
+	for (i = 0; i < len; i++) {
+		if ((i % 8) == 0)
+			gaim_debug_misc("oscar", "\nuserinfo:        ");
+		gaim_debug_misc("oscar", "0x%2x ", aimbs_get8(bs));
+	}
+
+	gaim_debug_misc("oscar", "\n");
+
+	return;
+}
+
+faim_internal void aim_info_free(aim_userinfo_t *info)
+{
+	free(info->sn);
+	free(info->iconcsum);
+	free(info->info);
+	free(info->info_encoding);
+	free(info->status);
+	free(info->status_encoding);
+	free(info->away);
+	free(info->away_encoding);
+}
+
+/*
+ * AIM is fairly regular about providing user info.  This is a generic
+ * routine to extract it in its standard form.
+ */
+faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo)
+{
+	int curtlv, tlvcnt;
+	guint8 snlen;
+
+	if (!bs || !outinfo)
+		return -EINVAL;
+
+	/* Clear out old data first */
+	memset(outinfo, 0x00, sizeof(aim_userinfo_t));
+
+	/*
+	 * Screen name.  Stored as an unterminated string prepended with a
+	 * byte containing its length.
+	 */
+	snlen = aimbs_get8(bs);
+	outinfo->sn = aimbs_getstr(bs, snlen);
+
+	/*
+	 * Warning Level.  Stored as an unsigned short.
+	 */
+	outinfo->warnlevel = aimbs_get16(bs);
+
+	/*
+	 * TLV Count. Unsigned short representing the number of
+	 * Type-Length-Value triples that follow.
+	 */
+	tlvcnt = aimbs_get16(bs);
+
+	/*
+	 * Parse out the Type-Length-Value triples as they're found.
+	 */
+	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
+		int endpos;
+		guint16 type, length;
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		endpos = aim_bstream_curpos(bs) + length;
+
+		if (type == 0x0001) {
+			/*
+			 * Type = 0x0001: User flags
+			 * 
+			 * Specified as any of the following ORed together:
+			 *      0x0001  Trial (user less than 60days)
+			 *      0x0002  Unknown bit 2
+			 *      0x0004  AOL Main Service user
+			 *      0x0008  Unknown bit 4
+			 *      0x0010  Free (AIM) user 
+			 *      0x0020  Away
+			 *      0x0400  ActiveBuddy
+			 *
+			 */
+			outinfo->flags = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
+
+		} else if (type == 0x0002) {
+			/*
+			 * Type = 0x0002: Account creation time. 
+			 *
+			 * The time/date that the user originally registered for
+			 * the service, stored in time_t format.
+			 *
+			 * I'm not sure how this differs from type 5 ("member
+			 * since").
+			 *
+			 * Note: This is the field formerly known as "member
+			 * since".  All these years and I finally found out
+			 * that I got the name wrong.
+			 */
+			outinfo->createtime = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
+
+		} else if (type == 0x0003) {
+			/*
+			 * Type = 0x0003: On-Since date.
+			 *
+			 * The time/date that the user started their current 
+			 * session, stored in time_t format.
+			 */
+			outinfo->onlinesince = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
+
+		} else if (type == 0x0004) {
+			/*
+			 * Type = 0x0004: Idle time.
+			 *
+			 * Number of minutes since the user actively used the 
+			 * service.
+			 *
+			 * Note that the client tells the server when to start
+			 * counting idle times, so this may or may not be 
+			 * related to reality.
+			 */
+			outinfo->idletime = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
+
+		} else if (type == 0x0005) {
+			/*
+			 * Type = 0x0005: Member since date. 
+			 *
+			 * The time/date that the user originally registered for
+			 * the service, stored in time_t format.
+			 *
+			 * This is sometimes sent instead of type 2 ("account
+			 * creation time"), particularly in the self-info.
+			 * And particularly for ICQ?
+			 */
+			outinfo->membersince = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
+
+		} else if (type == 0x0006) {
+			/*
+			 * Type = 0x0006: ICQ Online Status
+			 *
+			 * ICQ's Away/DND/etc "enriched" status. Some decoding 
+			 * of values done by Scott <darkagl@pcnet.com>
+			 */
+			aimbs_get16(bs);
+			outinfo->icqinfo.status = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
+
+		} else if (type == 0x0008) {
+			/*
+			 * Type = 0x0008
+			 *
+			 * Client type, or some such.
+			 */
+
+		} else if (type == 0x000a) {
+			/*
+			 * Type = 0x000a
+			 *
+			 * ICQ User IP Address.
+			 * Ahh, the joy of ICQ security.
+			 */
+			outinfo->icqinfo.ipaddr = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
+
+		} else if (type == 0x000c) {
+			/* 
+			 * Type = 0x000c
+			 *
+			 * random crap containing the IP address,
+			 * apparently a port number, and some Other Stuff.
+			 *
+			 * Format is:
+			 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
+			 * 
+			 *
+			 */
+			aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
+
+		} else if (type == 0x000d) {
+			/*
+			 * Type = 0x000d
+			 *
+			 * OSCAR Capability information.
+			 *
+			 */
+			outinfo->capabilities |= aim_locate_getcaps(sess, bs, length);
+			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+
+		} else if (type == 0x000e) {
+			/*
+			 * Type = 0x000e
+			 *
+			 * AOL capability information.
+			 *
+			 */
+
+		} else if ((type == 0x000f) || (type == 0x0010)) {
+			/*
+			 * Type = 0x000f: Session Length. (AIM)
+			 * Type = 0x0010: Session Length. (AOL)
+			 *
+			 * The duration, in seconds, of the user's current 
+			 * session.
+			 *
+			 * Which TLV type this comes in depends on the
+			 * service the user is using (AIM or AOL).
+			 *
+			 */
+			outinfo->sessionlen = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
+
+		} else if (type == 0x0019) {
+			/*
+			 * Type = 0x0019
+			 *
+			 * OSCAR short capability information.  A shortened 
+			 * form of the normal capabilities.
+			 */
+			outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length);
+			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+
+		} else if (type == 0x001b) {
+			/*
+			 * Type = 0x001a
+			 *
+			 * AOL short capability information.  A shortened 
+			 * form of the normal capabilities.
+			 */
+
+		} else if (type == 0x001b) {
+			/*
+			 * Type = 0x0019
+			 *
+			 * Encryption certification MD5 checksum.
+			 */
+
+		} else if (type == 0x001d) {
+			/*
+			 * Type = 0x001d
+			 *
+			 * Buddy icon information and status/available messages.
+			 *
+			 * This almost seems like the AIM protocol guys gave
+			 * the iChat guys a Type, and the iChat guys tried to
+			 * cram as much cool shit into it as possible.  Then
+			 * the Windows AIM guys were like, "hey, that's
+			 * pretty neat, let's copy those prawns."
+			 *
+			 * In that spirit, this can contain a custom message,
+			 * kind of like an away message, but you're not away
+			 * (it's called an "available" message).  Or it can
+			 * contain information about the buddy icon the user
+			 * has stored on the server.
+			 */
+			int type2, number, length2;
+
+			while (aim_bstream_curpos(bs) < endpos) {
+				type2 = aimbs_get16(bs);
+				number = aimbs_get8(bs);
+				length2 = aimbs_get8(bs);
+
+				switch (type2) {
+					case 0x0000: { /* This is an official buddy icon? */
+						/* This is always 5 bytes of "0x02 01 d2 04 72"? */
+						aim_bstream_advance(bs, length2);
+					} break;
+
+					case 0x0001: { /* A buddy icon checksum */
+						if ((length2 > 0) && ((number == 0x00) || (number == 0x01))) {
+							free(outinfo->iconcsum);
+							outinfo->iconcsumtype = number;
+							outinfo->iconcsum = aimbs_getraw(bs, length2);
+							outinfo->iconcsumlen = length2;
+						} else
+							aim_bstream_advance(bs, length2);
+					} break;
+
+					case 0x0002: { /* A status/available message */
+						free(outinfo->status);
+						free(outinfo->status_encoding);
+						if (length2 >= 4) {
+							outinfo->status_len = aimbs_get16(bs);
+							outinfo->status = aimbs_getstr(bs, outinfo->status_len);
+							if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */
+								aimbs_get16(bs);
+								outinfo->status_encoding = aimbs_getstr(bs, aimbs_get16(bs));
+							} else {
+								/* No explicit encoding, client should use UTF-8 */
+								outinfo->status_encoding = NULL;
+							}
+						} else {
+							aim_bstream_advance(bs, length2);
+							outinfo->status_len = 0;
+							outinfo->status = g_strdup("");
+							outinfo->status_encoding = NULL;
+						}
+					} break;
+
+					default: {
+						aim_bstream_advance(bs, length2);
+					} break;
+				}
+			}
+
+		} else if (type == 0x001e) {
+			/*
+			 * Type 30: Unknown.
+			 *
+			 * Always four bytes, but it doesn't look like an int.
+			 */
+
+		} else if (type == 0x001f) {
+			/*
+			 * Type 31: Unknown.
+			 *
+			 * Seen on a buddy using DeadAIM.  Data was 4 bytes:
+			 * 0x00 00 00 10
+			 */
+
+		} else {
+
+			/*
+			 * Reaching here indicates that either AOL has
+			 * added yet another TLV for us to deal with, 
+			 * or the parsing has gone Terribly Wrong.
+			 *
+			 * Either way, inform the owner and attempt
+			 * recovery.
+			 *
+			 */
+			gaim_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
+			gaim_debug_misc("oscar", "userinfo:   sn    =%s\n", outinfo->sn);
+			dumptlv(sess, type, bs, length);
+		}
+
+		/* Save ourselves. */
+		aim_bstream_setpos(bs, endpos);
+	}
+
+	aim_locate_adduserinfo(sess, outinfo);
+
+	return 0;
+}
+
+/*
+ * Inverse of aim_info_extract()
+ */
+faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info)
+{
+	aim_tlvlist_t *tlvlist = NULL;
+
+	if (!bs || !info)
+		return -EINVAL;
+
+	aimbs_put8(bs, strlen(info->sn));
+	aimbs_putstr(bs, info->sn);
+
+	aimbs_put16(bs, info->warnlevel);
+
+	if (info->present & AIM_USERINFO_PRESENT_FLAGS)
+		aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags);
+	if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE)
+		aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince);
+	if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
+		aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince);
+	if (info->present & AIM_USERINFO_PRESENT_IDLE)
+		aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime);
+
+/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
+#ifdef ICQ_OSCAR_SUPPORT
+	if (atoi(info->sn) != 0) {
+		if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
+			aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status);
+		if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR)
+			aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
+	}
+#endif
+
+	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
+		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities);
+
+	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
+		aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
+
+	aimbs_put16(bs, aim_tlvlist_count(&tlvlist));
+	aim_tlvlist_write(bs, &tlvlist);
+	aim_tlvlist_free(&tlvlist);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0001
+ */
+static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+	guint16 reason;
+	char *sn;
+	int was_explicit;
+
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from unknown request!\n");
+		return 0;
+	}
+
+	if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) {
+		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family);
+		return 0;
+	}
+
+	if (!(sn = snac2->data)) {
+		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from request without a screen name!\n");
+		return 0;
+	}
+
+	reason = aimbs_get16(bs);
+
+	/*
+	 * Remove this screen name from our queue.  If the client requested 
+	 * this buddy's info explicitly, then notify them that we do not have 
+	 * info for this buddy.
+	 */
+	was_explicit = aim_locate_gotuserinfo(sess, sn);
+	if (was_explicit == TRUE)
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx, reason, sn);
+
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0002
+ *
+ * Request Location services rights.
+ *
+ */
+faim_export int aim_locate_reqrights(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)))
+		return -EINVAL;
+
+	return aim_genericreq_n_snacid(sess, conn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQRIGHTS);
+}
+
+/*
+ * Subtype 0x0003
+ *
+ * Normally contains:
+ *   t(0001)  - short containing max profile length (value = 1024)
+ *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
+ *   t(0003)  - short - unknown (value = 10)
+ *   t(0004)  - short - unknown (value = 2048) [ICQ only?]
+ */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_tlvlist_t *tlvlist;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	guint16 maxsiglen = 0;
+
+	tlvlist = aim_tlvlist_read(bs);
+
+	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
+		maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxsiglen);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0004
+ *
+ * Gives BOS your profile.
+ *
+ * profile_encoding and awaymsg_encoding MUST be set if profile or
+ * away are set, respectively, and their value may or may not be
+ * restricted to a few choices.  I am currently aware of:
+ *
+ * us-ascii		Just that
+ * unicode-2-0		UCS2-BE
+ *
+ * profile_len and awaymsg_len MUST be set similarly, and they MUST
+ * be the length of their respective strings in bytes.
+ *
+ * To get the previous behavior of awaymsg == "" un-setting the away
+ * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
+ * obvious equivalent).
+ *
+ */
+faim_export int aim_locate_setprofile(aim_session_t *sess,
+				  const char *profile_encoding, const gchar *profile, const int profile_len,
+				  const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	char *encoding;
+	static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)))
+		return -EINVAL;
+
+	if (!profile && !awaymsg)
+		return -EINVAL;
+
+	if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
+		return -EINVAL;
+	}
+
+	/* Build the packet first to get real length */
+	if (profile) {
+		/* no + 1 here because of %s */
+		encoding = malloc(strlen(defencoding) + strlen(profile_encoding));
+		if (encoding == NULL) {
+			return -ENOMEM;
+		}
+		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);
+		free(encoding);
+	}
+
+	/*
+	 * So here's how this works:
+	 *   - You are away when you have a non-zero-length type 4 TLV stored.
+	 *   - You become unaway when you clear the TLV with a zero-length
+	 *       type 4 TLV.
+	 *   - If you do not send the type 4 TLV, your status does not change
+	 *       (that is, if you were away, you'll remain away).
+	 */
+	if (awaymsg) {
+		if (awaymsg_len) {
+			encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding));
+			if (encoding == NULL) {
+				return -ENOMEM;
+			}
+			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);
+			free(encoding);
+		} else
+			aim_tlvlist_add_noval(&tl, 0x0004);
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0004 - Set your client's capabilities.
+ */
+faim_export int aim_locate_setcaps(aim_session_t *sess, guint32 caps)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)))
+		return -EINVAL;
+
+	aim_tlvlist_add_caps(&tl, 0x0005, caps);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0005 - Request info of another AIM user.
+ *
+ * @param sn The screenname whose info you wish to request.
+ * @param infotype The type of info you wish to request.
+ *        0x0001 - Info/profile
+ *        0x0003 - Away message
+ *        0x0004 - Capabilities
+ */
+faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, guint16 infotype)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
+	aimbs_put16(&fr->data, infotype);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x0006 */
+static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_userinfo_t *userinfo, *userinfo2;
+	aim_tlvlist_t *tlvlist;
+	aim_tlv_t *tlv = NULL;
+	int was_explicit;
+
+	userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t));
+	aim_info_extract(sess, bs, userinfo);
+	tlvlist = aim_tlvlist_read(bs);
+
+	/* Profile will be 1 and 2 */
+	userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1);
+	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
+		userinfo->info = (char *)malloc(tlv->length);
+		memcpy(userinfo->info, tlv->value, tlv->length);
+		userinfo->info_len = tlv->length;
+	}
+
+	/* Away message will be 3 and 4 */
+	userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1);
+	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
+		userinfo->away = (char *)malloc(tlv->length);
+		memcpy(userinfo->away, tlv->value, tlv->length);
+		userinfo->away_len = tlv->length;
+	}
+
+	/* Caps will be 5 */
+	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
+		aim_bstream_t cbs;
+		aim_bstream_init(&cbs, tlv->value, tlv->length);
+		userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length);
+		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
+	}
+	aim_tlvlist_free(&tlvlist);
+
+	aim_locate_adduserinfo(sess, userinfo);
+	userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn);
+	aim_info_free(userinfo);
+	free(userinfo);
+
+	/*
+	 * Remove this screen name from our queue.  If the client requested
+	 * this buddy's info explicitly, then notify them that we have info
+	 * for this buddy.
+	 */
+	was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn);
+	if (was_explicit == TRUE)
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			ret = userfunc(sess, rx, userinfo2);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0009 - Set directory profile data.
+ *
+ * This is not the same as aim_location_setprofile!
+ * privacy: 1 to allow searching, 0 to disallow.
+ *
+ */
+faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)))
+		return -EINVAL;
+
+	aim_tlvlist_add_16(&tl, 0x000a, privacy);
+
+	if (first)
+		aim_tlvlist_add_str(&tl, 0x0001, first);
+	if (last)
+		aim_tlvlist_add_str(&tl, 0x0002, last);
+	if (middle)
+		aim_tlvlist_add_str(&tl, 0x0003, middle);
+	if (maiden)
+		aim_tlvlist_add_str(&tl, 0x0004, maiden);
+
+	if (state)
+		aim_tlvlist_add_str(&tl, 0x0007, state);
+	if (city)
+		aim_tlvlist_add_str(&tl, 0x0008, city);
+
+	if (nickname)
+		aim_tlvlist_add_str(&tl, 0x000c, nickname);
+	if (zip)
+		aim_tlvlist_add_str(&tl, 0x000d, zip);
+
+	if (street)
+		aim_tlvlist_add_str(&tl, 0x0021, street);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x000b - Huh? What is this?
+ */
+faim_export int aim_locate_000b(aim_session_t *sess, const char *sn)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x000f
+ *
+ * XXX pass these in better
+ *
+ */
+faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)))
+		return -EINVAL;
+
+	/* ?? privacy ?? */
+	aim_tlvlist_add_16(&tl, 0x000a, privacy);
+
+	if (interest1)
+		aim_tlvlist_add_str(&tl, 0x0000b, interest1);
+	if (interest2)
+		aim_tlvlist_add_str(&tl, 0x0000b, interest2);
+	if (interest3)
+		aim_tlvlist_add_str(&tl, 0x0000b, interest3);
+	if (interest4)
+		aim_tlvlist_add_str(&tl, 0x0000b, interest4);
+	if (interest5)
+		aim_tlvlist_add_str(&tl, 0x0000b, interest5);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0015 - Request the info a user using the short method.  This is
+ * what iChat uses.  It normally is VERY leniently rate limited.
+ *
+ * @param sn The screen name whose info you wish to request.
+ * @param flags The bitmask which specifies the type of info you wish to request.
+ *        0x00000001 - Info/profile.
+ *        0x00000002 - Away message.
+ *        0x00000004 - Capabilities.
+ *        0x00000008 - Certification.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, guint32 flags)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_LOCATE)) || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1);
+
+	aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid);
+	aimbs_put32(&fr->data, flags);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putstr(&fr->data, sn);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0001)
+		return error(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return userinfo(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+static void locate_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	aim_userinfo_t *del;
+
+	while (sess->locate.userinfo) {
+		del = sess->locate.userinfo;
+		sess->locate.userinfo = sess->locate.userinfo->next;
+		aim_info_free(del);
+		free(del);
+	}
+}
+
+faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = OSCAR_FAMILY_LOCATE;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "locate", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = locate_shutdown;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_odir.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,264 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000f - Newer Search Method
+ *
+ * Used for searching for other AIM users by email address, name,
+ * location, commmon interests, and a few other similar things.
+ *
+ */
+
+#include "oscar.h"
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * Search for an AIM screen name based on their email address.
+ *
+ * @param sess The oscar session.
+ * @param region Should be "us-ascii" unless you know what you're doing.
+ * @param email The email address you want to search for.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_odir_email(aim_session_t *sess, const char *region, const char *email)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 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);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * Search for an AIM screen name based on various info
+ * about the person.
+ *
+ * @param sess The oscar session.
+ * @param region Should be "us-ascii" unless you know what you're doing.
+ * @param first The first name of the person you want to search for.
+ * @param middle The middle name of the person you want to search for.
+ * @param last The last name of the person you want to search for.
+ * @param maiden The maiden name of the person you want to search for.
+ * @param nick The nick name of the person you want to search for.
+ * @param city The city where the person you want to search for resides.
+ * @param state The state where the person you want to search for resides.
+ * @param country The country where the person you want to search for resides.
+ * @param zip The zip code where the person you want to search for resides.
+ * @param address The street address where the person you want to seach for resides.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_odir_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 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 */
+	if (first)
+		aim_tlvlist_add_str(&tl, 0x0001, first);
+	if (last)
+		aim_tlvlist_add_str(&tl, 0x0002, last);
+	if (middle)
+		aim_tlvlist_add_str(&tl, 0x0003, middle);
+	if (maiden)
+		aim_tlvlist_add_str(&tl, 0x0004, maiden);
+	if (country)
+		aim_tlvlist_add_str(&tl, 0x0006, country);
+	if (state)
+		aim_tlvlist_add_str(&tl, 0x0007, state);
+	if (city)
+		aim_tlvlist_add_str(&tl, 0x0008, city);
+	if (nick)
+		aim_tlvlist_add_str(&tl, 0x000c, nick);
+	if (zip)
+		aim_tlvlist_add_str(&tl, 0x000d, zip);
+	if (address)
+		aim_tlvlist_add_str(&tl, 0x0021, address);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * @param sess The oscar session.
+ * @param interest1 An interest you want to search for.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_odir_interest(aim_session_t *sess, const char *region, const char *interest)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 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 */
+	if (interest)
+		aim_tlvlist_add_str(&tl, 0x0001, interest);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0003 - Receive Reply From a User Search
+ *
+ */
+static int parseresults(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 tmp, numresults;
+	struct aim_odir *results = NULL;
+
+	tmp = aimbs_get16(bs); /* Unknown */
+	tmp = aimbs_get16(bs); /* Unknown */
+	aim_bstream_advance(bs, tmp);
+
+	numresults = aimbs_get16(bs); /* Number of results to follow */
+
+	/* Allocate a linked list, 1 node per result */
+	while (numresults) {
+		struct aim_odir *new;
+		aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, aimbs_get16(bs));
+		new = (struct aim_odir *)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->next = results;
+		results = new;
+		numresults--;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, results);
+
+	/* Now free everything from above */
+	while (results) {
+		struct aim_odir *del = results;
+		results = results->next;
+		free(del->first);
+		free(del->last);
+		free(del->middle);
+		free(del->maiden);
+		free(del->email);
+		free(del->country);
+		free(del->state);
+		free(del->city);
+		free(del->sn);
+		free(del->interest);
+		free(del->nick);
+		free(del->zip);
+		free(del->region);
+		free(del->address);
+		free(del);
+	}
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return parseresults(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000f;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "odir", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_oservice.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,1144 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0001 - This is a very special group.  All connections support
+ * this group, as it does some particularly good things (like rate limiting).
+ */
+
+#include "oscar.h"
+
+#include "cipher.h"
+
+/* Subtype 0x0002 - Client Online */
+faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!ins)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
+
+	/*
+	 * Send only the tool versions that the server cares about (that it
+	 * marked as supporting in the server ready SNAC).
+	 */
+	for (sg = ins->groups; sg; sg = sg->next) {
+		aim_module_t *mod;
+
+		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+			aimbs_put16(&fr->data, mod->family);
+			aimbs_put16(&fr->data, mod->version);
+			aimbs_put16(&fr->data, mod->toolid);
+			aimbs_put16(&fr->data, mod->toolversion);
+		} else
+			gaim_debug_misc("oscar", "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0003 - Host Online
+ *
+ * See comments in conn.c about how the group associations are supposed
+ * to work, and how they really work.
+ *
+ * This info probably doesn't even need to make it to the client.
+ *
+ * We don't actually call the client here.  This starts off the connection
+ * initialization routine required by all AIM connections.  The next time
+ * the client is called is the CONNINITDONE callback, which should be
+ * shortly after the rate information is acknowledged.
+ *
+ */
+static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	guint16 *families;
+	int famcount;
+
+
+	if (!(families = malloc(aim_bstream_empty(bs))))
+		return 0;
+
+	for (famcount = 0; aim_bstream_empty(bs); famcount++) {
+		families[famcount] = aimbs_get16(bs);
+		aim_conn_addgroup(rx->conn, families[famcount]);
+	}
+
+	free(families);
+
+
+	/*
+	 * Next step is in the Host Versions handler.
+	 *
+	 * Note that we must send this before we request rates, since
+	 * the format of the rate information depends on the versions we
+	 * give it.
+	 *
+	 */
+	aim_setversions(sess, rx->conn);
+
+	return 1;
+}
+
+/* Subtype 0x0004 - Service request */
+faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid)
+{
+	return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
+}
+
+/*
+ * Join a room of name roomname.  This is the first step to joining an 
+ * already created room.  It's basically a Service Request for 
+ * family 0x000e, with a little added on to specify the exchange and room 
+ * name.
+ */
+faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	struct chatsnacinfo csi;
+
+	if (!sess || !conn || !roomname || !strlen(roomname))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM;
+
+	memset(&csi, 0, sizeof(csi));
+	csi.exchange = exchange;
+	strncpy(csi.name, roomname, sizeof(csi.name));
+	csi.instance = instance;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
+	aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
+
+	/*
+	 * Requesting service chat (0x000e)
+	 */
+	aimbs_put16(&fr->data, 0x000e);
+
+	aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance);
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x0005 - Redirect */
+static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	struct aim_redirect_data redir;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	aim_snac_t *origsnac = NULL;
+	int ret = 0;
+
+	memset(&redir, 0, sizeof(redir));
+
+	tlvlist = aim_tlvlist_read(bs);
+
+	if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
+			!aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
+			!aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
+		aim_tlvlist_free(&tlvlist);
+		return 0;
+	}
+
+	redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
+	redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
+	redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
+	redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
+
+	/* Fetch original SNAC so we can get csi if needed */
+	origsnac = aim_remsnac(sess, snac->id);
+
+	if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
+		struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
+
+		redir.chat.exchange = csi->exchange;
+		redir.chat.room = csi->name;
+		redir.chat.instance = csi->instance;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &redir);
+
+	free((void *)redir.ip);
+	free((void *)redir.cookie);
+
+	if (origsnac)
+		free(origsnac->data);
+	free(origsnac);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/* Subtype 0x0006 - Request Rate Information. */
+faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006);
+}
+
+/*
+ * OSCAR defines several 'rate classes'.  Each class has separate
+ * rate limiting properties (limit level, alert level, disconnect
+ * level, etc), and a set of SNAC family/type pairs associated with
+ * it.  The rate classes, their limiting properties, and the definitions
+ * of which SNACs are belong to which class, are defined in the
+ * Rate Response packet at login to each host.  
+ *
+ * Logically, all rate offenses within one class count against further
+ * offenses for other SNACs in the same class (ie, sending messages
+ * too fast will limit the number of user info requests you can send,
+ * since those two SNACs are in the same rate class).
+ *
+ * Since the rate classes are defined dynamically at login, the values
+ * below may change. But they seem to be fairly constant.
+ *
+ * Currently, BOS defines five rate classes, with the commonly used
+ * members as follows...
+ *
+ *  Rate class 0x0001:
+ *  	- Everything thats not in any of the other classes
+ *
+ *  Rate class 0x0002:
+ * 	- Buddy list add/remove
+ *	- Permit list add/remove
+ *	- Deny list add/remove
+ *
+ *  Rate class 0x0003:
+ *	- User information requests
+ *	- Outgoing ICBMs
+ *
+ *  Rate class 0x0004:
+ *	- A few unknowns: 2/9, 2/b, and f/2
+ *
+ *  Rate class 0x0005:
+ *	- Chat room create
+ *	- Outgoing chat ICBMs
+ *
+ * The only other thing of note is that class 5 (chat) has slightly looser
+ * limiting properties than class 3 (normal messages).  But thats just a 
+ * small bit of trivia for you.
+ *
+ * The last thing that needs to be learned about the rate limiting
+ * system is how the actual numbers relate to the passing of time.  This
+ * seems to be a big mystery.
+ * 
+ */
+
+static void rc_addclass(struct rateclass **head, struct rateclass *inrc)
+{
+	struct rateclass *rc, *rc2;
+
+	if (!(rc = malloc(sizeof(struct rateclass))))
+		return;
+
+	memcpy(rc, inrc, sizeof(struct rateclass));
+	rc->next = NULL;
+
+	for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next)
+		;
+
+	if (!rc2)
+		*head = rc;
+	else
+		rc2->next = rc;
+
+	return;
+}
+
+static struct rateclass *rc_findclass(struct rateclass **head, guint16 id)
+{
+	struct rateclass *rc;
+
+	for (rc = *head; rc; rc = rc->next) {
+		if (rc->classid == id)
+			return rc;
+	}
+
+	return NULL;
+}
+
+static void rc_addpair(struct rateclass *rc, guint16 group, guint16 type)
+{
+	struct snacpair *sp, *sp2;
+
+	if (!(sp = malloc(sizeof(struct snacpair))))
+		return;
+	memset(sp, 0, sizeof(struct snacpair));
+
+	sp->group = group;
+	sp->subtype = type;
+	sp->next = NULL;
+
+	for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next)
+		;
+
+	if (!sp2)
+		rc->members = sp;
+	else
+		sp2->next = sp;
+
+	return;
+}
+
+/* Subtype 0x0007 - Rate Parameters */
+static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
+	guint16 numclasses, i;
+	aim_rxcallback_t userfunc;
+
+
+	/*
+	 * First are the parameters for each rate class.
+	 */
+	numclasses = aimbs_get16(bs);
+	for (i = 0; i < numclasses; i++) {
+		struct rateclass rc;
+
+		memset(&rc, 0, sizeof(struct rateclass));
+
+		rc.classid = aimbs_get16(bs);
+		rc.windowsize = aimbs_get32(bs);
+		rc.clear = aimbs_get32(bs);
+		rc.alert = aimbs_get32(bs);
+		rc.limit = aimbs_get32(bs);
+		rc.disconnect = aimbs_get32(bs);
+		rc.current = aimbs_get32(bs);
+		rc.max = aimbs_get32(bs);
+
+		/*
+		 * The server will send an extra five bytes of parameters
+		 * depending on the version we advertised in 1/17.  If we
+		 * didn't send 1/17 (evil!), then this will crash and you
+		 * die, as it will default to the old version but we have 
+		 * the new version hardcoded here. 
+		 */
+		if (mod->version >= 3)
+			aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown));
+
+		gaim_debug_misc("oscar", "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max);
+
+		rc_addclass(&ins->rates, &rc);
+	}
+
+	/*
+	 * Then the members of each class.
+	 */
+	for (i = 0; i < numclasses; i++) {
+		guint16 classid, count;
+		struct rateclass *rc;
+		int j;
+
+		classid = aimbs_get16(bs);
+		count = aimbs_get16(bs);
+
+		rc = rc_findclass(&ins->rates, classid);
+
+		for (j = 0; j < count; j++) {
+			guint16 group, subtype;
+
+			group = aimbs_get16(bs);
+			subtype = aimbs_get16(bs);
+
+			if (rc)
+				rc_addpair(rc, group, subtype);
+		}
+	}
+
+	/*
+	 * We don't pass the rate information up to the client, as it really
+	 * doesn't care.  The information is stored in the connection, however
+	 * so that we can do more fun stuff later (not really).
+	 */
+
+	/*
+	 * Last step in the conn init procedure is to acknowledge that we
+	 * agree to these draconian limitations.
+	 */
+	aim_rates_addparam(sess, rx->conn);
+
+	/*
+	 * Finally, tell the client it's ready to go...
+	 */
+	if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
+		userfunc(sess, rx);
+
+
+	return 1;
+}
+
+/* Subtype 0x0008 - Add Rate Parameter */
+faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	struct rateclass *rc;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
+
+	for (rc = ins->rates; rc; rc = rc->next)
+		aimbs_put16(&fr->data, rc->classid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x0009 - Delete Rate Parameter */
+faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	struct rateclass *rc;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid);
+
+	for (rc = ins->rates; rc; rc = rc->next)
+		aimbs_put16(&fr->data, rc->classid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x000a - Rate Change */
+static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 code, rateclass;
+	guint32 currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
+
+	code = aimbs_get16(bs);
+	rateclass = aimbs_get16(bs);
+
+	windowsize = aimbs_get32(bs);
+	clear = aimbs_get32(bs);
+	alert = aimbs_get32(bs);
+	limit = aimbs_get32(bs);
+	disconnect = aimbs_get32(bs);
+	currentavg = aimbs_get32(bs);
+	maxavg = aimbs_get32(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
+
+	return ret;
+}
+
+/*
+ * How Migrations work.
+ *
+ * The server sends a Server Pause message, which the client should respond to 
+ * with a Server Pause Ack, which contains the families it needs on this 
+ * connection. The server will send a Migration Notice with an IP address, and 
+ * then disconnect. Next the client should open the connection and send the 
+ * cookie.  Repeat the normal login process and pretend this never happened.
+ *
+ * The Server Pause contains no data.
+ *
+ */
+
+/* Subtype 0x000b - Service Pause */
+static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000c - Service Pause Acknowledgement
+ *
+ * It is rather important that aim_sendpauseack() gets called for the exact
+ * same connection that the Server Pause callback was called for, since
+ * libfaim extracts the data for the SNAC from the connection structure.
+ *
+ * Of course, if you don't do that, more bad things happen than just what
+ * libfaim can cause.
+ *
+ */
+faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid);
+
+	/*
+	 * This list should have all the groups that the original 
+	 * Host Online / Server Ready said this host supports.  And 
+	 * we want them all back after the migration.
+	 */
+	for (sg = ins->groups; sg; sg = sg->next)
+		aimbs_put16(&fr->data, sg->group);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x000d - Service Resume */
+static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/* Subtype 0x000e - Request self-info */
+faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e);
+}
+
+/* Subtype 0x000f - Self User Info */
+static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_userinfo_t userinfo;
+
+	aim_info_extract(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo);
+
+	aim_info_free(&userinfo);
+
+	return ret;
+}
+
+/* Subtype 0x0010 - Evil Notification */
+static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 newevil;
+	aim_userinfo_t userinfo;
+
+	memset(&userinfo, 0, sizeof(aim_userinfo_t));
+
+	newevil = aimbs_get16(bs);
+
+	if (aim_bstream_empty(bs))
+		aim_info_extract(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, newevil, &userinfo);
+
+	aim_info_free(&userinfo);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0011 - Idle Notification
+ *
+ * Should set your current idle time in seconds.  Note that this should
+ * never be called consecutively with a non-zero idle time.  That makes
+ * OSCAR do funny things.  Instead, just set it once you go idle, and then
+ * call it again with zero when you're back.
+ *
+ */
+faim_export int aim_srv_setidle(aim_session_t *sess, guint32 idletime)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_BOS)))
+		return -EINVAL;
+
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
+}
+
+/*
+ * Subtype 0x0012 - Service Migrate
+ *
+ * This is the final SNAC sent on the original connection during a migration.
+ * It contains the IP and cookie used to connect to the new server, and 
+ * optionally a list of the SNAC groups being migrated.
+ *
+ */
+static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	guint16 groupcount, i;
+	aim_tlvlist_t *tl;
+	char *ip = NULL;
+	aim_tlv_t *cktlv;
+
+	/*
+	 * Apparently there's some fun stuff that can happen right here. The
+	 * migration can actually be quite selective about what groups it
+	 * moves to the new server.  When not all the groups for a connection
+	 * are migrated, or they are all migrated but some groups are moved
+	 * to a different server than others, it is called a bifurcated 
+	 * migration.
+	 *
+	 * Let's play dumb and not support that.
+	 *
+	 */
+	groupcount = aimbs_get16(bs);
+	for (i = 0; i < groupcount; i++) {
+		guint16 group;
+
+		group = aimbs_get16(bs);
+
+		gaim_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
+	}
+
+	tl = aim_tlvlist_read(bs);
+
+	if (aim_tlv_gettlv(tl, 0x0005, 1))
+		ip = aim_tlv_getstr(tl, 0x0005, 1);
+
+	cktlv = aim_tlv_gettlv(tl, 0x0006, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL);
+
+	aim_tlvlist_free(&tl);
+	free(ip);
+
+	return ret;
+}
+
+/* Subtype 0x0013 - Message of the Day */
+static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	char *msg = NULL;
+	int ret = 0;
+	aim_tlvlist_t *tlvlist;
+	guint16 id;
+
+	/*
+	 * Code.
+	 *
+	 * Valid values:
+	 *   1 Mandatory upgrade
+	 *   2 Advisory upgrade
+	 *   3 System bulletin
+	 *   4 Nothing's wrong ("top o the world" -- normal)
+	 *   5 Lets-break-something. 
+	 *
+	 */
+	id = aimbs_get16(bs);
+
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_tlvlist_read(bs);
+
+	msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, id, msg);
+
+	free(msg);
+
+	aim_tlvlist_free(&tlvlist);
+
+	return ret;
+}
+
+/* 
+ * Subtype 0x0014 - Set privacy flags
+ *
+ * Normally 0x03.
+ *
+ *  Bit 1:  Allows other AIM users to see how long you've been idle.
+ *  Bit 2:  Allows other AIM users to see how long you've been a member.
+ *
+ */
+faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, guint32 flags)
+{
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
+}
+
+/*
+ * Subtype 0x0016 - No-op
+ *
+ * WinAIM sends these every 4min or so to keep the connection alive.  Its not 
+ * really necessary.
+ *
+ * Wha?  No?  Since when?  I think WinAIM sends an empty channel 3 
+ * SNAC as a no-op...
+ */
+faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
+}
+
+/* 
+ * Subtype 0x0017 - Set client versions
+ *
+ * If you've seen the clientonline/clientready SNAC you're probably 
+ * wondering what the point of this one is.  And that point seems to be
+ * that the versions in the client online SNAC are sent too late for the
+ * server to be able to use them to change the protocol for the earlier
+ * login packets (client versions are sent right after Host Online is 
+ * received, but client online versions aren't sent until quite a bit later).
+ * We can see them already making use of this by changing the format of
+ * the rate information based on what version of group 1 we advertise here.
+ *
+ */
+faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!ins)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
+
+	/*
+	 * Send only the versions that the server cares about (that it
+	 * marked as supporting in the server ready SNAC).  
+	 */
+	for (sg = ins->groups; sg; sg = sg->next) {
+		aim_module_t *mod;
+
+		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+			aimbs_put16(&fr->data, mod->family);
+			aimbs_put16(&fr->data, mod->version);
+		} else
+			gaim_debug_misc("oscar", "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x0018 - Host versions */
+static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int vercount;
+	guint8 *versions;
+
+	/* This is frivolous. (Thank you SmarterChild.) */
+	vercount = aim_bstream_empty(bs)/4;
+	versions = aimbs_getraw(bs, aim_bstream_empty(bs));
+	free(versions);
+
+	/*
+	 * Now request rates.
+	 */
+	aim_reqrates(sess, rx->conn);
+
+	return 1;
+}
+
+/* 
+ * Subtype 0x001e - Set various account settings (mostly ICQ related).
+ *
+ * These settings are transient, not server-stored (i.e. they only
+ * apply to this session, and must be re-set the next time you sign
+ * on).
+ *
+ * You can set your ICQ status (available, away, do not disturb,
+ * etc.), or whether your IP address should be hidden or not, or
+ * if your status is visible on ICQ web sites, and you can set
+ * your IP address info and what not.
+ *
+ * These are the same TLVs seen in user info.  You can
+ * also set 0x0008 and 0x000c.
+ */
+faim_export int aim_setextstatus(aim_session_t *sess, guint32 status)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	guint32 data;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ICBM)))
+		return -EINVAL;
+
+	data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+
+	aim_tlvlist_add_32(&tl, 0x0006, data);
+#if 0
+	aim_tlvlist_add_raw(&tl, 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);
+#endif
+	aim_tlvlist_write(&fr->data, &tl);
+	aim_tlvlist_free(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x001e - Extended Status.
+ *
+ * Sets your "available" message.  This is currently only supported by iChat
+ * and Gaim.
+ *
+ * These are the same TLVs seen in user info.  You can
+ * also set 0x0008 and 0x000c.
+ */
+faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if ((msg != NULL) && *msg != '\0') {
+		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8)))
+			return -ENOMEM;
+
+		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+
+		aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */
+		aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */
+		aimbs_put16(&fr->data, 0x0002);
+		aimbs_put8(&fr->data, 0x04);
+		aimbs_put8(&fr->data, strlen(msg)+4);
+		aimbs_put16(&fr->data, strlen(msg));
+		aimbs_putstr(&fr->data, msg);
+		aimbs_put16(&fr->data, 0x0000);
+	} else {
+		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8)))
+			return -ENOMEM;
+
+		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+
+		aimbs_put16(&fr->data, 0x001d);
+		aimbs_put16(&fr->data, 0x0008);
+		aimbs_put16(&fr->data, 0x0002);
+		aimbs_put16(&fr->data, 0x0404);
+		aimbs_put16(&fr->data, 0x0000);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Starting this past week (26 Mar 2001, say), AOL has started sending
+ * this nice little extra SNAC.  AFAIK, it has never been used until now.
+ *
+ * The request contains eight bytes.  The first four are an offset, the
+ * second four are a length.
+ *
+ * The offset is an offset into aim.exe when it is mapped during execution
+ * on Win32.  So far, AOL has only been requesting bytes in static regions
+ * of memory.  (I won't put it past them to start requesting data in
+ * less static regions -- regions that are initialized at run time, but still
+ * before the client receives this request.)
+ *
+ * When the client receives the request, it adds it to the current ds
+ * (0x00400000) and dereferences it, copying the data into a buffer which
+ * it then runs directly through the MD5 hasher.  The 16 byte output of
+ * the hash is then sent back to the server.
+ *
+ * If the client does not send any data back, or the data does not match
+ * the data that the specific client should have, the client will get the
+ * following message from "AOL Instant Messenger":
+ *    "You have been disconnected from the AOL Instant Message Service (SM) 
+ *     for accessing the AOL network using unauthorized software.  You can
+ *     download a FREE, fully featured, and authorized client, here 
+ *     http://www.aol.com/aim/download2.html"
+ * The connection is then closed, receiving disconnect code 1, URL
+ * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.  
+ *
+ * Note, however, that numerous inconsistencies can cause the above error, 
+ * not just sending back a bad hash.  Do not immediatly suspect this code
+ * if you get disconnected.  AOL and the open/free software community have
+ * played this game for a couple years now, generating the above message
+ * on numerous ocassions.
+ *
+ * Anyway, neener.  We win again.
+ *
+ */
+/* Subtype 0x001f - Client verification */
+static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint32 offset, len;
+	aim_tlvlist_t *list;
+	char *modname;
+
+	offset = aimbs_get32(bs);
+	len = aimbs_get32(bs);
+	list = aim_tlvlist_read(bs);
+
+	modname = aim_tlv_getstr(list, 0x0001, 1);
+
+	gaim_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, offset, len, modname);
+
+	free(modname);
+	aim_tlvlist_free(&list);
+
+	return ret;
+}
+
+/* Subtype 0x0020 - Client verification reply */
+faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid);
+	aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */
+
+	if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
+
+		aimbs_putraw(&fr->data, buf, 0x10);
+
+	} else if (buf && (len > 0)) { /* use input buffer */
+		GaimCipher *cipher;
+		GaimCipherContext *context;
+		guchar digest[16];
+
+		cipher = gaim_ciphers_find_cipher("md5");
+
+		context = gaim_cipher_context_new(cipher, NULL);
+		gaim_cipher_context_append(context, buf, len);
+		gaim_cipher_context_digest(context, 16, digest, NULL);
+		gaim_cipher_context_destroy(context);
+
+		aimbs_putraw(&fr->data, digest, 0x10);
+
+	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
+		GaimCipher *cipher;
+		GaimCipherContext *context;
+		guchar digest[16];
+		guint8 nil = '\0';
+
+		/*
+		 * I'm not sure if we really need the empty append with the
+		 * new MD5 functions, so I'll leave it in, just in case.
+		 */
+		cipher = gaim_ciphers_find_cipher("md5");
+
+		context = gaim_cipher_context_new(cipher, NULL);
+		gaim_cipher_context_append(context, &nil, 0);
+		gaim_cipher_context_digest(context, 16, digest, NULL);
+		gaim_cipher_context_destroy(context);
+
+		aimbs_putraw(&fr->data, digest, 0x10);
+
+	} else {
+
+		/*
+		 * This data is correct for AIM 3.5.1670.
+		 *
+		 * Using these blocks is as close to "legal" as you can get
+		 * without using an AIM binary.
+		 *
+		 */
+		if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
+
+#if 1 /* with "AnrbnrAqhfzcd" */
+			aimbs_put32(&fr->data, 0x44a95d26);
+			aimbs_put32(&fr->data, 0xd2490423);
+			aimbs_put32(&fr->data, 0x93b8821f);
+			aimbs_put32(&fr->data, 0x51c54b01);
+#else /* no filename */
+			aimbs_put32(&fr->data, 0x1df8cbae);
+			aimbs_put32(&fr->data, 0x5523b839);
+			aimbs_put32(&fr->data, 0xa0e10db3);
+			aimbs_put32(&fr->data, 0xa46d3b39);
+#endif
+
+		} else if ((offset == 0x00001000) && (len == 0x00000000)) {
+
+			aimbs_put32(&fr->data, 0xd41d8cd9);
+			aimbs_put32(&fr->data, 0x8f00b204);
+			aimbs_put32(&fr->data, 0xe9800998);
+			aimbs_put32(&fr->data, 0xecf8427e);
+
+		} else
+			gaim_debug_warning("oscar", "sendmemblock: unknown hash request\n");
+
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0021 - Receive our extended status
+ *
+ * This is used for iChat's "available" messages, and maybe ICQ extended
+ * status messages?  It's also used to tell the client whether or not it
+ * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
+ */
+static int aim_parse_extstatus(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 type;
+	guint8 flags, length;
+
+	type = aimbs_get16(bs);
+	flags = aimbs_get8(bs);
+	length = aimbs_get8(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+		switch (type) {
+		case 0x0000:
+		case 0x0001: { /* buddy icon checksum */
+			/* not sure what the difference between 1 and 0 is */
+			guint8 *md5 = aimbs_getraw(bs, length);
+			ret = userfunc(sess, rx, type, flags, length, md5);
+			free(md5);
+			} break;
+		case 0x0002: { /* available message */
+			/* there is a second length that is just for the message */
+			char *msg = aimbs_getstr(bs, aimbs_get16(bs));
+			ret = userfunc(sess, rx, msg);
+			free(msg);
+			} break;
+		}
+	}
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return hostonline(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0005)
+		return redirect(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return rateresp(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000a)
+		return ratechange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000b)
+		return serverpause(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000d)
+		return serverresume(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000f)
+		return selfinfo(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0010)
+		return evilnotify(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0012)
+		return migrate(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0013)
+		return motd(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0018)
+		return hostversions(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x001f)
+		return memrequest(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0021)
+		return aim_parse_extstatus(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0001;
+	mod->version = 0x0003;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "oservice", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_popup.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,83 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x0008 - Popups.
+ *
+ * Popups are just what it sounds like.  They're a way for the server to
+ * open up an informative box on the client's screen.
+ */
+
+#include <oscar.h>
+
+/*
+ * This is all there is to it.
+ *
+ * The message is probably HTML.
+ *
+ */
+static int parsepopup(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tl;
+	int ret = 0;
+	char *msg, *url;
+	guint16 width, height, delay;
+
+	tl = 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);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, msg, url, width, height, delay);
+
+	aim_tlvlist_free(&tl);
+	free(msg);
+	free(url);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0002)
+		return parsepopup(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0008;
+	mod->version = 0x0001;
+	mod->toolid = 0x0104;
+	mod->toolversion = 0x0001;
+	mod->flags = 0;
+	strncpy(mod->name, "popup", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_stats.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,63 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000b - Statistics.
+ *
+ */
+
+#include <oscar.h>
+
+static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 interval;
+
+	interval = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, interval);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0002)
+		return reportinterval(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000b;
+	mod->version = 0x0001;
+	mod->toolid = 0x0104;
+	mod->toolversion = 0x0001;
+	mod->flags = 0;
+	strncpy(mod->name, "stats", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_translate.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,46 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000c - Translation.
+ *
+ * I have no idea why this group was issued.  I have never seen anything
+ * that uses it.  From what I remember, the last time I tried to poke at
+ * the server with this group, it whined about not supporting it.
+ *
+ * But we advertise it anyway, because its fun.
+ *
+ */
+
+#include "oscar.h"
+
+faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000c;
+	mod->version = 0x0001;
+	mod->toolid = 0x0104;
+	mod->toolversion = 0x0001;
+	mod->flags = 0;
+	strncpy(mod->name, "translate", sizeof(mod->name));
+	mod->snachandler = NULL;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/family_userlookup.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,153 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Family 0x000a - User Search.
+ *
+ * TODO: Add aim_usersearch_name()
+ *
+ */
+
+#include "oscar.h"
+
+/*
+ * Subtype 0x0001
+ *
+ * XXX can this be integrated with the rest of the error handling?
+ */
+static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+
+	/* XXX the modules interface should have already retrieved this for us */
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		gaim_debug_misc("oscar", "search error: couldn't get a snac for 0x%08lx\n", snac->id);
+		return 0;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->data /* address */);
+
+	/* XXX freesnac()? */
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	return ret;
+}
+
+/*
+ * Subtype 0x0002
+ *
+ */
+faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !address)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1);
+	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
+	
+	aimbs_putstr(&fr->data, address); 
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0003
+ *
+ */
+static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int j = 0, m, ret = 0;
+	aim_tlvlist_t *tlvlist;
+	char *cur = NULL, *buf = NULL;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+	char *searchaddr = NULL;
+
+	if ((snac2 = aim_remsnac(sess, snac->id)))
+		searchaddr = (char *)snac2->data;
+
+	tlvlist = aim_tlvlist_read(bs);
+	m = aim_tlvlist_count(&tlvlist);
+
+	/* XXX uhm.
+	 * This is the only place that uses something other than 1 for the 3rd 
+	 * parameter to aim_tlv_gettlv_whatever().
+	 */
+	while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) {
+		buf = realloc(buf, (j+1) * (MAXSNLEN+1));
+
+		strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
+		free(cur);
+
+		j++; 
+	}
+
+	aim_tlvlist_free(&tlvlist);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, searchaddr, j, buf);
+
+	/* XXX freesnac()? */
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	free(buf);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0001)
+		return error(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0003)
+		return reply(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000a;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "userlookup", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- a/src/protocols/oscar/ft.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1208 +0,0 @@
-/*
- * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
- * (ODC is also referred to as DirectIM and IM Image.)
- *
- * There are a few static helper functions at the top, then 
- * ODC stuff, then ft stuff.
- *
- * I feel like this is a good place to explain OFT, so I'm going to 
- * do just that.  Each OFT packet has a header type.  I guess this 
- * is pretty similar to the subtype of a SNAC packet.  The type 
- * basically tells the other client the meaning of the OFT packet.  
- * There are two distinct types of file transfer, which I usually 
- * call "sendfile" and "getfile."  Sendfile is when you send a file 
- * to another AIM user.  Getfile is when you share a group of files, 
- * and other users request that you send them the files.
- *
- * A typical sendfile file transfer goes like this:
- *   1) Sender sends a channel 2 ICBM telling the other user that 
- *      we want to send them a file.  At the same time, we open a 
- *      listener socket (this should be done before sending the 
- *      ICBM) on some port, and wait for them to connect to us.  
- *      The ICBM we sent should contain our IP address and the port 
- *      number that we're listening on.
- *   2) The receiver connects to the sender on the given IP address 
- *      and port.  After the connection is established, the receiver 
- *      sends an ICBM signifying that we are ready and waiting.
- *   3) The sender sends an OFT PROMPT message over the OFT 
- *      connection.
- *   4) The receiver of the file sends back an exact copy of this 
- *      OFT packet, except the cookie is filled in with the cookie 
- *      from the ICBM.  I think this might be an attempt to verify 
- *      that the user that is connected is actually the guy that 
- *      we sent the ICBM to.  Oh, I've been calling this the ACK.
- *   5) The sender starts sending raw data across the connection 
- *      until the entire file has been sent.
- *   6) The receiver knows the file is finished because the sender 
- *      sent the file size in an earlier OFT packet.  So then the 
- *      receiver sends the DONE thingy (after filling in the 
- *      "received" checksum and size) and closes the connection.
- */
-
-#define FAIM_INTERNAL
-#ifdef HAVE_CONFIG_H
-#include  <config.h>
-#endif
-
-#include <aim.h>
-
-#ifndef _WIN32
-#include <stdio.h>
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/utsname.h> /* for aim_odc_initiate */
-#include <arpa/inet.h> /* for inet_ntoa */
-#include <limits.h> /* for UINT_MAX */
-#define G_DIR_SEPARATOR '/'
-#endif
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-/*
- * I really want to switch all our networking code to using IPv6 only,
- * but that really isn't a good idea at all.  Evan S. of Adium says
- * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
- * nothing inherently IPv6 about them.  And I feel like Linux kernel
- * 2.6.5 is doing the same thing.  So we REALLY should accept
- * connections if they're showing up as IPv6.  Old OSes (Solaris?)
- * that might not have full IPv6 support yet will fail if we try
- * to use PF_INET6 but it isn't defined.  --Mark Doliner
- */
-#ifndef PF_INET6
-#define PF_INET6 PF_INET
-#endif
-
-struct aim_odc_intdata {
-	fu8_t cookie[8];
-	char sn[MAXSNLEN+1];
-	char ip[22];
-};
-
-/**
- * Convert the directory separator from / (0x2f) to ^A (0x01)
- *
- * @param name The filename to convert.
- */
-static void aim_oft_dirconvert_tostupid(char *name)
-{
-	while (name[0]) {
-		if (name[0] == 0x01)
-			name[0] = G_DIR_SEPARATOR;
-		name++;
-	}
-}
-
-/**
- * Convert the directory separator from ^A (0x01) to / (0x2f)
- *
- * @param name The filename to convert.
- */
-static void aim_oft_dirconvert_fromstupid(char *name)
-{
-	while (name[0]) {
-		if (name[0] == G_DIR_SEPARATOR)
-			name[0] = 0x01;
-		name++;
-	}
-}
-
-/**
- * Calculate oft checksum of buffer
- *
- * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The 
- * checksum is kind of a rolling checksum thing, so each time you get bytes 
- * of a file you just call this puppy and it updates the checksum.  You can 
- * calculate the checksum of an entire file by calling this in a while or a 
- * for loop, or something.
- *
- * Thanks to Graham Booker for providing this improved checksum routine, 
- * which is simpler and should be more accurate than Josh Myer's original 
- * code. -- wtm
- *
- * This algorithm works every time I have tried it.  The other fails 
- * sometimes.  So, AOL who thought this up?  It has got to be the weirdest 
- * checksum I have ever seen.
- *
- * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
- * @param bufsize Size of buffer.
- * @param prevcheck Previous checksum.
- */
-faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck)
-{
-	fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
-	int i;
-	unsigned short val;
-
-	for (i=0; i<bufferlen; i++) {
-		oldcheck = check;
-		if (i&1)
-			val = buffer[i];
-		else
-			val = buffer[i] << 8;
-		check -= val;
-		/*
-		 * The following appears to be necessary.... It happens 
-		 * every once in a while and the checksum doesn't fail.
-		 */
-		if (check > oldcheck)
-			check--;
-	}
-	check = ((check & 0x0000ffff) + (check >> 16));
-	check = ((check & 0x0000ffff) + (check >> 16));
-	return check << 16;
-}
-
-faim_export fu32_t aim_oft_checksum_file(char *filename) {
-	FILE *fd;
-	fu32_t checksum = 0xffff0000;
-
-	if ((fd = fopen(filename, "rb"))) {
-		int bytes;
-		fu8_t buffer[1024];
-
-		while ((bytes = fread(buffer, 1, 1024, fd)))
-			checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);
-		fclose(fd);
-	}
-
-	return checksum;
-}
-
-/**
- * After establishing a listening socket, this is called to accept a connection.  It
- * clones the conn used by the listener, and passes both of these to a signal handler.
- * The signal handler should close the listener conn and keep track of the new conn,
- * since this is what is used for file transfers and what not.
- *
- * @param sess The session.
- * @param cur The conn the incoming connection is on.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
-{
-	int acceptfd = 0;
-	struct sockaddr addr;
-	socklen_t addrlen = sizeof(addr);
-	int ret = 0;
-	aim_conn_t *newconn;
-	char ip[20];
-	unsigned short port;
-
-	if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)
-		return 0; /* not an error */
-
-	if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) {
-		close(acceptfd);
-		aim_conn_close(cur);
-		return -1;
-	}
-
-	strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));
-	port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
-
-	if (!(newconn = aim_cloneconn(sess, cur))) {
-		close(acceptfd);
-		aim_conn_close(cur);
-		return -ENOMEM;
-	}
-
-	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
-	newconn->fd = acceptfd;
-
-	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
-		aim_rxcallback_t userfunc;
-		struct aim_odc_intdata *priv;
-
-		priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);
-		cur->internal = NULL;
-		snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port);
-
-		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))
-			ret = userfunc(sess, NULL, newconn, cur);
-
-	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
-	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
-		aim_rxcallback_t userfunc;
-
-		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))
-			ret = userfunc(sess, NULL, newconn, cur);
-
-	} else {
-		gaim_debug_warning("oscar", "Got a connection on a listener that's not rendezvous.  Closing connection.\n");
-		aim_conn_close(newconn);
-		ret = -1;
-	}
-
-	return ret;
-}
-
-/**
- * Send client-to-client typing notification over an established direct connection.
- *
- * @param sess The session.
- * @param conn The already-connected ODC connection.
- * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and 
- *        0x0000 sends "stopped."
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
-{
-	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
-	aim_frame_t *fr;
-	aim_bstream_t *hdrbs;
-	fu8_t *hdr;
-	int hdrlen = 0x44;
-
-	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))
-		return -ENOMEM;
-	memcpy(fr->hdr.rend.magic, "ODC2", 4);
-	fr->hdr.rend.hdrlen = hdrlen + 8;
-
-	if (!(hdr = calloc(1, hdrlen))) {
-		aim_frame_destroy(fr);
-		return -ENOMEM;
-	}
-
-	hdrbs = &(fr->data);
-	aim_bstream_init(hdrbs, hdr, hdrlen);
-
-	aimbs_put16(hdrbs, 0x0006);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_putraw(hdrbs, intdata->cookie, 8);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put32(hdrbs, 0x00000000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-
-	if (typing == 0x0002)
-		aimbs_put16(hdrbs, 0x0002 | 0x0008);
-	else if (typing == 0x0001)
-		aimbs_put16(hdrbs, 0x0002 | 0x0004);
-	else
-		aimbs_put16(hdrbs, 0x0002);
-
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_putstr(hdrbs, sess->sn);
-
-	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
-
-	aimbs_put8(hdrbs, 0x00);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put8(hdrbs, 0x00);
-
-	/* end of hdr */
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Send client-to-client IM over an established direct connection.
- * Call this just like you would aim_send_im, to send a directim.
- * 
- * @param sess The session.
- * @param conn The already-connected ODC connection.
- * @param msg Null-terminated string to send.
- * @param len The length of the message to send, including binary data.
- * @param encoding See the AIM_CHARSET_* defines in aim.h
- * @param isawaymsg 0 if this is not an auto-response, 1 if it is.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg)
-{
-	aim_frame_t *fr;
-	aim_bstream_t *hdrbs;
-	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
-	int hdrlen = 0x44;
-	fu8_t *hdr;
-
-	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
-		return -ENOMEM;
-
-	memcpy(fr->hdr.rend.magic, "ODC2", 4);
-	fr->hdr.rend.hdrlen = hdrlen + 8;
-
-	if (!(hdr = calloc(1, hdrlen + len))) {
-		aim_frame_destroy(fr);
-		return -ENOMEM;
-	}
-
-	hdrbs = &(fr->data);
-	aim_bstream_init(hdrbs, hdr, hdrlen + len);
-
-	aimbs_put16(hdrbs, 0x0006);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_putraw(hdrbs, intdata->cookie, 8);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put32(hdrbs, len);
-	aimbs_put16(hdrbs, encoding);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-
-	/* flags - used for typing notification and to mark if this is an away message */
-	aimbs_put16(hdrbs, 0x0000 | isawaymsg);
-
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_putstr(hdrbs, sess->sn);
-
-	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
-
-	aimbs_put8(hdrbs, 0x00);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put8(hdrbs, 0x00);
-
-	/* end of hdr2 */
-
-#if 0 /* XXX - this is how you send buddy icon info... */	
-	aimbs_put16(hdrbs, 0x0008);
-	aimbs_put16(hdrbs, 0x000c);
-	aimbs_put16(hdrbs, 0x0000);
-	aimbs_put16(hdrbs, 0x1466);
-	aimbs_put16(hdrbs, 0x0001);
-	aimbs_put16(hdrbs, 0x2e0f);
-	aimbs_put16(hdrbs, 0x393e);
-	aimbs_put16(hdrbs, 0xcac8);
-#endif
-	aimbs_putraw(hdrbs, (guchar *)msg, len);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Get the screen name of the peer of a direct connection.
- *
- * @param conn The ODC connection.
- * @return The screen name of the dude, or NULL if there was an anomaly.
- */
-faim_export const char *aim_odc_getsn(aim_conn_t *conn)
-{
-	struct aim_odc_intdata *intdata;
-
-	if (!conn || !conn->internal)
-		return NULL;
-
-	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) ||
-			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
-		return NULL;
-
-	intdata = (struct aim_odc_intdata *)conn->internal;
-
-	return intdata->sn;
-}
-
-/**
- * Get the cookie of a direct connection.
- *
- * @param conn The ODC connection.
- * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly.
- */
-faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn)
-{
-	struct aim_odc_intdata *intdata;
-
-	if (!conn || !conn->internal)
-		return NULL;
-
-	intdata = (struct aim_odc_intdata *)conn->internal;
-
-	return intdata->cookie;
-}
-
-/**
- * Find the conn of a direct connection with the given buddy.
- *
- * @param sess The session.
- * @param sn The screen name of the buddy whose direct connection you want to find.
- * @return The conn for the direct connection with the given buddy, or NULL if no 
- *         connection was found.
- */
-faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
-{
-	aim_conn_t *cur;
-	struct aim_odc_intdata *intdata;
-
-	if (!sess || !sn || !strlen(sn))
-		return NULL;
-
-	for (cur = sess->connlist; cur; cur = cur->next) {
-		if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
-			intdata = cur->internal;
-			if (!aim_sncmp(intdata->sn, sn))
-				return cur;
-		}
-	}
-
-	return NULL;
-}
-
-/**
- * For those times when we want to open up the direct connection channel ourselves.
- *
- * You'll want to set up some kind of watcher on this socket.
- * When the state changes, call aim_handlerendconnection with
- * the connection returned by this.  aim_handlerendconnection
- * will accept the pending connection and stop listening.
- *
- * @param sess The session
- * @param sn The screen name to connect to.
- * @return The new connection.
- */
-faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd,
-                                         const fu8_t *localip, fu16_t port, const fu8_t *mycookie)
-{
-	aim_conn_t *newconn;
-	aim_msgcookie_t *cookie;
-	struct aim_odc_intdata *priv;
-	fu8_t ck[8];
-
-	if (!localip)
-		return NULL;
-
-	if (mycookie) {
-		memcpy(ck, mycookie, 8);
-		aim_im_sendch2_odcrequest(sess, ck, TRUE, sn, localip, port);
-	} else
-		aim_im_sendch2_odcrequest(sess, ck, FALSE, sn, localip, port);
-
-	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
-	memcpy(cookie->cookie, ck, 8);
-	cookie->type = AIM_COOKIETYPE_OFTIM;
-
-	/* this one is for the cookie */
-	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
-
-	memcpy(priv->cookie, ck, 8);
-	strncpy(priv->sn, sn, sizeof(priv->sn));
-	cookie->data = priv;
-	aim_cachecookie(sess, cookie);
-
-	/* XXX - switch to aim_cloneconn()? */
-	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) {
-		close(listenfd);
-		return NULL;
-	}
-
-	/* this one is for the conn */
-	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
-
-	memcpy(priv->cookie, ck, 8);
-	strncpy(priv->sn, sn, sizeof(priv->sn));
-
-	newconn->fd = listenfd;
-	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-	newconn->internal = priv;
-	newconn->lastactivity = time(NULL);
-
-	return newconn;
-}
-
-/**
- * Connect directly to the given buddy for directim.
- *
- * This is a wrapper for aim_newconn.
- *
- * If addr is NULL, the socket is not created, but the connection is 
- * allocated and setup to connect.
- *
- * @param sess The Godly session.
- * @param sn The screen name we're connecting to.  I hope it's a girl...
- * @param addr Address to connect to.
- * @return The new connection.
- */
-faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
-{
-	aim_conn_t *newconn;
-	struct aim_odc_intdata *intdata;
-
-	if (!sess || !sn)
-		return NULL;
-
-	if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata))))
-		return NULL;
-	memcpy(intdata->cookie, cookie, 8);
-	strncpy(intdata->sn, sn, sizeof(intdata->sn));
-	if (addr)
-		strncpy(intdata->ip, addr, sizeof(intdata->ip));
-
-	/* XXX - verify that non-blocking connects actually work */
-	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS))) {
-		free(intdata);
-		return NULL;
-	}
-
-	newconn->internal = intdata;
-	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-
-	return newconn;
-}
-
-/**
- * Sometimes you just don't know with these kinds of people.
- *
- * @param sess The session.
- * @param conn The ODC connection of the incoming data.
- * @param frr The frame allocated for the incoming data.
- * @param bs It stands for "bologna sandwich."
- * @return Return 0 if no errors, otherwise return the error number.
- */
-static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
-{
-	aim_frame_t fr;
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu32_t payloadlength;
-	fu16_t flags, encoding;
-	char *snptr = NULL;
-
-	fr.conn = conn;
-
-	/* AAA - ugly */
-	aim_bstream_setpos(bs, 20);
-	payloadlength = aimbs_get32(bs);
-
-	aim_bstream_setpos(bs, 24);
-	encoding = aimbs_get16(bs);
-
-	aim_bstream_setpos(bs, 30);
-	flags = aimbs_get16(bs);
-
-	aim_bstream_setpos(bs, 36);
-	/* XXX - create an aimbs_getnullstr function? */
-	snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */
-
-	gaim_debug_misc("oscar", "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
-
-	if (flags & 0x0008) {
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
-			ret = userfunc(sess, &fr, snptr, 2);
-	} else if (flags & 0x0004) {
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
-			ret = userfunc(sess, &fr, snptr, 1);
-	} else {
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
-			ret = userfunc(sess, &fr, snptr, 0);
-	}
-
-	if ((payloadlength != 0) && (payloadlength != UINT_MAX)) {
-		char *msg;
-		int recvd = 0;
-		int i, isawaymsg;
-
-		isawaymsg = flags & 0x0001;
-
-		if (!(msg = calloc(1, payloadlength+1))) {
-			free(snptr);
-			return -ENOMEM;
-		}
-
-		while (payloadlength - recvd) {
-			if (payloadlength - recvd >= 1024)
-				i = aim_recv(conn->fd, &msg[recvd], 1024);
-			else 
-				i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
-			if (i <= 0) {
-				free(msg);
-				free(snptr);
-				return -1;
-			}
-			recvd = recvd + i;
-			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
-				ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
-		}
-		
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)))
-			ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);
-
-		free(msg);
-	}
-
-	free(snptr);
-
-	return ret;
-}
-
-faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv, int method, int stage)
-{
-	struct aim_oft_info *new;
-
-	if (!sess)
-		return NULL;
-
-	if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info))))
-		return NULL;
-
-	new->sess = sess;
-	if (cookie)
-		memcpy(new->cookie, cookie, 8);
-	else
-		aim_icbm_makecookie(new->cookie);
-	if (ip)
-		new->clientip = strdup(ip);
-	else
-		new->clientip = NULL;
-	if (sn)
-		new->sn = strdup(sn);
-	else
-		new->sn = NULL;
-	new->method = method;
-	new->send_or_recv = send_or_recv;
-	new->stage = stage;
-	new->port = port;
-	new->xfer_reffed = FALSE;
-	new->success = FALSE;
-	new->fh.totfiles = 1;
-	new->fh.filesleft = 1;
-	new->fh.totparts = 1;
-	new->fh.partsleft = 1;
-	new->fh.totsize = size;
-	new->fh.size = size;
-	new->fh.modtime = modtime;
-	new->fh.checksum = 0xffff0000;
-	new->fh.rfrcsum = 0xffff0000;
-	new->fh.rfcsum = 0xffff0000;
-	new->fh.recvcsum = 0xffff0000;
-	strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31);
-	if (filename) {
-		strncpy(new->fh.name, filename, 63);
-		new->fh.name[63] = '\0';
-	}
-
-	new->next = sess->oft_info;
-	sess->oft_info = new;
-
-	return new;
-}
-
-faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const fu8_t *cookie,
-	fu16_t port)
-{
-	struct aim_rv_proxy_info *proxy_info;
-	
-	if (!(proxy_info = (struct aim_rv_proxy_info*)calloc(1, sizeof(struct aim_rv_proxy_info))))
-		return NULL;
-	
-	proxy_info->sess = sess;
-	proxy_info->port = port;
-	proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT;
-	proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT;
-	
-	if (cookie)
-		memcpy(proxy_info->cookie, cookie, 8);
-	
-	return proxy_info;
-}
-
-/**
- * Remove the given oft_info struct from the oft_info linked list, and 
- * then free its memory.
- *
- * @param sess The session.
- * @param oft_info The aim_oft_info struct that we're destroying.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info)
-{
-	aim_session_t *sess;
-
-	if (!oft_info || !(sess = oft_info->sess))
-		return -EINVAL;
-
-	if (sess->oft_info && (sess->oft_info == oft_info)) {
-		sess->oft_info = sess->oft_info->next;
-	} else {
-		struct aim_oft_info *cur;
-		for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next);
-		if (cur->next)
-			cur->next = cur->next->next;
-	}
-
-	free(oft_info->sn);
-	free(oft_info->proxyip);
-	free(oft_info->clientip);
-	free(oft_info->verifiedip);
-	free(oft_info);
-
-	return 0;
-}
-
-/**
- * Creates a listener socket so the other dude can connect to us.
- *
- * You'll want to set up some kind of watcher on this socket.  
- * When the state changes, call aim_handlerendconnection with 
- * the connection returned by this.  aim_handlerendconnection 
- * will accept the pending connection and stop listening.
- *
- * @param sess The session.
- * @param oft_info File transfer information associated with this 
- *        connection.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd)
-{
-	if (!oft_info)
-		return -EINVAL;
-
-	if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) {
-		close(listenfd);
-		return -ENOMEM;
-	}
-
-	oft_info->conn->fd = listenfd;
-	oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
-	oft_info->conn->lastactivity = time(NULL);
-
-	return 0;
-}
-
-/**
- * Extract an &aim_fileheader_t from the given buffer.
- *
- * @param bs The should be from an incoming rendezvous packet.
- * @return A pointer to new struct on success, or NULL on error.
- */
-static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
-{
-	struct aim_fileheader_t *fh;
-
-	if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
-		return NULL;
-
-	/* The bstream should be positioned after the hdrtype. */
-	aimbs_getrawbuf(bs, fh->bcookie, 8);
-	fh->encrypt = aimbs_get16(bs);
-	fh->compress = aimbs_get16(bs);
-	fh->totfiles = aimbs_get16(bs);
-	fh->filesleft = aimbs_get16(bs);
-	fh->totparts = aimbs_get16(bs);
-	fh->partsleft = aimbs_get16(bs);
-	fh->totsize = aimbs_get32(bs);
-	fh->size = aimbs_get32(bs);
-	fh->modtime = aimbs_get32(bs);
-	fh->checksum = aimbs_get32(bs);
-	fh->rfrcsum = aimbs_get32(bs);
-	fh->rfsize = aimbs_get32(bs);
-	fh->cretime = aimbs_get32(bs);
-	fh->rfcsum = aimbs_get32(bs);
-	fh->nrecvd = aimbs_get32(bs);
-	fh->recvcsum = aimbs_get32(bs);
-	aimbs_getrawbuf(bs, (guchar *)fh->idstring, 32);
-	fh->flags = aimbs_get8(bs);
-	fh->lnameoffset = aimbs_get8(bs);
-	fh->lsizeoffset = aimbs_get8(bs);
-	aimbs_getrawbuf(bs, (guchar *)fh->dummy, 69);
-	aimbs_getrawbuf(bs, (guchar *)fh->macfileinfo, 16);
-	fh->nencode = aimbs_get16(bs);
-	fh->nlanguage = aimbs_get16(bs);
-	aimbs_getrawbuf(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
-	fh->name[63] = '\0';
-
-	return fh;
-} 
-
-/**
- * Fills a buffer with network-order fh data
- *
- * @param bs A bstream to fill -- automatically initialized
- * @param fh A struct aim_fileheader_t to get data from.
- * @return Return non-zero on error.
- */
-static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
-{ 
-	fu8_t *hdr;
-
-	if (!bs || !fh)
-		return -EINVAL;
-
-	if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
-		return -ENOMEM;
-
-	aim_bstream_init(bs, hdr, 0x100 - 8);
-	aimbs_putraw(bs, fh->bcookie, 8);
-	aimbs_put16(bs, fh->encrypt);
-	aimbs_put16(bs, fh->compress);
-	aimbs_put16(bs, fh->totfiles);
-	aimbs_put16(bs, fh->filesleft);
-	aimbs_put16(bs, fh->totparts);
-	aimbs_put16(bs, fh->partsleft);
-	aimbs_put32(bs, fh->totsize);
-	aimbs_put32(bs, fh->size);
-	aimbs_put32(bs, fh->modtime);
-	aimbs_put32(bs, fh->checksum);
-	aimbs_put32(bs, fh->rfrcsum);
-	aimbs_put32(bs, fh->rfsize);
-	aimbs_put32(bs, fh->cretime);
-	aimbs_put32(bs, fh->rfcsum);
-	aimbs_put32(bs, fh->nrecvd);
-	aimbs_put32(bs, fh->recvcsum);
-	aimbs_putraw(bs, (guchar *)fh->idstring, 32);
-	aimbs_put8(bs, fh->flags);
-	aimbs_put8(bs, fh->lnameoffset);
-	aimbs_put8(bs, fh->lsizeoffset);
-	aimbs_putraw(bs, (guchar *)fh->dummy, 69);
-	aimbs_putraw(bs, (guchar *)fh->macfileinfo, 16);
-	aimbs_put16(bs, fh->nencode);
-	aimbs_put16(bs, fh->nlanguage);
-	aimbs_putraw(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
-
-	return 0;
-}
-
-/**
- * Create an OFT packet based on the given information, and send it on its merry way.
- *
- * @param sess The session.
- * @param type The subtype of the OFT packet we're sending.
- * @param oft_info The aim_oft_info struct with the connection and OFT 
- *        info we're sending.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info)
-{
-	aim_frame_t *fr;
-
-	if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
-		return -EINVAL;
-
-#if 0
-	/*
-	 * If you are receiving a file, the cookie should be null, if you are sending a 
-	 * file, the cookie should be the same as the one used in the ICBM negotiation 
-	 * SNACs.
-	 */
-	fh->lnameoffset = 0x1a;
-	fh->lsizeoffset = 0x10;
-
-	/* These should be the same as charset and charsubset in ICBMs */
-	fh->nencode = 0x0000;
-	fh->nlanguage = 0x0000;
-#endif
-
-	aim_oft_dirconvert_tostupid(oft_info->fh.name);
-
-	if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0)))
-		return -ENOMEM;
-
-	if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) {
-		aim_frame_destroy(fr);
-		return -ENOMEM;
-	}
-
-	memcpy(fr->hdr.rend.magic, "OFT2", 4);
-	fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data) + 8;
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Create a rendezvous "init recv" packet and send it on its merry way.
- * This is the first packet sent to the proxy server by the second client
- * involved in this rendezvous proxy session.
- *
- * @param sess The session.
- * @param proxy_info Changable pieces of data for this packet
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info)
-{
-#if 0
-	aim_tlvlist_t *tlvlist_sendfile;
-#endif
-	aim_bstream_t bs;
-	fu8_t *bs_raw;
-	fu16_t packet_len;
-	fu8_t sn_len;
-	int err;
-
-	err = 0;
-
-	if (!proxy_info)
-		return -EINVAL;
-
-	sn_len = strlen(proxy_info->sess->sn);
-	packet_len = 2 + 2	/* packet_len, packet_ver */
-		+ 2 + 4		/* cmd_type,  unknownA */
-		+ 2		/* flags */
-		+ 1 + sn_len	/* Length/value pair for screenname */
-		+ 8		/* ICBM Cookie */
-		+ 2		/* port */
-		+ 2 + 2 + 16;	/* TLV for Filesend capability block */
-
-	if (!(bs_raw = malloc(packet_len)))
-		return -ENOMEM;
-
-	aim_bstream_init(&bs, bs_raw, packet_len);
-	aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
-	aimbs_put16(&bs, proxy_info->packet_ver);
-	aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV);
-	aimbs_put32(&bs, proxy_info->unknownA);
-	aimbs_put16(&bs, proxy_info->flags);
-	aimbs_put8(&bs, sn_len);
-	aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
-	aimbs_put16(&bs, proxy_info->port);
-	aimbs_putraw(&bs, proxy_info->cookie, 8);
-
-	aimbs_put16(&bs, 0x0001);		/* Type */
-	aimbs_put16(&bs, 16);			/* Length */
-	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
-
-
-#if 0
-	/* TODO: Use built-in TLV */
-	aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
-	aim_tlvlist_write(&bs, &tlvlist_sendfile);
-#endif
-
-	aim_bstream_rewind(&bs);
-	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
-		err = errno;
-	proxy_info->conn->lastactivity = time(NULL);
-
-#if 0
-	aim_tlvlist_free(tlvlist_sendfile);
-#endif
-	free(bs_raw);
-
-	return err;
-}
-
-
-/**
- * Create a rendezvous "init send" packet and send it on its merry way.
- * This is the first packet sent to the proxy server by the client
- * first indicating that this will be a proxied connection
- *
- * @param sess The session.
- * @param proxy_info Changable pieces of data for this packet
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info)
-{
-#if 0
-	aim_tlvlist_t *tlvlist_sendfile;
-#endif
-	aim_bstream_t bs;
-	fu8_t *bs_raw;
-	fu16_t packet_len;
-	fu8_t sn_len;
-	int err;
-
-	err = 0;
-
-	if (!proxy_info)
-		return -EINVAL;
-
-	sn_len = strlen(proxy_info->sess->sn);
-	packet_len = 2 + 2	/* packet_len, packet_ver */
-		+ 2 + 4		/* cmd_type,  unknownA */
-		+ 2		/* flags */
-		+ 1 + sn_len	/* Length/value pair for screenname */
-		+ 8		/* ICBM Cookie */
-		+ 2 + 2 + 16;	/* TLV for Filesend capability block */
-
-	if (!(bs_raw = malloc(packet_len)))
-		return -ENOMEM;
-
-	aim_bstream_init(&bs, bs_raw, packet_len);
-	aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
-	aimbs_put16(&bs, proxy_info->packet_ver);
-	aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND);
-	aimbs_put32(&bs, proxy_info->unknownA);
-	aimbs_put16(&bs, proxy_info->flags);
-	aimbs_put8(&bs, sn_len);
-	aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
-	aimbs_putraw(&bs, proxy_info->cookie, 8);
-
-	aimbs_put16(&bs, 0x0001);		/* Type */
-	aimbs_put16(&bs, 16);			/* Length */
-	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
-
-	/* TODO: Use built-in TLV */
-#if 0
-	aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
-	aim_tlvlist_write(&bs, &tlvlist_sendfile);
-#endif
-
-	aim_bstream_rewind(&bs);
-	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
-		err = errno;
-	proxy_info->conn->lastactivity = time(NULL);
-
-#if 0
-	aim_tlvlist_free(tlvlist_sendfile);
-#endif
-	free(bs_raw);
-
-	return err;
-}
-
-/**
- * Handle incoming data on a rendezvous connection.  This is analogous to the
- * consumesnac function in rxhandlers.c, and I really think this should probably
- * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
- *
- * @param sess The session.
- * @param fr The frame allocated for the incoming data.
- * @return Return 0 if the packet was handled correctly, otherwise return the
- *         error number.
- */
-faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
-{
-	aim_conn_t *conn = fr->conn;
-	int ret = 1;
-
-	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
-		if (fr->hdr.rend.type == 0x0001)
-			ret = handlehdr_odc(sess, conn, fr, &fr->data);
-		else
-			gaim_debug_info("oscar", "ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type);
-
-	} else {
-		aim_rxcallback_t userfunc;
-		struct aim_fileheader_t *header = aim_oft_getheader(&fr->data);
-		aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */
-
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
-			ret = userfunc(sess, fr, conn, header->bcookie, header);
-
-		free(header);
-	}
-
-	if (ret == -1)
-		aim_conn_close(conn);
-
-	return ret;
-}
-
-/**
- * Handle incoming data on a rendezvous proxy connection.  This is similar to
- * aim_rxdispatch_rendezvous above and should probably be kept with that function.
- *
- * @param sess The session.
- * @param fr The frame allocated for the incoming data.
- * @return Return 0 if the packet was handled correctly, otherwise return the 
- *         error number.
- */
-faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_bstream_t bs_hdr;
-	fu8_t hdr_buf[AIM_RV_PROXY_HDR_LEN];
-	aim_bstream_t bs_body; /* The body (everything but the header) of the packet */
-	fu8_t *body_buf = NULL;
-	fu8_t body_len;
-	
-	char str_ip[30] = {""};
-	fu8_t ip_temp[4];
-	
-	fu16_t len;
-	struct aim_rv_proxy_info *proxy_info;
-	
-	if(!(proxy_info = malloc(sizeof(struct aim_rv_proxy_info))))
-		return NULL;
-
-	aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN);
-	if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) {
-		aim_bstream_rewind(&bs_hdr);
-		len = aimbs_get16(&bs_hdr);
-		proxy_info->packet_ver = aimbs_get16(&bs_hdr);
-		proxy_info->cmd_type = aimbs_get16(&bs_hdr);
-		proxy_info->unknownA = aimbs_get32(&bs_hdr);
-		proxy_info->flags = aimbs_get16(&bs_hdr);
-		if(proxy_info->cmd_type == AIM_RV_PROXY_READY) {
-			/* Do a little victory dance
-			 * A ready packet contains no additional information */
-		} else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) {
-			if(len == AIM_RV_PROXY_ERROR_LEN - 2) {
-				body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN;
-				body_buf = malloc(body_len);
-				aim_bstream_init(&bs_body, body_buf, body_len);
-				if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
-					aim_bstream_rewind(&bs_body);
-					proxy_info->err_code = aimbs_get16(&bs_body);
-				} else {
-					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
-					aim_conn_close(conn);
-					free(proxy_info);
-					proxy_info = NULL;
-				}
-			} else {
-				gaim_debug_warning("oscar","invalid length for proxy error packet\n");
-				free(proxy_info);
-				proxy_info = NULL;
-			}
-		} else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) {
-			if(len == AIM_RV_PROXY_ACK_LEN - 2) {
-				body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN;
-				body_buf = malloc(body_len);
-				aim_bstream_init(&bs_body, body_buf, body_len);
-				if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
-					int i;
-					aim_bstream_rewind(&bs_body);
-					proxy_info->port = aimbs_get16(&bs_body);
-					for(i=0; i<4; i++)
-						ip_temp[i] = aimbs_get8(&bs_body);
-					snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu",
-						ip_temp[0], ip_temp[1],
-						ip_temp[2], ip_temp[3]);
-					proxy_info->ip = strdup(str_ip);
-				} else {
-					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
-					aim_conn_close(conn);
-					free(proxy_info);
-					proxy_info = NULL;
-				}
-			} else {
-				gaim_debug_warning("oscar","invalid length for proxy error packet\n");
-				free(proxy_info);
-				proxy_info = NULL;
-			}
-		} else {
-			gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n");
-		}	
-	} else {
-		gaim_debug_warning("oscar","error reading header of rv proxy packet\n");
-		aim_conn_close(conn);
-		free(proxy_info);
-		proxy_info = NULL;
-	}
-	if(body_buf) {
-		free(body_buf);
-		body_buf = NULL;
-	}
-	return proxy_info;
-}
--- a/src/protocols/oscar/icq.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,696 +0,0 @@
-/*
- * Family 0x0015 - Encapsulated ICQ.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 4 + 2 + 2;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x003c); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 4 + 2 + 2;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x003e); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-faim_export int
-aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x0c3a); /* shrug. */
-	aimbs_putle16(&fr->data, 0x030c);
-	aimbs_putle16(&fr->data, 0x0001);
-	aimbs_putle8(&fr->data, webaware);
-	aimbs_putle8(&fr->data, 0xf8);
-	aimbs_putle8(&fr->data, 0x02);
-	aimbs_putle8(&fr->data, 0x01);
-	aimbs_putle8(&fr->data, 0x00);
-	aimbs_putle8(&fr->data, !auth_required);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Change your ICQ password.
- *
- * @param sess The oscar session
- * @param passwd The new password.  If this is longer than 8 characters it 
- *        will be truncated.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen, passwdlen;
-
-	if (!passwd)
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	passwdlen = strlen(passwd);
-	if (passwdlen > MAXICQPASSLEN)
-		passwdlen = MAXICQPASSLEN;
-	bslen = 2+4+2+2+2+2+passwdlen+1;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x042e); /* shrug. */
-	aimbs_putle16(&fr->data, passwdlen+1);
-	aimbs_putstr(&fr->data, passwd);
-	aimbs_putle8(&fr->data, '\0');
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-	struct aim_icq_info *info;
-
-	if (!uin || uin[0] < '0' || uin[0] > '9')
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 4 + 2 + 2 + 2 + 4;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
-	aimbs_putle32(&fr->data, atoi(uin));
-
-	aim_tx_enqueue(sess, fr);
-
-	/* Keep track of this request and the ICQ number and request ID */
-	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
-	info->reqid = snacid;
-	info->uin = atoi(uin);
-	info->next = sess->icq_info;
-	sess->icq_info = info;
-
-	return 0;
-}
-
-faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-	struct aim_icq_info *info;
-
-	if (!uin || uin[0] < '0' || uin[0] > '9')
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 4 + 2 + 2 + 2 + 4;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x04ba); /* shrug. */
-	aimbs_putle32(&fr->data, atoi(uin));
-
-	aim_tx_enqueue(sess, fr);
-
-	/* Keep track of this request and the ICQ number and request ID */
-	info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
-	info->reqid = snacid;
-	info->uin = atoi(uin);
-	info->next = sess->icq_info;
-	sess->icq_info = info;
-
-	return 0;
-}
-
-faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-
-	if (!uin || uin[0] < '0' || uin[0] > '9')
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 4 + 2 + 2 + 2 + 4;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x051f); /* shrug. */
-	aimbs_putle32(&fr->data, atoi(uin));
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-#if 0
-faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen;
-
-	if (!xml || !strlen(xml))
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	bslen = 2 + 10 + 2 + strlen(xml) + 1;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x0998); /* shrug. */
-	aimbs_putle16(&fr->data, strlen(xml) + 1);
-	aimbs_putraw(&fr->data, (fu8_t *)xml, strlen(xml) + 1);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-#endif
-
-#if 0
-/*
- * Send an SMS message.  This is the non-US way.  The US-way is to IM 
- * their cell phone number (+19195551234).
- *
- * We basically construct and send an XML message.  The format is:
- * <icq_sms_message>
- *   <destination>full_phone_without_leading_+</destination>
- *   <text>message</text>
- *   <codepage>1252</codepage>
- *   <senders_UIN>self_uin</senders_UIN>
- *   <senders_name>self_name</senders_name>
- *   <delivery_receipt>Yes|No</delivery_receipt>
- *   <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time>
- * </icq_sms_message>
- *
- * Yeah hi Peter, whaaaat's happening.  If there's any way to use 
- * a codepage other than 1252 that would be great.  Thaaaanks.
- */
-faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int bslen, xmllen;
-	char *xml;
-	const char *timestr;
-	time_t t;
-	struct tm *tm;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
-		return -EINVAL;
-
-	if (!name || !msg || !alias)
-		return -EINVAL;
-
-	time(&t);
-	tm = gmtime(&t);
-	timestr = gaim_utf8_strftime("%a, %d %b %Y %T %Z", tm);
-
-	/* The length of xml included the null terminating character */
-	xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1;
-
-	if (!(xml = (char *)malloc(xmllen*sizeof(char))))
-		return -ENOMEM;
-	snprintf(xml, xmllen, "<icq_sms_message>\n"
-		"\t<destination>%s</destination>\n"
-		"\t<text>%s</text>\n"
-		"\t<codepage>1252</codepage>\n"
-		"\t<senders_UIN>%s</senders_UIN>\n"
-		"\t<senders_name>%s</senders_name>\n"
-		"\t<delivery_receipt>Yes</delivery_receipt>\n"
-		"\t<time>%s</time>\n"
-		"</icq_sms_message>\n",
-		name, msg, sess->sn, alias, timestr);
-
-	bslen = 37 + xmllen;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) {
-		free(xml);
-		return -ENOMEM;
-	}
-
-	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
-
-	/* For simplicity, don't bother using a tlvlist */
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, bslen);
-
-	aimbs_putle16(&fr->data, bslen - 2);
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
-	aimbs_putle16(&fr->data, snacid); /* eh. */
-
-	/* From libicq200-0.3.2/src/SNAC-SRV.cpp */
-	aimbs_putle16(&fr->data, 0x8214);
-	aimbs_put16(&fr->data, 0x0001);
-	aimbs_put16(&fr->data, 0x0016);
-	aimbs_put32(&fr->data, 0x00000000);
-	aimbs_put32(&fr->data, 0x00000000);
-	aimbs_put32(&fr->data, 0x00000000);
-	aimbs_put32(&fr->data, 0x00000000);
-
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_put16(&fr->data, xmllen);
-	aimbs_putstr(&fr->data, xml);
-
-	aim_tx_enqueue(sess, fr);
-
-	free(xml);
-
-	return 0;
-}
-#endif
-
-static void aim_icq_freeinfo(struct aim_icq_info *info) {
-	int i;
-
-	if (!info)
-		return;
-	free(info->nick);
-	free(info->first);
-	free(info->last);
-	free(info->email);
-	free(info->homecity);
-	free(info->homestate);
-	free(info->homephone);
-	free(info->homefax);
-	free(info->homeaddr);
-	free(info->mobile);
-	free(info->homezip);
-	free(info->personalwebpage);
-	if (info->email2)
-		for (i = 0; i < info->numaddresses; i++)
-			free(info->email2[i]);
-	free(info->email2);
-	free(info->workcity);
-	free(info->workstate);
-	free(info->workphone);
-	free(info->workfax);
-	free(info->workaddr);
-	free(info->workzip);
-	free(info->workcompany);
-	free(info->workdivision);
-	free(info->workposition);
-	free(info->workwebpage);
-	free(info->info);
-	free(info);
-}
-
-/**
- * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
- */
-static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_tlvlist_t *tl;
-	aim_tlv_t *datatlv;
-	aim_bstream_t qbs;
-	fu32_t ouruin;
-	fu16_t cmdlen, cmd, reqid;
-
-	if (!(tl = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tl, 0x0001, 1))) {
-		aim_tlvlist_free(&tl);
-		gaim_debug_misc("oscar", "corrupt ICQ response\n");
-		return 0;
-	}
-
-	aim_bstream_init(&qbs, datatlv->value, datatlv->length);
-
-	cmdlen = aimbs_getle16(&qbs);
-	ouruin = aimbs_getle32(&qbs);
-	cmd = aimbs_getle16(&qbs);
-	reqid = aimbs_getle16(&qbs);
-
-	gaim_debug_misc("oscar", "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid);
-
-	if (cmd == 0x0041) { /* offline message */
-		struct aim_icq_offlinemsg msg;
-		aim_rxcallback_t userfunc;
-
-		memset(&msg, 0, sizeof(msg));
-
-		msg.sender = aimbs_getle32(&qbs);
-		msg.year = aimbs_getle16(&qbs);
-		msg.month = aimbs_getle8(&qbs);
-		msg.day = aimbs_getle8(&qbs);
-		msg.hour = aimbs_getle8(&qbs);
-		msg.minute = aimbs_getle8(&qbs);
-		msg.type = aimbs_getle8(&qbs);
-		msg.flags = aimbs_getle8(&qbs);
-		msg.msglen = aimbs_getle16(&qbs);
-		msg.msg = aimbs_getstr(&qbs, msg.msglen);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG)))
-			ret = userfunc(sess, rx, &msg);
-
-		free(msg.msg);
-
-	} else if (cmd == 0x0042) {
-		aim_rxcallback_t userfunc;
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE)))
-			ret = userfunc(sess, rx);
-
-	} else if (cmd == 0x07da) { /* information */
-		fu16_t subtype;
-		struct aim_icq_info *info;
-		aim_rxcallback_t userfunc;
-
-		subtype = aimbs_getle16(&qbs);
-		aim_bstream_advance(&qbs, 1); /* 0x0a */
-
-		/* find other data from the same request */
-		for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next);
-		if (!info) {
-			info = (struct aim_icq_info *)calloc(1, sizeof(struct aim_icq_info));
-			info->reqid = reqid;
-			info->next = sess->icq_info;
-			sess->icq_info = info;
-		}
-
-		switch (subtype) {
-		case 0x00a0: { /* hide ip status */
-			/* nothing */
-		} break;
-
-		case 0x00aa: { /* password change status */
-			/* nothing */
-		} break;
-
-		case 0x00c8: { /* general and "home" information */
-			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->homecountry = aimbs_getle16(&qbs);
-			/* 0x0a 00 02 00 */
-			/* 1 byte timezone? */
-			/* 1 byte hide email flag? */
-		} break;
-
-		case 0x00dc: { /* personal information */
-			info->age = aimbs_getle8(&qbs);
-			info->unknown = aimbs_getle8(&qbs);
-			info->gender = aimbs_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */
-			info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->birthyear = aimbs_getle16(&qbs);
-			info->birthmonth = aimbs_getle8(&qbs);
-			info->birthday = aimbs_getle8(&qbs);
-			info->language1 = aimbs_getle8(&qbs);
-			info->language2 = aimbs_getle8(&qbs);
-			info->language3 = aimbs_getle8(&qbs);
-			/* 0x00 00 01 00 00 01 00 00 00 00 00 */
-		} break;
-
-		case 0x00d2: { /* work information */
-			info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workcountry = aimbs_getle16(&qbs);
-			info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			aim_bstream_advance(&qbs, 2); /* 0x01 00 */
-			info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-		} break;
-
-		case 0x00e6: { /* additional personal information */
-			info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1);
-		} break;
-
-		case 0x00eb: { /* email address(es) */
-			int i;
-			info->numaddresses = aimbs_getle16(&qbs);
-			info->email2 = (char **)calloc(info->numaddresses, sizeof(char *));
-			for (i = 0; i < info->numaddresses; i++) {
-				info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-				if (i+1 != info->numaddresses)
-					aim_bstream_advance(&qbs, 1); /* 0x00 */
-			}
-		} break;
-
-		case 0x00f0: { /* personal interests */
-		} break;
-
-		case 0x00fa: { /* past background and current organizations */
-		} break;
-
-		case 0x0104: { /* alias info */
-			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); /* email address? */
-			/* Then 0x00 02 00 */
-		} break;
-
-		case 0x010e: { /* unknown */
-			/* 0x00 00 */
-		} break;
-
-		case 0x019a: { /* simple info */
-			aim_bstream_advance(&qbs, 2);
-			info->uin = aimbs_getle32(&qbs);
-			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
-			/* Then 0x00 02 00 00 00 00 00 */
-		} break;
-		} /* End switch statement */
-
-		if (!(snac->flags & 0x0001)) {
-			if (subtype != 0x0104)
-				if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO)))
-					ret = userfunc(sess, rx, info);
-
-			if (info->uin && info->nick)
-				if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS)))
-					ret = userfunc(sess, rx, info);
-
-			if (sess->icq_info == info) {
-				sess->icq_info = info->next;
-			} else {
-				struct aim_icq_info *cur;
-				for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
-				if (cur->next)
-					cur->next = cur->next->next;
-			}
-			aim_icq_freeinfo(info);
-		}
-	}
-
-	aim_tlvlist_free(&tl);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return icqresponse(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-static void icq_shutdown(aim_session_t *sess, aim_module_t *mod)
-{
-	struct aim_icq_info *del;
-
-	while (sess->icq_info) {
-		del = sess->icq_info;
-		sess->icq_info = sess->icq_info->next;
-		aim_icq_freeinfo(del);
-	}
-
-	return;
-}
-
-faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0015;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x047c;
-	mod->flags = 0;
-	strncpy(mod->name, "icq", sizeof(mod->name));
-	mod->snachandler = snachandler;
-	mod->shutdown = icq_shutdown;
-
-	return 0;
-}
--- a/src/protocols/oscar/im.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2480 +0,0 @@
-/*
- * Family 0x0004 - Routines for sending/receiving Instant Messages.
- *
- * Note the term ICBM (Inter-Client Basic Message) which blankets
- * all types of generically routed through-server messages.  Within
- * the ICBM types (family 4), a channel is defined.  Each channel
- * represents a different type of message.  Channel 1 is used for
- * what would commonly be called an "instant message".  Channel 2
- * is used for negotiating "rendezvous".  These transactions end in
- * something more complex happening, such as a chat invitation, or
- * a file transfer.  Channel 3 is used for chat messages (not in
- * the same family as these channels).  Channel 4 is used for
- * various ICQ messages.  Examples are normal messages, URLs, and
- * old-style authorization.
- *
- * In addition to the channel, every ICBM contains a cookie.  For
- * standard IMs, these are only used for error messages.  However,
- * the more complex rendezvous messages make suitably more complex
- * use of this field.
- *
- * TODO: Split this up into an im.c file an an icbm.c file.  It
- *       will be beautiful, you'll see.
- *
- *       Need to rename all the mpmsg messages to aim_im_bleh.
- *
- *       Make sure aim_conn_findbygroup is used by all functions.
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-/**
- * Add a standard ICBM header to the given bstream with the given
- * information.
- *
- * @param bs The bstream to write the ICBM header to.
- * @param c c is for cookie, and cookie is for me.
- * @param channel The ICBM channel (1 through 4).
- * @param sn Null-terminated scrizeen nizame.
- * @return The number of bytes written.  It's really not useful.
- */
-static int aim_im_puticbm(aim_bstream_t *bs, const guchar *c, fu16_t channel, const char *sn)
-{
-	aimbs_putraw(bs, c, 8);
-	aimbs_put16(bs, channel);
-	aimbs_put8(bs, strlen(sn));
-	aimbs_putstr(bs, sn);
-	return 8+2+1+strlen(sn);
-}
-
-/*
- * Extracted from aim_im_sendch2_sendfile_ask
- * Generates a random ICBM cookie in a character array of length 8
- * and copies it into the variable passed as cookie
- */
-faim_export void aim_icbm_makecookie(guchar *cookie)
-{
-	int i;
-
-	/* Should be like "21CBF95" and null terminated */
-	for (i = 0; i < 7; i++)
-		cookie[i] = 0x30 + ((guchar)rand() % 10);
-	cookie[7] = '\0';
-}
-
-/*
- * Takes a msghdr (and a length) and returns a client type
- * code.  Note that this is *only a guess* and has a low likelihood
- * of actually being accurate.
- *
- * Its based on experimental data, with the help of Eric Warmenhoven
- * who seems to have collected a wide variety of different AIM clients.
- *
- *
- * Heres the current collection:
- *  0501 0003 0101 0101 01		AOL Mobile Communicator, WinAIM 1.0.414
- *  0501 0003 0101 0201 01		WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
- *					4.3.2229, 4.4.2286
- *  0501 0004 0101 0102 0101		WinAIM 4.1.2010, libfaim (right here)
- *  0501 0003 0101 02			WinAIM 5
- *  0501 0001 01			iChat x.x, mobile buddies
- *  0501 0001 0101 01			AOL v6.0, CompuServe 2000 v6.0, any TOC client
- *  0501 0002 0106			WinICQ 5.45.1.3777.85
- *
- * Note that in this function, only the feature bytes are tested, since
- * the rest will always be the same.
- *
- */
-faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len)
-{
-	static const struct {
-		fu16_t clientid;
-		int len;
-		fu8_t data[10];
-	} fingerprints[] = {
-		/* AOL Mobile Communicator, WinAIM 1.0.414 */
-		{ AIM_CLIENTTYPE_MC,
-		  3, {0x01, 0x01, 0x01}},
-
-		/* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
-		{ AIM_CLIENTTYPE_WINAIM,
-		  3, {0x01, 0x01, 0x02}},
-
-		/* WinAIM 4.1.2010, libfaim */
-		{ AIM_CLIENTTYPE_WINAIM41,
-		  4, {0x01, 0x01, 0x01, 0x02}},
-
-		/* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
-		{ AIM_CLIENTTYPE_AOL_TOC,
-		  1, {0x01}},
-
-		{ 0, 0, {0x00}}
-	};
-	int i;
-
-	if (!msghdr || (len <= 0))
-		return AIM_CLIENTTYPE_UNKNOWN;
-
-	for (i = 0; fingerprints[i].len; i++) {
-		if (fingerprints[i].len != len)
-			continue;
-		if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
-			return fingerprints[i].clientid;
-	}
-
-	return AIM_CLIENTTYPE_UNKNOWN;
-}
-
-/**
- * Subtype 0x0002 - Set ICBM parameters.
- *
- * I definitely recommend sending this.  If you don't, you'll be stuck
- * with the rather unreasonable defaults.
- *
- */
-faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!params)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
-
-	/* This is read-only (see Parameter Reply). Must be set to zero here. */
-	aimbs_put16(&fr->data, 0x0000);
-
-	/* These are all read-write */
-	aimbs_put32(&fr->data, params->flags);
-	aimbs_put16(&fr->data, params->maxmsglen);
-	aimbs_put16(&fr->data, params->maxsenderwarn);
-	aimbs_put16(&fr->data, params->maxrecverwarn);
-	aimbs_put32(&fr->data, params->minmsginterval);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0004 - Request ICBM parameter information.
- *
- */
-faim_export int aim_im_reqparams(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	return aim_genericreq_n_snacid(sess, conn, 0x0004, 0x0004);
-}
-
-/**
- * Subtype 0x0005 - Receive parameter information.
- *
- */
-static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	struct aim_icbmparameters params;
-
-	params.maxchan = aimbs_get16(bs);
-	params.flags = aimbs_get32(bs);
-	params.maxmsglen = aimbs_get16(bs);
-	params.maxsenderwarn = aimbs_get16(bs);
-	params.maxrecverwarn = aimbs_get16(bs);
-	params.minmsginterval = aimbs_get32(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		return userfunc(sess, rx, &params);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an ICBM (instant message).
- *
- *
- * Possible flags:
- *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
- *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
- *                        when the message is received (of type 0x0004/0x000c)
- *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
- *                        online (probably ICQ only).
- *
- * Generally, you should use the lowest encoding possible to send
- * your message.  If you only use basic punctuation and the generic
- * Latin alphabet, use ASCII7 (no flags).  If you happen to use non-ASCII7
- * characters, but they are all clearly defined in ISO-8859-1, then
- * use that.  Keep in mind that not all characters in the PC ASCII8
- * character set are defined in the ISO standard. For those cases (most
- * notably when the (r) symbol is used), you must use the full UNICODE
- * encoding for your message.  In UNICODE mode, _all_ characters must
- * occupy 16bits, including ones that are not special.  (Remember that
- * the first 128 UNICODE symbols are equivalent to ASCII7, however they
- * must be prefixed with a zero high order byte.)
- *
- * I strongly discourage the use of UNICODE mode, mainly because none
- * of the clients I use can parse those messages (and besides that,
- * wchars are difficult and non-portable to handle in most UNIX environments).
- * If you really need to include special characters, use the HTML UNICODE
- * entities.  These are of the form &#2026; where 2026 is the hex
- * representation of the UNICODE index (in this case, UNICODE
- * "Horizontal Ellipsis", or 133 in in ASCII8).
- *
- * Implementation note:  Since this is one of the most-used functions
- * in all of libfaim, it is written with performance in mind.  As such,
- * it is not as clear as it could be in respect to how this message is
- * supposed to be layed out. Most obviously, tlvlists should be used
- * instead of writing out the bytes manually.
- *
- * XXX - more precise verification that we never send SNACs larger than 8192
- * XXX - check SNAC size for multipart
- *
- */
-faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-	int msgtlvlen;
-	static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!args)
-		return -EINVAL;
-
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		if (args->mpmsg->numparts == 0)
-			return -EINVAL;
-	} else {
-		if (!args->msg || (args->msglen <= 0))
-			return -EINVAL;
-
-		if (args->msglen >= MAXMSGLEN)
-			return -E2BIG;
-	}
-
-	/* Painfully calculate the size of the message TLV */
-	msgtlvlen = 1 + 1; /* 0501 */
-
-	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
-		msgtlvlen += 2 + args->featureslen;
-	else
-		msgtlvlen += 2 + sizeof(deffeatures);
-
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		aim_mpmsg_section_t *sec;
-
-		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
-			msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
-			msgtlvlen += 4 /* charset */ + sec->datalen;
-		}
-
-	} else {
-		msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
-		msgtlvlen += 4 /* charset */ + args->msglen;
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
-		return -ENOMEM;
-
-	/* XXX - should be optional */
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* Generate an ICBM cookie */
-	aim_icbm_makecookie(cookie);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0001, args->destsn);
-
-	/* Message TLV (type 0x0002) */
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, msgtlvlen);
-
-	/* Features TLV (type 0x0501) */
-	aimbs_put16(&fr->data, 0x0501);
-	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
-		aimbs_put16(&fr->data, args->featureslen);
-		aimbs_putraw(&fr->data, args->features, args->featureslen);
-	} else {
-		aimbs_put16(&fr->data, sizeof(deffeatures));
-		aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
-	}
-
-	if (args->flags & AIM_IMFLAGS_MULTIPART) {
-		aim_mpmsg_section_t *sec;
-
-		/* Insert each message part in a TLV (type 0x0101) */
-		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
-			aimbs_put16(&fr->data, 0x0101);
-			aimbs_put16(&fr->data, sec->datalen + 4);
-			aimbs_put16(&fr->data, sec->charset);
-			aimbs_put16(&fr->data, sec->charsubset);
-			aimbs_putraw(&fr->data, (guchar *)sec->data, sec->datalen);
-		}
-
-	} else {
-
-		/* Insert message text in a TLV (type 0x0101) */
-		aimbs_put16(&fr->data, 0x0101);
-
-		/* Message block length */
-		aimbs_put16(&fr->data, args->msglen + 0x04);
-
-		/* Character set */
-		aimbs_put16(&fr->data, args->charset);
-		aimbs_put16(&fr->data, args->charsubset);
-
-		/* Message.  Not terminated */
-		aimbs_putraw(&fr->data, (guchar *)args->msg, args->msglen);
-	}
-
-	/* Set the Autoresponse flag */
-	if (args->flags & AIM_IMFLAGS_AWAY) {
-		aimbs_put16(&fr->data, 0x0004);
-		aimbs_put16(&fr->data, 0x0000);
-	} else if (args->flags & AIM_IMFLAGS_ACK) {
-		/* Set the Request Acknowledge flag */
-		aimbs_put16(&fr->data, 0x0003);
-		aimbs_put16(&fr->data, 0x0000);
-	}
-
-	if (args->flags & AIM_IMFLAGS_OFFLINE) {
-		aimbs_put16(&fr->data, 0x0006);
-		aimbs_put16(&fr->data, 0x0000);
-	}
-
-	/*
-	 * Set the I HAVE A REALLY PURTY ICON flag.
-	 * XXX - This should really only be sent on initial
-	 * IMs and when you change your icon.
-	 */
-	if (args->flags & AIM_IMFLAGS_HASICON) {
-		aimbs_put16(&fr->data, 0x0008);
-		aimbs_put16(&fr->data, 0x000c);
-		aimbs_put32(&fr->data, args->iconlen);
-		aimbs_put16(&fr->data, 0x0001);
-		aimbs_put16(&fr->data, args->iconsum);
-		aimbs_put32(&fr->data, args->iconstamp);
-	}
-
-	/*
-	 * Set the Buddy Icon Requested flag.
-	 * XXX - Every time?  Surely not...
-	 */
-	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
-		aimbs_put16(&fr->data, 0x0009);
-		aimbs_put16(&fr->data, 0x0000);
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	/* clean out SNACs over 60sec old */
-	aim_cleansnacs(sess, 60);
-
-	return 0;
-}
-
-/*
- * Simple wrapper for aim_im_sendch1_ext()
- *
- * You cannot use aim_send_im if you need the HASICON flag.  You must
- * use aim_im_sendch1_ext directly for that.
- *
- * aim_send_im also cannot be used if you require UNICODE messages, because
- * that requires an explicit message length.  Use aim_im_sendch1_ext().
- *
- */
-faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg)
-{
-	struct aim_sendimext_args args;
-
-	args.destsn = sn;
-	args.flags = flags;
-	args.msg = msg;
-	args.msglen = strlen(msg);
-	args.charset = 0x0000;
-	args.charsubset = 0x0000;
-
-	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
-	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
-
-	return aim_im_sendch1_ext(sess, &args);
-}
-
-/*
- * Subtype 0x0006 - Send a chat invitation.
- */
-faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_msgcookie_t *msgcookie;
-	struct aim_invite_priv *priv;
-	guchar cookie[8];
-	aim_tlvlist_t *otl = NULL, *itl = NULL;
-	fu8_t *hdr;
-	int hdrlen;
-	aim_bstream_t hdrbs;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!sn || !msg || !roomname)
-		return -EINVAL;
-
-	aim_icbm_makecookie(cookie);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* XXX should be uncached by an unwritten 'invite accept' handler */
-	if ((priv = malloc(sizeof(struct aim_invite_priv)))) {
-		priv->sn = strdup(sn);
-		priv->roomname = strdup(roomname);
-		priv->exchange = exchange;
-		priv->instance = instance;
-	}
-
-	if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
-		aim_cachecookie(sess, msgcookie);
-	else
-		free(priv);
-
-	/* ICBM Header */
-	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * Everything else is inside this TLV.
-	 *
-	 * Sigh.  AOL was rather inconsistent right here.  So we have
-	 * to play some minor tricks.  Right inside the type 5 is some
-	 * raw data, followed by a series of TLVs.
-	 *
-	 */
-	hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2;
-	hdr = malloc(hdrlen);
-	aim_bstream_init(&hdrbs, hdr, hdrlen);
-
-	aimbs_put16(&hdrbs, 0x0000); /* Unknown! */
-	aimbs_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
-	aimbs_putcaps(&hdrbs, AIM_CAPS_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, aim_bstream_curpos(&hdrbs), hdr);
-
-	aim_tlvlist_write(&fr->data, &otl);
-
-	free(hdr);
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&otl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send your icon to a given user.
- *
- * This is also performance sensitive. (If you can believe it...)
- *
- */
-faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
-		return -EINVAL;
-
-	aim_icbm_makecookie(cookie);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * Encompasses everything below.
-	 */
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
-
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_putraw(&fr->data, cookie, 8);
-	aimbs_putcaps(&fr->data, AIM_CAPS_BUDDYICON);
-
-	/* TLV t(000a) */
-	aimbs_put16(&fr->data, 0x000a);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, 0x0001);
-
-	/* TLV t(000f) */
-	aimbs_put16(&fr->data, 0x000f);
-	aimbs_put16(&fr->data, 0x0000);
-
-	/* TLV t(2711) */
-	aimbs_put16(&fr->data, 0x2711);
-	aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_put16(&fr->data, iconsum);
-	aimbs_put32(&fr->data, iconlen);
-	aimbs_put32(&fr->data, stamp);
-	aimbs_putraw(&fr->data, icon, iconlen);
-	aimbs_putstr(&fr->data, AIM_ICONIDENT);
-
-	/* TLV t(0003) */
-	aimbs_put16(&fr->data, 0x0003);
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0006 - Send a rich text message.
- *
- * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
- * send it to clients advertising the RTF capability.  In fact, if you send
- * it to a client that doesn't support that capability, the server will gladly
- * bounce it back to you.
- *
- * You'd think this would be in icq.c, but, well, I'm trying to stick with
- * the one-group-per-file scheme as much as possible.  This could easily
- * be an exception, since Rendezvous IMs are external of the Oscar core,
- * and therefore are undefined.  Really I just need to think of a good way to
- * make an interface similar to what AOL actually uses.  But I'm not using COM.
- *
- */
-faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
-	int servdatalen;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!args || !args->destsn || !args->rtfmsg)
-		return -EINVAL;
-
-	servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
-
-	aim_icbm_makecookie(cookie);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0002, args->destsn);
-
-	/* TLV t(0005) - Encompasses everything below. */
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
-
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_putraw(&fr->data, cookie, 8);
-	aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY);
-
-	/* t(000a) l(0002) v(0001) */
-	aimbs_put16(&fr->data, 0x000a);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, 0x0001);
-
-	/* t(000f) l(0000) v() */
-	aimbs_put16(&fr->data, 0x000f);
-	aimbs_put16(&fr->data, 0x0000);
-
-	/* Service Data TLV */
-	aimbs_put16(&fr->data, 0x2711);
-	aimbs_put16(&fr->data, servdatalen);
-
-	aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */);
-	aimbs_putle16(&fr->data, 9);
-	aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY);
-	aimbs_putle16(&fr->data, 0);
-	aimbs_putle32(&fr->data, 0);
-	aimbs_putle8(&fr->data, 0);
-	aimbs_putle16(&fr->data, 0x03ea); /* trid1 */
-
-	aimbs_putle16(&fr->data, 14);
-	aimbs_putle16(&fr->data, 0x03eb); /* trid2 */
-	aimbs_putle32(&fr->data, 0);
-	aimbs_putle32(&fr->data, 0);
-	aimbs_putle32(&fr->data, 0);
-
-	aimbs_putle16(&fr->data, 0x0001);
-	aimbs_putle32(&fr->data, 0);
-	aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1);
-	aimbs_putraw(&fr->data, (const fu8_t *)args->rtfmsg, strlen(args->rtfmsg)+1);
-
-	aimbs_putle32(&fr->data, args->fgcolor);
-	aimbs_putle32(&fr->data, args->bgcolor);
-	aimbs_putle32(&fr->data, strlen(rtfcap)+1);
-	aimbs_putraw(&fr->data, (const fu8_t *)rtfcap, strlen(rtfcap)+1);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an "I want to directly connect to you" message
- *
- */
-faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *usercookie, gboolean usecookie, const char *sn, const fu8_t *ip, fu16_t port)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-	aim_tlvlist_t *tl = NULL, *itl = NULL;
-	int hdrlen;
-	fu8_t *hdr;
-	aim_bstream_t hdrbs;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/*
-	 * Generate a random message cookie
-	 *
-	 * This cookie needs to be alphanumeric and NULL-terminated to be
-	 * TOC-compatible.
-	 *
-	 * XXX - have I mentioned these should be generated in msgcookie.c?
-	 *
-	 */
-
-	if (usercookie && usecookie) /* allow user-specified cookie */
-		memcpy(cookie, usercookie, 8);
-	else
-		aim_icbm_makecookie(cookie);
-	cookie[7] = '\0';
-
-	if (usercookie && !usecookie)
-		memcpy(cookie, usercookie, 8);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
-
-	aim_tlvlist_add_noval(&tl, 0x0003);
-
-	hdrlen = 2+8+16+6+8+6+4;
-	hdr = malloc(hdrlen);
-	aim_bstream_init(&hdrbs, hdr, hdrlen);
-
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_putraw(&hdrbs, cookie, 8);
-	aimbs_putcaps(&hdrbs, AIM_CAPS_DIRECTIM);
-
-	aim_tlvlist_add_16(&itl, 0x000a, 0x0001);
-	aim_tlvlist_add_raw(&itl, 0x0003, 4, ip);
-	aim_tlvlist_add_16(&itl, 0x0005, port);
-	aim_tlvlist_add_noval(&itl, 0x000f);
-
-	aim_tlvlist_write(&hdrbs, &itl);
-
-	aim_tlvlist_add_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
-
-	aim_tlvlist_write(&fr->data, &tl);
-
-	free(hdr);
-	aim_tlvlist_free(&itl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an "I want to send you this file" message
- *
- */
-faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl=NULL, *subtl=NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
-		return -EINVAL;
-
-	/* The cookie must already have been generated by this point */
-
-	{ /* Create the subTLV chain */
-		fu8_t *buf;
-		int buflen;
-		aim_bstream_t bs;
-		fu8_t ip[4];
-		fu8_t ip_comp[4]; /* The bitwise complement of the ip */
-		char *nexttoken;
-		int i;
-
-		/* In a stage 2 proxied transfer & a transfer redirect, we send a second "reply request"
-		 * Being the second request for this transfer, its request number is 2
-		 * You can fill in the blank for a stage 3's request number... */
-		if( (oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG2)
-			|| (oft_info->send_or_recv == AIM_XFER_RECV
-				&& oft_info->stage == AIM_XFER_PROXY_STG3)
-			|| oft_info->method == AIM_XFER_REDIR)
-			aim_tlvlist_add_16(&subtl, 0x000a, 0x0002);
-		else if(oft_info->send_or_recv == AIM_XFER_SEND && oft_info->stage == AIM_XFER_PROXY_STG3)
-			aim_tlvlist_add_16(&subtl, 0x000a, 0x0003);
-		else
-			aim_tlvlist_add_16(&subtl, 0x000a, 0x0001);
-
-		/* This is usually necessary, but ruins a redirect and a stg3 proxy request */
-		if(!(oft_info->send_or_recv == AIM_XFER_RECV
-			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
-			aim_tlvlist_add_noval(&subtl, 0x000f);
-		}
-
-		/* If the following is ever enabled, ensure that it is not sent with a receive redirect
-		 * or stage 3 proxy redirect for a file receive (same conditions for sending 0x000f above) */
-/*		aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en");
-		aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii");
-		aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */
-		/* XXX - Change oft_info->clientip to an array of 4 bytes */
-		if (oft_info->clientip) {
-			i = 0;
-			nexttoken = strtok(oft_info->clientip, ".");
-			while (nexttoken && i<4) {
-				ip[i] = atoi(nexttoken);
-				ip_comp[i] = ~ip[i];
-				nexttoken = strtok(NULL, ".");
-				i++;
-			}
-
-			/* If there is no proxyip, we must fill it in with the clientip */
-			if(!oft_info->proxyip) {
-				aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
-				aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); /* check? value */
-			}
-
-			aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip);
-		}
-
-		/* Don't send the proxyip & accompanying info during a receive redirect or stg3 proxy request */
-		if(!(oft_info->send_or_recv == AIM_XFER_RECV
-			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
-			if (oft_info->proxyip) { /* Generate the proxyip */
-				i = 0;
-				nexttoken = strtok(oft_info->proxyip, ".");
-				while (nexttoken && i<4) {
-					ip[i] = atoi(nexttoken);
-					ip_comp[i] = ~ip[i];
-					nexttoken = strtok(NULL, ".");
-					i++;
-				}
-				aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
-				/* This zero-length TLV specifies a proxy will be used */
-				aim_tlvlist_add_noval(&subtl, 0x0010);
-
-				/* Proxied transfers fail without this next (check?) value */
-				aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp);
-			}
-		}
-
-		/* Don't send the port & its check during a stage 3 proxy request */
-		if(!(oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG3)) {
-			aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port);
-
-			/* Check value? Bitwise complement of the port */
-			aim_tlvlist_add_16(&subtl, 0x0017, ~(oft_info->port));
-		}
-
-		/* winAIM gets mad at us if we send too much info during a send redirect or stg3 proxy request */
-		if(!(oft_info->send_or_recv == AIM_XFER_RECV
-			&& (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
-			/* TLV t(2711) */
-			buflen = 2+2+4+strlen(oft_info->fh.name)+1;
-			buf = malloc(buflen);
-			aim_bstream_init(&bs, buf, buflen);
-			aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
-			aimbs_put16(&bs, oft_info->fh.totfiles);
-			aimbs_put32(&bs, oft_info->fh.totsize);
-
-			/* Filename - NULL terminated, for some odd reason */
-			aimbs_putstr(&bs, oft_info->fh.name);
-			aimbs_put8(&bs, 0x00);
-
-			aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data);
-			free(buf);
-		}
-	}
-
-	{ /* Create the main TLV chain */
-		fu8_t *buf;
-		int buflen;
-		aim_bstream_t bs;
-
-		/* TLV t(0005) - Encompasses everything from above. Gee. */
-		buflen = 2+8+16+aim_tlvlist_size(&subtl);
-		buf = malloc(buflen);
-		aim_bstream_init(&bs, buf, buflen);
-		aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE);
-		aimbs_putraw(&bs, oft_info->cookie, 8);
-		aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);
-		aim_tlvlist_write(&bs, &subtl);
-		aim_tlvlist_free(&subtl);
-		aim_tlvlist_add_raw(&tl, 0x0005, bs.len, bs.data);
-		free(buf);
-
-		/* TLV t(0003) - Request an ack */
-		aim_tlvlist_add_noval(&tl, 0x0003);
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie));
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
-
-	/* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an "I will accept this file" message?
- *
- * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE)
- */
-faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
-
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 0x001a);
-	aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT);
-	aimbs_putraw(&fr->data, oft_info->cookie, 8);
-	aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send a "cancel this file transfer" message?
- *
- */
-faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
-
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 0x001a);
-	aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL);
-	aimbs_putraw(&fr->data, (const guchar *)oft_info->cookie, 8);
-	aimbs_putcaps(&fr->data, AIM_CAPS_SENDFILE);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Request the status message of the given ICQ user.
- *
- * @param sess The oscar session.
- * @param sn The UIN of the user of whom you wish to request info.
- * @param type The type of info you wish to request.  This should be the current
- *        state of the user, as one of the AIM_ICQ_STATE_* defines.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
-		return -EINVAL;
-
-	aim_icbm_makecookie(cookie);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
-
-	/* TLV t(0005) - Encompasses almost everything below. */
-	aimbs_put16(&fr->data, 0x0005); /* T */
-	aimbs_put16(&fr->data, 0x005e); /* L */
-	{ /* V */
-		aimbs_put16(&fr->data, 0x0000);
-
-		/* Cookie */
-		aimbs_putraw(&fr->data, cookie, 8);
-
-		/* Put the 16 byte server relay capability */
-		aimbs_putcaps(&fr->data, AIM_CAPS_ICQSERVERRELAY);
-
-		/* TLV t(000a) */
-		aimbs_put16(&fr->data, 0x000a);
-		aimbs_put16(&fr->data, 0x0002);
-		aimbs_put16(&fr->data, 0x0001);
-
-		/* TLV t(000f) */
-		aimbs_put16(&fr->data, 0x000f);
-		aimbs_put16(&fr->data, 0x0000);
-
-		/* TLV t(2711) */
-		aimbs_put16(&fr->data, 0x2711);
-		aimbs_put16(&fr->data, 0x0036);
-		{ /* V */
-			aimbs_putle16(&fr->data, 0x001b); /* L */
-			aimbs_putle16(&fr->data, 0x0009); /* Protocol version */
-			aimbs_putcaps(&fr->data, AIM_CAPS_EMPTY);
-			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
-			aimbs_putle16(&fr->data, 0x0001); /* Client features? */
-			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
-			aimbs_putle8(&fr->data, 0x00); /* Unkizown */
-			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
-
-			aimbs_putle16(&fr->data, 0x000e); /* L */
-			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-
-			/* The type of status message being requested */
-			if (type & AIM_ICQ_STATE_CHAT)
-				aimbs_putle16(&fr->data, 0x03ec);
-			else if(type & AIM_ICQ_STATE_DND)
-				aimbs_putle16(&fr->data, 0x03eb);
-			else if(type & AIM_ICQ_STATE_OUT)
-				aimbs_putle16(&fr->data, 0x03ea);
-			else if(type & AIM_ICQ_STATE_BUSY)
-				aimbs_putle16(&fr->data, 0x03e9);
-			else if(type & AIM_ICQ_STATE_AWAY)
-				aimbs_putle16(&fr->data, 0x03e8);
-
-			aimbs_putle16(&fr->data, 0x0001); /* Status? */
-			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
-			aimbs_putle16(&fr->data, 0x0001); /* L */
-			aimbs_putle8(&fr->data, 0x00); /* String of length L */
-		} /* End TLV t(2711) */
-	} /* End TLV t(0005) */
-
-	/* TLV t(0003) */
-	aimbs_put16(&fr->data, 0x0003);
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/**
- * Subtype 0x0006 - Send an ICQ-esque ICBM.
- *
- * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."
- * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack,
- * mostly because it's in network byte order.  Figuring this stuff out sometimes takes a while,
- * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people
- * were taking when they merged the two protocols.
- *
- * @param sn The destination screen name.
- * @param type The type of message.  0x0007 for authorization denied.  0x0008 for authorization granted.
- * @param message The message you want to send, it should be null terminated.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, fu16_t type, const char *message)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	guchar cookie[8];
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
-		return -EINVAL;
-
-	if (!sn || !type || !message)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+3+strlen(sn)+12+strlen(message)+1+4)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
-
-	aim_icbm_makecookie(cookie);
-
-	/* ICBM header */
-	aim_im_puticbm(&fr->data, cookie, 0x0004, sn);
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * ICQ data (the UIN and the message).
-	 */
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 4 + 2+2+strlen(message)+1);
-
-	/*
-	 * Your UIN
-	 */
-	aimbs_putle32(&fr->data, atoi(sess->sn));
-
-	/*
-	 * TLV t(type) l(strlen(message)+1) v(message+NULL)
-	 */
-	aimbs_putle16(&fr->data, type);
-	aimbs_putle16(&fr->data, strlen(message)+1);
-	aimbs_putraw(&fr->data, (const fu8_t *)message, strlen(message)+1);
-
-	/*
-	 * TLV t(0006) l(0000) v()
-	 */
-	aimbs_put16(&fr->data, 0x0006);
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * XXX - I don't see when this would ever get called...
- */
-static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	guchar cookie[8];
-	fu16_t channel;
-	aim_tlvlist_t *tlvlist;
-	char *sn;
-	int snlen;
-	fu16_t icbmflags = 0;
-	fu8_t flag1 = 0, flag2 = 0;
-	gchar *msg = NULL;
-	aim_tlv_t *msgblock;
-
-	/* ICBM Cookie. */
-	aim_icbm_makecookie(cookie);
-
-	/* Channel ID */
-	channel = aimbs_get16(bs);
-
-	if (channel != 0x01) {
-		gaim_debug_misc("oscar", "icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
-		return 0;
-	}
-
-	snlen = aimbs_get8(bs);
-	sn = aimbs_getstr(bs, snlen);
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	if (aim_tlv_gettlv(tlvlist, 0x0003, 1))
-		icbmflags |= AIM_IMFLAGS_ACK;
-	if (aim_tlv_gettlv(tlvlist, 0x0004, 1))
-		icbmflags |= AIM_IMFLAGS_AWAY;
-
-	if ((msgblock = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
-		aim_bstream_t mbs;
-		int featurelen, msglen;
-
-		aim_bstream_init(&mbs, msgblock->value, msgblock->length);
-
-		aimbs_get8(&mbs);
-		aimbs_get8(&mbs);
-		for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
-			aimbs_get8(&mbs);
-		aimbs_get8(&mbs);
-		aimbs_get8(&mbs);
-
-		msglen = aimbs_get16(&mbs) - 4; /* final block length */
-
-		flag1 = aimbs_get16(&mbs);
-		flag2 = aimbs_get16(&mbs);
-
-		msg = aimbs_getstr(&mbs, msglen);
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2);
-
-	free(sn);
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/*
- * Ahh, the joys of nearly ridiculous over-engineering.
- *
- * Not only do AIM ICBM's support multiple channels.  Not only do they
- * support multiple character sets.  But they support multiple character 
- * sets / encodings within the same ICBM.
- *
- * These multipart messages allow for complex space savings techniques, which
- * seem utterly unnecessary by today's standards.  In fact, there is only
- * one client still in popular use that still uses this method: AOL for the
- * Macintosh, Version 5.0.  Obscure, yes, I know.  
- *
- * In modern (non-"legacy") clients, if the user tries to send a character
- * that is not ISO-8859-1 or ASCII, the client will send the entire message
- * as UNICODE, meaning that every character in the message will occupy the
- * full 16 bit UNICODE field, even if the high order byte would be zero.
- * Multipart messages prevent this wasted space by allowing the client to
- * only send the characters in UNICODE that need to be sent that way, and
- * the rest of the message can be sent in whatever the native character 
- * set is (probably ASCII).
- *
- * An important note is that sections will be displayed in the order that
- * they appear in the ICBM.  There is no facility for merging or rearranging
- * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
- * you must supply two ASCII sections with a UNICODE in the middle, and incur
- * the associated overhead.
- *
- * Normally I would have laughed and given a firm 'no' to supporting this
- * seldom-used feature, but something is attracting me to it.  In the future,
- * it may be possible to abuse this to send mixed-media messages to other
- * open source clients (like encryption or something) -- see faimtest for
- * examples of how to do this.
- *
- * I would definitely recommend avoiding this feature unless you really
- * know what you are doing, and/or you have something neat to do with it.
- *
- */
-faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
-{
-
-	memset(mpm, 0, sizeof(aim_mpmsg_t));
-
-	return 0;
-}
-
-static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, gchar *data, fu16_t datalen)
-{
-	aim_mpmsg_section_t *sec; 
-	
-	if (!(sec = malloc(sizeof(aim_mpmsg_section_t))))
-		return -1;
-
-	sec->charset = charset;
-	sec->charsubset = charsubset;
-	sec->data = data;
-	sec->datalen = datalen;
-	sec->next = NULL;
-
-	if (!mpm->parts)
-		mpm->parts = sec;
-	else {
-		aim_mpmsg_section_t *cur;
-
-		for (cur = mpm->parts; cur->next; cur = cur->next)
-			;
-		cur->next = sec;
-	}
-
-	mpm->numparts++;
-
-	return 0;
-}
-
-faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const gchar *data, fu16_t datalen)
-{
-	gchar *dup;
-
-	if (!(dup = malloc(datalen)))
-		return -1;
-	memcpy(dup, data, datalen);
-
-	if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
-		free(dup);
-		return -1;
-	}
-
-	return 0;
-}
-
-/* XXX - should provide a way of saying ISO-8859-1 specifically */
-faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
-{
-	gchar *dup;
-
-	if (!(dup = strdup(ascii))) 
-		return -1;
-
-	if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) {
-		free(dup);
-		return -1;
-	}
-
-	return 0;
-}
-
-faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen)
-{
-	gchar *buf;
-	aim_bstream_t bs;
-	int i;
-
-	if (!(buf = malloc(unicodelen * 2)))
-		return -1;
-
-	aim_bstream_init(&bs, (guchar *)buf, unicodelen * 2);
-
-	/* We assume unicode is in /host/ byte order -- convert to network */
-	for (i = 0; i < unicodelen; i++)
-		aimbs_put16(&bs, unicode[i]);
-
-	if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
-		free(buf);
-		return -1;
-	}
-	
-	return 0;
-}
-
-faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
-{
-	aim_mpmsg_section_t *cur;
-
-	for (cur = mpm->parts; cur; ) {
-		aim_mpmsg_section_t *tmp;
-		
-		tmp = cur->next;
-		free(cur->data);
-		free(cur);
-		cur = tmp;
-	}
-	
-	mpm->numparts = 0;
-	mpm->parts = NULL;
-
-	return;
-}
-
-/*
- * Start by building the multipart structures, then pick the first 
- * human-readable section and stuff it into args->msg so no one gets
- * suspicious.
- *
- */
-static int incomingim_ch1_parsemsgs(aim_session_t *sess, aim_userinfo_t *userinfo, fu8_t *data, int len, struct aim_incomingim_ch1_args *args)
-{
-	/* Should this be ASCII -> UNICODE -> Custom */
-	static const fu16_t charsetpri[] = {
-		AIM_CHARSET_ASCII, /* ASCII first */
-		AIM_CHARSET_CUSTOM, /* then ISO-8859-1 */
-		AIM_CHARSET_UNICODE, /* UNICODE as last resort */
-	};
-	static const int charsetpricount = 3;
-	int i;
-	aim_bstream_t mbs;
-	aim_mpmsg_section_t *sec;
-
-	aim_bstream_init(&mbs, data, len);
-
-	while (aim_bstream_empty(&mbs)) {
-		fu16_t msglen, flag1, flag2;
-		gchar *msgbuf;
-
-		aimbs_get8(&mbs); /* 01 */
-		aimbs_get8(&mbs); /* 01 */
-
-		/* Message string length, including character set info. */
-		msglen = aimbs_get16(&mbs);
-		if (msglen > aim_bstream_empty(&mbs))
-		{
-			gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.", userinfo->sn);
-			break;
-		}
-
-		/* Character set info */
-		flag1 = aimbs_get16(&mbs);
-		flag2 = aimbs_get16(&mbs);
-
-		/* Message. */
-		msglen -= 4;
-
-		/*
-		 * For now, we don't care what the encoding is.  Just copy
-		 * it into a multipart struct and deal with it later. However,
-		 * always pad the ending with a NULL.  This makes it easier
-		 * to treat ASCII sections as strings.  It won't matter for
-		 * UNICODE or binary data, as you should never read past
-		 * the specified data length, which will not include the pad.
-		 *
-		 * XXX - There's an API bug here.  For sending, the UNICODE is
-		 * given in host byte order (aim_mpmsg_addunicode), but here
-		 * the received messages are given in network byte order.
-		 *
-		 */
-		msgbuf = (gchar *)aimbs_getraw(&mbs, msglen);
-		mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen);
-
-	} /* while */
-
-	args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
-
-	/*
-	 * Clients that support multiparts should never use args->msg, as it
-	 * will point to an arbitrary section.
-	 *
-	 * Here, we attempt to provide clients that do not support multipart
-	 * messages with something to look at -- hopefully a human-readable
-	 * string.  But, failing that, a UNICODE message, or nothing at all.
-	 *
-	 * Which means that even if args->msg is NULL, it does not mean the
-	 * message was blank.
-	 *
-	 */
-	for (i = 0; i < charsetpricount; i++) {
-		for (sec = args->mpmsg.parts; sec; sec = sec->next) {
-
-			if (sec->charset != charsetpri[i])
-				continue;
-
-			/* Great. We found one.  Fill it in. */
-			args->charset = sec->charset;
-			args->charsubset = sec->charsubset;
-
-			/* Set up the simple flags */
-			switch (args->charsubset)
-			{
-				case 0x0000:
-					/* standard subencoding? */
-					break;
-				case 0x000b:
-					args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
-					break;
-				case 0xffff:
-					/* no subencoding */
-					break;
-				default:
-					break;
-			}
-
-			args->msg = sec->data;
-			args->msglen = sec->datalen;
-
-			return 0;
-		}
-	}
-
-	/* No human-readable sections found.  Oh well. */
-	args->charset = args->charsubset = 0xffff;
-	args->msg = NULL;
-	args->msglen = 0;
-
-	return 0;
-}
-
-static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, fu8_t *cookie)
-{
-	fu16_t type, length;
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-	struct aim_incomingim_ch1_args args;
-	unsigned int endpos;
-
-	memset(&args, 0, sizeof(args));
-
-	aim_mpmsg_init(sess, &args.mpmsg);
-
-	/*
-	 * This used to be done using tlvchains.  For performance reasons,
-	 * I've changed it to process the TLVs in-place.  This avoids lots
-	 * of per-IM memory allocations.
-	 */
-	while (aim_bstream_empty(bs))
-	{
-		type = aimbs_get16(bs);
-		length = aimbs_get16(bs);
-
-		if (length > aim_bstream_empty(bs))
-		{
-			gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
-			break;
-		}
-
-		endpos = aim_bstream_curpos(bs) + length;
-
-		if (type == 0x0002) { /* Message Block */
-
-			/*
-			 * This TLV consists of the following:
-			 *   - 0501 -- Unknown
-			 *   - Features: Don't know how to interpret these
-			 *   - 0101 -- Unknown
-			 *   - Message
-			 *
-			 */
-
-			aimbs_get8(bs); /* 05 */
-			aimbs_get8(bs); /* 01 */
-
-			args.featureslen = aimbs_get16(bs);
-			if (args.featureslen > aim_bstream_empty(bs))
-			{
-				gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
-				break;
-			}
-			if (args.featureslen == 0)
-			{
-				args.features = NULL;
-			}
-			else
-			{
-				args.features = aimbs_getraw(bs, args.featureslen);
-				args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
-			}
-
-			/*
-			 * The rest of the TLV contains one or more message
-			 * blocks...
-			 */
-			incomingim_ch1_parsemsgs(sess, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
-
-		} else if (type == 0x0003) { /* Server Ack Requested */
-
-			args.icbmflags |= AIM_IMFLAGS_ACK;
-
-		} else if (type == 0x0004) { /* Message is Auto Response */
-
-			args.icbmflags |= AIM_IMFLAGS_AWAY;
-
-		} else if (type == 0x0006) { /* Message was received offline. */
-
-			/* XXX - not sure if this actually gets sent. */
-			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
-
-		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
-
-			args.iconlen = aimbs_get32(bs);
-			aimbs_get16(bs); /* 0x0001 */
-			args.iconsum = aimbs_get16(bs);
-			args.iconstamp = aimbs_get32(bs);
-
-			/*
-			 * This looks to be a client bug.  MacAIM 4.3 will
-			 * send this tag, but with all zero values, in the
-			 * first message of a conversation. This makes no
-			 * sense whatsoever, so I'm going to say its a bug.
-			 *
-			 * You really shouldn't advertise a zero-length icon
-			 * anyway.
-			 * 
-			 */
-			if (args.iconlen)
-				args.icbmflags |= AIM_IMFLAGS_HASICON;
-
-		} else if (type == 0x0009) {
-
-			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
-
-		} else if (type == 0x000b) { /* Non-direct connect typing notification */
-
-			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
-
-		} else if (type == 0x0017) {
-
-			free(args.extdata);
-			args.extdatalen = length;
-			if (args.extdatalen > aim_bstream_empty(bs))
-			{
-				gaim_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
-				break;
-			}
-			if (args.extdatalen == 0)
-				args.extdata = NULL;
-			else
-				args.extdata = aimbs_getraw(bs, args.extdatalen);
-
-		} else {
-			gaim_debug_misc("oscar", "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
-		}
-
-		/*
-		 * This is here to protect ourselves from ourselves.  That
-		 * is, if something above doesn't completely parse its value
-		 * section, or, worse, overparses it, this will set the
-		 * stream where it needs to be in order to land on the next
-		 * TLV when the loop continues.
-		 *
-		 */
-		aim_bstream_setpos(bs, endpos);
-	}
-
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, channel, userinfo, &args);
-
-	aim_mpmsg_free(sess, &args.mpmsg);
-	free(args.features);
-	free(args.extdata);
-
-	return ret;
-}
-
-static void incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
-{
-
-	/*
-	 * This goes like this...
-	 *
-	 *   group name length
-	 *   group name
-	 *     num of buddies in group
-	 *     buddy name length
-	 *     buddy name
-	 *     buddy name length
-	 *     buddy name
-	 *     ...
-	 *   group name length
-	 *   group name
-	 *     num of buddies in group
-	 *     buddy name length
-	 *     buddy name
-	 *     ...
-	 *   ...
-	 */
-	while (servdata && aim_bstream_empty(servdata)) {
-		fu16_t gnlen, numb;
-		int i;
-		char *gn;
-
-		gnlen = aimbs_get16(servdata);
-		gn = aimbs_getstr(servdata, gnlen);
-		numb = aimbs_get16(servdata);
-
-		for (i = 0; i < numb; i++) {
-			fu16_t bnlen;
-			char *bn;
-
-			bnlen = aimbs_get16(servdata);
-			bn = aimbs_getstr(servdata, bnlen);
-
-			gaim_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
-
-			free(bn);
-		}
-
-		free(gn);
-	}
-
-	return;
-}
-
-static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
-{
-
-	free(args->info.icon.icon);
-
-	return;
-}
-
-static void incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
-{
-
-	if (servdata) {
-		args->info.icon.checksum = aimbs_get32(servdata);
-		args->info.icon.length = aimbs_get32(servdata);
-		args->info.icon.timestamp = aimbs_get32(servdata);
-		args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length);
-	}
-
-	args->destructor = (void *)incomingim_ch2_buddyicon_free;
-
-	return;
-}
-
-static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
-{
-
-	/* XXX - aim_chat_roominfo_free() */
-	free(args->info.chat.roominfo.name);
-
-	return;
-}
-
-static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
-{
-
-	/*
-	 * Chat room info.
-	 */
-	if (servdata)
-		aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
-
-	args->destructor = (void *)incomingim_ch2_chat_free;
-
-	return;
-}
-
-static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
-{
-
-	free((char *)args->info.rtfmsg.rtfmsg);
-
-	return;
-}
-
-/*
- * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is 
- * kind of odd. This sends the client ICQRTF since that is all that I've seen
- * SERVERRELAY used for.
- *
- * Note that this is all little-endian.  Cringe.
- *
- */
-static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
-{
-	fu16_t hdrlen, anslen, msglen;
-
-	hdrlen = aimbs_getle16(servdata);
-	aim_bstream_advance(servdata, hdrlen);
-
-	hdrlen = aimbs_getle16(servdata);
-	aim_bstream_advance(servdata, hdrlen);
-
-	args->info.rtfmsg.msgtype = aimbs_getle16(servdata);
-
-	anslen = aimbs_getle32(servdata);
-	aim_bstream_advance(servdata, anslen);
-
-	msglen = aimbs_getle16(servdata);
-	args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen);
-
-	args->info.rtfmsg.fgcolor = aimbs_getle32(servdata);
-	args->info.rtfmsg.bgcolor = aimbs_getle32(servdata);
-
-	hdrlen = aimbs_getle32(servdata);
-	aim_bstream_advance(servdata, hdrlen);
-
-	args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
-
-	return;
-}
-
-static void incomingim_ch2_sendfile_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
-{
-	free(args->info.sendfile.filename);
-}
-
-static void incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
-{
-
-	args->destructor = (void *)incomingim_ch2_sendfile_free;
-
-	/* Maybe there is a better way to tell what kind of sendfile 
-	 * this is?  Maybe TLV t(000a)? */
-	if (servdata) { /* Someone is sending us a file */
-		int flen;
-
-		/* subtype is one of AIM_OFT_SUBTYPE_* */
-		args->info.sendfile.subtype = aimbs_get16(servdata);
-		args->info.sendfile.totfiles = aimbs_get16(servdata);
-		args->info.sendfile.totsize = aimbs_get32(servdata);
-
-		/*
-		 * I hope to God I'm right when I guess that there is a 
-		 * 32 char max filename length for single files.  I think 
-		 * OFT tends to do that.  Gotta love inconsistency.  I saw 
-		 * a 26 byte filename?
-		 */
-		/* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */
-		/* Use an inelegant way of getting the null-terminated filename, 
-		 * since there's no easy bstream routine. */
-		for (flen = 0; aimbs_get8(servdata); flen++);
-		aim_bstream_advance(servdata, -flen -1);
-		args->info.sendfile.filename = aimbs_getstr(servdata, flen);
-
-		/* There is sometimes more after the null-terminated filename, 
-		 * but I'm unsure of its format. */
-		/* I don't believe him. */
-		/* There is sometimes a null byte inside a unicode filename,
-		 * but as far as I can tell the filename is the last
-		 * piece of data that will be in this message. --Jonathan */
-	}
-
-	return;
-}
-
-typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args);
-
-static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
-{
-	aim_rxcallback_t userfunc;
-	aim_tlv_t *block1, *servdatatlv;
-	aim_tlvlist_t *list2;
-	struct aim_incomingim_ch2_args args;
-	aim_bstream_t bbs, sdbs, *sdbsptr = NULL;
-	fu8_t *cookie2;
-	int ret = 0;
-
-	char proxyip[30] = {""};
-	char clientip[30] = {""};
-	char verifiedip[30] = {""};
-
-	memset(&args, 0, sizeof(args));
-
-	/*
-	 * There's another block of TLVs embedded in the type 5 here.
-	 */
-	block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
-	aim_bstream_init(&bbs, block1->value, block1->length);
-
-	/*
-	 * First two bytes represent the status of the connection.
-	 *
-	 * 0 is a request, 1 is a cancel, 2 is an accept
-	 */
-	args.status = aimbs_get16(&bbs);
-
-	/*
-	 * Next comes the cookie.  Should match the ICBM cookie.
-	 */
-	cookie2 = aimbs_getraw(&bbs, 8);
-	if (memcmp(cookie, cookie2, 8) != 0)
-		gaim_debug_misc("oscar", "rend: warning cookies don't match!\n");
-	memcpy(args.cookie, cookie2, 8);
-	free(cookie2);
-
-	/*
-	 * The next 16bytes are a capability block so we can
-	 * identify what type of rendezvous this is.
-	 */
-	args.reqclass = aim_locate_getcaps(sess, &bbs, 0x10);
-
-	/* 
-	 * What follows may be TLVs or nothing, depending on the
-	 * purpose of the message.
-	 *
-	 * Ack packets for instance have nothing more to them.
-	 */
-	list2 = aim_tlvlist_read(&bbs);
-
-	/*
-	 * IP address to proxy the file transfer through.
-	 *
-	 * XXX - I don't like this.  Maybe just read in an int?  Or inet_ntoa...
-	 */
-	if (aim_tlv_gettlv(list2, 0x0002, 1)) {
-		aim_tlv_t *iptlv;
-
-		iptlv = aim_tlv_gettlv(list2, 0x0002, 1);
-		if (iptlv->length == 4)
-			snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu",
-				iptlv->value[0], iptlv->value[1],
-				iptlv->value[2], iptlv->value[3]);
-	}
-
-	/*
-	 * IP address from the perspective of the client.
-	 */
-	if (aim_tlv_gettlv(list2, 0x0003, 1)) {
-		aim_tlv_t *iptlv;
-
-		iptlv = aim_tlv_gettlv(list2, 0x0003, 1);
-		if (iptlv->length == 4)
-			snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu",
-				iptlv->value[0], iptlv->value[1],
-				iptlv->value[2], iptlv->value[3]);
-	}
-
-	/*
-	 * Verified IP address (from the perspective of Oscar).
-	 *
-	 * This is added by the server.
-	 */
-	if (aim_tlv_gettlv(list2, 0x0004, 1)) {
-		aim_tlv_t *iptlv;
-
-		iptlv = aim_tlv_gettlv(list2, 0x0004, 1);
-		if (iptlv->length == 4)
-			snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu",
-				iptlv->value[0], iptlv->value[1],
-				iptlv->value[2], iptlv->value[3]);
-	}
-
-	/*
-	 * Port number for something.
-	 */
-	if (aim_tlv_gettlv(list2, 0x0005, 1))
-		args.port = aim_tlv_get16(list2, 0x0005, 1);
-
-	/*
-	 * Something to do with ft? -- two bytes
-	 * 0x0001 - "I want to send you this file"
-	 * 0x0002 - "I will accept this file from you"
-	 * 0x0002 - Also used in ICQ Lite Beta 4.0 URLs
-	 */
-	 /*
-	  * This is what I call the request number of the file transfer
-	  * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
-	  * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
-	  * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
-	  * -- Jonathan
-	  */
-	if (aim_tlv_gettlv(list2, 0x000a, 1))
-		args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1);
-
-	/*
-	 * Error code.
-	 */
-	if (aim_tlv_gettlv(list2, 0x000b, 1))
-		args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
-
-	/*
-	 * Invitation message / chat description.
-	 */
-	if (aim_tlv_gettlv(list2, 0x000c, 1)) {
-		args.msg = aim_tlv_getstr(list2, 0x000c, 1);
-		args.msglen = aim_tlv_getlength(list2, 0x000c, 1);
-	}
-
-	/*
-	 * Character set.
-	 */
-	if (aim_tlv_gettlv(list2, 0x000d, 1))
-		args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
-	
-	/*
-	 * Language.
-	 */
-	if (aim_tlv_gettlv(list2, 0x000e, 1))
-		args.language = aim_tlv_getstr(list2, 0x000e, 1);
-
-#if 0
-	/*
-	 * Unknown -- no value
-	 *
-	 * Maybe means we should connect directly to transfer the file?
-	 * Also used in ICQ Lite Beta 4.0 URLs.  Also empty.
-	 */
-	 /* I don't think this indicates a direct transfer; this flag is
-	  * also present in a stage 1 proxied file send request -- Jonathan */
-	if (aim_tlv_gettlv(list2, 0x000f, 1)) {
-		/* Unhandled */
-	}
-#endif
-
-	/*
-	 * Flag meaning we should proxy the file transfer through an AIM server
-	 */
-	if (aim_tlv_gettlv(list2, 0x0010, 1))
-		args.info.sendfile.use_proxy = TRUE;
-	else
-		args.info.sendfile.use_proxy = FALSE;
-
-	if (strlen(proxyip))
-		args.proxyip = (char *)proxyip;
-	if (strlen(clientip))
-		args.clientip = (char *)clientip;
-	if (strlen(verifiedip))
-		args.verifiedip = (char *)verifiedip;
-
-	/*
-	 * This must be present in PROPOSALs, but will probably not
-	 * exist in CANCELs and ACCEPTs.  Also exists in ICQ Lite
-	 * Beta 4.0 URLs (AIM_CAPS_ICQSERVERRELAY).
-	 *
-	 * Service Data blocks are module-specific in format.
-	 */
-	if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
-
-		aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length);
-		sdbsptr = &sdbs;
-	}
-
-	/*
-	 * The rest of the handling depends on what type it is.
-	 *
-	 * Not all of them have special handling (yet).
-	 */
-	if (args.reqclass & AIM_CAPS_BUDDYICON)
-		incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr);
-	else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST)
-		incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr);
-	else if (args.reqclass & AIM_CAPS_CHAT)
-		incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr);
-	else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)
-		incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr);
-	else if (args.reqclass & AIM_CAPS_SENDFILE)
-		incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, sdbsptr);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, channel, userinfo, &args);
-
-
-	if (args.destructor)
-		((ch2_args_destructor_t)args.destructor)(sess, &args);
-
-	free((char *)args.msg);
-	free((char *)args.encoding);
-	free((char *)args.language);
-
-	aim_tlvlist_free(&list2);
-
-	return ret;
-}
-
-static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
-{
-	aim_bstream_t meat;
-	aim_rxcallback_t userfunc;
-	aim_tlv_t *block;
-	struct aim_incomingim_ch4_args args;
-	int ret = 0;
-
-	/*
-	 * Make a bstream for the meaty part.  Yum.  Meat.
-	 */
-	if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
-		return -1;
-	aim_bstream_init(&meat, block->value, block->length);
-
-	args.uin = aimbs_getle32(&meat);
-	args.type = aimbs_getle8(&meat);
-	args.flags = aimbs_getle8(&meat);
-	args.msglen = aimbs_getle16(&meat);
-	args.msg = (gchar *)aimbs_getraw(&meat, args.msglen);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, channel, userinfo, &args);
-
-	free(args.msg);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0007
- *
- * It can easily be said that parsing ICBMs is THE single
- * most difficult thing to do in the in AIM protocol.  In
- * fact, I think I just did say that.
- *
- * Below is the best damned solution I've come up with
- * over the past sixteen months of battling with it. This
- * can parse both away and normal messages from every client
- * I have access to.  Its not fast, its not clean.  But it works.
- *
- */
-static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	guchar *cookie;
-	fu16_t channel;
-	aim_userinfo_t userinfo;
-
-	memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
-
-	/*
-	 * Read ICBM Cookie.
-	 */
-	cookie = aimbs_getraw(bs, 8);
-
-	/*
-	 * Channel ID.
-	 *
-	 * Channel 0x0001 is the message channel.  It is
-	 * used to send basic ICBMs.
-	 *
-	 * Channel 0x0002 is the Rendezvous channel, which
-	 * is where Chat Invitiations and various client-client
-	 * connection negotiations come from.
-	 *
-	 * Channel 0x0003 is used for chat messages.
-	 *
-	 * Channel 0x0004 is used for ICQ authorization, or
-	 * possibly any system notice.
-	 *
-	 */
-	channel = aimbs_get16(bs);
-
-	/*
-	 * Extract the standard user info block.
-	 *
-	 * Note that although this contains TLVs that appear contiguous
-	 * with the TLVs read below, they are two different pieces.  The
-	 * userinfo block contains the number of TLVs that contain user
-	 * information, the rest are not even though there is no separation.
-	 * You can start reading the message TLVs after aim_info_extract()
-	 * parses out the standard userinfo block.
-	 *
-	 * That also means that TLV types can be duplicated between the
-	 * userinfo block and the rest of the message, however there should
-	 * never be two TLVs of the same type in one block.
-	 *
-	 */
-	aim_info_extract(sess, bs, &userinfo);
-
-	/*
-	 * From here on, its depends on what channel we're on.
-	 *
-	 * Technically all channels have a TLV list have this, however,
-	 * for the common channel 1 case, in-place parsing is used for
-	 * performance reasons (less memory allocation).
-	 */
-	if (channel == 1) {
-
-		ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
-
-	} else if (channel == 2) {
-		aim_tlvlist_t *tlvlist;
-
-		/*
-		 * Read block of TLVs (not including the userinfo data).  All
-		 * further data is derived from what is parsed here.
-		 */
-		tlvlist = aim_tlvlist_read(bs);
-
-		ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
-
-		aim_tlvlist_free(&tlvlist);
-
-	} else if (channel == 4) {
-		aim_tlvlist_t *tlvlist;
-
-		tlvlist = aim_tlvlist_read(bs);
-		ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
-		aim_tlvlist_free(&tlvlist);
-
-	} else {
-		gaim_debug_misc("oscar", "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
-	}
-
-	aim_info_free(&userinfo);
-	free(cookie);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0008 - Send a warning to sn.
- *
- * Flags:
- *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
- *
- * returns -1 on error (couldn't alloc packet), 0 on success.
- *
- */
-faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
-	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
-
-	aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x000a */
-static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t channel, nummissed, reason;
-	aim_userinfo_t userinfo;
-
-	while (aim_bstream_empty(bs)) {
-
-		channel = aimbs_get16(bs);
-		aim_info_extract(sess, bs, &userinfo);
-		nummissed = aimbs_get16(bs);
-		reason = aimbs_get16(bs);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			 ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
-
-		aim_info_free(&userinfo);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x000b
- *
- * Possible codes:
- *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
- *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
- *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
- * 
- */
-faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, fu16_t code)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
-
-	aimbs_putraw(&fr->data, cookie, 8);
-
-	aimbs_put16(&fr->data, 0x0002); /* channel */
-	aimbs_put8(&fr->data, strlen(sender));
-	aimbs_putstr(&fr->data, sender);
-
-	aim_tlvlist_add_16(&tl, 0x0003, code);
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x000b - Receive the response from an ICQ status message request.
- *
- * This contains the ICQ status message.  Go figure.
- *
- */
-static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t channel, reason;
-	char *sn;
-	guchar *cookie;
-	guint8 snlen;
-
-	cookie = aimbs_getraw(bs, 8);
-	channel = aimbs_get16(bs);
-	snlen = aimbs_get8(bs);
-	sn = aimbs_getstr(bs, snlen);
-	reason = aimbs_get16(bs);
-
-	if (channel == 0x0002) { /* File transfer declined */
-		aimbs_get16(bs); /* Unknown */
-		aimbs_get16(bs); /* Unknown */
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, sn, reason, cookie);
-	} else if (channel == 0x0004) { /* ICQ message */
-		switch (reason) {
-			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
-				fu8_t statusmsgtype, *msg;
-				fu16_t len;
-				fu32_t state;
-
-				len = aimbs_getle16(bs); /* Should be 0x001b */
-				aim_bstream_advance(bs, len); /* Unknown */
-
-				len = aimbs_getle16(bs); /* Should be 0x000e */
-				aim_bstream_advance(bs, len); /* Unknown */
-
-				statusmsgtype = aimbs_getle8(bs);
-				switch (statusmsgtype) {
-					case 0xe8:
-						state = AIM_ICQ_STATE_AWAY;
-						break;
-					case 0xe9:
-						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
-						break;
-					case 0xea:
-						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
-						break;
-					case 0xeb:
-						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
-						break;
-					case 0xec:
-						state = AIM_ICQ_STATE_CHAT;
-						break;
-					default:
-						state = 0;
-						break;
-				}
-
-				aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
-				aimbs_getle16(bs); /* Unknown - 0x0000 */
-				aimbs_getle16(bs); /* Unknown - 0x0000 */
-
-				len = aimbs_getle16(bs);
-				msg = aimbs_getraw(bs, len);
-
-				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-					ret = userfunc(sess, rx, channel, sn, reason, state, msg);
-
-				free(msg);
-			} break;
-
-			default: {
-				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-					ret = userfunc(sess, rx, channel, sn, reason);
-			} break;
-		} /* end switch */
-	}
-
-	free(cookie);
-	free(sn);
-
-	return ret;
-}
-
-/*
- * Subtype 0x000c - Receive an ack after sending an ICBM.
- *
- * You have to have send the message with the AIM_IMFLAGS_ACK flag set
- * (TLV t(0003)).  The ack contains the ICBM header of the message you
- * sent.
- *
- */
-static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	fu16_t ch;
-	guchar *cookie;
-	char *sn;
-	int ret = 0;
-
-	cookie = aimbs_getraw(bs, 8);
-	ch = aimbs_get16(bs);
-	sn = aimbs_getstr(bs, aimbs_get8(bs));
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, ch, sn);
-
-	free(sn);
-	free(cookie);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
- *
- * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
- * and Gaim 0.60 and newer.
- *
- */
-faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
-		return -EINVAL;
-
-	if (!sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid);
-
-	/*
-	 * 8 days of light
-	 * Er, that is to say, 8 bytes of 0's
-	 */
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_put16(&fr->data, 0x0000);
-
-	/*
-	 * Type 1 (should be 0x0001 for mtn)
-	 */
-	aimbs_put16(&fr->data, type1);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	/*
-	 * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn)
-	 */
-	aimbs_put16(&fr->data, type2);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
- *
- * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
- * and Gaim 0.60 and newer.
- *
- */
-static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	char *sn;
-	fu8_t snlen;
-	fu16_t type1, type2;
-
-	aim_bstream_advance(bs, 8); /* Unknown - All 0's */
-	type1 = aimbs_get16(bs);
-	snlen = aimbs_get8(bs);
-	sn = aimbs_getstr(bs, snlen);
-	type2 = aimbs_get16(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, type1, sn, type2);
-
-	free(sn);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0005)
-		return aim_im_paraminfo(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0006)
-		return outgoingim(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0007)
-		return incomingim(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000a)
-		return missedcall(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000b)
-		return clientautoresp(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000c)
-		return msgack(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0014)
-		return mtn_receive(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0004;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "messaging", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/invite.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * Family 0x0006 - This isn't really ever used by anyone anymore.
- *
- * Once upon a time, there used to be a menu item in AIM clients that
- * said something like "Invite a friend to use AIM..." and then it would
- * ask for an email address and it would sent a mail to them saying
- * how perfectly wonderful the AIM service is and why you should use it
- * and click here if you hate the person who sent this to you and want to
- * complain and yell at them in a small box with pretty fonts.
- *
- * I could've sworn libfaim had this implemented once, a long long time ago,
- * but I can't find it.
- *
- * I'm mainly adding this so that I can keep advertising that we support
- * group 6, even though we don't.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0006;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "invite", sizeof(mod->name));
-	mod->snachandler = NULL;
-
-	return 0;
-}
--- a/src/protocols/oscar/locate.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1390 +0,0 @@
-/*
- * Family 0x0002 - Locate.
- *
- * The functions here are responsible for requesting and parsing information-
- * gathering SNACs.  Or something like that.  This family contains the SNACs
- * for getting and setting info, away messages, directory profile thingy, etc.
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-/*
- * Capability blocks.
- *
- * These are CLSIDs. They should actually be of the form:
- *
- * {0x0946134b, 0x4c7f, 0x11d1,
- *  {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
- *
- * But, eh.
- */
-static const struct {
-	fu32_t flag;
-	fu8_t data[16];
-} aim_caps[] = {
-
-	/*
-	 * These are in ascending numerical order.
-	 */
-
-	/*
-	 * Perhaps better called AIM_CAPS_SHORTCAPS
-	 */
-	{AIM_CAPS_ICHAT,
-	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_SECUREIM,
-	 {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_VIDEO,
-	 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/* "Live Video" support in Windows AIM 5.5.3501 and newer */
-	{AIM_CAPS_LIVEVIDEO,
-	 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/* "Camera" support in Windows AIM 5.5.3501 and newer */
-	{AIM_CAPS_CAMERA,
-	 {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/* In Windows AIM 5.5.3501 and newer */
-	{AIM_CAPS_GENERICUNKNOWN,
-	 {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/* In iChatAV (version numbers...?) */
-	{AIM_CAPS_ICHATAV,
-	 {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
-
-	/*
-	 * Not really sure about this one.  In an email from
-	 * 26 Sep 2003, Matthew Sachs suggested that, "this
-	 * is probably the capability for the SMS features."
-	 */
-	{AIM_CAPS_SMS,
-	 {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_GENERICUNKNOWN,
-	 {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_GENERICUNKNOWN,
-	 {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_GENERICUNKNOWN,
-	 {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_HIPTOP,
-	 {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_TALK,
-	 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_SENDFILE,
-	 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_ICQ_DIRECT,
-	 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_DIRECTIM,
-	 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_BUDDYICON,
-	 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_ADDINS,
-	 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_GETFILE,
-	 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_ICQSERVERRELAY,
-	 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/*
-	 * Indeed, there are two of these.  The former appears to be correct,
-	 * but in some versions of winaim, the second one is set.  Either they
-	 * forgot to fix endianness, or they made a typo. It really doesn't
-	 * matter which.
-	 */
-	{AIM_CAPS_GAMES,
-	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-	{AIM_CAPS_GAMES2,
-	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_SENDBUDDYLIST,
-	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/*
-	 * Setting this lets AIM users receive messages from ICQ users, and ICQ
-	 * users receive messages from AIM users.  It also lets ICQ users show
-	 * up in buddy lists for AIM users, and AIM users show up in buddy lists
-	 * for ICQ users.  And ICQ privacy/invisibility acts like AIM privacy,
-	 * in that if you add a user to your deny list, you will not be able to
-	 * see them as online (previous you could still see them, but they
-	 * couldn't see you.
-	 */
-	{AIM_CAPS_INTEROPERATE,
-	 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_ICQUTF8,
-	 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	{AIM_CAPS_ICQUTF8OLD,
-	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
-	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
-
-	/*
-	 * Chat is oddball.
-	 */
-	{AIM_CAPS_CHAT,
-	 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/*
-	{AIM_CAPS_ICQ2GO,
-	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
-	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
-	*/
-
-	{AIM_CAPS_ICQRTF,
-	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
-	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
-
-	/* This is added by the servers and it only shows up for ourselves... */
-	{AIM_CAPS_GENERICUNKNOWN,
-	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
-	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}},
-
-	{AIM_CAPS_APINFO,
-	 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
-	  0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
-
-	{AIM_CAPS_TRILLIANCRYPT,
-	 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
-	  0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
-
-	{AIM_CAPS_EMPTY,
-	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
-
-	{AIM_CAPS_LAST,
-	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
-};
-
-/*
- * Add the userinfo to our linked list.  If we already have userinfo
- * for this buddy, then just overwrite parts of the old data.
- *
- * @param userinfo Contains the new information for the buddy.
- */
-static void aim_locate_adduserinfo(aim_session_t *sess, aim_userinfo_t *userinfo) {
-	aim_userinfo_t *cur;
-	aim_conn_t *conn;
-	aim_rxcallback_t userfunc;
-
-	cur = aim_locate_finduserinfo(sess, userinfo->sn);
-
-	if (cur == NULL) {
-		cur = (aim_userinfo_t *)calloc(1, sizeof(aim_userinfo_t));
-		cur->sn = strdup(userinfo->sn);
-		cur->next = sess->locate.userinfo;
-		sess->locate.userinfo = cur;
-	}
-
-	cur->warnlevel = userinfo->warnlevel;
-	cur->idletime = userinfo->idletime;
-	if (userinfo->flags != 0)
-		cur->flags = userinfo->flags;
-	if (userinfo->createtime != 0)
-		cur->createtime = userinfo->createtime;
-	if (userinfo->membersince != 0)
-		cur->membersince = userinfo->membersince;
-	if (userinfo->onlinesince != 0)
-		cur->onlinesince = userinfo->onlinesince;
-	if (userinfo->sessionlen != 0)
-		cur->sessionlen = userinfo->sessionlen;
-	if (userinfo->capabilities != 0)
-		cur->capabilities = userinfo->capabilities;
-	cur->present |= userinfo->present;
-
-	if (userinfo->iconcsumlen > 0) {
-		free(cur->iconcsum);
-		cur->iconcsum = (fu8_t *)malloc(userinfo->iconcsumlen);
-		memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen);
-		cur->iconcsumlen = userinfo->iconcsumlen;
-	}
-
-	if (userinfo->info != NULL) {
-		free(cur->info);
-		free(cur->info_encoding);
-		if (userinfo->info_len > 0) {
-			cur->info = (char *)malloc(userinfo->info_len);
-			memcpy(cur->info, userinfo->info, userinfo->info_len);
-		} else
-			cur->info = NULL;
-		cur->info_encoding = strdup(userinfo->info_encoding);
-		cur->info_len = userinfo->info_len;
-	}
-
-	if (userinfo->status != NULL) {
-		free(cur->status);
-		free(cur->status_encoding);
-		if (userinfo->status_len > 0) {
-			cur->status = (char *)malloc(userinfo->status_len);
-			memcpy(cur->status, userinfo->status, userinfo->status_len);
-		} else
-			cur->status = NULL;
-		if (userinfo->status_encoding != NULL)
-			cur->status_encoding = strdup(userinfo->status_encoding);
-		else
-			cur->status_encoding = NULL;
-		cur->status_len = userinfo->status_len;
-	}
-
-	if (userinfo->away != NULL) {
-		free(cur->away);
-		free(cur->away_encoding);
-		if (userinfo->away_len > 0) {
-			cur->away = (char *)malloc(userinfo->away_len);
-			memcpy(cur->away, userinfo->away, userinfo->away_len);
-		} else
-			cur->away = NULL;
-		cur->away_encoding = strdup(userinfo->away_encoding);
-		cur->away_len = userinfo->away_len;
-	}
-
-	/*
-	 * This callback can be used by a client if they want to know whenever
-	 * info for a buddy is updated.  For example, if a client shows away
-	 * messages in its buddy list, then it would need to know if a user's
-	 * away message changes.
-	 */
-	conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC);
-	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_GOTINFOBLOCK)))
-		userfunc(sess, NULL, cur);
-}
-
-faim_export void aim_locate_dorequest(aim_session_t *sess) {
-	struct userinfo_node *cur = sess->locate.torequest;
-
-	if (cur == NULL)
-		return;
-
-	if (sess->locate.waiting_for_response == TRUE)
-		return;
-
-	sess->locate.waiting_for_response = TRUE;
-	aim_locate_getinfoshort(sess, cur->sn, 0x00000003);
-
-	/* Move this node to the "requested" queue */
-	sess->locate.torequest = cur->next;
-	cur->next = sess->locate.requested;
-	sess->locate.requested = cur;
-}
-
-/**
- * Remove this screen name from our queue.  If this info was requested
- * by our info request queue, then pop the next element off of the queue.
- *
- * @param sess The aim session.
- * @param sn Screen name of the info we just received.
- * @return True if the request was explicit (client requested the info),
- *         false if the request was implicit (libfaim request the info).
- */
-static int aim_locate_gotuserinfo(aim_session_t *sess, const char *sn) {
-	struct userinfo_node *cur, *del;
-	int was_explicit = TRUE;
-
-	while ((sess->locate.requested != NULL) && (aim_sncmp(sn, sess->locate.requested->sn) == 0)) {
-		del = sess->locate.requested;
-		sess->locate.requested = del->next;
-		was_explicit = FALSE;
-		free(del->sn);
-		free(del);
-	}
-
-	cur = sess->locate.requested;
-	while ((cur != NULL) && (cur->next != NULL)) {
-		if (aim_sncmp(sn, cur->next->sn) == 0) {
-			del = cur->next;
-			cur->next = del->next;
-			was_explicit = FALSE;
-			free(del->sn);
-			free(del);
-		} else
-			cur = cur->next;
-	}
-
-	if (!was_explicit) {
-		aim_conn_t *conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC);
-		aim_rxcallback_t userfunc;
-
-		sess->locate.waiting_for_response = FALSE;
-
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQUESTINFOTIMEOUT)))
-			userfunc(sess, NULL);
-		else
-			aim_locate_dorequest(sess);
-	}
-
-	return was_explicit;
-}
-
-faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) {
-	struct userinfo_node *cur;
-
-	/* Make sure we aren't already requesting info for this buddy */
-	cur = sess->locate.torequest;
-	while (cur != NULL) {
-		if (aim_sncmp(sn, cur->sn) == 0)
-			return;
-		cur = cur->next;
-	}
-
-	/* Add a new node to our request queue */
-	cur = (struct userinfo_node *)malloc(sizeof(struct userinfo_node));
-	cur->sn = strdup(sn);
-	cur->next = sess->locate.torequest;
-	sess->locate.torequest = cur;
-
-	/* Actually request some info up in this piece */
-	aim_locate_dorequest(sess);
-}
-
-faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn) {
-	aim_userinfo_t *cur = NULL;
-
-	if (sn == NULL)
-		return NULL;
-
-	cur = sess->locate.userinfo;
-
-	while (cur != NULL) {
-		if (aim_sncmp(cur->sn, sn) == 0)
-			return cur;
-		cur = cur->next;
-	}
-
-	return NULL;
-}
-
-faim_internal fu32_t aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len)
-{
-	fu32_t flags = 0;
-	int offset;
-
-	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
-		fu8_t *cap;
-		int i, identified;
-
-		cap = aimbs_getraw(bs, 0x10);
-
-		for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
-			if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
-				flags |= aim_caps[i].flag;
-				identified++;
-				break; /* should only match once... */
-			}
-		}
-
-		if (!identified)
-			gaim_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
-					cap[0], cap[1], cap[2], cap[3],
-					cap[4], cap[5],
-					cap[6], cap[7],
-					cap[8], cap[9],
-					cap[10], cap[11], cap[12], cap[13],
-					cap[14], cap[15]);
-
-		free(cap);
-	}
-
-	return flags;
-}
-
-faim_internal fu32_t aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len)
-{
-	fu32_t flags = 0;
-	int offset;
-
-	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x02) {
-		fu8_t *cap;
-		int i, identified;
-
-		cap = aimbs_getraw(bs, 0x02);
-
-		for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
-			if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) {
-				flags |= aim_caps[i].flag;
-				identified++;
-				break; /* should only match once... */
-			}
-		}
-
-		if (!identified)
-			gaim_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
-
-		free(cap);
-	}
-
-	return flags;
-}
-
-faim_internal int aimbs_putcaps(aim_bstream_t *bs, fu32_t caps)
-{
-	int i;
-
-	if (!bs)
-		return -EINVAL;
-
-	for (i = 0; aim_bstream_empty(bs); i++) {
-
-		if (aim_caps[i].flag == AIM_CAPS_LAST)
-			break;
-
-		if (caps & aim_caps[i].flag)
-			aimbs_putraw(bs, aim_caps[i].data, 0x10);
-
-	}
-
-	return 0;
-}
-
-static void dumptlv(aim_session_t *sess, fu16_t type, aim_bstream_t *bs, fu8_t len)
-{
-	int i;
-
-	if (!sess || !bs || !len)
-		return;
-
-	gaim_debug_misc("oscar", "userinfo:   type  =0x%04x\n", type);
-	gaim_debug_misc("oscar", "userinfo:   length=0x%04x\n", len);
-	gaim_debug_misc("oscar", "userinfo:   value:\n");
-
-	for (i = 0; i < len; i++) {
-		if ((i % 8) == 0)
-			gaim_debug_misc("oscar", "\nuserinfo:        ");
-		gaim_debug_misc("oscar", "0x%2x ", aimbs_get8(bs));
-	}
-
-	gaim_debug_misc("oscar", "\n");
-
-	return;
-}
-
-faim_internal void aim_info_free(aim_userinfo_t *info)
-{
-	free(info->sn);
-	free(info->iconcsum);
-	free(info->info);
-	free(info->info_encoding);
-	free(info->status);
-	free(info->status_encoding);
-	free(info->away);
-	free(info->away_encoding);
-}
-
-/*
- * AIM is fairly regular about providing user info.  This is a generic
- * routine to extract it in its standard form.
- */
-faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo)
-{
-	int curtlv, tlvcnt;
-	fu8_t snlen;
-
-	if (!bs || !outinfo)
-		return -EINVAL;
-
-	/* Clear out old data first */
-	memset(outinfo, 0x00, sizeof(aim_userinfo_t));
-
-	/*
-	 * Screen name.  Stored as an unterminated string prepended with a
-	 * byte containing its length.
-	 */
-	snlen = aimbs_get8(bs);
-	outinfo->sn = aimbs_getstr(bs, snlen);
-
-	/*
-	 * Warning Level.  Stored as an unsigned short.
-	 */
-	outinfo->warnlevel = aimbs_get16(bs);
-
-	/*
-	 * TLV Count. Unsigned short representing the number of
-	 * Type-Length-Value triples that follow.
-	 */
-	tlvcnt = aimbs_get16(bs);
-
-	/*
-	 * Parse out the Type-Length-Value triples as they're found.
-	 */
-	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
-		int endpos;
-		fu16_t type, length;
-
-		type = aimbs_get16(bs);
-		length = aimbs_get16(bs);
-
-		endpos = aim_bstream_curpos(bs) + length;
-
-		if (type == 0x0001) {
-			/*
-			 * Type = 0x0001: User flags
-			 * 
-			 * Specified as any of the following ORed together:
-			 *      0x0001  Trial (user less than 60days)
-			 *      0x0002  Unknown bit 2
-			 *      0x0004  AOL Main Service user
-			 *      0x0008  Unknown bit 4
-			 *      0x0010  Free (AIM) user 
-			 *      0x0020  Away
-			 *      0x0400  ActiveBuddy
-			 *
-			 */
-			outinfo->flags = aimbs_get16(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
-
-		} else if (type == 0x0002) {
-			/*
-			 * Type = 0x0002: Account creation time. 
-			 *
-			 * The time/date that the user originally registered for
-			 * the service, stored in time_t format.
-			 *
-			 * I'm not sure how this differs from type 5 ("member
-			 * since").
-			 *
-			 * Note: This is the field formerly known as "member
-			 * since".  All these years and I finally found out
-			 * that I got the name wrong.
-			 */
-			outinfo->createtime = aimbs_get32(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
-
-		} else if (type == 0x0003) {
-			/*
-			 * Type = 0x0003: On-Since date.
-			 *
-			 * The time/date that the user started their current 
-			 * session, stored in time_t format.
-			 */
-			outinfo->onlinesince = aimbs_get32(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
-
-		} else if (type == 0x0004) {
-			/*
-			 * Type = 0x0004: Idle time.
-			 *
-			 * Number of minutes since the user actively used the 
-			 * service.
-			 *
-			 * Note that the client tells the server when to start
-			 * counting idle times, so this may or may not be 
-			 * related to reality.
-			 */
-			outinfo->idletime = aimbs_get16(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
-
-		} else if (type == 0x0005) {
-			/*
-			 * Type = 0x0005: Member since date. 
-			 *
-			 * The time/date that the user originally registered for
-			 * the service, stored in time_t format.
-			 *
-			 * This is sometimes sent instead of type 2 ("account
-			 * creation time"), particularly in the self-info.
-			 * And particularly for ICQ?
-			 */
-			outinfo->membersince = aimbs_get32(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
-
-		} else if (type == 0x0006) {
-			/*
-			 * Type = 0x0006: ICQ Online Status
-			 *
-			 * ICQ's Away/DND/etc "enriched" status. Some decoding 
-			 * of values done by Scott <darkagl@pcnet.com>
-			 */
-			aimbs_get16(bs);
-			outinfo->icqinfo.status = aimbs_get16(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
-
-		} else if (type == 0x0008) {
-			/*
-			 * Type = 0x0008
-			 *
-			 * Client type, or some such.
-			 */
-
-		} else if (type == 0x000a) {
-			/*
-			 * Type = 0x000a
-			 *
-			 * ICQ User IP Address.
-			 * Ahh, the joy of ICQ security.
-			 */
-			outinfo->icqinfo.ipaddr = aimbs_get32(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
-
-		} else if (type == 0x000c) {
-			/* 
-			 * Type = 0x000c
-			 *
-			 * random crap containing the IP address,
-			 * apparently a port number, and some Other Stuff.
-			 *
-			 * Format is:
-			 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
-			 * 
-			 *
-			 */
-			aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
-			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
-
-		} else if (type == 0x000d) {
-			/*
-			 * Type = 0x000d
-			 *
-			 * OSCAR Capability information.
-			 *
-			 */
-			outinfo->capabilities |= aim_locate_getcaps(sess, bs, length);
-			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
-
-		} else if (type == 0x000e) {
-			/*
-			 * Type = 0x000e
-			 *
-			 * AOL capability information.
-			 *
-			 */
-
-		} else if ((type == 0x000f) || (type == 0x0010)) {
-			/*
-			 * Type = 0x000f: Session Length. (AIM)
-			 * Type = 0x0010: Session Length. (AOL)
-			 *
-			 * The duration, in seconds, of the user's current 
-			 * session.
-			 *
-			 * Which TLV type this comes in depends on the
-			 * service the user is using (AIM or AOL).
-			 *
-			 */
-			outinfo->sessionlen = aimbs_get32(bs);
-			outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
-
-		} else if (type == 0x0019) {
-			/*
-			 * Type = 0x0019
-			 *
-			 * OSCAR short capability information.  A shortened 
-			 * form of the normal capabilities.
-			 */
-			outinfo->capabilities |= aim_locate_getcaps_short(sess, bs, length);
-			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
-
-		} else if (type == 0x001b) {
-			/*
-			 * Type = 0x001a
-			 *
-			 * AOL short capability information.  A shortened 
-			 * form of the normal capabilities.
-			 */
-
-		} else if (type == 0x001b) {
-			/*
-			 * Type = 0x0019
-			 *
-			 * Encryption certification MD5 checksum.
-			 */
-
-		} else if (type == 0x001d) {
-			/*
-			 * Type = 0x001d
-			 *
-			 * Buddy icon information and status/available messages.
-			 *
-			 * This almost seems like the AIM protocol guys gave
-			 * the iChat guys a Type, and the iChat guys tried to
-			 * cram as much cool shit into it as possible.  Then
-			 * the Windows AIM guys were like, "hey, that's
-			 * pretty neat, let's copy those prawns."
-			 *
-			 * In that spirit, this can contain a custom message,
-			 * kind of like an away message, but you're not away
-			 * (it's called an "available" message).  Or it can
-			 * contain information about the buddy icon the user
-			 * has stored on the server.
-			 */
-			int type2, number, length2;
-
-			while (aim_bstream_curpos(bs) < endpos) {
-				type2 = aimbs_get16(bs);
-				number = aimbs_get8(bs);
-				length2 = aimbs_get8(bs);
-
-				switch (type2) {
-					case 0x0000: { /* This is an official buddy icon? */
-						/* This is always 5 bytes of "0x02 01 d2 04 72"? */
-						aim_bstream_advance(bs, length2);
-					} break;
-
-					case 0x0001: { /* A buddy icon checksum */
-						if ((length2 > 0) && ((number == 0x00) || (number == 0x01))) {
-							free(outinfo->iconcsum);
-							outinfo->iconcsumtype = number;
-							outinfo->iconcsum = aimbs_getraw(bs, length2);
-							outinfo->iconcsumlen = length2;
-						} else
-							aim_bstream_advance(bs, length2);
-					} break;
-
-					case 0x0002: { /* A status/available message */
-						free(outinfo->status);
-						free(outinfo->status_encoding);
-						if (length2 >= 4) {
-							outinfo->status_len = aimbs_get16(bs);
-							outinfo->status = aimbs_getstr(bs, outinfo->status_len);
-							if (aimbs_get16(bs) == 0x0001) { /* We have an encoding */
-								aimbs_get16(bs);
-								outinfo->status_encoding = aimbs_getstr(bs, aimbs_get16(bs));
-							} else {
-								/* No explicit encoding, client should use UTF-8 */
-								outinfo->status_encoding = NULL;
-							}
-						} else {
-							aim_bstream_advance(bs, length2);
-							outinfo->status_len = 0;
-							outinfo->status = g_strdup("");
-							outinfo->status_encoding = NULL;
-						}
-					} break;
-
-					default: {
-						aim_bstream_advance(bs, length2);
-					} break;
-				}
-			}
-
-		} else if (type == 0x001e) {
-			/*
-			 * Type 30: Unknown.
-			 *
-			 * Always four bytes, but it doesn't look like an int.
-			 */
-
-		} else if (type == 0x001f) {
-			/*
-			 * Type 31: Unknown.
-			 *
-			 * Seen on a buddy using DeadAIM.  Data was 4 bytes:
-			 * 0x00 00 00 10
-			 */
-
-		} else {
-
-			/*
-			 * Reaching here indicates that either AOL has
-			 * added yet another TLV for us to deal with, 
-			 * or the parsing has gone Terribly Wrong.
-			 *
-			 * Either way, inform the owner and attempt
-			 * recovery.
-			 *
-			 */
-			gaim_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
-			gaim_debug_misc("oscar", "userinfo:   sn    =%s\n", outinfo->sn);
-			dumptlv(sess, type, bs, length);
-		}
-
-		/* Save ourselves. */
-		aim_bstream_setpos(bs, endpos);
-	}
-
-	aim_locate_adduserinfo(sess, outinfo);
-
-	return 0;
-}
-
-/*
- * Inverse of aim_info_extract()
- */
-faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info)
-{
-	aim_tlvlist_t *tlvlist = NULL;
-
-	if (!bs || !info)
-		return -EINVAL;
-
-	aimbs_put8(bs, strlen(info->sn));
-	aimbs_putstr(bs, info->sn);
-
-	aimbs_put16(bs, info->warnlevel);
-
-	if (info->present & AIM_USERINFO_PRESENT_FLAGS)
-		aim_tlvlist_add_16(&tlvlist, 0x0001, info->flags);
-	if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE)
-		aim_tlvlist_add_32(&tlvlist, 0x0002, info->membersince);
-	if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
-		aim_tlvlist_add_32(&tlvlist, 0x0003, info->onlinesince);
-	if (info->present & AIM_USERINFO_PRESENT_IDLE)
-		aim_tlvlist_add_16(&tlvlist, 0x0004, info->idletime);
-
-/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
-#ifdef ICQ_OSCAR_SUPPORT
-	if (atoi(info->sn) != 0) {
-		if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
-			aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status);
-		if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR)
-			aim_tlvlist_add_32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
-	}
-#endif
-
-	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
-		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities);
-
-	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
-		aim_tlvlist_add_32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
-
-	aimbs_put16(bs, aim_tlvlist_count(&tlvlist));
-	aim_tlvlist_write(bs, &tlvlist);
-	aim_tlvlist_free(&tlvlist);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0001
- */
-static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	aim_snac_t *snac2;
-	fu16_t reason;
-	char *sn;
-	int was_explicit;
-
-	if (!(snac2 = aim_remsnac(sess, snac->id))) {
-		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from unknown request!\n");
-		return 0;
-	}
-
-	if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) {
-		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family);
-		return 0;
-	}
-
-	if (!(sn = snac2->data)) {
-		gaim_debug_misc("oscar", "faim: locate.c, error(): received response from request without a screen name!\n");
-		return 0;
-	}
-
-	reason = aimbs_get16(bs);
-
-	/*
-	 * Remove this screen name from our queue.  If the client requested 
-	 * this buddy's info explicitly, then notify them that we do not have 
-	 * info for this buddy.
-	 */
-	was_explicit = aim_locate_gotuserinfo(sess, sn);
-	if (was_explicit == TRUE)
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, reason, sn);
-
-	if (snac2)
-		free(snac2->data);
-	free(snac2);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0002
- *
- * Request Location services rights.
- *
- */
-faim_export int aim_locate_reqrights(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
-		return -EINVAL;
-
-	return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQRIGHTS);
-}
-
-/*
- * Subtype 0x0003
- *
- * Normally contains:
- *   t(0001)  - short containing max profile length (value = 1024)
- *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
- *   t(0003)  - short - unknown (value = 10)
- *   t(0004)  - short - unknown (value = 2048) [ICQ only?]
- */
-static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_tlvlist_t *tlvlist;
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-	fu16_t maxsiglen = 0;
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
-		maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, maxsiglen);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0004
- *
- * Gives BOS your profile.
- *
- * profile_encoding and awaymsg_encoding MUST be set if profile or
- * away are set, respectively, and their value may or may not be
- * restricted to a few choices.  I am currently aware of:
- *
- * us-ascii		Just that
- * unicode-2-0		UCS2-BE
- *
- * profile_len and awaymsg_len MUST be set similarly, and they MUST
- * be the length of their respective strings in bytes.
- *
- * To get the previous behavior of awaymsg == "" un-setting the away
- * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
- * obvious equivalent).
- *
- */
-faim_export int aim_locate_setprofile(aim_session_t *sess,
-				  const char *profile_encoding, const gchar *profile, const int profile_len,
-				  const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-	char *encoding;
-	static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
-		return -EINVAL;
-
-	if (!profile && !awaymsg)
-		return -EINVAL;
-
-	if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
-		return -EINVAL;
-	}
-
-	/* Build the packet first to get real length */
-	if (profile) {
-		/* no + 1 here because of %s */
-		encoding = malloc(strlen(defencoding) + strlen(profile_encoding));
-		if (encoding == NULL) {
-			return -ENOMEM;
-		}
-		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);
-		free(encoding);
-	}
-
-	/*
-	 * So here's how this works:
-	 *   - You are away when you have a non-zero-length type 4 TLV stored.
-	 *   - You become unaway when you clear the TLV with a zero-length
-	 *       type 4 TLV.
-	 *   - If you do not send the type 4 TLV, your status does not change
-	 *       (that is, if you were away, you'll remain away).
-	 */
-	if (awaymsg) {
-		if (awaymsg_len) {
-			encoding = malloc(strlen(defencoding) + strlen(awaymsg_encoding));
-			if (encoding == NULL) {
-				return -ENOMEM;
-			}
-			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);
-			free(encoding);
-		} else
-			aim_tlvlist_add_noval(&tl, 0x0004);
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0004 - Set your client's capabilities.
- */
-faim_export int aim_locate_setcaps(aim_session_t *sess, fu32_t caps)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
-		return -EINVAL;
-
-	aim_tlvlist_add_caps(&tl, 0x0005, caps);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0005 - Request info of another AIM user.
- *
- * @param sn The screenname whose info you wish to request.
- * @param infotype The type of info you wish to request.
- *        0x0001 - Info/profile
- *        0x0003 - Away message
- *        0x0004 - Capabilities
- */
-faim_export int aim_locate_getinfo(aim_session_t *sess, const char *sn, fu16_t infotype)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
-	aimbs_put16(&fr->data, infotype);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x0006 */
-static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	aim_userinfo_t *userinfo, *userinfo2;
-	aim_tlvlist_t *tlvlist;
-	aim_tlv_t *tlv = NULL;
-	int was_explicit;
-
-	userinfo = (aim_userinfo_t *)malloc(sizeof(aim_userinfo_t));
-	aim_info_extract(sess, bs, userinfo);
-	tlvlist = aim_tlvlist_read(bs);
-
-	/* Profile will be 1 and 2 */
-	userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1);
-	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
-		userinfo->info = (char *)malloc(tlv->length);
-		memcpy(userinfo->info, tlv->value, tlv->length);
-		userinfo->info_len = tlv->length;
-	}
-
-	/* Away message will be 3 and 4 */
-	userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1);
-	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
-		userinfo->away = (char *)malloc(tlv->length);
-		memcpy(userinfo->away, tlv->value, tlv->length);
-		userinfo->away_len = tlv->length;
-	}
-
-	/* Caps will be 5 */
-	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
-		aim_bstream_t cbs;
-		aim_bstream_init(&cbs, tlv->value, tlv->length);
-		userinfo->capabilities = aim_locate_getcaps(sess, &cbs, tlv->length);
-		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
-	}
-	aim_tlvlist_free(&tlvlist);
-
-	aim_locate_adduserinfo(sess, userinfo);
-	userinfo2 = aim_locate_finduserinfo(sess, userinfo->sn);
-	aim_info_free(userinfo);
-	free(userinfo);
-
-	/*
-	 * Remove this screen name from our queue.  If the client requested
-	 * this buddy's info explicitly, then notify them that we have info
-	 * for this buddy.
-	 */
-	was_explicit = aim_locate_gotuserinfo(sess, userinfo2->sn);
-	if (was_explicit == TRUE)
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, userinfo2);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0009 - Set directory profile data.
- *
- * This is not the same as aim_location_setprofile!
- * privacy: 1 to allow searching, 0 to disallow.
- *
- */
-faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, fu16_t privacy)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
-		return -EINVAL;
-
-	aim_tlvlist_add_16(&tl, 0x000a, privacy);
-
-	if (first)
-		aim_tlvlist_add_str(&tl, 0x0001, first);
-	if (last)
-		aim_tlvlist_add_str(&tl, 0x0002, last);
-	if (middle)
-		aim_tlvlist_add_str(&tl, 0x0003, middle);
-	if (maiden)
-		aim_tlvlist_add_str(&tl, 0x0004, maiden);
-
-	if (state)
-		aim_tlvlist_add_str(&tl, 0x0007, state);
-	if (city)
-		aim_tlvlist_add_str(&tl, 0x0008, city);
-
-	if (nickname)
-		aim_tlvlist_add_str(&tl, 0x000c, nickname);
-	if (zip)
-		aim_tlvlist_add_str(&tl, 0x000d, zip);
-
-	if (street)
-		aim_tlvlist_add_str(&tl, 0x0021, street);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x000b - Huh? What is this?
- */
-faim_export int aim_locate_000b(aim_session_t *sess, const char *sn)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-		return -EINVAL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x000f
- *
- * XXX pass these in better
- *
- */
-faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)))
-		return -EINVAL;
-
-	/* ?? privacy ?? */
-	aim_tlvlist_add_16(&tl, 0x000a, privacy);
-
-	if (interest1)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest1);
-	if (interest2)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest2);
-	if (interest3)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest3);
-	if (interest4)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest4);
-	if (interest5)
-		aim_tlvlist_add_str(&tl, 0x0000b, interest5);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0015 - Request the info a user using the short method.  This is
- * what iChat uses.  It normally is VERY leniently rate limited.
- *
- * @param sn The screen name whose info you wish to request.
- * @param flags The bitmask which specifies the type of info you wish to request.
- *        0x00000001 - Info/profile.
- *        0x00000002 - Away message.
- *        0x00000004 - Capabilities.
- *        0x00000008 - Certification.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, fu32_t flags)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_LOC)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1);
-
-	aim_putsnac(&fr->data, 0x0002, 0x0015, 0x0000, snacid);
-	aimbs_put32(&fr->data, flags);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0001)
-		return error(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0003)
-		return rights(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0006)
-		return userinfo(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-static void locate_shutdown(aim_session_t *sess, aim_module_t *mod)
-{
-	aim_userinfo_t *del;
-
-	while (sess->locate.userinfo) {
-		del = sess->locate.userinfo;
-		sess->locate.userinfo = sess->locate.userinfo->next;
-		aim_info_free(del);
-		free(del);
-	}
-}
-
-faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = AIM_CB_FAM_LOC;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "locate", sizeof(mod->name));
-	mod->snachandler = snachandler;
-	mod->shutdown = locate_shutdown;
-
-	return 0;
-}
--- a/src/protocols/oscar/misc.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/misc.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,13 +1,32 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * misc.c
  *
- * Random stuff.  Basically just a few functions for sending 
+ * Random stuff.  Basically just a few functions for sending
  * simple SNACs, and then the generic error handler.
  *
  */
 
-#define FAIM_INTERNAL
-#include <aim.h> 
+#include "oscar.h"
 
 /*
  * Generic routine for sending commands.
@@ -20,7 +39,7 @@
  * back to the single.  I don't see any advantage to doing it either way.
  *
  */
-faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype)
+faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid = 0x00000000;
@@ -35,7 +54,7 @@
 	return 0;
 }
 
-faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype)
+faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
@@ -51,7 +70,7 @@
 	return 0;
 }
 
-faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *longdata)
+faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *longdata)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
@@ -72,7 +91,7 @@
 	return 0;
 }
 
-faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *shortdata)
+faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *shortdata)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
--- a/src/protocols/oscar/msgcookie.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/msgcookie.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * Cookie Caching stuff. Adam wrote this, apparently just some
  * derivatives of n's SNAC work. I cleaned it up, added comments.
@@ -11,8 +31,7 @@
  * wrong, we get quirky behavior when cookies step on each others' toes.
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
 
 /**
  * aim_cachecookie - appends a cookie to the cookie list
@@ -59,7 +78,7 @@
  * @param type cookie type to look for
  * @return if found, returns the struct; if none found (or on error), returns NULL:
  */
-faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type)
+faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type)
 {
 	aim_msgcookie_t *cur, **prev;
 
@@ -87,7 +106,7 @@
  * @return returns NULL on error, a pointer to the newly-allocated
  *         cookie on success.
  */
-faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data)
+faim_internal aim_msgcookie_t *aim_mkcookie(guint8 *c, int type, void *data)
 {
 	aim_msgcookie_t *cookie;
 
@@ -114,7 +133,7 @@
  *         on success; returns NULL on error/not found
  */
 
-faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const fu8_t *cookie, int type)
+faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const guint8 *cookie, int type)
 {
 	aim_msgcookie_t *cur;
 
--- a/src/protocols/oscar/odir.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-/*
- * Family 0x000f - Newer Search Method
- *
- * Used for searching for other AIM users by email address, name, 
- * location, commmon interests, and a few other similar things.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * Search for an AIM screen name based on their email address.
- *
- * @param sess The oscar session.
- * @param region Should be "us-ascii" unless you know what you're doing.
- * @param email The email address you want to search for.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_odir_email(aim_session_t *sess, const char *region, const char *email)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 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);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * Search for an AIM screen name based on various info 
- * about the person.
- *
- * @param sess The oscar session.
- * @param region Should be "us-ascii" unless you know what you're doing.
- * @param first The first name of the person you want to search for.
- * @param middle The middle name of the person you want to search for.
- * @param last The last name of the person you want to search for.
- * @param maiden The maiden name of the person you want to search for.
- * @param nick The nick name of the person you want to search for.
- * @param city The city where the person you want to search for resides.
- * @param state The state where the person you want to search for resides.
- * @param country The country where the person you want to search for resides.
- * @param zip The zip code where the person you want to search for resides.
- * @param address The street address where the person you want to seach for resides.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_odir_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 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 */
-	if (first)
-		aim_tlvlist_add_str(&tl, 0x0001, first);
-	if (last)
-		aim_tlvlist_add_str(&tl, 0x0002, last);
-	if (middle)
-		aim_tlvlist_add_str(&tl, 0x0003, middle);
-	if (maiden)
-		aim_tlvlist_add_str(&tl, 0x0004, maiden);
-	if (country)
-		aim_tlvlist_add_str(&tl, 0x0006, country);
-	if (state)
-		aim_tlvlist_add_str(&tl, 0x0007, state);
-	if (city)
-		aim_tlvlist_add_str(&tl, 0x0008, city);
-	if (nick)
-		aim_tlvlist_add_str(&tl, 0x000c, nick);
-	if (zip)
-		aim_tlvlist_add_str(&tl, 0x000d, zip);
-	if (address)
-		aim_tlvlist_add_str(&tl, 0x0021, address);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-
-/**
- * Subtype 0x0002 - Submit a User Search Request
- *
- * @param sess The oscar session.
- * @param interest1 An interest you want to search for.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_odir_interest(aim_session_t *sess, const char *region, const char *interest)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 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 */
-	if (interest)
-		aim_tlvlist_add_str(&tl, 0x0001, interest);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_tlvlist_size(&tl))))
-		return -ENOMEM;
-	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
-
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-
-/**
- * Subtype 0x0003 - Receive Reply From a User Search
- *
- */
-static int parseresults(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t tmp, numresults;
-	struct aim_odir *results = NULL;
-
-	tmp = aimbs_get16(bs); /* Unknown */
-	tmp = aimbs_get16(bs); /* Unknown */
-	aim_bstream_advance(bs, tmp);
-
-	numresults = aimbs_get16(bs); /* Number of results to follow */
-
-	/* Allocate a linked list, 1 node per result */
-	while (numresults) {
-		struct aim_odir *new;
-		aim_tlvlist_t *tl = aim_tlvlist_readnum(bs, aimbs_get16(bs));
-		new = (struct aim_odir *)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->next = results;
-		results = new;
-		numresults--;
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, results);
-
-	/* Now free everything from above */
-	while (results) {
-		struct aim_odir *del = results;
-		results = results->next;
-		free(del->first);
-		free(del->last);
-		free(del->middle);
-		free(del->maiden);
-		free(del->email);
-		free(del->country);
-		free(del->state);
-		free(del->city);
-		free(del->sn);
-		free(del->interest);
-		free(del->nick);
-		free(del->zip);
-		free(del->region);
-		free(del->address);
-		free(del);
-	}
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return parseresults(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000f;
-	mod->version = 0x0001;
-	mod->toolid = 0x0010;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "odir", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/oscar.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/oscar.c	Sat Feb 11 21:45:18 2006 +0000
@@ -45,7 +45,8 @@
 #include "util.h"
 #include "version.h"
 
-#include "aim.h"
+#include "oscar.h"
+#include "peer.h"
 
 #define OSCAR_STATUS_ID_INVISIBLE	"invisible"
 #define OSCAR_STATUS_ID_OFFLINE		"offline"
@@ -77,10 +78,10 @@
 static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT;
 static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT;
 
-static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02};
-static fu8_t features_icq[] = {0x01, 0x06};
-static fu8_t features_icq_offline[] = {0x01};
-static fu8_t ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
+static guint8 features_icq[] = {0x01, 0x06};
+static guint8 features_icq_offline[] = {0x01};
+static guint8 ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
 typedef struct _OscarData OscarData;
 struct _OscarData {
@@ -139,8 +140,8 @@
 struct chat_connection {
 	char *name;
 	char *show; /* AOL did something funny to us */
-	fu16_t exchange;
-	fu16_t instance;
+	guint16 exchange;
+	guint16 instance;
 	aim_conn_t *conn;
 	int inpa;
 	int id;
@@ -165,7 +166,7 @@
 	GaimConnection *gc;
 	char *sn;
 	char ip[64];
-	fu8_t cookie[8];
+	guint8 cookie[8];
 	gboolean donttryagain;
 };
 
@@ -178,7 +179,7 @@
  */
 struct buddyinfo {
 	gboolean typingnot;
-	fu32_t ipaddr;
+	guint32 ipaddr;
 
 	unsigned long ico_me_len;
 	unsigned long ico_me_csum;
@@ -325,7 +326,7 @@
 	g_free(bi);
 }
 
-static fu32_t oscar_charset_check(const char *utf8)
+static guint32 oscar_charset_check(const char *utf8)
 {
 	int i = 0;
 	int charset = AIM_CHARSET_ASCII;
@@ -482,7 +483,7 @@
  * charsetstr1 is always set to what the correct encoding should be.
  */
 static gchar *
-gaim_plugin_oscar_decode_im_part(GaimAccount *account, const char *sourcesn, fu16_t charset, fu16_t charsubset, const gchar *data, gsize datalen)
+gaim_plugin_oscar_decode_im_part(GaimAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
 {
 	gchar *ret = NULL;
 	const gchar *charsetstr1, *charsetstr2;
@@ -531,7 +532,7 @@
 gaim_plugin_oscar_convert_to_best_encoding(GaimConnection *gc,
 				const char *destsn, const gchar *from,
 				gchar **msg, int *msglen_int,
-				fu16_t *charset, fu16_t *charsubset)
+				guint16 *charset, guint16 *charsubset)
 {
 	OscarData *od = gc->proto_data;
 	GaimAccount *account = gaim_connection_get_account(gc);
@@ -1120,9 +1121,9 @@
 	}
 
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT,
-						AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
+						OFT_TYPE_DIRECTIMINCOMING, gaim_odc_incoming, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT,
-						AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
+						OFT_TYPE_DIRECTIMTYPING, gaim_odc_typing, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL,
 						AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
 
@@ -1192,8 +1193,8 @@
 	g_free(sn);
 	gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
 
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMINCOMING, gaim_odc_incoming, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING, gaim_odc_typing, 0);
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
 
 	return 1;
@@ -1539,7 +1540,7 @@
 		od->direct_ims = g_slist_append(od->direct_ims, dim);
 		dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
 						oscar_callback, dim->conn);
-		aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED,
+		aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIM_ESTABLISHED,
 					gaim_odc_initiate, 0);
 
 		conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, dim->gc->account, dim->name);
@@ -1864,7 +1865,7 @@
 	aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
 	aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
 	aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
-	aim_conn_addhandler(sess, conn, AIM_CB_FAM_ATH, AIM_CB_ATH_SECURID_REQUEST, gaim_parse_auth_securid_request, 0);
+	aim_conn_addhandler(sess, conn, OSCAR_FAMILY_AUTH, OSCAR_SUBTYPE_AUTH_SECURID_REQUEST, gaim_parse_auth_securid_request, 0);
 
 	conn->status |= AIM_CONN_STATUS_INPROGRESS;
 	if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER),
@@ -1987,10 +1988,10 @@
  *  -User chooses a file and oscar_xfer_init is called.  It establishes a
  *   listening socket, then asks the remote user to connect to us (and
  *   gives them the file name, port, IP, etc.)
- *  -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens
+ *  -They connect to us and we send them an OFT_TYPE_PROMPT (this happens
  *   in oscar_sendfile_estblsh)
- *  -They send us an AIM_CB_OFT_ACK and then we start sending data
- *  -When we finish, they send us an AIM_CB_OFT_DONE and they close the
+ *  -They send us an OFT_TYPE_ACK and then we start sending data
+ *  -When we finish, they send us an OFT_TYPE_DONE and they close the
  *   connection.
  *  -We get drunk because file transfer kicks ass.
  *
@@ -1999,10 +2000,10 @@
  *  -Gaim user selects file to name and location to save file to and
  *   oscar_xfer_init is called
  *  -It connects to the remote user using the IP they gave us earlier
- *  -After connecting, they send us an AIM_CB_OFT_PROMPT.  In reply, we send
- *   them an AIM_CB_OFT_ACK.
+ *  -After connecting, they send us an OFT_TYPE_PROMPT.  In reply, we send
+ *   them an OFT_TYPE_ACK.
  *  -They begin to send us lots of raw data.
- *  -When they finish sending data we send an AIM_CB_OFT_DONE and then close
+ *  -When they finish sending data we send an OFT_TYPE_DONE and then close
  *   the connection.
  *
  * Update August 2005:
@@ -2019,7 +2020,7 @@
 /*
  * Miscellaneous xfer functions
  */
-static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const fu8_t *ck)
+static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const guint8 *ck)
 {
 	GaimXfer *xfer;
 	struct aim_oft_info *oft_info;
@@ -2070,7 +2071,7 @@
 
 	if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
 		oft_info->fh.nrecvd = gaim_xfer_get_bytes_sent(xfer);
-		aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_DONE, oft_info);
+		aim_oft_sendheader(oft_info->sess, OFT_TYPE_DONE, oft_info);
 	}
 
 	aim_conn_kill(oft_info->sess, &oft_info->conn);
@@ -2269,7 +2270,7 @@
 
 	if (oft_info->conn) {
 		oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
-		aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT,
+		aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_PROMPT,
 			oscar_sendfile_prompt, 0);
 		rc = gaim_proxy_connect(gaim_connection_get_account(gc),
 					xfer->remote_ip, xfer->remote_port, nextstop_cb, xfer);
@@ -2359,15 +2360,15 @@
 		}
 
 		/* The following is taken from oscar_sendfile_estblsh */
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK,
 			oscar_sendfile_ack, 0);
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE,
 			oscar_sendfile_done, 0);
 		xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback,
 			oft_info->conn);
 
 		/* Inform the other user that we are connected and ready to transfer */
-		aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info);
+		aim_oft_sendheader(oft_info->sess, OFT_TYPE_PROMPT, oft_info);
 	} else if(oft_info->send_or_recv == AIM_XFER_RECV) {
 		oscar_sendfile_connected(xfer, fd, GAIM_INPUT_READ);
 	} else {
@@ -2601,7 +2602,7 @@
 
 		aim_im_sendch2_sendfile_ask(od->sess, oft_info);
 		aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT,
-			AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0);
+			OFT_TYPE_ESTABLISHED, oscar_sendfile_estblsh, 0);
 	} else {
 		gaim_xfer_error(GAIM_XFER_SEND, gaim_xfer_get_account(xfer), xfer->who,
 						_("Unable to establish listener socket or no AOL proxy connection present."));
@@ -2898,26 +2899,26 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_userinfo, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_REQUESTINFOTIMEOUT, gaim_reqinfo_timeout, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_GOTINFOBLOCK, gaim_got_infoblock, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_REDIRECT, gaim_handle_redirect, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_RIGHTSINFO, gaim_parse_locaterights, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_RIGHTSINFO, gaim_parse_buddyrights, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_ONCOMING, gaim_parse_oncoming, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_BUDDY, OSCAR_SUBTYPE_BUDDY_OFFGOING, gaim_parse_offgoing, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_INCOMING, gaim_parse_incoming_im, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_ERROR, gaim_parse_locerr, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_MISSEDCALL, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_CLIENTAUTORESP, gaim_parse_clientauto, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_RATECHANGE, gaim_parse_ratechange, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_EVIL, gaim_parse_evilnotify, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_USERLOOKUP, OSCAR_SUBTYPE_USERLOOKUP_ERROR, gaim_parse_searcherror, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_USERLOOKUP, 0x0003, gaim_parse_searchreply, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_ERROR, gaim_parse_msgerr, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_MTN, gaim_parse_mtn, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_USERINFO, gaim_parse_userinfo, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT, gaim_reqinfo_timeout, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_LOCATE, OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK, gaim_got_infoblock, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICBM, OSCAR_SUBTYPE_ICBM_ACK, gaim_parse_msgack, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_OSERVICE, OSCAR_SUBTYPE_OSERVICE_MOTD, gaim_parse_motd, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, gaim_icbm_param_info, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0);
@@ -2925,21 +2926,21 @@
 	aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0001, 0x000f, gaim_selfinfo, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0021, oscar_icon_req,0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS, gaim_icqalias, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, gaim_ssi_parseerr, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADD, gaim_ssi_parseadd, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, gaim_ssi_authgiven, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, gaim_ssi_authrequest, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, gaim_ssi_authreply, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_POPUP, 0x0002, gaim_popup, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_ALIAS, gaim_icqalias, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_ICQ, OSCAR_SUBTYPE_ICQ_INFO, gaim_icqinfo, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ERROR, gaim_ssi_parseerr, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO, gaim_ssi_parserights, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_LIST, gaim_ssi_parselist, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_NOLIST, gaim_ssi_parselist, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_SRVACK, gaim_ssi_parseack, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ADD, gaim_ssi_parseadd, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTH, gaim_ssi_authgiven, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ, gaim_ssi_authrequest, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP, gaim_ssi_authreply, 0);
+	aim_conn_addhandler(sess, bosconn, OSCAR_FAMILY_FEEDBAG, OSCAR_SUBTYPE_FEEDBAG_ADDED, gaim_ssi_gotadded, 0);
 
 	od->conn = bosconn;
 	for (i = 0; i < (int)strlen(info->bosip); i++) {
@@ -3100,12 +3101,12 @@
 int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct pieceofcrap *pos;
-	fu32_t offset, len;
+	guint32 offset, len;
 	char *modname;
 
 	va_start(ap, fr);
-	offset = va_arg(ap, fu32_t);
-	len = va_arg(ap, fu32_t);
+	offset = va_arg(ap, guint32);
+	len = va_arg(ap, guint32);
 	modname = va_arg(ap, char *);
 	va_end(ap);
 
@@ -3195,11 +3196,11 @@
 	struct chat_connection *chatcon;
 	static int id = 1;
 
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, 0x0001, gaim_parse_genericerr, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_conv_chat_join, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_conv_chat_leave, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, 0x0001, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_USERJOIN, gaim_conv_chat_join, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_USERLEAVE, gaim_conv_chat_leave, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHAT, OSCAR_SUBTYPE_CHAT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0);
 
 	aim_clientready(sess, fr->conn);
 
@@ -3213,7 +3214,7 @@
 static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
 
 	aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_CHATNAV, OSCAR_SUBTYPE_CHATNAV_INFO, gaim_chatnav_info, 0);
 
 	aim_clientready(sess, fr->conn);
 
@@ -3225,7 +3226,7 @@
 static int conninitdone_email(aim_session_t *sess, aim_frame_t *fr, ...) {
 
 	aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_EML, AIM_CB_EML_MAILSTATUS, gaim_email_parseupdate, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ALERT, OSCAR_SUBTYPE_ALERT_MAILSTATUS, gaim_email_parseupdate, 0);
 
 	aim_email_sendcookies(sess);
 	aim_email_activate(sess);
@@ -3239,8 +3240,8 @@
 	OscarData *od = gc->proto_data;
 
 	aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_ERROR, gaim_icon_error, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_RESPONSE, gaim_icon_parseicon, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_BART, OSCAR_SUBTYPE_BART_ERROR, gaim_icon_error, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_BART, OSCAR_SUBTYPE_BART_RESPONSE, gaim_icon_parseicon, 0);
 
 	aim_clientready(sess, fr->conn);
 
@@ -3765,7 +3766,7 @@
  * This is called after a remote AIM user has connected to us.
  * If not using a rendezvous proxy, then we want to do some
  * voodoo with the socket file descriptors. Then we always
- * add a callback or two, and then send the AIM_CB_OFT_PROMPT.
+ * add a callback or two, and then send the OFT_TYPE_PROMPT.
  */
 static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) {
 	GaimConnection *gc = sess->aux_data;
@@ -3812,13 +3813,13 @@
 	xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
 
 	if(oft_info->send_or_recv == AIM_XFER_SEND) {
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK,
 			oscar_sendfile_ack, 0);
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE,
 			oscar_sendfile_done, 0);
 
 		/* Inform the other user that we are connected and ready to transfer */
-		aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info);
+		aim_oft_sendheader(sess, OFT_TYPE_PROMPT, oft_info);
 	}
 
 	/* For a file send, we'll hopefully end up in oscar_sendfile_ack next
@@ -3874,13 +3875,13 @@
 	/* Don't wait around if this is a redirected send */
 	if(oft_info->send_or_recv == AIM_XFER_SEND) {
 		/* We should only get here if this is a redirected file send */
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_ACK,
 			oscar_sendfile_ack, 0);
-		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+		aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, OFT_TYPE_DONE,
 			oscar_sendfile_done, 0);
 
 		/* Inform the other user that we are ready to transfer */
-		aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info);
+		aim_oft_sendheader(oft_info->sess, OFT_TYPE_PROMPT, oft_info);
 	}
 }
 
@@ -3897,14 +3898,14 @@
 	struct aim_oft_info *oft_info;
 	va_list ap;
 	aim_conn_t *conn;
-	fu8_t *cookie;
+	guint8 *cookie;
 	struct aim_fileheader_t *fh;
 
 	gaim_debug_info("oscar", "AAA - in oscar_sendfile_prompt\n");
 
 	va_start(ap, fr);
 	conn = va_arg(ap, aim_conn_t *);
-	cookie = va_arg(ap, fu8_t *);
+	cookie = va_arg(ap, guint8 *);
 	fh = va_arg(ap, struct aim_fileheader_t *);
 	va_end(ap);
 
@@ -3925,7 +3926,7 @@
 	memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8);
 
 	/* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */
-	aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_ACK, oft_info);
+	aim_oft_sendheader(oft_info->sess, OFT_TYPE_ACK, oft_info);
 	gaim_xfer_start(xfer, xfer->fd, NULL, 0);
 
 	return 0;
@@ -3941,13 +3942,13 @@
 	GaimXfer *xfer;
 	va_list ap;
 	aim_conn_t *conn;
-	fu8_t *cookie;
+	guint8 *cookie;
 	struct aim_fileheader_t *fh;
 
 	gaim_debug_info("oscar", "AAA - in oscar_sendfile_ack\n");
 	va_start(ap, fr);
 	conn = va_arg(ap, aim_conn_t *);
-	cookie = va_arg(ap, fu8_t *);
+	cookie = va_arg(ap, guint8 *);
 	fh = va_arg(ap, struct aim_fileheader_t *);
 	va_end(ap);
 
@@ -3973,14 +3974,14 @@
 	GaimXfer *xfer;
 	va_list ap;
 	aim_conn_t *conn;
-	fu8_t *cookie;
+	guint8 *cookie;
 	struct aim_fileheader_t *fh;
 	struct aim_oft_info *oft_info;
 
 	gaim_debug_info("oscar", "AAA - in oscar_sendfile_done\n");
 	va_start(ap, fr);
 	conn = va_arg(ap, aim_conn_t *);
-	cookie = va_arg(ap, fu8_t *);
+	cookie = va_arg(ap, guint8 *);
 	fh = va_arg(ap, struct aim_fileheader_t *);
 	va_end(ap);
 
@@ -4840,13 +4841,13 @@
 }
 
 static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
-	fu16_t channel;
+	guint16 channel;
 	int ret = 0;
 	aim_userinfo_t *userinfo;
 	va_list ap;
 
 	va_start(ap, fr);
-	channel = (fu16_t)va_arg(ap, unsigned int);
+	channel = (guint16)va_arg(ap, unsigned int);
 	userinfo = va_arg(ap, aim_userinfo_t *);
 
 	switch (channel) {
@@ -4885,14 +4886,14 @@
 	GaimAccount *account = gaim_connection_get_account(gc);
 	char *buf;
 	va_list ap;
-	fu16_t chan, nummissed, reason;
+	guint16 chan, nummissed, reason;
 	aim_userinfo_t *userinfo;
 
 	va_start(ap, fr);
-	chan = (fu16_t)va_arg(ap, unsigned int);
+	chan = (guint16)va_arg(ap, unsigned int);
 	userinfo = va_arg(ap, aim_userinfo_t *);
-	nummissed = (fu16_t)va_arg(ap, unsigned int);
-	reason = (fu16_t)va_arg(ap, unsigned int);
+	nummissed = (guint16)va_arg(ap, unsigned int);
+	reason = (guint16)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	switch(reason) {
@@ -4959,7 +4960,7 @@
 	return 1;
 }
 
-static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const guchar *cookie) {
+static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, guint16 reason, const guchar *cookie) {
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = gc->proto_data;
 
@@ -4991,7 +4992,7 @@
 	return 0;
 }
 
-static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, fu16_t reason, fu32_t state, char *msg) {
+static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, guint16 reason, guint32 state, char *msg) {
 	GaimConnection *gc = sess->aux_data;
 
 	switch(reason) {
@@ -5025,22 +5026,22 @@
 
 static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t chan, reason;
+	guint16 chan, reason;
 	char *who;
 
 	va_start(ap, fr);
-	chan = (fu16_t)va_arg(ap, unsigned int);
+	chan = (guint16)va_arg(ap, unsigned int);
 	who = va_arg(ap, char *);
-	reason = (fu16_t)va_arg(ap, unsigned int);
+	reason = (guint16)va_arg(ap, unsigned int);
 
 	if (chan == 0x0002) { /* File transfer declined */
 		guchar *cookie = va_arg(ap, guchar *);
 		return gaim_parse_clientauto_ch2(sess, who, reason, cookie);
 	} else if (chan == 0x0004) { /* ICQ message */
-		fu32_t state = 0;
+		guint32 state = 0;
 		char *msg = NULL;
 		if (reason == 0x0003) {
-			state = va_arg(ap, fu32_t);
+			state = va_arg(ap, guint32);
 			msg = va_arg(ap, char *);
 		}
 		return gaim_parse_clientauto_ch4(sess, who, reason, state, msg);
@@ -5053,11 +5054,11 @@
 
 static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t reason;
+	guint16 reason;
 	char *m;
 
 	va_start(ap, fr);
-	reason = (fu16_t) va_arg(ap, unsigned int);
+	reason = (guint16) va_arg(ap, unsigned int);
 	va_end(ap);
 
 	gaim_debug_error("oscar",
@@ -5079,11 +5080,11 @@
 	GaimXfer *xfer;
 #endif
 	va_list ap;
-	fu16_t reason;
+	guint16 reason;
 	char *data, *buf;
 
 	va_start(ap, fr);
-	reason = (fu16_t)va_arg(ap, unsigned int);
+	reason = (guint16)va_arg(ap, unsigned int);
 	data = va_arg(ap, char *);
 	va_end(ap);
 
@@ -5116,13 +5117,13 @@
 static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) {
 	GaimConnection *gc = sess->aux_data;
 	va_list ap;
-	fu16_t type1, type2;
+	guint16 type1, type2;
 	char *sn;
 
 	va_start(ap, fr);
-	type1 = (fu16_t) va_arg(ap, unsigned int);
+	type1 = (guint16) va_arg(ap, unsigned int);
 	sn = va_arg(ap, char *);
-	type2 = (fu16_t) va_arg(ap, unsigned int);
+	type2 = (guint16) va_arg(ap, unsigned int);
 	va_end(ap);
 
 	switch (type2) {
@@ -5153,11 +5154,11 @@
 static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	gchar *buf;
 	va_list ap;
-	fu16_t reason;
+	guint16 reason;
 	char *destn;
 
 	va_start(ap, fr);
-	reason = (fu16_t) va_arg(ap, unsigned int);
+	reason = (guint16) va_arg(ap, unsigned int);
 	destn = va_arg(ap, char *);
 	va_end(ap);
 
@@ -5327,11 +5328,11 @@
 static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...)
 {
 	char *msg;
-	fu16_t id;
+	guint16 id;
 	va_list ap;
 
 	va_start(ap, fr);
-	id  = (fu16_t) va_arg(ap, unsigned int);
+	id  = (guint16) va_arg(ap, unsigned int);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
@@ -5346,20 +5347,20 @@
 
 static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t type;
+	guint16 type;
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = (OscarData *)gc->proto_data;
 
 	va_start(ap, fr);
-	type = (fu16_t) va_arg(ap, unsigned int);
+	type = (guint16) va_arg(ap, unsigned int);
 
 	switch(type) {
 		case 0x0002: {
-			fu8_t maxrooms;
+			guint8 maxrooms;
 			struct aim_chat_exchangeinfo *exchanges;
 			int exchangecount, i;
 
-			maxrooms = (fu8_t) va_arg(ap, unsigned int);
+			maxrooms = (guint8) va_arg(ap, unsigned int);
 			exchangecount = va_arg(ap, int);
 			exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
 
@@ -5386,19 +5387,19 @@
 			break;
 		case 0x0008: {
 			char *fqcn, *name, *ck;
-			fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
-			fu8_t createperms;
-			fu32_t createtime;
+			guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
+			guint8 createperms;
+			guint32 createtime;
 
 			fqcn = va_arg(ap, char *);
-			instance = (fu16_t)va_arg(ap, unsigned int);
-			exchange = (fu16_t)va_arg(ap, unsigned int);
-			flags = (fu16_t)va_arg(ap, unsigned int);
-			createtime = va_arg(ap, fu32_t);
-			maxmsglen = (fu16_t)va_arg(ap, unsigned int);
-			maxoccupancy = (fu16_t)va_arg(ap, unsigned int);
-			createperms = (fu8_t)va_arg(ap, unsigned int);
-			unknown = (fu16_t)va_arg(ap, unsigned int);
+			instance = (guint16)va_arg(ap, unsigned int);
+			exchange = (guint16)va_arg(ap, unsigned int);
+			flags = (guint16)va_arg(ap, unsigned int);
+			createtime = va_arg(ap, guint32);
+			maxmsglen = (guint16)va_arg(ap, unsigned int);
+			maxoccupancy = (guint16)va_arg(ap, unsigned int);
+			createperms = (guint8)va_arg(ap, unsigned int);
+			unknown = (guint16)va_arg(ap, unsigned int);
 			name = va_arg(ap, char *);
 			ck = va_arg(ap, char *);
 
@@ -5476,8 +5477,8 @@
 	char *roomname;
 	int usercount;
 	char *roomdesc;
-	fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
-	fu32_t creationtime;
+	guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
+	guint32 creationtime;
 	GaimConnection *gc = sess->aux_data;
 	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
 
@@ -5487,12 +5488,12 @@
 	usercount= va_arg(ap, int);
 	userinfo = va_arg(ap, aim_userinfo_t *);
 	roomdesc = va_arg(ap, char *);
-	unknown_c9 = (fu16_t)va_arg(ap, unsigned int);
-	creationtime = va_arg(ap, fu32_t);
-	maxmsglen = (fu16_t)va_arg(ap, unsigned int);
-	unknown_d2 = (fu16_t)va_arg(ap, unsigned int);
-	unknown_d5 = (fu16_t)va_arg(ap, unsigned int);
-	maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int);
+	unknown_c9 = (guint16)va_arg(ap, unsigned int);
+	creationtime = va_arg(ap, guint32);
+	maxmsglen = (guint16)va_arg(ap, unsigned int);
+	unknown_d2 = (guint16)va_arg(ap, unsigned int);
+	unknown_d5 = (guint16)va_arg(ap, unsigned int);
+	maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	gaim_debug_misc("oscar",
@@ -5585,15 +5586,15 @@
 	GSList *cur;
 	va_list ap;
 	char *sn;
-	fu8_t iconcsumtype, *iconcsum, *icon;
-	fu16_t iconcsumlen, iconlen;
+	guint8 iconcsumtype, *iconcsum, *icon;
+	guint16 iconcsumlen, iconlen;
 
 	va_start(ap, fr);
 	sn = va_arg(ap, char *);
 	iconcsumtype = va_arg(ap, int);
-	iconcsum = va_arg(ap, fu8_t *);
+	iconcsum = va_arg(ap, guint8 *);
 	iconcsumlen = va_arg(ap, int);
-	icon = va_arg(ap, fu8_t *);
+	icon = va_arg(ap, guint8 *);
 	iconlen = va_arg(ap, int);
 	va_end(ap);
 
@@ -5697,11 +5698,11 @@
  */
 static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t type;
+	guint16 type;
 	char *sn;
 
 	va_start(ap, fr);
-	type = (fu16_t) va_arg(ap, unsigned int);
+	type = (guint16) va_arg(ap, unsigned int);
 	sn = va_arg(ap, char *);
 	va_end(ap);
 
@@ -5719,19 +5720,19 @@
 		"limit cleared",
 	};
 	va_list ap;
-	fu16_t code, rateclass;
-	fu32_t windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
+	guint16 code, rateclass;
+	guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
 
 	va_start(ap, fr);
-	code = (fu16_t)va_arg(ap, unsigned int);
-	rateclass= (fu16_t)va_arg(ap, unsigned int);
-	windowsize = va_arg(ap, fu32_t);
-	clear = va_arg(ap, fu32_t);
-	alert = va_arg(ap, fu32_t);
-	limit = va_arg(ap, fu32_t);
-	disconnect = va_arg(ap, fu32_t);
-	currentavg = va_arg(ap, fu32_t);
-	maxavg = va_arg(ap, fu32_t);
+	code = (guint16)va_arg(ap, unsigned int);
+	rateclass= (guint16)va_arg(ap, unsigned int);
+	windowsize = va_arg(ap, guint32);
+	clear = va_arg(ap, guint32);
+	alert = va_arg(ap, guint32);
+	limit = va_arg(ap, guint32);
+	disconnect = va_arg(ap, guint32);
+	currentavg = va_arg(ap, guint32);
+	maxavg = va_arg(ap, guint32);
 	va_end(ap);
 
 	gaim_debug_misc("oscar",
@@ -5765,11 +5766,11 @@
 
 static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t newevil;
+	guint16 newevil;
 	aim_userinfo_t *userinfo;
 
 	va_start(ap, fr);
-	newevil = (fu16_t) va_arg(ap, unsigned int);
+	newevil = (guint16) va_arg(ap, unsigned int);
 	userinfo = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
@@ -5809,11 +5810,11 @@
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = gc->proto_data;
 	va_list ap;
-	fu16_t code;
+	guint16 code;
 	char *msg;
 
 	va_start(ap, fr);
-	code = (fu16_t)va_arg(ap, int);
+	code = (guint16)va_arg(ap, int);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
@@ -5879,9 +5880,9 @@
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = gc->proto_data;
 
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0003, gaim_info_change, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0005, gaim_info_change, 0);
-	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0007, gaim_account_confirm, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0003, gaim_info_change, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0005, gaim_info_change, 0);
+	aim_conn_addhandler(sess, fr->conn, OSCAR_FAMILY_ADMIN, 0x0007, gaim_account_confirm, 0);
 
 	aim_clientready(sess, fr->conn);
 	gaim_debug_info("oscar", "connected to admin\n");
@@ -5955,10 +5956,10 @@
 	GaimAccount *account = gaim_connection_get_account(gc);
 	OscarData *od = (OscarData *)gc->proto_data;
 	va_list ap;
-	fu16_t maxsiglen;
+	guint16 maxsiglen;
 
 	va_start(ap, fr);
-	maxsiglen = (fu16_t) va_arg(ap, int);
+	maxsiglen = (guint16) va_arg(ap, int);
 	va_end(ap);
 
 	gaim_debug_misc("oscar",
@@ -5978,13 +5979,13 @@
 
 static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t maxbuddies, maxwatchers;
+	guint16 maxbuddies, maxwatchers;
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = (OscarData *)gc->proto_data;
 
 	va_start(ap, fr);
-	maxbuddies = (fu16_t) va_arg(ap, unsigned int);
-	maxwatchers = (fu16_t) va_arg(ap, unsigned int);
+	maxbuddies = (guint16) va_arg(ap, unsigned int);
+	maxwatchers = (guint16) va_arg(ap, unsigned int);
 	va_end(ap);
 
 	gaim_debug_misc("oscar",
@@ -6004,15 +6005,15 @@
 	const char *message;
 	char *tmp;
 	va_list ap;
-	fu16_t maxpermits, maxdenies;
+	guint16 maxpermits, maxdenies;
 
 	gc = sess->aux_data;
 	od = (OscarData *)gc->proto_data;
 	account = gaim_connection_get_account(gc);
 
 	va_start(ap, fr);
-	maxpermits = (fu16_t) va_arg(ap, unsigned int);
-	maxdenies = (fu16_t) va_arg(ap, unsigned int);
+	maxpermits = (guint16) va_arg(ap, unsigned int);
+	maxdenies = (guint16) va_arg(ap, unsigned int);
 	va_end(ap);
 
 	gaim_debug_misc("oscar",
@@ -6229,14 +6230,14 @@
 	gchar *text;
 	va_list ap;
 	char *msg, *url;
-	fu16_t wid, hei, delay;
+	guint16 wid, hei, delay;
 
 	va_start(ap, fr);
 	msg = va_arg(ap, char *);
 	url = va_arg(ap, char *);
-	wid = (fu16_t) va_arg(ap, int);
-	hei = (fu16_t) va_arg(ap, int);
-	delay = (fu16_t) va_arg(ap, int);
+	wid = (guint16) va_arg(ap, int);
+	hei = (guint16) va_arg(ap, int);
+	delay = (guint16) va_arg(ap, int);
 	va_end(ap);
 
 	text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url);
@@ -6320,12 +6321,12 @@
 
 static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) {
 	GaimConnection *gc = sess->aux_data;
-	fu16_t status;
+	guint16 status;
 	va_list ap;
 	char msg[256];
 
 	va_start(ap, fr);
-	status = (fu16_t) va_arg(ap, unsigned int); /* status code of confirmation request */
+	status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
 	va_end(ap);
 
 	gaim_debug_info("oscar",
@@ -6343,14 +6344,14 @@
 static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) {
 	GaimConnection *gc = sess->aux_data;
 	va_list ap;
-	fu16_t perms, err;
+	guint16 perms, err;
 	char *url, *sn, *email;
 	int change;
 
 	va_start(ap, fr);
 	change = va_arg(ap, int);
-	perms = (fu16_t) va_arg(ap, unsigned int);
-	err = (fu16_t) va_arg(ap, unsigned int);
+	perms = (guint16) va_arg(ap, unsigned int);
+	err = (guint16) va_arg(ap, unsigned int);
 	url = va_arg(ap, char *);
 	sn = va_arg(ap, char *);
 	email = va_arg(ap, char *);
@@ -6651,7 +6652,7 @@
 	GaimAccount *account;
 	GaimStatus *status;
 	const gchar *status_id;
-	fu32_t data = 0x00000000;
+	guint32 data = 0x00000000;
 
 	od = gc->proto_data;
 	account = gaim_connection_get_account(gc);
@@ -6950,10 +6951,10 @@
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = gc->proto_data;
 	va_list ap;
-	fu16_t reason;
+	guint16 reason;
 
 	va_start(ap, fr);
-	reason = (fu16_t)va_arg(ap, unsigned int);
+	reason = (guint16)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	gaim_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
@@ -6983,11 +6984,11 @@
 	int i;
 	va_list ap;
 	int numtypes;
-	fu16_t *maxitems;
+	guint16 *maxitems;
 
 	va_start(ap, fr);
 	numtypes = va_arg(ap, int);
-	maxitems = va_arg(ap, fu16_t *);
+	maxitems = va_arg(ap, guint16 *);
 	va_end(ap);
 
 	gaim_debug_misc("oscar", "ssi rights:");
@@ -7018,21 +7019,21 @@
 	GaimGroup *g;
 	GaimBuddy *b;
 	struct aim_ssi_item *curitem;
-	fu32_t tmp;
+	guint32 tmp;
 	va_list ap;
-	fu16_t fmtver, numitems;
+	guint16 fmtver, numitems;
 	struct aim_ssi_item *items;
-	fu32_t timestamp;
+	guint32 timestamp;
 
 	gc = sess->aux_data;
 	od = gc->proto_data;
 	account = gaim_connection_get_account(gc);
 
 	va_start(ap, fr);
-	fmtver = (fu16_t)va_arg(ap, int);
-	numitems = (fu16_t)va_arg(ap, int);
+	fmtver = (guint16)va_arg(ap, int);
+	numitems = (guint16)va_arg(ap, int);
 	items = va_arg(ap, struct aim_ssi_item *);
-	timestamp = va_arg(ap, fu32_t);
+	timestamp = va_arg(ap, guint32);
 	va_end(ap);
 
 	/* Don't attempt to re-request our buddy list later */
@@ -7207,7 +7208,7 @@
 
 			case 0x0004: { /* Permit/deny setting */
 				if (curitem->data) {
-					fu8_t permdeny;
+					guint8 permdeny;
 					if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) {
 						gaim_debug_info("oscar",
 								   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
@@ -7264,7 +7265,7 @@
 			}
 
 			case 0x000e: { /* buddy requires authorization */
-				if ((retval->action == AIM_CB_SSI_ADD) && (retval->name))
+				if ((retval->action == OSCAR_SUBTYPE_FEEDBAG_ADD) && (retval->name))
 					gaim_auth_sendrequest(gc, retval->name);
 			} break;
 
@@ -7290,11 +7291,11 @@
 	GaimBuddy *b;
 	GaimGroup *g;
 	va_list ap;
-	fu16_t type;
+	guint16 type;
 	const char *name;
 
 	va_start(ap, fr);
-	type = (fu16_t)va_arg(ap, int);
+	type = (guint16)va_arg(ap, int);
 	name = va_arg(ap, char *);
 	va_end(ap);
 
@@ -7429,12 +7430,12 @@
 	va_list ap;
 	char *sn, *msg;
 	gchar *dialog_msg, *nombre;
-	fu8_t reply;
+	guint8 reply;
 	GaimBuddy *buddy;
 
 	va_start(ap, fr);
 	sn = va_arg(ap, char *);
-	reply = (fu8_t)va_arg(ap, int);
+	reply = (guint8)va_arg(ap, int);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
@@ -7582,7 +7583,7 @@
 	GaimConversation *conv = NULL;
 	struct chat_connection *c = NULL;
 	char *buf, *buf2;
-	fu16_t charset, charsubset;
+	guint16 charset, charsubset;
 	char *charsetstr = NULL;
 	int len;
 
@@ -7824,8 +7825,8 @@
 	GaimConnection *gc = sess->aux_data;
 	OscarData *od = gc->proto_data;
 	va_list ap;
-	fu16_t type;
-	fu8_t flags = 0, length = 0;
+	guint16 type;
+	guint8 flags = 0, length = 0;
 	guchar *md5 = NULL;
 
 	va_start(ap, fr);
@@ -8527,7 +8528,7 @@
 	GaimConnection *gc = data;
 	OscarData *od = gc->proto_data;
 	aim_session_t *sess = od->sess;
-	fu32_t presence;
+	guint32 presence;
 
 	presence = aim_ssi_getpresence(sess->ssi.local);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/oscar.h	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,1494 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Main libfaim header.  Must be included in client for prototypes/macros.
+ *
+ * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
+ *                                                -- Josh Myer
+ *
+ */
+
+#ifndef _OSCAR_H_
+#define _OSCAR_H_
+
+#include "snactypes.h"
+
+#include "debug.h"
+#include "internal.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#else
+#include "libc_interface.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef guint32 aim_snacid_t;
+typedef guint16 flap_seqnum_t;
+
+#define WIN32_STATIC
+#if defined(_WIN32) && !defined(WIN32_STATIC)
+/*
+ * For a win32 DLL, we define WIN32_INDLL if this file
+ * is included while compiling the DLL. If it's not
+ * defined (it's included in a client app), the symbols
+ * will be imported instead of exported.
+ */
+#ifdef WIN32_INDLL
+#define faim_export __declspec(dllexport)
+#else
+#define faim_export __declspec(dllimport)
+#endif /* WIN32_INDLL */
+#define faim_internal
+#else
+/*
+ * Nothing normally needed for unix...
+ */
+#define faim_export
+#define faim_internal
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define FAIM_SNAC_HASH_SIZE 16
+
+/*
+ * Current Maximum Length for Screen Names (not including NULL)
+ *
+ * Currently only names up to 16 characters can be registered
+ * however it is apparently legal for them to be larger.
+ */
+#define MAXSNLEN 97
+
+/*
+ * Current Maximum Length for Instant Messages
+ *
+ * This was found basically by experiment, but not wholly
+ * accurate experiment.  It should not be regarded
+ * as completely correct.  But its a decent approximation.
+ *
+ * Note that although we can send this much, its impossible
+ * for WinAIM clients (up through the latest (4.0.1957)) to
+ * send any more than 1kb.  Amaze all your windows friends
+ * with utterly oversized instant messages!
+ *
+ * XXX: the real limit is the total SNAC size at 8192. Fix this.
+ *
+ */
+#define MAXMSGLEN 7987
+
+/*
+ * Maximum size of a Buddy Icon.
+ */
+#define MAXICONLEN 7168
+#define AIM_ICONIDENT "AVT1picture.id"
+
+/*
+ * Current Maximum Length for Chat Room Messages
+ *
+ * This is actually defined by the protocol to be
+ * dynamic, but I have yet to see due cause to 
+ * define it dynamically here.  Maybe later.
+ *
+ */
+#define MAXCHATMSGLEN 512
+
+/**
+ * Maximum length for the password of an ICQ account
+ */
+#define MAXICQPASSLEN 8
+
+#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
+
+/*
+ * Client info.  Filled in by the client and passed in to 
+ * aim_send_login().  The information ends up getting passed to OSCAR
+ * through the initial login command.
+ *
+ */
+struct client_info_s {
+	const char *clientstring;
+	guint16 clientid;
+	guint16 major;
+	guint16 minor;
+	guint16 point;
+	guint16 build;
+	guint32 distrib;
+	const char *country; /* two-letter abbrev */
+	const char *lang; /* two-letter abbrev */
+};
+
+/* Needs to be checked */
+#define CLIENTINFO_AIM_3_5_1670 { \
+	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
+	0x0004, \
+	0x0003, 0x0005, \
+	0x0000, 0x0686, \
+	0x0000002a, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+/* Latest winaim without ssi */
+#define CLIENTINFO_AIM_4_1_2010 { \
+	"AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
+	0x0004, \
+	0x0004, 0x0001, \
+	0x0000, 0x07da, \
+	0x0000004b, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+#define CLIENTINFO_AIM_4_3_2188 { \
+	"AOL Instant Messenger (SM), version 4.3.2188/WIN32", \
+	0x0109, \
+	0x0400, 0x0003, \
+	0x0000, 0x088c, \
+	0x00000086, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+#define CLIENTINFO_AIM_4_8_2540 { \
+	"AOL Instant Messenger (SM), version 4.8.2540/WIN32", \
+	0x0109, \
+	0x0004, 0x0008, \
+	0x0000, 0x09ec, \
+	0x000000af, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+#define CLIENTINFO_AIM_5_0_2938 { \
+	"AOL Instant Messenger, version 5.0.2938/WIN32", \
+	0x0109, \
+	0x0005, 0x0000, \
+	0x0000, 0x0b7a, \
+	0x00000000, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_AIM_5_1_3036 { \
+	"AOL Instant Messenger, version 5.1.3036/WIN32", \
+	0x0109, \
+	0x0005, 0x0001, \
+	0x0000, 0x0bdc, \
+	0x000000d2, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_AIM_5_5_3415 { \
+	"AOL Instant Messenger, version 5.5.3415/WIN32", \
+	0x0109, \
+	0x0005, 0x0005, \
+	0x0000, 0x0057, \
+	0x000000ef, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_AIM_5_9_3702 { \
+	"AOL Instant Messenger, version 5.9.3702/WIN32", \
+	0x0109, \
+	0x0005, 0x0009, \
+	0x0000, 0x0e76, \
+	0x00000111, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_ICHAT_1_0 { \
+	"Apple iChat", \
+	0x311a, \
+	0x0001, 0x0000, \
+	0x0000, 0x003c, \
+	0x000000c6, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+#define CLIENTINFO_ICQ_4_65_3281 { \
+	"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \
+	0x010a, \
+	0x0004, 0x0041, \
+	0x0001, 0x0cd1, \
+	0x00000055, \
+	"us", "en", \
+}
+
+/* Needs to be checked */
+#define CLIENTINFO_ICQ_5_34_3728 { \
+	"ICQ Inc. - Product of ICQ (TM).2002a.5.34.1.3728.85", \
+	0x010a, \
+	0x0005, 0x0022, \
+	0x0001, 0x0e8f, \
+	0x00000055, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_ICQ_5_45_3777 { \
+	"ICQ Inc. - Product of ICQ (TM).2003a.5.45.1.3777.85", \
+	0x010a, \
+	0x0005, 0x002d, \
+	0x0001, 0x0ec1, \
+	0x00000055, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_ICQBASIC_14_3_1068 { \
+	"ICQBasic", \
+	0x010a, \
+	0x0014, 0x0003, \
+	0x0000, 0x042c, \
+	0x0000043d, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_NETSCAPE_7_0_1 { \
+	"Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
+	0x1d0d, \
+	0x0007, 0x0000, \
+	0x0001, 0x0000, \
+	0x00000058, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_GAIM { \
+	"Gaim/" VERSION, \
+	0x0109, \
+	0x0005, 0x0001, \
+	0x0000, 0x0bdc, \
+	0x000000d2, \
+	"us", "en", \
+}
+
+#define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
+#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777
+
+/*
+ * These could be arbitrary, but its easier to use the actual AIM values
+ */
+#define AIM_CONN_TYPE_BOS		0x0002
+#define AIM_CONN_TYPE_ADS		0x0005
+#define AIM_CONN_TYPE_AUTH		0x0007
+#define AIM_CONN_TYPE_CHATNAV	0x000d
+#define AIM_CONN_TYPE_CHAT		0x000e
+#define AIM_CONN_TYPE_SEARCH	0x000f
+#define AIM_CONN_TYPE_ICON		0x0010
+#define AIM_CONN_TYPE_EMAIL		0x0018
+
+/* they start getting arbitrary for rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS_PROXY	0xfffd /* these speak a strange language */
+#define AIM_CONN_TYPE_RENDEZVOUS	0xfffe /* these do not speak FLAP! */
+#define AIM_CONN_TYPE_LISTENER		0xffff /* socket waiting for accept() */
+
+/* Command types for doing a rendezvous proxy login
+ * Thanks to Keith Lea and the Joust project for documenting these commands well */
+#define AIM_RV_PROXY_PACKETVER_DFLT	0x044a
+#define AIM_RV_PROXY_ERROR		0x0001
+#define AIM_RV_PROXY_INIT_SEND		0x0002 /* First command sent when creating a connection */
+#define AIM_RV_PROXY_INIT_RECV		0x0004 /* First command sent when receiving existing connection */
+#define AIM_RV_PROXY_ACK		0x0003
+#define AIM_RV_PROXY_READY		0x0005
+
+/* Number of bytes expected in each of the above packets, including the 2 bytes specifying length */
+#define AIM_RV_PROXY_ERROR_LEN		14
+#define AIM_RV_PROXY_INIT_SEND_LEN	55
+#define AIM_RV_PROXY_INIT_RECV_LEN	57
+#define AIM_RV_PROXY_ACK_LEN		18
+#define AIM_RV_PROXY_READY_LEN		12
+#define AIM_RV_PROXY_HDR_LEN		12	/* Bytes in just the header alone */
+
+/* Default values for unknown/unused values in rendezvous proxy negotiation packets */
+#define AIM_RV_PROXY_SERVER_FLAGS	0x0220		/* Default flags sent by proxy server */
+#define AIM_RV_PROXY_CLIENT_FLAGS	0x0000		/* Default flags sent by client */
+#define AIM_RV_PROXY_UNKNOWNA_DFLT	0x00000000	/* Default value for an unknown block */
+#define AIM_RV_PROXY_SERVER_URL		"ars.oscar.aol.com"
+#define AIM_RV_PROXY_CONNECT_PORT	5190		/* The port we should always connect to */
+
+/* What is the purpose of this transfer? (Who will end up with a new file?)
+ * These values are used in oft_info->send_or_recv */
+#define AIM_XFER_SEND			0x0001
+#define AIM_XFER_RECV			0x0002
+
+/* Via what method is the data getting routed?
+ * These values are used in oft_info->method */
+#define AIM_XFER_DIRECT			0x0001	/* Direct connection; receiver connects to sender */
+#define AIM_XFER_REDIR			0x0002	/* Redirected connection; sender connects to receiver */
+#define AIM_XFER_PROXY			0x0003	/* Proxied connection */
+
+/* Who requested the proxy?
+ * The difference between a stage 2 and stage 3 proxied transfer is that the receiver does the
+ * initial login for a stage 2, but the sender must do it for a stage 3.
+ * These values are used in oft_info->stage */
+#define AIM_XFER_PROXY_NONE		0x0001
+#define AIM_XFER_PROXY_STG1		0x0002	/* Sender requested a proxy be used (stage1) */
+#define AIM_XFER_PROXY_STG2		0x0003	/* Receiver requested a proxy be used (stage2) */
+#define AIM_XFER_PROXY_STG3		0x0004	/* Receiver requested a proxy be used (stage3) */
+
+/*
+ * Subtypes, we need these for OFT stuff.
+ */
+#define AIM_CONN_SUBTYPE_OFT_DIRECTIM	0x0001
+#define AIM_CONN_SUBTYPE_OFT_GETFILE	0x0002
+#define AIM_CONN_SUBTYPE_OFT_SENDFILE	0x0003
+#define AIM_CONN_SUBTYPE_OFT_BUDDYICON	0x0004
+#define AIM_CONN_SUBTYPE_OFT_VOICE	0x0005
+
+/*
+ * Status values returned from aim_conn_new().  ORed together.
+ */
+#define AIM_CONN_STATUS_READY		0x0001
+#define AIM_CONN_STATUS_INTERNALERR	0x0002
+#define AIM_CONN_STATUS_RESOLVERR	0x0040
+#define AIM_CONN_STATUS_CONNERR		0x0080
+#define AIM_CONN_STATUS_INPROGRESS	0x0100
+
+#define AIM_FRAMETYPE_FLAP		0x0000
+#define AIM_FRAMETYPE_OFT		0x0001
+
+typedef struct aim_conn_s {
+	int fd;
+	guint16 type;
+	guint16 subtype;
+	flap_seqnum_t seqnum;
+	guint32 status;
+	void *priv; /* misc data the client may want to store */
+	void *internal; /* internal conn-specific libfaim data */
+	time_t lastactivity; /* time of last transmit */
+	int forcedlatency;
+	void *handlerlist;
+	void *sessv; /* pointer to parent session */
+	void *inside; /* only accessible from inside libfaim */
+	struct aim_conn_s *next;
+} aim_conn_t;
+
+/*
+ * Byte Stream type. Sort of.
+ *
+ * Use of this type serves a couple purposes:
+ *   - Buffer/buflen pairs are passed all around everywhere. This turns
+ *     that into one value, as well as abstracting it slightly.
+ *   - Through the abstraction, it is possible to enable bounds checking
+ *     for robustness at the cost of performance.  But a clean failure on
+ *     weird packets is much better than a segfault.
+ *   - I like having variables named "bs".
+ *
+ * Don't touch the insides of this struct.  Or I'll have to kill you.
+ *
+ */
+typedef struct aim_bstream_s {
+	guint8 *data;
+	guint32 len;
+	guint32 offset;
+} aim_bstream_t;
+
+typedef struct aim_frame_s {
+	guint8 hdrtype; /* defines which piece of the union to use */
+	union {
+		struct {
+			guint8 channel;
+			flap_seqnum_t seqnum;
+		} flap;
+		struct {
+			guint8 magic[4];	/* ODC2 or OFT2 */
+			guint16 hdrlen;
+			guint16 type;
+		} rend;
+	} hdr;
+	aim_bstream_t data;		/* payload stream */
+	aim_conn_t *conn;		/* the connection it came in on/is going out on */
+	guint8 handled;			/* 0 = new, !0 = been handled */
+	struct aim_frame_s *next;
+} aim_frame_t;
+
+typedef struct aim_msgcookie_s {
+	guchar cookie[8];
+	int type;
+	void *data;
+	time_t addtime;
+	struct aim_msgcookie_s *next;
+} aim_msgcookie_t;
+
+/*
+ * AIM Session: The main client-data interface.
+ *
+ */
+typedef struct aim_session_s {
+
+	/* ---- Client Accessible ------------------------ */
+
+	/* Our screen name. */
+	char sn[MAXSNLEN+1];
+
+	/*
+	 * Pointer to anything the client wants to 
+	 * explicitly associate with this session.
+	 *
+	 * This is for use in the callbacks mainly. In any
+	 * callback, you can access this with sess->aux_data.
+	 *
+	 */
+	void *aux_data;
+
+	/* ---- Internal Use Only ------------------------ */
+
+	/* Connection information */
+	aim_conn_t *connlist;
+
+	/*
+	 * Transmit/receive queues.
+	 *
+	 * These are only used when you don't use your own lowlevel
+	 * I/O.  I don't suggest that you use libfaim's internal I/O.
+	 * Its really bad and the API/event model is quirky at best.
+	 *  
+	 */
+	aim_frame_t *queue_outgoing;
+	aim_frame_t *queue_incoming;
+
+	/*
+	 * Tx Enqueuing function.
+	 *
+	 * This is how you override the transmit direction of libfaim's
+	 * internal I/O.  This function will be called whenever it needs
+	 * to send something.
+	 *
+	 */
+	int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *);
+
+	void *modlistv;
+
+	struct {
+		char server[128];
+		char username[128];
+		char password[128];
+	} socksproxy;
+
+	guint8 nonblocking;
+
+	/*
+	 * Outstanding snac handling
+	 *
+	 * XXX: Should these be per-connection? -mid
+	 */
+	void *snac_hash[FAIM_SNAC_HASH_SIZE];
+	aim_snacid_t snacid_next;
+
+	aim_msgcookie_t *msgcookies;
+	struct aim_icq_info *icq_info;
+	struct aim_oft_info *oft_info;
+	struct aim_authresp_info *authinfo;
+	struct aim_emailinfo *emailinfo;
+
+	struct {
+		struct aim_userinfo_s *userinfo;
+		struct userinfo_node *torequest;
+		struct userinfo_node *requested;
+		int waiting_for_response;
+	} locate;
+
+	/* Server-stored information (ssi) */
+	struct {
+		int received_data;
+		guint16 numitems;
+		struct aim_ssi_item *official;
+		struct aim_ssi_item *local;
+		struct aim_ssi_tmp *pending;
+		time_t timestamp;
+		int waiting_for_ack;
+	} ssi;
+} aim_session_t;
+
+/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
+#define AIM_ICQ_STATE_NORMAL		0x00000000
+#define AIM_ICQ_STATE_AWAY		0x00000001
+#define AIM_ICQ_STATE_DND		0x00000002
+#define AIM_ICQ_STATE_OUT		0x00000004
+#define AIM_ICQ_STATE_BUSY		0x00000010
+#define AIM_ICQ_STATE_CHAT		0x00000020
+#define AIM_ICQ_STATE_INVISIBLE		0x00000100
+#define AIM_ICQ_STATE_WEBAWARE		0x00010000
+#define AIM_ICQ_STATE_HIDEIP		0x00020000
+#define AIM_ICQ_STATE_BIRTHDAY		0x00080000
+#define AIM_ICQ_STATE_DIRECTDISABLED	0x00100000
+#define AIM_ICQ_STATE_ICQHOMEPAGE	0x00200000
+#define AIM_ICQ_STATE_DIRECTREQUIREAUTH	0x10000000
+#define AIM_ICQ_STATE_DIRECTCONTACTLIST	0x20000000
+
+/*
+ * Get command from connections
+ *
+ * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction.
+ * XXX Make this easily overridable.
+ *
+ */
+faim_export int aim_get_command(aim_session_t *, aim_conn_t *);
+
+/*
+ * Dispatch commands that are in the rx queue.
+ */
+faim_export void aim_rxdispatch(aim_session_t *);
+
+faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn);
+
+faim_export int aim_logoff(aim_session_t *);
+
+/* the library should never call aim_conn_kill */
+faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
+
+typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...);
+
+
+/* auth.c */
+struct aim_clientrelease {
+	char *name;
+	guint32 build;
+	char *url;
+	char *info;
+};
+
+struct aim_authresp_info {
+	char *sn;
+	guint16 errorcode;
+	char *errorurl;
+	guint16 regstatus;
+	char *email;
+	char *bosip;
+	guint16 cookielen;
+	guint8 *cookie;
+	char *chpassurl;
+	struct aim_clientrelease latestrelease;
+	struct aim_clientrelease latestbeta;
+};
+
+/* Callback data for redirect. */
+struct aim_redirect_data {
+	guint16 group;
+	const char *ip;
+	guint16 cookielen;
+	const guint8 *cookie;
+	struct { /* group == AIM_CONN_TYPE_CHAT */
+		guint16 exchange;
+		const char *room;
+		guint16 instance;
+	} chat;
+};
+
+faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+faim_export int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key);
+/* 0x000b */ faim_export int aim_auth_securid_send(aim_session_t *sess, const char *securid);
+
+faim_export void aim_purge_rxqueue(aim_session_t *);
+faim_export void aim_cleansnacs(aim_session_t *, int maxage);
+
+#define AIM_TX_QUEUED    0 /* default */
+#define AIM_TX_IMMEDIATE 1
+#define AIM_TX_USER      2
+faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *));
+
+faim_export int aim_tx_flushqueue(aim_session_t *);
+faim_export void aim_tx_purgequeue(aim_session_t *);
+
+faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval);
+
+faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags);
+faim_export int aim_clearhandlers(aim_conn_t *conn);
+
+faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group);
+faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn);
+faim_export void aim_conn_close(aim_conn_t *deadconn);
+faim_export aim_conn_t *aim_newconn(aim_session_t *, int type);
+faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_conn_isready(aim_conn_t *);
+faim_export int aim_conn_setstatus(aim_conn_t *, int);
+faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_conn_isconnecting(aim_conn_t *conn);
+
+faim_export void aim_session_init(aim_session_t *, guint8 nonblocking);
+faim_export void aim_session_kill(aim_session_t *);
+faim_export aim_conn_t *aim_getconn_type(aim_session_t *, int type);
+faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *, int type);
+faim_export aim_conn_t *aim_getconn_fd(aim_session_t *, int fd);
+
+/* 0x0001 - service.c */
+faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg);
+faim_export int aim_srv_setidle(aim_session_t *sess, guint32 idletime);
+
+/* misc.c */
+
+#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
+#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
+#define AIM_VISIBILITYCHANGE_DENYADD      0x07
+#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
+
+#define AIM_PRIVFLAGS_ALLOWIDLE           0x01
+#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE    0x02
+
+#define AIM_WARN_ANON                     0x01
+
+faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_nop(aim_session_t *, aim_conn_t *);
+faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *);
+faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, guint32 mask);
+faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, guint32);
+faim_export int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *);
+faim_export int aim_reqservice(aim_session_t *, aim_conn_t *, guint16);
+faim_export int aim_bos_reqrights(aim_session_t *, aim_conn_t *);
+faim_export int aim_setextstatus(aim_session_t *sess, guint32 status);
+
+#define AIM_CLIENTTYPE_UNKNOWN  0x0000
+#define AIM_CLIENTTYPE_MC       0x0001
+#define AIM_CLIENTTYPE_WINAIM   0x0002
+#define AIM_CLIENTTYPE_WINAIM41 0x0003
+#define AIM_CLIENTTYPE_AOL_TOC  0x0004
+faim_export guint16 aim_im_fingerprint(const guint8 *msghdr, int len);
+
+#define AIM_RATE_CODE_CHANGE     0x0001
+#define AIM_RATE_CODE_WARNING    0x0002
+#define AIM_RATE_CODE_LIMIT      0x0003
+#define AIM_RATE_CODE_CLEARLIMIT 0x0004
+faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
+
+
+
+/* im.c */
+#define AIM_OFT_SUBTYPE_SEND_FILE	0x0001
+#define AIM_OFT_SUBTYPE_SEND_DIR	0x0002
+#define AIM_OFT_SUBTYPE_GET_FILE	0x0011
+#define AIM_OPT_SUBTYPE_GET_LIST	0x0012
+
+#define AIM_TRANSFER_DENY_NOTSUPPORTED	0x0000
+#define AIM_TRANSFER_DENY_DECLINE	0x0001
+#define AIM_TRANSFER_DENY_NOTACCEPTING	0x0002
+
+#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
+#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
+
+/* This is what the server will give you if you don't set them yourself. */
+#define AIM_IMPARAM_DEFAULTS { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	512, /* !! Note how small this is. */ \
+	(99.9)*10, (99.9)*10, \
+	1000 /* !! And how large this is. */ \
+}
+
+/* This is what most AIM versions use. */
+#define AIM_IMPARAM_REASONABLE { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	8000, \
+	(99.9)*10, (99.9)*10, \
+	0 \
+}
+
+struct aim_icbmparameters {
+	guint16 maxchan;
+	guint32 flags; /* AIM_IMPARAM_FLAG_ */
+	guint16 maxmsglen; /* message size that you will accept */
+	guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
+	guint16 maxrecverwarn;
+	guint32 minmsginterval; /* in milliseconds? */
+};
+
+struct aim_chat_roominfo {
+	guint16 exchange;
+	char *name;
+	guint16 instance;
+};
+
+#define AIM_IMFLAGS_AWAY				0x0001 /* mark as an autoreply */
+#define AIM_IMFLAGS_ACK					0x0002 /* request a receipt notice */
+#define AIM_IMFLAGS_BUDDYREQ			0x0010 /* buddy icon requested */
+#define AIM_IMFLAGS_HASICON				0x0020 /* already has icon */
+#define AIM_IMFLAGS_SUBENC_MACINTOSH	0x0040 /* damn that Steve Jobs! */
+#define AIM_IMFLAGS_CUSTOMFEATURES		0x0080 /* features field present */
+#define AIM_IMFLAGS_EXTDATA				0x0100
+#define AIM_IMFLAGS_X					0x0200
+#define AIM_IMFLAGS_MULTIPART			0x0400 /* ->mpmsg section valid */
+#define AIM_IMFLAGS_OFFLINE				0x0800 /* send to offline user */
+#define AIM_IMFLAGS_TYPINGNOT			0x1000 /* typing notification */
+
+#define AIM_CHARSET_ASCII		0x0000
+#define AIM_CHARSET_UNICODE	0x0002 /* UCS-2BE */
+#define AIM_CHARSET_CUSTOM	0x0003
+
+/*
+ * Multipart message structures.
+ */
+typedef struct aim_mpmsg_section_s {
+	guint16 charset;
+	guint16 charsubset;
+	gchar *data;
+	guint16 datalen;
+	struct aim_mpmsg_section_s *next;
+} aim_mpmsg_section_t;
+
+typedef struct aim_mpmsg_s {
+	unsigned int numparts;
+	aim_mpmsg_section_t *parts;
+} aim_mpmsg_t;
+
+faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm);
+faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const gchar *data, guint16 datalen);
+faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii);
+faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen);
+faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm);
+
+/*
+ * Arguments to aim_send_im_ext().
+ *
+ * This is really complicated.  But immensely versatile.
+ *
+ */
+struct aim_sendimext_args {
+
+	/* These are _required_ */
+	const char *destsn;
+	guint32 flags; /* often 0 */
+
+	/* Only required if not using multipart messages */
+	const char *msg;
+	int msglen;
+
+	/* Required if ->msg is not provided */
+	aim_mpmsg_t *mpmsg;
+
+	/* Only used if AIM_IMFLAGS_HASICON is set */
+	guint32 iconlen;
+	time_t iconstamp;
+	guint32 iconsum;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
+	guint16 featureslen;
+	guint8 *features;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
+	guint16 charset;
+	guint16 charsubset;
+};
+
+/*
+ * Arguments to aim_send_rtfmsg().
+ */
+struct aim_sendrtfmsg_args {
+	const char *destsn;
+	guint32 fgcolor;
+	guint32 bgcolor;
+	const char *rtfmsg; /* must be in RTF */
+};
+
+/*
+ * This information is provided in the Incoming ICBM callback for
+ * Channel 1 ICBM's.  
+ *
+ * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
+ * are optional, both are always set by the current libfaim code.
+ * That may or may not change in the future.  It is mainly for
+ * consistency with aim_sendimext_args.
+ *
+ * Multipart messages require some explanation. If you want to use them,
+ * I suggest you read all the comments in im.c.
+ *
+ */
+struct aim_incomingim_ch1_args {
+
+	/* Always provided */
+	aim_mpmsg_t mpmsg;
+	guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
+	
+	/* Only provided if message has a human-readable section */
+	gchar *msg;
+	int msglen;
+
+	/* Only provided if AIM_IMFLAGS_HASICON is set */
+	time_t iconstamp;
+	guint32 iconlen;
+	guint16 iconsum;
+
+	/* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
+	guint8 *features;
+	guint8 featureslen;
+
+	/* Only provided if AIM_IMFLAGS_EXTDATA is set */
+	guint8 extdatalen;
+	guint8 *extdata;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
+	guint16 charset;
+	guint16 charsubset;
+};
+
+/* Valid values for channel 2 args->status */
+#define AIM_RENDEZVOUS_PROPOSE	0x0000
+#define AIM_RENDEZVOUS_CANCEL	0x0001
+#define AIM_RENDEZVOUS_ACCEPT	0x0002
+
+struct aim_incomingim_ch2_args {
+	guint16 status;
+	guchar cookie[8];
+	int reqclass;
+	const char *proxyip;
+	const char *clientip;
+	const char *verifiedip;
+	guint16 port;
+	guint16 errorcode;
+	const char *msg; /* invite message or file description */
+	guint16 msglen;
+	const char *encoding;
+	const char *language;
+	union {
+		struct {
+			guint32 checksum;
+			guint32 length;
+			time_t timestamp;
+			guint8 *icon;
+		} icon;
+		struct {
+			struct aim_chat_roominfo roominfo;
+		} chat;
+		struct {
+			guint16 msgtype;
+			guint32 fgcolor;
+			guint32 bgcolor;
+			const char *rtfmsg;
+		} rtfmsg;
+		struct {
+			guint16 subtype;
+			guint16 totfiles;
+			guint32 totsize;
+			char *filename;
+			/* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */
+			guint16 reqnum;
+			guint8 use_proxy; /* Did the user request that we use a rv proxy? */
+		} sendfile;
+	} info;
+	void *destructor; /* used internally only */
+};
+
+/* Valid values for channel 4 args->type */
+#define AIM_ICQMSG_AUTHREQUEST	0x0006
+#define AIM_ICQMSG_AUTHDENIED	0x0007
+#define AIM_ICQMSG_AUTHGRANTED	0x0008
+
+struct aim_incomingim_ch4_args {
+	guint32 uin; /* Of the sender of the ICBM */
+	guint8 type;
+	guint8 flags;
+	gchar *msg; /* Reason for auth request, deny, or accept */
+	int msglen;
+};
+
+/* SNAC sending functions */
+/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params);
+/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess);
+/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args);
+/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, guint16 flags, const char *msg);
+/* 0x0006 */ faim_export int aim_im_sendch2_chatinvite(aim_session_t *sess, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
+/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
+/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
+/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, guchar *cookie, gboolean usecookie, const char *sn, const guint8 *ip, guint16 port);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *info);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info);
+/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type);
+/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, const char *sn, guint16 type, const char *message);
+/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags);
+/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const guchar *cookie, guint16 code);
+/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2);
+faim_export void aim_icbm_makecookie(guchar* cookie);
+
+
+/* 0x0002 - locate.c */
+/*
+ * AIM User Info, Standard Form.
+ */
+#define AIM_FLAG_UNCONFIRMED	0x0001 /* "damned transients" */
+#define AIM_FLAG_ADMINISTRATOR	0x0002
+#define AIM_FLAG_AOL			0x0004
+#define AIM_FLAG_OSCAR_PAY		0x0008
+#define AIM_FLAG_FREE 			0x0010
+#define AIM_FLAG_AWAY			0x0020
+#define AIM_FLAG_ICQ			0x0040
+#define AIM_FLAG_WIRELESS		0x0080
+#define AIM_FLAG_UNKNOWN100		0x0100
+#define AIM_FLAG_UNKNOWN200		0x0200
+#define AIM_FLAG_ACTIVEBUDDY	0x0400
+#define AIM_FLAG_UNKNOWN800		0x0800
+#define AIM_FLAG_ABINTERNAL		0x1000
+#define AIM_FLAG_ALLUSERS		0x001f
+
+#define AIM_USERINFO_PRESENT_FLAGS        0x00000001
+#define AIM_USERINFO_PRESENT_MEMBERSINCE  0x00000002
+#define AIM_USERINFO_PRESENT_ONLINESINCE  0x00000004
+#define AIM_USERINFO_PRESENT_IDLE         0x00000008
+#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
+#define AIM_USERINFO_PRESENT_ICQIPADDR    0x00000020
+#define AIM_USERINFO_PRESENT_ICQDATA      0x00000040
+#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
+#define AIM_USERINFO_PRESENT_SESSIONLEN   0x00000100
+#define AIM_USERINFO_PRESENT_CREATETIME   0x00000200
+
+struct userinfo_node {
+	char *sn;
+	struct userinfo_node *next;
+};
+
+typedef struct aim_userinfo_s {
+	char *sn;
+	guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */
+	guint16 idletime; /* in seconds */
+	guint16 flags;
+	guint32 createtime; /* time_t */
+	guint32 membersince; /* time_t */
+	guint32 onlinesince; /* time_t */
+	guint32 sessionlen;  /* in seconds */
+	guint32 capabilities;
+	struct {
+		guint32 status;
+		guint32 ipaddr;
+		guint8 crap[0x25]; /* until we figure it out... */
+	} icqinfo;
+	guint32 present;
+
+	guint8 iconcsumtype;
+	guint16 iconcsumlen;
+	guint8 *iconcsum;
+
+	char *info;
+	char *info_encoding;
+	guint16 info_len;
+
+	char *status;
+	char *status_encoding;
+	guint16 status_len;
+
+	char *away;
+	char *away_encoding;
+	guint16 away_len;
+
+	struct aim_userinfo_s *next;
+} aim_userinfo_t;
+
+#define AIM_CAPS_BUDDYICON		0x00000001
+#define AIM_CAPS_TALK			0x00000002
+#define AIM_CAPS_DIRECTIM		0x00000004
+#define AIM_CAPS_CHAT			0x00000008
+#define AIM_CAPS_GETFILE		0x00000010
+#define AIM_CAPS_SENDFILE		0x00000020
+#define AIM_CAPS_GAMES			0x00000040
+#define AIM_CAPS_ADDINS			0x00000080
+#define AIM_CAPS_SENDBUDDYLIST	0x00000100
+#define AIM_CAPS_GAMES2			0x00000200
+#define AIM_CAPS_ICQ_DIRECT		0x00000400
+#define AIM_CAPS_APINFO			0x00000800
+#define AIM_CAPS_ICQRTF			0x00001000
+#define AIM_CAPS_EMPTY			0x00002000
+#define AIM_CAPS_ICQSERVERRELAY	0x00004000
+#define AIM_CAPS_ICQUTF8OLD		0x00008000
+#define AIM_CAPS_TRILLIANCRYPT	0x00010000
+#define AIM_CAPS_ICQUTF8		0x00020000
+#define AIM_CAPS_INTEROPERATE	0x00040000
+#define AIM_CAPS_ICHAT			0x00080000
+#define AIM_CAPS_HIPTOP			0x00100000
+#define AIM_CAPS_SECUREIM		0x00200000
+#define AIM_CAPS_SMS			0x00400000
+#define AIM_CAPS_GENERICUNKNOWN	0x00800000
+#define AIM_CAPS_VIDEO			0x01000000
+#define AIM_CAPS_ICHATAV		0x02000000
+#define AIM_CAPS_LIVEVIDEO		0x04000000
+#define AIM_CAPS_CAMERA			0x08000000
+#define AIM_CAPS_LAST			0x10000000
+
+#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST  0
+#define AIM_SENDMEMBLOCK_FLAG_ISHASH     1
+
+faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag);
+
+struct aim_invite_priv {
+	char *sn;
+	char *roomname;
+	guint16 exchange;
+	guint16 instance;
+};
+
+#define AIM_COOKIETYPE_UNKNOWN  0x00
+#define AIM_COOKIETYPE_ICBM     0x01
+#define AIM_COOKIETYPE_ADS      0x02
+#define AIM_COOKIETYPE_BOS      0x03
+#define AIM_COOKIETYPE_IM       0x04
+#define AIM_COOKIETYPE_CHAT     0x05
+#define AIM_COOKIETYPE_CHATNAV  0x06
+#define AIM_COOKIETYPE_INVITE   0x07
+/* we'll move OFT up a bit to give breathing room.  not like it really
+ * matters. */
+#define AIM_COOKIETYPE_OFTIM    0x10
+#define AIM_COOKIETYPE_OFTGET   0x11
+#define AIM_COOKIETYPE_OFTSEND  0x12
+#define AIM_COOKIETYPE_OFTVOICE 0x13
+#define AIM_COOKIETYPE_OFTIMAGE 0x14
+#define AIM_COOKIETYPE_OFTICON  0x15
+
+faim_export aim_userinfo_t *aim_locate_finduserinfo(aim_session_t *sess, const char *sn);
+faim_export void aim_locate_dorequest(aim_session_t *sess);
+
+/* 0x0002 */ faim_export int aim_locate_reqrights(aim_session_t *sess);
+/* 0x0004 */ faim_export int aim_locate_setcaps(aim_session_t *sess, guint32 caps);
+/* 0x0004 */ faim_export int aim_locate_setprofile(aim_session_t *sess, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
+/* 0x0005 */ faim_export int aim_locate_getinfo(aim_session_t *sess, const char *, guint16);
+/* 0x0009 */ faim_export int aim_locate_setdirinfo(aim_session_t *sess, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy);
+/* 0x000b */ faim_export int aim_locate_000b(aim_session_t *sess, const char *sn);
+/* 0x000f */ faim_export int aim_locate_setinterests(aim_session_t *sess, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy);
+/* 0x0015 */ faim_export int aim_locate_getinfoshort(aim_session_t *sess, const char *sn, guint32 flags);
+
+
+
+/* 0x0003 - buddylist.c */
+/* 0x0002 */ faim_export int aim_buddylist_reqrights(aim_session_t *, aim_conn_t *);
+/* 0x0004 */ faim_export int aim_buddylist_set(aim_session_t *, aim_conn_t *, const char *);
+/* 0x0004 */ faim_export int aim_buddylist_addbuddy(aim_session_t *, aim_conn_t *, const char *);
+/* 0x0005 */ faim_export int aim_buddylist_removebuddy(aim_session_t *, aim_conn_t *, const char *);
+/* 0x000b */ faim_export int aim_buddylist_oncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info);
+/* 0x000c */ faim_export int aim_buddylist_offgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+
+
+
+/* 0x000a - search.c */
+faim_export int aim_search_address(aim_session_t *, aim_conn_t *, const char *);
+
+
+
+/* 0x000d - chatnav.c */
+/* 0x000e - chat.c */
+/* These apply to exchanges as well. */
+#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
+#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
+#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
+#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
+
+struct aim_chat_exchangeinfo {
+	guint16 number;
+	guint16 flags;
+	char *name;
+	char *charset1;
+	char *lang1;
+	char *charset2;
+	char *lang2;
+};
+
+#define AIM_CHATFLAGS_NOREFLECT 0x0001
+#define AIM_CHATFLAGS_AWAY      0x0002
+faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language);
+faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+faim_export int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+faim_export char *aim_chat_getname(aim_conn_t *conn);
+faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name);
+
+faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn);
+
+faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange);
+faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name);
+
+
+
+/* 0x000f - odir.c */
+struct aim_odir {
+	char *first;
+	char *last;
+	char *middle;
+	char *maiden;
+	char *email;
+	char *country;
+	char *state;
+	char *city;
+	char *sn;
+	char *interest;
+	char *nick;
+	char *zip;
+	char *region;
+	char *address;
+	struct aim_odir *next;
+};
+
+faim_export int aim_odir_email(aim_session_t *, const char *, const char *);
+faim_export int aim_odir_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *);
+faim_export int aim_odir_interest(aim_session_t *, const char *, const char *);
+
+
+
+/* 0x0010 - icon.c */
+faim_export int aim_bart_upload(aim_session_t *sess, const guint8 *icon, guint16 iconlen);
+faim_export int aim_bart_request(aim_session_t *sess, const char *sn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen);
+
+
+
+/* 0x0013 - ssi.c */
+#define AIM_SSI_TYPE_BUDDY		0x0000
+#define AIM_SSI_TYPE_GROUP		0x0001
+#define AIM_SSI_TYPE_PERMIT		0x0002
+#define AIM_SSI_TYPE_DENY		0x0003
+#define AIM_SSI_TYPE_PDINFO		0x0004
+#define AIM_SSI_TYPE_PRESENCEPREFS	0x0005
+#define AIM_SSI_TYPE_ICONINFO		0x0014
+
+#define AIM_SSI_ACK_SUCCESS		0x0000
+#define AIM_SSI_ACK_ITEMNOTFOUND	0x0002
+#define AIM_SSI_ACK_IDNUMINUSE		0x000a
+#define AIM_SSI_ACK_ATMAX		0x000c
+#define AIM_SSI_ACK_INVALIDNAME		0x000d
+#define AIM_SSI_ACK_AUTHREQUIRED	0x000e
+
+/* These flags are set in the 0x00c9 TLV of SSI teyp 0x0005 */
+#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE        0x00000400
+#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000
+
+struct aim_ssi_item {
+	char *name;
+	guint16 gid;
+	guint16 bid;
+	guint16 type;
+	struct aim_tlvlist_s *data;
+	struct aim_ssi_item *next;
+};
+
+struct aim_ssi_tmp {
+	guint16 action;
+	guint16 ack;
+	char *name;
+	struct aim_ssi_item *item;
+	struct aim_ssi_tmp *next;
+};
+
+/* These build the actual SNACs and queue them to be sent */
+/* 0x0002 */ faim_export int aim_ssi_reqrights(aim_session_t *sess);
+/* 0x0004 */ faim_export int aim_ssi_reqdata(aim_session_t *sess);
+/* 0x0005 */ faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t localstamp, guint16 localrev);
+/* 0x0007 */ faim_export int aim_ssi_enable(aim_session_t *sess);
+/* 0x0008 */ faim_export int aim_ssi_addmoddel(aim_session_t *sess);
+/* 0x0011 */ faim_export int aim_ssi_modbegin(aim_session_t *sess);
+/* 0x0012 */ faim_export int aim_ssi_modend(aim_session_t *sess);
+/* 0x0014 */ faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg);
+/* 0x0018 */ faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg);
+/* 0x001a */ faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, guint8 reply, const char *msg);
+
+/* Client functions for retrieving SSI data */
+faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
+faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type);
+faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn);
+faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn);
+faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list);
+faim_export guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
+faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn);
+faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn);
+faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn);
+
+/* Client functions for changing SSI data */
+faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth);
+faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name);
+faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name);
+faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group);
+faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name);
+faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name);
+faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn);
+faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias);
+faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *alias);
+faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn);
+faim_export int aim_ssi_cleanlist(aim_session_t *sess);
+faim_export int aim_ssi_deletelist(aim_session_t *sess);
+faim_export int aim_ssi_setpermdeny(aim_session_t *sess, guint8 permdeny, guint32 vismask);
+faim_export int aim_ssi_setpresence(aim_session_t *sess, guint32 presence);
+faim_export int aim_ssi_seticon(aim_session_t *sess, guint8 *iconsum, guint16 iconsumlen);
+faim_export int aim_ssi_delicon(aim_session_t *sess);
+
+
+
+/* 0x0015 - icq.c */
+#define AIM_ICQ_INFO_SIMPLE	0x001
+#define AIM_ICQ_INFO_SUMMARY	0x002
+#define AIM_ICQ_INFO_EMAIL	0x004
+#define AIM_ICQ_INFO_PERSONAL	0x008
+#define AIM_ICQ_INFO_ADDITIONAL	0x010
+#define AIM_ICQ_INFO_WORK	0x020
+#define AIM_ICQ_INFO_INTERESTS	0x040
+#define AIM_ICQ_INFO_ORGS	0x080
+#define AIM_ICQ_INFO_UNKNOWN	0x100
+#define AIM_ICQ_INFO_HAVEALL	0x1ff
+
+struct aim_icq_offlinemsg {
+	guint32 sender;
+	guint16 year;
+	guint8 month, day, hour, minute;
+	guint8 type;
+	guint8 flags;
+	char *msg;
+	int msglen;
+};
+
+struct aim_icq_info {
+	guint16 reqid;
+
+	/* simple */
+	guint32 uin;
+
+	/* general and "home" information (0x00c8) */
+	char *nick;
+	char *first;
+	char *last;
+	char *email;
+	char *homecity;
+	char *homestate;
+	char *homephone;
+	char *homefax;
+	char *homeaddr;
+	char *mobile;
+	char *homezip;
+	guint16 homecountry;
+/*	guint8 timezone;
+	guint8 hideemail; */
+
+	/* personal (0x00dc) */
+	guint8 age;
+	guint8 unknown;
+	guint8 gender;
+	char *personalwebpage;
+	guint16 birthyear;
+	guint8 birthmonth;
+	guint8 birthday;
+	guint8 language1;
+	guint8 language2;
+	guint8 language3;
+
+	/* work (0x00d2) */
+	char *workcity;
+	char *workstate;
+	char *workphone;
+	char *workfax;
+	char *workaddr;
+	char *workzip;
+	guint16 workcountry;
+	char *workcompany;
+	char *workdivision;
+	char *workposition;
+	char *workwebpage;
+
+	/* additional personal information (0x00e6) */
+	char *info;
+
+	/* email (0x00eb) */
+	guint16 numaddresses;
+	char **email2;
+
+	/* we keep track of these in a linked list because we're 1337 */
+	struct aim_icq_info *next;
+};
+
+faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess);
+faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess);
+faim_export int aim_icq_setsecurity(aim_session_t *sess, gboolean auth_required, gboolean webaware);
+faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd);
+faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
+faim_export int aim_icq_getalias(aim_session_t *sess, const char *uin);
+faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin);
+
+
+
+/* 0x0017 - auth.c */
+faim_export int aim_sendcookie(aim_session_t *, aim_conn_t *, const guint16 length, const guint8 *);
+faim_export int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw);
+faim_export int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info);
+faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
+faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick);
+
+
+
+/* 0x0018 - email.c */
+struct aim_emailinfo {
+	guint8 *cookie16;
+	guint8 *cookie8;
+	char *url;
+	guint16 nummsgs;
+	guint8 unread;
+	char *domain;
+	guint16 flag;
+	struct aim_emailinfo *next;
+};
+
+faim_export int aim_email_sendcookies(aim_session_t *sess);
+faim_export int aim_email_activate(aim_session_t *sess);
+
+
+
+/* tlv.c - TLV handling */
+
+/* TLV structure */
+typedef struct aim_tlv_s {
+	guint16 type;
+	guint16 length;
+	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 */
+faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, guint16 type, const int nth);
+faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, guint16 type, const int nth);
+faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth);
+faim_internal guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth);
+faim_internal guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth);
+faim_internal guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth);
+
+/* TLV list handling functions */
+faim_internal aim_tlvlist_t *aim_tlvlist_read(aim_bstream_t *bs);
+faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, guint16 num);
+faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, guint16 len);
+faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig);
+
+faim_internal int aim_tlvlist_count(aim_tlvlist_t **list);
+faim_internal int aim_tlvlist_size(aim_tlvlist_t **list);
+faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two);
+faim_internal int aim_tlvlist_write(aim_bstream_t *bs, aim_tlvlist_t **list);
+faim_internal void aim_tlvlist_free(aim_tlvlist_t **list);
+
+faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value);
+faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type);
+faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value);
+faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value);
+faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value);
+faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value);
+faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps);
+faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo);
+faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
+faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl);
+
+faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 lenth, const guint8 *value);
+faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str);
+faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type);
+faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value);
+faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const guint16 type, const guint16 value);
+faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value);
+
+faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type);
+
+
+
+/* util.c */
+/*
+ * These are really ugly.  You'd think this was LISP.  I wish it was.
+ *
+ * XXX With the advent of bstream's, these should be removed to enforce
+ * their use.
+ *
+ */
+#define aimutil_put8(buf, data) ((*(buf) = (guint8)(data)&0xff),1)
+#define aimutil_get8(buf) ((*(buf))&0xff)
+#define aimutil_put16(buf, data) ( \
+		(*(buf) = (guint8)((data)>>8)&0xff), \
+		(*((buf)+1) = (guint8)(data)&0xff),  \
+		2)
+#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define aimutil_put32(buf, data) ( \
+		(*((buf)) = (guint8)((data)>>24)&0xff), \
+		(*((buf)+1) = (guint8)((data)>>16)&0xff), \
+		(*((buf)+2) = (guint8)((data)>>8)&0xff), \
+		(*((buf)+3) = (guint8)(data)&0xff), \
+		4)
+#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
+
+/* Little-endian versions (damn ICQ) */
+#define aimutil_putle8(buf, data) ( \
+		(*(buf) = (guint8)(data) & 0xff), \
+		1)
+#define aimutil_getle8(buf) ( \
+		(*(buf)) & 0xff \
+		)
+#define aimutil_putle16(buf, data) ( \
+		(*((buf)+0) = (guint8)((data) >> 0) & 0xff),  \
+		(*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
+		2)
+#define aimutil_getle16(buf) ( \
+		(((*((buf)+0)) << 0) & 0x00ff) + \
+		(((*((buf)+1)) << 8) & 0xff00) \
+		)
+#define aimutil_putle32(buf, data) ( \
+		(*((buf)+0) = (guint8)((data) >>  0) & 0xff), \
+		(*((buf)+1) = (guint8)((data) >>  8) & 0xff), \
+		(*((buf)+2) = (guint8)((data) >> 16) & 0xff), \
+		(*((buf)+3) = (guint8)((data) >> 24) & 0xff), \
+		4)
+#define aimutil_getle32(buf) ( \
+		(((*((buf)+0)) <<  0) & 0x000000ff) + \
+		(((*((buf)+1)) <<  8) & 0x0000ff00) + \
+		(((*((buf)+2)) << 16) & 0x00ff0000) + \
+		(((*((buf)+3)) << 24) & 0xff000000))
+
+
+faim_export int aimutil_putstr(char *, const char *, int);
+faim_export guint16 aimutil_iconsum(const guint8 *buf, int buflen);
+faim_export int aimutil_tokslen(char *toSearch, int theindex, char dl);
+faim_export int aimutil_itemcnt(char *toSearch, char dl);
+faim_export char *aimutil_itemindex(char *toSearch, int theindex, char dl);
+
+faim_export int aim_snvalid(const char *sn);
+faim_export int aim_sn_is_icq(const char *sn);
+faim_export int aim_sn_is_sms(const char *sn);
+faim_export int aim_snlen(const char *sn);
+faim_export int aim_sncmp(const char *sn1, const char *sn2);
+
+#include "oscar_internal.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OSCAR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/oscar_internal.h	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,231 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * oscar_internal.h -- prototypes/structs for the guts of libfaim
+ *
+ */
+
+#ifndef _OSCAR_INTERNAL_H_
+#define _OSCAR_INTERNAL_H_
+
+typedef struct {
+	guint16 family;
+	guint16 subtype;
+	guint16 flags;
+	guint32 id;
+} aim_modsnac_t;
+
+#define AIM_MODULENAME_MAXLEN 16
+#define AIM_MODFLAG_MULTIFAMILY 0x0001
+typedef struct aim_module_s {
+	guint16 family;
+	guint16 version;
+	guint16 toolid;
+	guint16 toolversion;
+	guint16 flags;
+	char name[AIM_MODULENAME_MAXLEN+1];
+	int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs);
+
+	void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod);
+	void *priv;
+	struct aim_module_s *next;
+} aim_module_t;
+
+faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *));
+faim_internal void aim__shutdownmodules(aim_session_t *sess);
+faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group);
+faim_internal aim_module_t *aim__findmodule(aim_session_t *sess, const char *name);
+
+faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int auth_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int invite_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int odir_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int bart_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod);
+
+faim_internal int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+faim_internal int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+faim_internal int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *);
+faim_internal int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *);
+
+#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset)
+
+/* bstream.c */
+faim_internal int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len);
+faim_internal int aim_bstream_empty(aim_bstream_t *bs);
+faim_internal int aim_bstream_curpos(aim_bstream_t *bs);
+faim_internal int aim_bstream_setpos(aim_bstream_t *bs, unsigned int off);
+faim_internal void aim_bstream_rewind(aim_bstream_t *bs);
+faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n);
+faim_internal guint8 aimbs_get8(aim_bstream_t *bs);
+faim_internal guint16 aimbs_get16(aim_bstream_t *bs);
+faim_internal guint32 aimbs_get32(aim_bstream_t *bs);
+faim_internal guint8 aimbs_getle8(aim_bstream_t *bs);
+faim_internal guint16 aimbs_getle16(aim_bstream_t *bs);
+faim_internal guint32 aimbs_getle32(aim_bstream_t *bs);
+faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len);
+faim_internal guint8 *aimbs_getraw(aim_bstream_t *bs, int len);
+faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len);
+faim_internal int aimbs_put8(aim_bstream_t *bs, guint8 v);
+faim_internal int aimbs_put16(aim_bstream_t *bs, guint16 v);
+faim_internal int aimbs_put32(aim_bstream_t *bs, guint32 v);
+faim_internal int aimbs_putle8(aim_bstream_t *bs, guint8 v);
+faim_internal int aimbs_putle16(aim_bstream_t *bs, guint16 v);
+faim_internal int aimbs_putle32(aim_bstream_t *bs, guint32 v);
+faim_internal int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len);
+faim_internal int aimbs_putstr(aim_bstream_t *bs, const char *str);
+faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len);
+faim_internal int aimbs_putcaps(aim_bstream_t *bs, guint32 caps);
+
+/* conn.c */
+faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
+
+/* rxhandlers.c */
+faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type);
+faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_frame_t *ptr);
+faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
+faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
+
+/* rxqueue.c */
+faim_internal int aim_recv(int fd, void *buf, size_t count);
+faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count);
+faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
+faim_internal void aim_frame_destroy(aim_frame_t *);
+
+/* txqueue.c */
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint16 chan, int datalen);
+faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
+faim_internal int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count);
+faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
+faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);
+
+/*
+ * Generic SNAC structure.  Rarely if ever used.
+ */
+typedef struct aim_snac_s {
+	aim_snacid_t id;
+	guint16 family;
+	guint16 type;
+	guint16 flags;
+	void *data;
+	time_t issuetime;
+	struct aim_snac_s *next;
+} aim_snac_t;
+
+/* snac.c */
+faim_internal void aim_initsnachash(aim_session_t *sess);
+faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac);
+faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
+faim_internal aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id);
+faim_internal int aim_putsnac(aim_bstream_t *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id);
+
+/* Stored in ->priv of the service request SNAC for chats. */
+struct chatsnacinfo {
+	guint16 exchange;
+	char name[128];
+	guint16 instance;
+};
+
+/*
+ * In SNACland, the terms 'family' and 'group' are synonymous -- the former
+ * is my term, the latter is AOL's.
+ */
+struct snacgroup {
+	guint16 group;
+	struct snacgroup *next;
+};
+
+struct snacpair {
+	guint16 group;
+	guint16 subtype;
+	struct snacpair *next;
+};
+
+struct rateclass {
+	guint16 classid;
+	guint32 windowsize;
+	guint32 clear;
+	guint32 alert;
+	guint32 limit;
+	guint32 disconnect;
+	guint32 current;
+	guint32 max;
+	guint8 unknown[5]; /* only present in versions >= 3 */
+	struct snacpair *members;
+	struct rateclass *next;
+};
+
+/*
+ * This is inside every connection.  But it is a void * to anything
+ * outside of libfaim.  It should remain that way.  It's called data
+ * abstraction.  Maybe you've heard of it.  (Probably not if you're a
+ * libfaim user.)
+ *
+ */
+typedef struct aim_conn_inside_s {
+	struct snacgroup *groups;
+	struct rateclass *rates;
+} aim_conn_inside_t;
+
+faim_internal void aim_conn_addgroup(aim_conn_t *conn, guint16 group);
+
+faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type);
+faim_internal aim_msgcookie_t *aim_mkcookie(guint8 *, int, void *);
+faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *, const unsigned char *, const int);
+faim_internal int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+faim_internal int aim_msgcookie_gettype(int reqclass);
+faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie);
+
+/* 0x0002 - locate.c */
+faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn);
+faim_internal guint32 aim_locate_getcaps(aim_session_t *sess, aim_bstream_t *bs, int len);
+faim_internal guint32 aim_locate_getcaps_short(aim_session_t *sess, aim_bstream_t *bs, int len);
+faim_internal void aim_info_free(aim_userinfo_t *);
+faim_internal int aim_info_extract(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *);
+faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info);
+
+faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo);
+
+faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
+
+/* These are all handled internally now. */
+faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
+faim_internal int aim_reqrates(aim_session_t *, aim_conn_t *);
+faim_internal int aim_rates_addparam(aim_session_t *, aim_conn_t *);
+faim_internal int aim_rates_delparam(aim_session_t *, aim_conn_t *);
+
+#endif /* _OSCAR_INTERNAL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/peer.c	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,1228 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
+ * (ODC is also referred to as DirectIM and IM Image.)
+ *
+ * There are a few static helper functions at the top, then 
+ * ODC stuff, then ft stuff.
+ *
+ * I feel like this is a good place to explain OFT, so I'm going to 
+ * do just that.  Each OFT packet has a header type.  I guess this 
+ * is pretty similar to the subtype of a SNAC packet.  The type 
+ * basically tells the other client the meaning of the OFT packet.  
+ * There are two distinct types of file transfer, which I usually 
+ * call "sendfile" and "getfile."  Sendfile is when you send a file 
+ * to another AIM user.  Getfile is when you share a group of files, 
+ * and other users request that you send them the files.
+ *
+ * A typical sendfile file transfer goes like this:
+ *   1) Sender sends a channel 2 ICBM telling the other user that 
+ *      we want to send them a file.  At the same time, we open a 
+ *      listener socket (this should be done before sending the 
+ *      ICBM) on some port, and wait for them to connect to us.  
+ *      The ICBM we sent should contain our IP address and the port 
+ *      number that we're listening on.
+ *   2) The receiver connects to the sender on the given IP address 
+ *      and port.  After the connection is established, the receiver 
+ *      sends an ICBM signifying that we are ready and waiting.
+ *   3) The sender sends an OFT PROMPT message over the OFT 
+ *      connection.
+ *   4) The receiver of the file sends back an exact copy of this 
+ *      OFT packet, except the cookie is filled in with the cookie 
+ *      from the ICBM.  I think this might be an attempt to verify 
+ *      that the user that is connected is actually the guy that 
+ *      we sent the ICBM to.  Oh, I've been calling this the ACK.
+ *   5) The sender starts sending raw data across the connection 
+ *      until the entire file has been sent.
+ *   6) The receiver knows the file is finished because the sender 
+ *      sent the file size in an earlier OFT packet.  So then the 
+ *      receiver sends the DONE thingy (after filling in the 
+ *      "received" checksum and size) and closes the connection.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include  <config.h>
+#endif
+
+#include "oscar.h"
+#include "peer.h"
+
+#ifndef _WIN32
+#include <stdio.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/utsname.h> /* for aim_odc_initiate */
+#include <arpa/inet.h> /* for inet_ntoa */
+#include <limits.h> /* for UINT_MAX */
+#define G_DIR_SEPARATOR '/'
+#endif
+
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+/*
+ * I really want to switch all our networking code to using IPv6 only,
+ * but that really isn't a good idea at all.  Evan S. of Adium says
+ * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
+ * nothing inherently IPv6 about them.  And I feel like Linux kernel
+ * 2.6.5 is doing the same thing.  So we REALLY should accept
+ * connections if they're showing up as IPv6.  Old OSes (Solaris?)
+ * that might not have full IPv6 support yet will fail if we try
+ * to use PF_INET6 but it isn't defined.  --Mark Doliner
+ */
+#ifndef PF_INET6
+#define PF_INET6 PF_INET
+#endif
+
+struct aim_odc_intdata {
+	guint8 cookie[8];
+	char sn[MAXSNLEN+1];
+	char ip[22];
+};
+
+/**
+ * Convert the directory separator from / (0x2f) to ^A (0x01)
+ *
+ * @param name The filename to convert.
+ */
+static void aim_oft_dirconvert_tostupid(char *name)
+{
+	while (name[0]) {
+		if (name[0] == 0x01)
+			name[0] = G_DIR_SEPARATOR;
+		name++;
+	}
+}
+
+/**
+ * Convert the directory separator from ^A (0x01) to / (0x2f)
+ *
+ * @param name The filename to convert.
+ */
+static void aim_oft_dirconvert_fromstupid(char *name)
+{
+	while (name[0]) {
+		if (name[0] == G_DIR_SEPARATOR)
+			name[0] = 0x01;
+		name++;
+	}
+}
+
+/**
+ * Calculate oft checksum of buffer
+ *
+ * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The 
+ * checksum is kind of a rolling checksum thing, so each time you get bytes 
+ * of a file you just call this puppy and it updates the checksum.  You can 
+ * calculate the checksum of an entire file by calling this in a while or a 
+ * for loop, or something.
+ *
+ * Thanks to Graham Booker for providing this improved checksum routine, 
+ * which is simpler and should be more accurate than Josh Myer's original 
+ * code. -- wtm
+ *
+ * This algorithm works every time I have tried it.  The other fails 
+ * sometimes.  So, AOL who thought this up?  It has got to be the weirdest 
+ * checksum I have ever seen.
+ *
+ * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
+ * @param bufsize Size of buffer.
+ * @param prevcheck Previous checksum.
+ */
+faim_export guint32 aim_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevcheck)
+{
+	guint32 check = (prevcheck >> 16) & 0xffff, oldcheck;
+	int i;
+	unsigned short val;
+
+	for (i=0; i<bufferlen; i++) {
+		oldcheck = check;
+		if (i&1)
+			val = buffer[i];
+		else
+			val = buffer[i] << 8;
+		check -= val;
+		/*
+		 * The following appears to be necessary.... It happens 
+		 * every once in a while and the checksum doesn't fail.
+		 */
+		if (check > oldcheck)
+			check--;
+	}
+	check = ((check & 0x0000ffff) + (check >> 16));
+	check = ((check & 0x0000ffff) + (check >> 16));
+	return check << 16;
+}
+
+faim_export guint32 aim_oft_checksum_file(char *filename) {
+	FILE *fd;
+	guint32 checksum = 0xffff0000;
+
+	if ((fd = fopen(filename, "rb"))) {
+		int bytes;
+		guint8 buffer[1024];
+
+		while ((bytes = fread(buffer, 1, 1024, fd)))
+			checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);
+		fclose(fd);
+	}
+
+	return checksum;
+}
+
+/**
+ * After establishing a listening socket, this is called to accept a connection.  It
+ * clones the conn used by the listener, and passes both of these to a signal handler.
+ * The signal handler should close the listener conn and keep track of the new conn,
+ * since this is what is used for file transfers and what not.
+ *
+ * @param sess The session.
+ * @param cur The conn the incoming connection is on.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
+{
+	int acceptfd = 0;
+	struct sockaddr addr;
+	socklen_t addrlen = sizeof(addr);
+	int ret = 0;
+	aim_conn_t *newconn;
+	char ip[20];
+	unsigned short port;
+
+	if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)
+		return 0; /* not an error */
+
+	if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) {
+		close(acceptfd);
+		aim_conn_close(cur);
+		return -1;
+	}
+
+	strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));
+	port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
+
+	if (!(newconn = aim_cloneconn(sess, cur))) {
+		close(acceptfd);
+		aim_conn_close(cur);
+		return -ENOMEM;
+	}
+
+	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
+	newconn->fd = acceptfd;
+
+	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
+		aim_rxcallback_t userfunc;
+		struct aim_odc_intdata *priv;
+
+		priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);
+		cur->internal = NULL;
+		snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port);
+
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIM_ESTABLISHED)))
+			ret = userfunc(sess, NULL, newconn, cur);
+
+	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
+		aim_rxcallback_t userfunc;
+
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, OFT_TYPE_ESTABLISHED)))
+			ret = userfunc(sess, NULL, newconn, cur);
+
+	} else {
+		gaim_debug_warning("oscar", "Got a connection on a listener that's not rendezvous.  Closing connection.\n");
+		aim_conn_close(newconn);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+/**
+ * Send client-to-client typing notification over an established direct connection.
+ *
+ * @param sess The session.
+ * @param conn The already-connected ODC connection.
+ * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and 
+ *        0x0000 sends "stopped."
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
+{
+	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
+	aim_frame_t *fr;
+	aim_bstream_t *hdrbs;
+	guint8 *hdr;
+	int hdrlen = 0x44;
+
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))
+		return -ENOMEM;
+	memcpy(fr->hdr.rend.magic, "ODC2", 4);
+	fr->hdr.rend.hdrlen = hdrlen + 8;
+
+	if (!(hdr = calloc(1, hdrlen))) {
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+
+	hdrbs = &(fr->data);
+	aim_bstream_init(hdrbs, hdr, hdrlen);
+
+	aimbs_put16(hdrbs, 0x0006);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, intdata->cookie, 8);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put32(hdrbs, 0x00000000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+
+	if (typing == 0x0002)
+		aimbs_put16(hdrbs, 0x0002 | 0x0008);
+	else if (typing == 0x0001)
+		aimbs_put16(hdrbs, 0x0002 | 0x0004);
+	else
+		aimbs_put16(hdrbs, 0x0002);
+
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putstr(hdrbs, sess->sn);
+
+	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
+
+	aimbs_put8(hdrbs, 0x00);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put8(hdrbs, 0x00);
+
+	/* end of hdr */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Send client-to-client IM over an established direct connection.
+ * Call this just like you would aim_send_im, to send a directim.
+ * 
+ * @param sess The session.
+ * @param conn The already-connected ODC connection.
+ * @param msg Null-terminated string to send.
+ * @param len The length of the message to send, including binary data.
+ * @param encoding See the AIM_CHARSET_* defines in oscar.h
+ * @param isawaymsg 0 if this is not an auto-response, 1 if it is.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg)
+{
+	aim_frame_t *fr;
+	aim_bstream_t *hdrbs;
+	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
+	int hdrlen = 0x44;
+	guint8 *hdr;
+
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
+		return -ENOMEM;
+
+	memcpy(fr->hdr.rend.magic, "ODC2", 4);
+	fr->hdr.rend.hdrlen = hdrlen + 8;
+
+	if (!(hdr = calloc(1, hdrlen + len))) {
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+
+	hdrbs = &(fr->data);
+	aim_bstream_init(hdrbs, hdr, hdrlen + len);
+
+	aimbs_put16(hdrbs, 0x0006);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, intdata->cookie, 8);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put32(hdrbs, len);
+	aimbs_put16(hdrbs, encoding);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+
+	/* flags - used for typing notification and to mark if this is an away message */
+	aimbs_put16(hdrbs, 0x0000 | isawaymsg);
+
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putstr(hdrbs, sess->sn);
+
+	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
+
+	aimbs_put8(hdrbs, 0x00);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put8(hdrbs, 0x00);
+
+	/* end of hdr2 */
+
+#if 0 /* XXX - this is how you send buddy icon info... */	
+	aimbs_put16(hdrbs, 0x0008);
+	aimbs_put16(hdrbs, 0x000c);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x1466);
+	aimbs_put16(hdrbs, 0x0001);
+	aimbs_put16(hdrbs, 0x2e0f);
+	aimbs_put16(hdrbs, 0x393e);
+	aimbs_put16(hdrbs, 0xcac8);
+#endif
+	aimbs_putraw(hdrbs, (guchar *)msg, len);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Get the screen name of the peer of a direct connection.
+ *
+ * @param conn The ODC connection.
+ * @return The screen name of the dude, or NULL if there was an anomaly.
+ */
+faim_export const char *aim_odc_getsn(aim_conn_t *conn)
+{
+	struct aim_odc_intdata *intdata;
+
+	if (!conn || !conn->internal)
+		return NULL;
+
+	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) ||
+			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+		return NULL;
+
+	intdata = (struct aim_odc_intdata *)conn->internal;
+
+	return intdata->sn;
+}
+
+/**
+ * Get the cookie of a direct connection.
+ *
+ * @param conn The ODC connection.
+ * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly.
+ */
+faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn)
+{
+	struct aim_odc_intdata *intdata;
+
+	if (!conn || !conn->internal)
+		return NULL;
+
+	intdata = (struct aim_odc_intdata *)conn->internal;
+
+	return intdata->cookie;
+}
+
+/**
+ * Find the conn of a direct connection with the given buddy.
+ *
+ * @param sess The session.
+ * @param sn The screen name of the buddy whose direct connection you want to find.
+ * @return The conn for the direct connection with the given buddy, or NULL if no 
+ *         connection was found.
+ */
+faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
+{
+	aim_conn_t *cur;
+	struct aim_odc_intdata *intdata;
+
+	if (!sess || !sn || !strlen(sn))
+		return NULL;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+			intdata = cur->internal;
+			if (!aim_sncmp(intdata->sn, sn))
+				return cur;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * For those times when we want to open up the direct connection channel ourselves.
+ *
+ * You'll want to set up some kind of watcher on this socket.
+ * When the state changes, call aim_handlerendconnection with
+ * the connection returned by this.  aim_handlerendconnection
+ * will accept the pending connection and stop listening.
+ *
+ * @param sess The session
+ * @param sn The screen name to connect to.
+ * @return The new connection.
+ */
+faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd,
+                                         const guint8 *localip, guint16 port, const guint8 *mycookie)
+{
+	aim_conn_t *newconn;
+	aim_msgcookie_t *cookie;
+	struct aim_odc_intdata *priv;
+	guint8 ck[8];
+
+	if (!localip)
+		return NULL;
+
+	if (mycookie) {
+		memcpy(ck, mycookie, 8);
+		aim_im_sendch2_odcrequest(sess, ck, TRUE, sn, localip, port);
+	} else
+		aim_im_sendch2_odcrequest(sess, ck, FALSE, sn, localip, port);
+
+	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
+	memcpy(cookie->cookie, ck, 8);
+	cookie->type = AIM_COOKIETYPE_OFTIM;
+
+	/* this one is for the cookie */
+	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, sn, sizeof(priv->sn));
+	cookie->data = priv;
+	aim_cachecookie(sess, cookie);
+
+	/* XXX - switch to aim_cloneconn()? */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) {
+		close(listenfd);
+		return NULL;
+	}
+
+	/* this one is for the conn */
+	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, sn, sizeof(priv->sn));
+
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = priv;
+	newconn->lastactivity = time(NULL);
+
+	return newconn;
+}
+
+/**
+ * Connect directly to the given buddy for directim.
+ *
+ * This is a wrapper for aim_newconn.
+ *
+ * If addr is NULL, the socket is not created, but the connection is 
+ * allocated and setup to connect.
+ *
+ * @param sess The Godly session.
+ * @param sn The screen name we're connecting to.  I hope it's a girl...
+ * @param addr Address to connect to.
+ * @return The new connection.
+ */
+faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guint8 *cookie)
+{
+	aim_conn_t *newconn;
+	struct aim_odc_intdata *intdata;
+
+	if (!sess || !sn)
+		return NULL;
+
+	if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata))))
+		return NULL;
+	memcpy(intdata->cookie, cookie, 8);
+	strncpy(intdata->sn, sn, sizeof(intdata->sn));
+	if (addr)
+		strncpy(intdata->ip, addr, sizeof(intdata->ip));
+
+	/* XXX - verify that non-blocking connects actually work */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS))) {
+		free(intdata);
+		return NULL;
+	}
+
+	newconn->internal = intdata;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+
+	return newconn;
+}
+
+/**
+ * Sometimes you just don't know with these kinds of people.
+ *
+ * @param sess The session.
+ * @param conn The ODC connection of the incoming data.
+ * @param frr The frame allocated for the incoming data.
+ * @param bs It stands for "bologna sandwich."
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
+{
+	aim_frame_t fr;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint32 payloadlength;
+	guint16 flags, encoding;
+	char *snptr = NULL;
+
+	fr.conn = conn;
+
+	/* AAA - ugly */
+	aim_bstream_setpos(bs, 20);
+	payloadlength = aimbs_get32(bs);
+
+	aim_bstream_setpos(bs, 24);
+	encoding = aimbs_get16(bs);
+
+	aim_bstream_setpos(bs, 30);
+	flags = aimbs_get16(bs);
+
+	aim_bstream_setpos(bs, 36);
+	/* XXX - create an aimbs_getnullstr function? */
+	snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */
+
+	gaim_debug_misc("oscar", "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
+
+	if (flags & 0x0008) {
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr, 2);
+	} else if (flags & 0x0004) {
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr, 1);
+	} else {
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr, 0);
+	}
+
+	if ((payloadlength != 0) && (payloadlength != UINT_MAX)) {
+		char *msg;
+		int recvd = 0;
+		int i, isawaymsg;
+
+		isawaymsg = flags & 0x0001;
+
+		if (!(msg = calloc(1, payloadlength+1))) {
+			free(snptr);
+			return -ENOMEM;
+		}
+
+		while (payloadlength - recvd) {
+			if (payloadlength - recvd >= 1024)
+				i = aim_recv(conn->fd, &msg[recvd], 1024);
+			else 
+				i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
+			if (i <= 0) {
+				free(msg);
+				free(snptr);
+				return -1;
+			}
+			recvd = recvd + i;
+			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
+				ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
+		}
+		
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, OFT_TYPE_DIRECTIMINCOMING)))
+			ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);
+
+		free(msg);
+	}
+
+	free(snptr);
+
+	return ret;
+}
+
+faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guint8 *cookie, const char *sn, const char *ip, guint16 port, guint32 size, guint32 modtime, char *filename, int send_or_recv, int method, int stage)
+{
+	struct aim_oft_info *new;
+
+	if (!sess)
+		return NULL;
+
+	if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info))))
+		return NULL;
+
+	new->sess = sess;
+	if (cookie)
+		memcpy(new->cookie, cookie, 8);
+	else
+		aim_icbm_makecookie(new->cookie);
+	if (ip)
+		new->clientip = strdup(ip);
+	else
+		new->clientip = NULL;
+	if (sn)
+		new->sn = strdup(sn);
+	else
+		new->sn = NULL;
+	new->method = method;
+	new->send_or_recv = send_or_recv;
+	new->stage = stage;
+	new->port = port;
+	new->xfer_reffed = FALSE;
+	new->success = FALSE;
+	new->fh.totfiles = 1;
+	new->fh.filesleft = 1;
+	new->fh.totparts = 1;
+	new->fh.partsleft = 1;
+	new->fh.totsize = size;
+	new->fh.size = size;
+	new->fh.modtime = modtime;
+	new->fh.checksum = 0xffff0000;
+	new->fh.rfrcsum = 0xffff0000;
+	new->fh.rfcsum = 0xffff0000;
+	new->fh.recvcsum = 0xffff0000;
+	strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31);
+	if (filename) {
+		strncpy(new->fh.name, filename, 63);
+		new->fh.name[63] = '\0';
+	}
+
+	new->next = sess->oft_info;
+	sess->oft_info = new;
+
+	return new;
+}
+
+faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guint8 *cookie,
+	guint16 port)
+{
+	struct aim_rv_proxy_info *proxy_info;
+	
+	if (!(proxy_info = (struct aim_rv_proxy_info*)calloc(1, sizeof(struct aim_rv_proxy_info))))
+		return NULL;
+	
+	proxy_info->sess = sess;
+	proxy_info->port = port;
+	proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT;
+	proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT;
+	
+	if (cookie)
+		memcpy(proxy_info->cookie, cookie, 8);
+	
+	return proxy_info;
+}
+
+/**
+ * Remove the given oft_info struct from the oft_info linked list, and 
+ * then free its memory.
+ *
+ * @param sess The session.
+ * @param oft_info The aim_oft_info struct that we're destroying.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info)
+{
+	aim_session_t *sess;
+
+	if (!oft_info || !(sess = oft_info->sess))
+		return -EINVAL;
+
+	if (sess->oft_info && (sess->oft_info == oft_info)) {
+		sess->oft_info = sess->oft_info->next;
+	} else {
+		struct aim_oft_info *cur;
+		for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next);
+		if (cur->next)
+			cur->next = cur->next->next;
+	}
+
+	free(oft_info->sn);
+	free(oft_info->proxyip);
+	free(oft_info->clientip);
+	free(oft_info->verifiedip);
+	free(oft_info);
+
+	return 0;
+}
+
+/**
+ * Creates a listener socket so the other dude can connect to us.
+ *
+ * You'll want to set up some kind of watcher on this socket.  
+ * When the state changes, call aim_handlerendconnection with 
+ * the connection returned by this.  aim_handlerendconnection 
+ * will accept the pending connection and stop listening.
+ *
+ * @param sess The session.
+ * @param oft_info File transfer information associated with this 
+ *        connection.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd)
+{
+	if (!oft_info)
+		return -EINVAL;
+
+	if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER))) {
+		close(listenfd);
+		return -ENOMEM;
+	}
+
+	oft_info->conn->fd = listenfd;
+	oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+	oft_info->conn->lastactivity = time(NULL);
+
+	return 0;
+}
+
+/**
+ * Extract an &aim_fileheader_t from the given buffer.
+ *
+ * @param bs The should be from an incoming rendezvous packet.
+ * @return A pointer to new struct on success, or NULL on error.
+ */
+static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
+{
+	struct aim_fileheader_t *fh;
+
+	if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+		return NULL;
+
+	/* The bstream should be positioned after the hdrtype. */
+	aimbs_getrawbuf(bs, fh->bcookie, 8);
+	fh->encrypt = aimbs_get16(bs);
+	fh->compress = aimbs_get16(bs);
+	fh->totfiles = aimbs_get16(bs);
+	fh->filesleft = aimbs_get16(bs);
+	fh->totparts = aimbs_get16(bs);
+	fh->partsleft = aimbs_get16(bs);
+	fh->totsize = aimbs_get32(bs);
+	fh->size = aimbs_get32(bs);
+	fh->modtime = aimbs_get32(bs);
+	fh->checksum = aimbs_get32(bs);
+	fh->rfrcsum = aimbs_get32(bs);
+	fh->rfsize = aimbs_get32(bs);
+	fh->cretime = aimbs_get32(bs);
+	fh->rfcsum = aimbs_get32(bs);
+	fh->nrecvd = aimbs_get32(bs);
+	fh->recvcsum = aimbs_get32(bs);
+	aimbs_getrawbuf(bs, (guchar *)fh->idstring, 32);
+	fh->flags = aimbs_get8(bs);
+	fh->lnameoffset = aimbs_get8(bs);
+	fh->lsizeoffset = aimbs_get8(bs);
+	aimbs_getrawbuf(bs, (guchar *)fh->dummy, 69);
+	aimbs_getrawbuf(bs, (guchar *)fh->macfileinfo, 16);
+	fh->nencode = aimbs_get16(bs);
+	fh->nlanguage = aimbs_get16(bs);
+	aimbs_getrawbuf(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
+	fh->name[63] = '\0';
+
+	return fh;
+} 
+
+/**
+ * Fills a buffer with network-order fh data
+ *
+ * @param bs A bstream to fill -- automatically initialized
+ * @param fh A struct aim_fileheader_t to get data from.
+ * @return Return non-zero on error.
+ */
+static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
+{ 
+	guint8 *hdr;
+
+	if (!bs || !fh)
+		return -EINVAL;
+
+	if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
+		return -ENOMEM;
+
+	aim_bstream_init(bs, hdr, 0x100 - 8);
+	aimbs_putraw(bs, fh->bcookie, 8);
+	aimbs_put16(bs, fh->encrypt);
+	aimbs_put16(bs, fh->compress);
+	aimbs_put16(bs, fh->totfiles);
+	aimbs_put16(bs, fh->filesleft);
+	aimbs_put16(bs, fh->totparts);
+	aimbs_put16(bs, fh->partsleft);
+	aimbs_put32(bs, fh->totsize);
+	aimbs_put32(bs, fh->size);
+	aimbs_put32(bs, fh->modtime);
+	aimbs_put32(bs, fh->checksum);
+	aimbs_put32(bs, fh->rfrcsum);
+	aimbs_put32(bs, fh->rfsize);
+	aimbs_put32(bs, fh->cretime);
+	aimbs_put32(bs, fh->rfcsum);
+	aimbs_put32(bs, fh->nrecvd);
+	aimbs_put32(bs, fh->recvcsum);
+	aimbs_putraw(bs, (guchar *)fh->idstring, 32);
+	aimbs_put8(bs, fh->flags);
+	aimbs_put8(bs, fh->lnameoffset);
+	aimbs_put8(bs, fh->lsizeoffset);
+	aimbs_putraw(bs, (guchar *)fh->dummy, 69);
+	aimbs_putraw(bs, (guchar *)fh->macfileinfo, 16);
+	aimbs_put16(bs, fh->nencode);
+	aimbs_put16(bs, fh->nlanguage);
+	aimbs_putraw(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
+
+	return 0;
+}
+
+/**
+ * Create an OFT packet based on the given information, and send it on its merry way.
+ *
+ * @param sess The session.
+ * @param type The subtype of the OFT packet we're sending.
+ * @param oft_info The aim_oft_info struct with the connection and OFT 
+ *        info we're sending.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info)
+{
+	aim_frame_t *fr;
+
+	if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
+		return -EINVAL;
+
+#if 0
+	/*
+	 * If you are receiving a file, the cookie should be null, if you are sending a 
+	 * file, the cookie should be the same as the one used in the ICBM negotiation 
+	 * SNACs.
+	 */
+	fh->lnameoffset = 0x1a;
+	fh->lsizeoffset = 0x10;
+
+	/* These should be the same as charset and charsubset in ICBMs */
+	fh->nencode = 0x0000;
+	fh->nlanguage = 0x0000;
+#endif
+
+	aim_oft_dirconvert_tostupid(oft_info->fh.name);
+
+	if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0)))
+		return -ENOMEM;
+
+	if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) {
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+
+	memcpy(fr->hdr.rend.magic, "OFT2", 4);
+	fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data) + 8;
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Create a rendezvous "init recv" packet and send it on its merry way.
+ * This is the first packet sent to the proxy server by the second client
+ * involved in this rendezvous proxy session.
+ *
+ * @param sess The session.
+ * @param proxy_info Changable pieces of data for this packet
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info)
+{
+#if 0
+	aim_tlvlist_t *tlvlist_sendfile;
+#endif
+	aim_bstream_t bs;
+	guint8 *bs_raw;
+	guint16 packet_len;
+	guint8 sn_len;
+	int err;
+
+	err = 0;
+
+	if (!proxy_info)
+		return -EINVAL;
+
+	sn_len = strlen(proxy_info->sess->sn);
+	packet_len = 2 + 2	/* packet_len, packet_ver */
+		+ 2 + 4		/* cmd_type,  unknownA */
+		+ 2		/* flags */
+		+ 1 + sn_len	/* Length/value pair for screenname */
+		+ 8		/* ICBM Cookie */
+		+ 2		/* port */
+		+ 2 + 2 + 16;	/* TLV for Filesend capability block */
+
+	if (!(bs_raw = malloc(packet_len)))
+		return -ENOMEM;
+
+	aim_bstream_init(&bs, bs_raw, packet_len);
+	aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
+	aimbs_put16(&bs, proxy_info->packet_ver);
+	aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV);
+	aimbs_put32(&bs, proxy_info->unknownA);
+	aimbs_put16(&bs, proxy_info->flags);
+	aimbs_put8(&bs, sn_len);
+	aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
+	aimbs_put16(&bs, proxy_info->port);
+	aimbs_putraw(&bs, proxy_info->cookie, 8);
+
+	aimbs_put16(&bs, 0x0001);		/* Type */
+	aimbs_put16(&bs, 16);			/* Length */
+	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
+
+
+#if 0
+	/* TODO: Use built-in TLV */
+	aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
+	aim_tlvlist_write(&bs, &tlvlist_sendfile);
+#endif
+
+	aim_bstream_rewind(&bs);
+	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+		err = errno;
+	proxy_info->conn->lastactivity = time(NULL);
+
+#if 0
+	aim_tlvlist_free(tlvlist_sendfile);
+#endif
+	free(bs_raw);
+
+	return err;
+}
+
+
+/**
+ * Create a rendezvous "init send" packet and send it on its merry way.
+ * This is the first packet sent to the proxy server by the client
+ * first indicating that this will be a proxied connection
+ *
+ * @param sess The session.
+ * @param proxy_info Changable pieces of data for this packet
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info)
+{
+#if 0
+	aim_tlvlist_t *tlvlist_sendfile;
+#endif
+	aim_bstream_t bs;
+	guint8 *bs_raw;
+	guint16 packet_len;
+	guint8 sn_len;
+	int err;
+
+	err = 0;
+
+	if (!proxy_info)
+		return -EINVAL;
+
+	sn_len = strlen(proxy_info->sess->sn);
+	packet_len = 2 + 2	/* packet_len, packet_ver */
+		+ 2 + 4		/* cmd_type,  unknownA */
+		+ 2		/* flags */
+		+ 1 + sn_len	/* Length/value pair for screenname */
+		+ 8		/* ICBM Cookie */
+		+ 2 + 2 + 16;	/* TLV for Filesend capability block */
+
+	if (!(bs_raw = malloc(packet_len)))
+		return -ENOMEM;
+
+	aim_bstream_init(&bs, bs_raw, packet_len);
+	aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
+	aimbs_put16(&bs, proxy_info->packet_ver);
+	aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND);
+	aimbs_put32(&bs, proxy_info->unknownA);
+	aimbs_put16(&bs, proxy_info->flags);
+	aimbs_put8(&bs, sn_len);
+	aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
+	aimbs_putraw(&bs, proxy_info->cookie, 8);
+
+	aimbs_put16(&bs, 0x0001);		/* Type */
+	aimbs_put16(&bs, 16);			/* Length */
+	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
+
+	/* TODO: Use built-in TLV */
+#if 0
+	aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
+	aim_tlvlist_write(&bs, &tlvlist_sendfile);
+#endif
+
+	aim_bstream_rewind(&bs);
+	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+		err = errno;
+	proxy_info->conn->lastactivity = time(NULL);
+
+#if 0
+	aim_tlvlist_free(tlvlist_sendfile);
+#endif
+	free(bs_raw);
+
+	return err;
+}
+
+/**
+ * Handle incoming data on a rendezvous connection.  This is analogous to the
+ * consumesnac function in rxhandlers.c, and I really think this should probably
+ * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
+ *
+ * @param sess The session.
+ * @param fr The frame allocated for the incoming data.
+ * @return Return 0 if the packet was handled correctly, otherwise return the
+ *         error number.
+ */
+faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
+{
+	aim_conn_t *conn = fr->conn;
+	int ret = 1;
+
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
+		if (fr->hdr.rend.type == 0x0001)
+			ret = handlehdr_odc(sess, conn, fr, &fr->data);
+		else
+			gaim_debug_info("oscar", "ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type);
+
+	} else {
+		aim_rxcallback_t userfunc;
+		struct aim_fileheader_t *header = aim_oft_getheader(&fr->data);
+		aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */
+
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
+			ret = userfunc(sess, fr, conn, header->bcookie, header);
+
+		free(header);
+	}
+
+	if (ret == -1)
+		aim_conn_close(conn);
+
+	return ret;
+}
+
+/**
+ * Handle incoming data on a rendezvous proxy connection.  This is similar to
+ * aim_rxdispatch_rendezvous above and should probably be kept with that function.
+ *
+ * @param sess The session.
+ * @param fr The frame allocated for the incoming data.
+ * @return Return 0 if the packet was handled correctly, otherwise return the 
+ *         error number.
+ */
+faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_bstream_t bs_hdr;
+	guint8 hdr_buf[AIM_RV_PROXY_HDR_LEN];
+	aim_bstream_t bs_body; /* The body (everything but the header) of the packet */
+	guint8 *body_buf = NULL;
+	guint8 body_len;
+	
+	char str_ip[30] = {""};
+	guint8 ip_temp[4];
+	
+	guint16 len;
+	struct aim_rv_proxy_info *proxy_info;
+	
+	if(!(proxy_info = malloc(sizeof(struct aim_rv_proxy_info))))
+		return NULL;
+
+	aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN);
+	if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) {
+		aim_bstream_rewind(&bs_hdr);
+		len = aimbs_get16(&bs_hdr);
+		proxy_info->packet_ver = aimbs_get16(&bs_hdr);
+		proxy_info->cmd_type = aimbs_get16(&bs_hdr);
+		proxy_info->unknownA = aimbs_get32(&bs_hdr);
+		proxy_info->flags = aimbs_get16(&bs_hdr);
+		if(proxy_info->cmd_type == AIM_RV_PROXY_READY) {
+			/* Do a little victory dance
+			 * A ready packet contains no additional information */
+		} else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) {
+			if(len == AIM_RV_PROXY_ERROR_LEN - 2) {
+				body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN;
+				body_buf = malloc(body_len);
+				aim_bstream_init(&bs_body, body_buf, body_len);
+				if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
+					aim_bstream_rewind(&bs_body);
+					proxy_info->err_code = aimbs_get16(&bs_body);
+				} else {
+					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
+					aim_conn_close(conn);
+					free(proxy_info);
+					proxy_info = NULL;
+				}
+			} else {
+				gaim_debug_warning("oscar","invalid length for proxy error packet\n");
+				free(proxy_info);
+				proxy_info = NULL;
+			}
+		} else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) {
+			if(len == AIM_RV_PROXY_ACK_LEN - 2) {
+				body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN;
+				body_buf = malloc(body_len);
+				aim_bstream_init(&bs_body, body_buf, body_len);
+				if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
+					int i;
+					aim_bstream_rewind(&bs_body);
+					proxy_info->port = aimbs_get16(&bs_body);
+					for(i=0; i<4; i++)
+						ip_temp[i] = aimbs_get8(&bs_body);
+					snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu",
+						ip_temp[0], ip_temp[1],
+						ip_temp[2], ip_temp[3]);
+					proxy_info->ip = strdup(str_ip);
+				} else {
+					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
+					aim_conn_close(conn);
+					free(proxy_info);
+					proxy_info = NULL;
+				}
+			} else {
+				gaim_debug_warning("oscar","invalid length for proxy error packet\n");
+				free(proxy_info);
+				proxy_info = NULL;
+			}
+		} else {
+			gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n");
+		}	
+	} else {
+		gaim_debug_warning("oscar","error reading header of rv proxy packet\n");
+		aim_conn_close(conn);
+		free(proxy_info);
+		proxy_info = NULL;
+	}
+	if(body_buf) {
+		free(body_buf);
+		body_buf = NULL;
+	}
+	return proxy_info;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/peer.h	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,161 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * OFT Services
+ *
+ * For all of the above #defines, the number is the subtype
+ * of the SNAC.  For OFT #defines, the number is the
+ * "hdrtype" which comes after the magic string and OFT
+ * packet length.
+ *
+ * I'm pretty sure the ODC ones are arbitrary right now,
+ * that should be changed.
+ */
+
+#ifndef _PEER_H_
+#define _PEER_H_
+
+#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
+
+#define OFT_TYPE_DIRECTIMCONNECTREQ 0x0001	/* connect request -- actually an OSCAR CAP */
+#define OFT_TYPE_DIRECTIMINCOMING 0x0002
+#define OFT_TYPE_DIRECTIMDISCONNECT 0x0003
+#define OFT_TYPE_DIRECTIMTYPING 0x0004
+#define OFT_TYPE_DIRECTIM_ESTABLISHED 0x0005
+
+#define OFT_TYPE_PROMPT 0x0101		/* "I am going to send you this file, is that ok?" */
+#define OFT_TYPE_RESUMESOMETHING 0x0106	/* I really don't know */
+#define OFT_TYPE_ACK 0x0202			/* "Yes, it is ok for you to send me that file" */
+#define OFT_TYPE_DONE 0x0204			/* "I received that file with no problems, thanks a bunch" */
+#define OFT_TYPE_RESUME 0x0205		/* Resume transferring, sent by whoever paused? */
+#define OFT_TYPE_RESUMEACK 0x0207		/* Not really sure */
+
+#define OFT_TYPE_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */
+#define OFT_TYPE_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */
+#define OFT_TYPE_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */
+#define OFT_TYPE_GETFILE_ACKLISTING 0x120b	/* "I received the listing.txt file successfully" */
+#define OFT_TYPE_GETFILE_REQUESTFILE 0x120c	/* "Please send me this file" */
+
+#define OFT_TYPE_ESTABLISHED 0xFFFF		/* connection to buddy initiated */
+
+
+struct aim_fileheader_t {
+#if 0
+	char magic[4];		/* 0 */
+	guint16 hdrlen;		/* 4 */
+	guint16 hdrtype;		/* 6 */
+#endif
+	guchar bcookie[8];	/* 8 */
+	guint16 encrypt;		/* 16 */
+	guint16 compress;	/* 18 */
+	guint16 totfiles;	/* 20 */
+	guint16 filesleft;	/* 22 */
+	guint16 totparts;	/* 24 */
+	guint16 partsleft;	/* 26 */
+	guint32 totsize;		/* 28 */
+	guint32 size;		/* 32 */
+	guint32 modtime;		/* 36 */
+	guint32 checksum;	/* 40 */
+	guint32 rfrcsum;		/* 44 */
+	guint32 rfsize;		/* 48 */
+	guint32 cretime;		/* 52 */
+	guint32 rfcsum;		/* 56 */
+	guint32 nrecvd;		/* 60 */
+	guint32 recvcsum;	/* 64 */
+	char idstring[32];	/* 68 */
+	guint8 flags;		/* 100 */
+	guint8 lnameoffset;	/* 101 */
+	guint8 lsizeoffset;	/* 102 */
+	char dummy[69];		/* 103 */
+	char macfileinfo[16];	/* 172 */
+	guint16 nencode;		/* 188 */
+	guint16 nlanguage;	/* 190 */
+	char name[64];		/* 192 */
+				/* 256 */
+};
+
+struct aim_rv_proxy_info {
+	guint16 packet_ver;
+	guint16 cmd_type;
+	guint16 flags;
+	char* ip; /* IP address sent along with this packet */
+	guint16 port; /* This is NOT the port we should use to connect. Always connect to 5190 */
+	guchar cookie[8];
+	guint32 unknownA;
+	guint16 err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */
+	aim_conn_t *conn;
+	aim_session_t *sess;
+};
+
+struct aim_oft_info {
+	guchar cookie[8];
+	char *sn;
+	char *proxyip;
+	char *clientip;
+	char *verifiedip;
+	guint16 port;
+
+	int send_or_recv; /* Send or receive */
+	int method; /* What method is being used to transfer this file? DIRECT, REDIR, or PROXY */
+	int stage; /* At what stage was a proxy requested? NONE, STG1, STG2*/
+	int xfer_reffed; /* There are many timers, but we should only ref the xfer once */
+	int redir_attempted; /* Have we previously attempted to redirect the connection? */
+	guint32 res_bytes; /* The bytes already received for resuming a transfer */
+
+	aim_conn_t *conn;
+	aim_session_t *sess;
+	int success; /* Was the connection successful? Used for timing out the transfer. */
+	struct aim_fileheader_t fh;
+	struct aim_oft_info *next;
+	struct aim_rv_proxy_info *proxy_info;
+};
+
+faim_export guint32 aim_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevcheck);
+faim_export guint32 aim_oft_checksum_file(char *filename);
+faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
+faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
+faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg);
+faim_export const char *aim_odc_getsn(aim_conn_t *conn);
+faim_export const guchar *aim_odc_getcookie(aim_conn_t *conn);
+faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn);
+faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn, int listenfd,
+                                         const guint8 *localip, guint16 port, const guchar *mycookie);
+faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const guchar *cookie);
+
+faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const guchar *cookie, const char *sn,
+	const char *ip, guint16 port, guint32 size, guint32 modtime, char *filename, int send_or_recv,
+	int method, int stage);
+faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info);
+faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const guchar *cookie, guint16 port);
+faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
+faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info);
+
+faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info);
+faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info);
+
+faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
+faim_export int aim_oft_sendheader(aim_session_t *sess, guint16 type, struct aim_oft_info *oft_info);
+faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn);
+
+
+faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr);
+
+#endif /* _PEER_H_ */
--- a/src/protocols/oscar/popups.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Family 0x0008 - Popups.
- *
- * Popups are just what it sounds like.  They're a way for the server to
- * open up an informative box on the client's screen.
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/*
- * This is all there is to it.
- *
- * The message is probably HTML.
- * 
- */
-static int parsepopup(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tl;
-	int ret = 0;
-	char *msg, *url;
-	fu16_t width, height, delay;
-
-	tl = 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);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, msg, url, width, height, delay);
-
-	aim_tlvlist_free(&tl);
-	free(msg);
-	free(url);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0002)
-		return parsepopup(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int popups_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0008;
-	mod->version = 0x0001;
-	mod->toolid = 0x0104;
-	mod->toolversion = 0x0001;
-	mod->flags = 0;
-	strncpy(mod->name, "popups", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/rxhandlers.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/rxhandlers.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * rxhandlers.c
  *
@@ -7,18 +27,18 @@
  *
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
+#include "peer.h"
 
 struct aim_rxcblist_s {
-	fu16_t family;
-	fu16_t type;
+	guint16 family;
+	guint16 type;
 	aim_rxcallback_t handler;
-	fu16_t flags;
+	guint16 flags;
 	struct aim_rxcblist_s *next;
 };
 
-faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, fu16_t group)
+faim_internal aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group)
 {
 	aim_module_t *cur;
 
@@ -143,7 +163,7 @@
 	return 0;
 }
 
-static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, fu16_t family, fu16_t subtype)
+static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, guint16 family, guint16 subtype)
 {
 	aim_module_t *cur;
 	aim_modsnac_t snac;
@@ -170,7 +190,7 @@
 {
 	aim_tlvlist_t *tlvlist;
 	char *msg = NULL;
-	fu16_t code = 0;
+	guint16 code = 0;
 	aim_rxcallback_t userfunc;
 	int ret = 1;
 
@@ -208,8 +228,8 @@
  */
 static int bleck(aim_session_t *sess, aim_frame_t *frame, ...)
 {
-	fu16_t family, subtype;
-	fu16_t maxf, maxs;
+	guint16 family, subtype;
+	guint16 maxf, maxs;
 
 	static const char *channels[6] = {
 		"Invalid (0)",
@@ -391,7 +411,7 @@
 	return 1;
 }
 
-faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags)
+faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags)
 {
 	struct aim_rxcblist_s *newcb;
 
@@ -441,7 +461,7 @@
 	return 0;
 }
 
-faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type)
+faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type)
 {
 	struct aim_rxcblist_s *cur;
 
@@ -477,7 +497,7 @@
 	return;
 }
 
-faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,fu16_t family, fu16_t type, aim_frame_t *ptr)
+faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,guint16 family, guint16 type, aim_frame_t *ptr)
 {
 	aim_rxcallback_t userfunc;
 
--- a/src/protocols/oscar/rxqueue.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/rxqueue.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,11 +1,30 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * This file contains the management routines for the receive
  * (incoming packet) queue.  The actual packet handlers are in
  * rxhandlers.c.
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
 
 #ifndef _WIN32
 #include <sys/socket.h>
@@ -84,7 +103,7 @@
  */
 static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
 {
-	fu8_t hdr_raw[6];
+	guint8 hdr_raw[6];
 	aim_bstream_t hdr;
 
 	fr->hdrtype = AIM_FRAMETYPE_FLAP;
@@ -132,7 +151,7 @@
  */
 static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
 {
-	fu8_t hdr_raw[8];
+	guint8 hdr_raw[8];
 	aim_bstream_t hdr;
 
 	fr->hdrtype = AIM_FRAMETYPE_OFT;
@@ -204,9 +223,9 @@
 	}
 
 	if (payloadlen > 0) {
-		fu8_t *payload = NULL;
+		guint8 *payload = NULL;
 
-		if (!(payload = (fu8_t *) malloc(payloadlen))) {
+		if (!(payload = (guint8 *) malloc(payloadlen))) {
 			aim_frame_destroy(fr);
 			return -1;
 		}
--- a/src/protocols/oscar/search.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/*
- * Family 0x000a - User Search.
- *
- * TODO: Add aim_usersearch_name()
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/*
- * Subtype 0x0001
- *
- * XXX can this be integrated with the rest of the error handling?
- */
-static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	aim_snac_t *snac2;
-
-	/* XXX the modules interface should have already retrieved this for us */
-	if (!(snac2 = aim_remsnac(sess, snac->id))) {
-		gaim_debug_misc("oscar", "search error: couldn't get a snac for 0x%08lx\n", snac->id);
-		return 0;
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, snac2->data /* address */);
-
-	/* XXX freesnac()? */
-	if (snac2)
-		free(snac2->data);
-	free(snac2);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0002
- *
- */
-faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !address)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1);
-	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
-	
-	aimbs_putstr(&fr->data, address); 
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0003
- *
- */
-static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int j = 0, m, ret = 0;
-	aim_tlvlist_t *tlvlist;
-	char *cur = NULL, *buf = NULL;
-	aim_rxcallback_t userfunc;
-	aim_snac_t *snac2;
-	char *searchaddr = NULL;
-
-	if ((snac2 = aim_remsnac(sess, snac->id)))
-		searchaddr = (char *)snac2->data;
-
-	tlvlist = aim_tlvlist_read(bs);
-	m = aim_tlvlist_count(&tlvlist);
-
-	/* XXX uhm.
-	 * This is the only place that uses something other than 1 for the 3rd 
-	 * parameter to aim_tlv_gettlv_whatever().
-	 */
-	while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) {
-		buf = realloc(buf, (j+1) * (MAXSNLEN+1));
-
-		strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
-		free(cur);
-
-		j++; 
-	}
-
-	aim_tlvlist_free(&tlvlist);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, searchaddr, j, buf);
-
-	/* XXX freesnac()? */
-	if (snac2)
-		free(snac2->data);
-	free(snac2);
-
-	free(buf);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0001)
-		return error(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0003)
-		return reply(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000a;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "search", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/service.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1126 +0,0 @@
-/*
- * Family 0x0001 - This is a very special group.  All connections support
- * this group, as it does some particularly good things (like rate limiting).
- */
-
-#define FAIM_INTERNAL
-#define FAIM_NEED_CONN_INTERNAL
-#include "aim.h"
-
-#include "cipher.h"
-
-/* Subtype 0x0002 - Client Online */
-faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
-	struct snacgroup *sg;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!ins)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
-
-	/*
-	 * Send only the tool versions that the server cares about (that it
-	 * marked as supporting in the server ready SNAC).
-	 */
-	for (sg = ins->groups; sg; sg = sg->next) {
-		aim_module_t *mod;
-
-		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
-			aimbs_put16(&fr->data, mod->family);
-			aimbs_put16(&fr->data, mod->version);
-			aimbs_put16(&fr->data, mod->toolid);
-			aimbs_put16(&fr->data, mod->toolversion);
-		} else
-			gaim_debug_misc("oscar", "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group);
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0003 - Host Online
- *
- * See comments in conn.c about how the group associations are supposed
- * to work, and how they really work.
- *
- * This info probably doesn't even need to make it to the client.
- *
- * We don't actually call the client here.  This starts off the connection
- * initialization routine required by all AIM connections.  The next time
- * the client is called is the CONNINITDONE callback, which should be
- * shortly after the rate information is acknowledged.
- *
- */
-static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	fu16_t *families;
-	int famcount;
-
-
-	if (!(families = malloc(aim_bstream_empty(bs))))
-		return 0;
-
-	for (famcount = 0; aim_bstream_empty(bs); famcount++) {
-		families[famcount] = aimbs_get16(bs);
-		aim_conn_addgroup(rx->conn, families[famcount]);
-	}
-
-	free(families);
-
-
-	/*
-	 * Next step is in the Host Versions handler.
-	 *
-	 * Note that we must send this before we request rates, since
-	 * the format of the rate information depends on the versions we
-	 * give it.
-	 *
-	 */
-	aim_setversions(sess, rx->conn);
-
-	return 1;
-}
-
-/* Subtype 0x0004 - Service request */
-faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid)
-{
-	return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
-}
-
-/*
- * Join a room of name roomname.  This is the first step to joining an 
- * already created room.  It's basically a Service Request for 
- * family 0x000e, with a little added on to specify the exchange and room 
- * name.
- */
-faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-	struct chatsnacinfo csi;
-
-	if (!sess || !conn || !roomname || !strlen(roomname))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
-		return -ENOMEM;
-
-	memset(&csi, 0, sizeof(csi));
-	csi.exchange = exchange;
-	strncpy(csi.name, roomname, sizeof(csi.name));
-	csi.instance = instance;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
-	aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
-
-	/*
-	 * Requesting service chat (0x000e)
-	 */
-	aimbs_put16(&fr->data, 0x000e);
-
-	aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance);
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x0005 - Redirect */
-static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	struct aim_redirect_data redir;
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
-	aim_snac_t *origsnac = NULL;
-	int ret = 0;
-
-	memset(&redir, 0, sizeof(redir));
-
-	tlvlist = aim_tlvlist_read(bs);
-
-	if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
-			!aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
-			!aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
-		aim_tlvlist_free(&tlvlist);
-		return 0;
-	}
-
-	redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
-	redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
-	redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
-	redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
-
-	/* Fetch original SNAC so we can get csi if needed */
-	origsnac = aim_remsnac(sess, snac->id);
-
-	if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
-		struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
-
-		redir.chat.exchange = csi->exchange;
-		redir.chat.room = csi->name;
-		redir.chat.instance = csi->instance;
-	}
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, &redir);
-
-	free((void *)redir.ip);
-	free((void *)redir.cookie);
-
-	if (origsnac)
-		free(origsnac->data);
-	free(origsnac);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/* Subtype 0x0006 - Request Rate Information. */
-faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006);
-}
-
-/*
- * OSCAR defines several 'rate classes'.  Each class has separate
- * rate limiting properties (limit level, alert level, disconnect
- * level, etc), and a set of SNAC family/type pairs associated with
- * it.  The rate classes, their limiting properties, and the definitions
- * of which SNACs are belong to which class, are defined in the
- * Rate Response packet at login to each host.  
- *
- * Logically, all rate offenses within one class count against further
- * offenses for other SNACs in the same class (ie, sending messages
- * too fast will limit the number of user info requests you can send,
- * since those two SNACs are in the same rate class).
- *
- * Since the rate classes are defined dynamically at login, the values
- * below may change. But they seem to be fairly constant.
- *
- * Currently, BOS defines five rate classes, with the commonly used
- * members as follows...
- *
- *  Rate class 0x0001:
- *  	- Everything thats not in any of the other classes
- *
- *  Rate class 0x0002:
- * 	- Buddy list add/remove
- *	- Permit list add/remove
- *	- Deny list add/remove
- *
- *  Rate class 0x0003:
- *	- User information requests
- *	- Outgoing ICBMs
- *
- *  Rate class 0x0004:
- *	- A few unknowns: 2/9, 2/b, and f/2
- *
- *  Rate class 0x0005:
- *	- Chat room create
- *	- Outgoing chat ICBMs
- *
- * The only other thing of note is that class 5 (chat) has slightly looser
- * limiting properties than class 3 (normal messages).  But thats just a 
- * small bit of trivia for you.
- *
- * The last thing that needs to be learned about the rate limiting
- * system is how the actual numbers relate to the passing of time.  This
- * seems to be a big mystery.
- * 
- */
-
-static void rc_addclass(struct rateclass **head, struct rateclass *inrc)
-{
-	struct rateclass *rc, *rc2;
-
-	if (!(rc = malloc(sizeof(struct rateclass))))
-		return;
-
-	memcpy(rc, inrc, sizeof(struct rateclass));
-	rc->next = NULL;
-
-	for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next)
-		;
-
-	if (!rc2)
-		*head = rc;
-	else
-		rc2->next = rc;
-
-	return;
-}
-
-static struct rateclass *rc_findclass(struct rateclass **head, fu16_t id)
-{
-	struct rateclass *rc;
-
-	for (rc = *head; rc; rc = rc->next) {
-		if (rc->classid == id)
-			return rc;
-	}
-
-	return NULL;
-}
-
-static void rc_addpair(struct rateclass *rc, fu16_t group, fu16_t type)
-{
-	struct snacpair *sp, *sp2;
-
-	if (!(sp = malloc(sizeof(struct snacpair))))
-		return;
-	memset(sp, 0, sizeof(struct snacpair));
-
-	sp->group = group;
-	sp->subtype = type;
-	sp->next = NULL;
-
-	for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next)
-		;
-
-	if (!sp2)
-		rc->members = sp;
-	else
-		sp2->next = sp;
-
-	return;
-}
-
-/* Subtype 0x0007 - Rate Parameters */
-static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
-	fu16_t numclasses, i;
-	aim_rxcallback_t userfunc;
-
-
-	/*
-	 * First are the parameters for each rate class.
-	 */
-	numclasses = aimbs_get16(bs);
-	for (i = 0; i < numclasses; i++) {
-		struct rateclass rc;
-
-		memset(&rc, 0, sizeof(struct rateclass));
-
-		rc.classid = aimbs_get16(bs);
-		rc.windowsize = aimbs_get32(bs);
-		rc.clear = aimbs_get32(bs);
-		rc.alert = aimbs_get32(bs);
-		rc.limit = aimbs_get32(bs);
-		rc.disconnect = aimbs_get32(bs);
-		rc.current = aimbs_get32(bs);
-		rc.max = aimbs_get32(bs);
-
-		/*
-		 * The server will send an extra five bytes of parameters
-		 * depending on the version we advertised in 1/17.  If we
-		 * didn't send 1/17 (evil!), then this will crash and you
-		 * die, as it will default to the old version but we have 
-		 * the new version hardcoded here. 
-		 */
-		if (mod->version >= 3)
-			aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown));
-
-		gaim_debug_misc("oscar", "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max);
-
-		rc_addclass(&ins->rates, &rc);
-	}
-
-	/*
-	 * Then the members of each class.
-	 */
-	for (i = 0; i < numclasses; i++) {
-		fu16_t classid, count;
-		struct rateclass *rc;
-		int j;
-
-		classid = aimbs_get16(bs);
-		count = aimbs_get16(bs);
-
-		rc = rc_findclass(&ins->rates, classid);
-
-		for (j = 0; j < count; j++) {
-			fu16_t group, subtype;
-
-			group = aimbs_get16(bs);
-			subtype = aimbs_get16(bs);
-
-			if (rc)
-				rc_addpair(rc, group, subtype);
-		}
-	}
-
-	/*
-	 * We don't pass the rate information up to the client, as it really
-	 * doesn't care.  The information is stored in the connection, however
-	 * so that we can do more fun stuff later (not really).
-	 */
-
-	/*
-	 * Last step in the conn init procedure is to acknowledge that we
-	 * agree to these draconian limitations.
-	 */
-	aim_rates_addparam(sess, rx->conn);
-
-	/*
-	 * Finally, tell the client it's ready to go...
-	 */
-	if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
-		userfunc(sess, rx);
-
-
-	return 1;
-}
-
-/* Subtype 0x0008 - Add Rate Parameter */
-faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	struct rateclass *rc;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
-
-	for (rc = ins->rates; rc; rc = rc->next)
-		aimbs_put16(&fr->data, rc->classid);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x0009 - Delete Rate Parameter */
-faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	struct rateclass *rc;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid);
-
-	for (rc = ins->rates; rc; rc = rc->next)
-		aimbs_put16(&fr->data, rc->classid);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x000a - Rate Change */
-static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t code, rateclass;
-	fu32_t currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
-
-	code = aimbs_get16(bs);
-	rateclass = aimbs_get16(bs);
-
-	windowsize = aimbs_get32(bs);
-	clear = aimbs_get32(bs);
-	alert = aimbs_get32(bs);
-	limit = aimbs_get32(bs);
-	disconnect = aimbs_get32(bs);
-	currentavg = aimbs_get32(bs);
-	maxavg = aimbs_get32(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
-
-	return ret;
-}
-
-/*
- * How Migrations work.
- *
- * The server sends a Server Pause message, which the client should respond to 
- * with a Server Pause Ack, which contains the families it needs on this 
- * connection. The server will send a Migration Notice with an IP address, and 
- * then disconnect. Next the client should open the connection and send the 
- * cookie.  Repeat the normal login process and pretend this never happened.
- *
- * The Server Pause contains no data.
- *
- */
-
-/* Subtype 0x000b - Service Pause */
-static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx);
-
-	return ret;
-}
-
-/*
- * Subtype 0x000c - Service Pause Acknowledgement
- *
- * It is rather important that aim_sendpauseack() gets called for the exact
- * same connection that the Server Pause callback was called for, since
- * libfaim extracts the data for the SNAC from the connection structure.
- *
- * Of course, if you don't do that, more bad things happen than just what
- * libfaim can cause.
- *
- */
-faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
-	struct snacgroup *sg;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid);
-
-	/*
-	 * This list should have all the groups that the original 
-	 * Host Online / Server Ready said this host supports.  And 
-	 * we want them all back after the migration.
-	 */
-	for (sg = ins->groups; sg; sg = sg->next)
-		aimbs_put16(&fr->data, sg->group);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x000d - Service Resume */
-static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx);
-
-	return ret;
-}
-
-/* Subtype 0x000e - Request self-info */
-faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e);
-}
-
-/* Subtype 0x000f - Self User Info */
-static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	aim_userinfo_t userinfo;
-
-	aim_info_extract(sess, bs, &userinfo);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, &userinfo);
-
-	aim_info_free(&userinfo);
-
-	return ret;
-}
-
-/* Subtype 0x0010 - Evil Notification */
-static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t newevil;
-	aim_userinfo_t userinfo;
-
-	memset(&userinfo, 0, sizeof(aim_userinfo_t));
-
-	newevil = aimbs_get16(bs);
-
-	if (aim_bstream_empty(bs))
-		aim_info_extract(sess, bs, &userinfo);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, newevil, &userinfo);
-
-	aim_info_free(&userinfo);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0011 - Idle Notification
- *
- * Should set your current idle time in seconds.  Note that this should
- * never be called consecutively with a non-zero idle time.  That makes
- * OSCAR do funny things.  Instead, just set it once you go idle, and then
- * call it again with zero when you're back.
- *
- */
-faim_export int aim_srv_setidle(aim_session_t *sess, fu32_t idletime)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_BOS)))
-		return -EINVAL;
-
-	return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
-}
-
-/*
- * Subtype 0x0012 - Service Migrate
- *
- * This is the final SNAC sent on the original connection during a migration.
- * It contains the IP and cookie used to connect to the new server, and 
- * optionally a list of the SNAC groups being migrated.
- *
- */
-static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-	fu16_t groupcount, i;
-	aim_tlvlist_t *tl;
-	char *ip = NULL;
-	aim_tlv_t *cktlv;
-
-	/*
-	 * Apparently there's some fun stuff that can happen right here. The
-	 * migration can actually be quite selective about what groups it
-	 * moves to the new server.  When not all the groups for a connection
-	 * are migrated, or they are all migrated but some groups are moved
-	 * to a different server than others, it is called a bifurcated 
-	 * migration.
-	 *
-	 * Let's play dumb and not support that.
-	 *
-	 */
-	groupcount = aimbs_get16(bs);
-	for (i = 0; i < groupcount; i++) {
-		fu16_t group;
-
-		group = aimbs_get16(bs);
-
-		gaim_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
-	}
-
-	tl = aim_tlvlist_read(bs);
-
-	if (aim_tlv_gettlv(tl, 0x0005, 1))
-		ip = aim_tlv_getstr(tl, 0x0005, 1);
-
-	cktlv = aim_tlv_gettlv(tl, 0x0006, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL);
-
-	aim_tlvlist_free(&tl);
-	free(ip);
-
-	return ret;
-}
-
-/* Subtype 0x0013 - Message of the Day */
-static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	aim_rxcallback_t userfunc;
-	char *msg = NULL;
-	int ret = 0;
-	aim_tlvlist_t *tlvlist;
-	fu16_t id;
-
-	/*
-	 * Code.
-	 *
-	 * Valid values:
-	 *   1 Mandatory upgrade
-	 *   2 Advisory upgrade
-	 *   3 System bulletin
-	 *   4 Nothing's wrong ("top o the world" -- normal)
-	 *   5 Lets-break-something. 
-	 *
-	 */
-	id = aimbs_get16(bs);
-
-	/* 
-	 * TLVs follow 
-	 */
-	tlvlist = aim_tlvlist_read(bs);
-
-	msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, id, msg);
-
-	free(msg);
-
-	aim_tlvlist_free(&tlvlist);
-
-	return ret;
-}
-
-/* 
- * Subtype 0x0014 - Set privacy flags
- *
- * Normally 0x03.
- *
- *  Bit 1:  Allows other AIM users to see how long you've been idle.
- *  Bit 2:  Allows other AIM users to see how long you've been a member.
- *
- */
-faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, fu32_t flags)
-{
-	return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
-}
-
-/*
- * Subtype 0x0016 - No-op
- *
- * WinAIM sends these every 4min or so to keep the connection alive.  Its not 
- * really necessary.
- *
- * Wha?  No?  Since when?  I think WinAIM sends an empty channel 3 
- * SNAC as a no-op...
- */
-faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
-}
-
-/* 
- * Subtype 0x0017 - Set client versions
- *
- * If you've seen the clientonline/clientready SNAC you're probably 
- * wondering what the point of this one is.  And that point seems to be
- * that the versions in the client online SNAC are sent too late for the
- * server to be able to use them to change the protocol for the earlier
- * login packets (client versions are sent right after Host Online is 
- * received, but client online versions aren't sent until quite a bit later).
- * We can see them already making use of this by changing the format of
- * the rate information based on what version of group 1 we advertise here.
- *
- */
-faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
-	struct snacgroup *sg;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!ins)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
-
-	/*
-	 * Send only the versions that the server cares about (that it
-	 * marked as supporting in the server ready SNAC).  
-	 */
-	for (sg = ins->groups; sg; sg = sg->next) {
-		aim_module_t *mod;
-
-		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
-			aimbs_put16(&fr->data, mod->family);
-			aimbs_put16(&fr->data, mod->version);
-		} else
-			gaim_debug_misc("oscar", "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group);
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* Subtype 0x0018 - Host versions */
-static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int vercount;
-	fu8_t *versions;
-
-	/* This is frivolous. (Thank you SmarterChild.) */
-	vercount = aim_bstream_empty(bs)/4;
-	versions = aimbs_getraw(bs, aim_bstream_empty(bs));
-	free(versions);
-
-	/*
-	 * Now request rates.
-	 */
-	aim_reqrates(sess, rx->conn);
-
-	return 1;
-}
-
-/* 
- * Subtype 0x001e - Set various account settings (mostly ICQ related).
- *
- * These settings are transient, not server-stored (i.e. they only
- * apply to this session, and must be re-set the next time you sign
- * on).
- *
- * You can set your ICQ status (available, away, do not disturb,
- * etc.), or whether your IP address should be hidden or not, or
- * if your status is visible on ICQ web sites, and you can set
- * your IP address info and what not.
- *
- * These are the same TLVs seen in user info.  You can
- * also set 0x0008 and 0x000c.
- */
-faim_export int aim_setextstatus(aim_session_t *sess, fu32_t status)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-	fu32_t data;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_MSG)))
-		return -EINVAL;
-
-	data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
-
-	aim_tlvlist_add_32(&tl, 0x0006, data);
-#if 0
-	aim_tlvlist_add_raw(&tl, 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);
-#endif
-	aim_tlvlist_write(&fr->data, &tl);
-	aim_tlvlist_free(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x001e - Extended Status.
- *
- * Sets your "available" message.  This is currently only supported by iChat
- * and Gaim.
- *
- * These are the same TLVs seen in user info.  You can
- * also set 0x0008 and 0x000c.
- */
-faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if ((msg != NULL) && *msg != '\0') {
-		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8)))
-			return -ENOMEM;
-
-		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
-		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
-
-		aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */
-		aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */
-		aimbs_put16(&fr->data, 0x0002);
-		aimbs_put8(&fr->data, 0x04);
-		aimbs_put8(&fr->data, strlen(msg)+4);
-		aimbs_put16(&fr->data, strlen(msg));
-		aimbs_putstr(&fr->data, msg);
-		aimbs_put16(&fr->data, 0x0000);
-	} else {
-		if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8)))
-			return -ENOMEM;
-
-		snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
-		aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
-
-		aimbs_put16(&fr->data, 0x001d);
-		aimbs_put16(&fr->data, 0x0008);
-		aimbs_put16(&fr->data, 0x0002);
-		aimbs_put16(&fr->data, 0x0404);
-		aimbs_put16(&fr->data, 0x0000);
-		aimbs_put16(&fr->data, 0x0000);
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Starting this past week (26 Mar 2001, say), AOL has started sending
- * this nice little extra SNAC.  AFAIK, it has never been used until now.
- *
- * The request contains eight bytes.  The first four are an offset, the
- * second four are a length.
- *
- * The offset is an offset into aim.exe when it is mapped during execution
- * on Win32.  So far, AOL has only been requesting bytes in static regions
- * of memory.  (I won't put it past them to start requesting data in
- * less static regions -- regions that are initialized at run time, but still
- * before the client receives this request.)
- *
- * When the client receives the request, it adds it to the current ds
- * (0x00400000) and dereferences it, copying the data into a buffer which
- * it then runs directly through the MD5 hasher.  The 16 byte output of
- * the hash is then sent back to the server.
- *
- * If the client does not send any data back, or the data does not match
- * the data that the specific client should have, the client will get the
- * following message from "AOL Instant Messenger":
- *    "You have been disconnected from the AOL Instant Message Service (SM) 
- *     for accessing the AOL network using unauthorized software.  You can
- *     download a FREE, fully featured, and authorized client, here 
- *     http://www.aol.com/aim/download2.html"
- * The connection is then closed, receiving disconnect code 1, URL
- * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.  
- *
- * Note, however, that numerous inconsistencies can cause the above error, 
- * not just sending back a bad hash.  Do not immediatly suspect this code
- * if you get disconnected.  AOL and the open/free software community have
- * played this game for a couple years now, generating the above message
- * on numerous ocassions.
- *
- * Anyway, neener.  We win again.
- *
- */
-/* Subtype 0x001f - Client verification */
-static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu32_t offset, len;
-	aim_tlvlist_t *list;
-	char *modname;
-
-	offset = aimbs_get32(bs);
-	len = aimbs_get32(bs);
-	list = aim_tlvlist_read(bs);
-
-	modname = aim_tlv_getstr(list, 0x0001, 1);
-
-	gaim_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, offset, len, modname);
-
-	free(modname);
-	aim_tlvlist_free(&list);
-
-	return ret;
-}
-
-/* Subtype 0x0020 - Client verification reply */
-faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid);
-	aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */
-
-	if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
-
-		aimbs_putraw(&fr->data, buf, 0x10);
-
-	} else if (buf && (len > 0)) { /* use input buffer */
-		GaimCipher *cipher;
-		GaimCipherContext *context;
-		guchar digest[16];
-
-		cipher = gaim_ciphers_find_cipher("md5");
-
-		context = gaim_cipher_context_new(cipher, NULL);
-		gaim_cipher_context_append(context, buf, len);
-		gaim_cipher_context_digest(context, 16, digest, NULL);
-		gaim_cipher_context_destroy(context);
-
-		aimbs_putraw(&fr->data, digest, 0x10);
-
-	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
-		GaimCipher *cipher;
-		GaimCipherContext *context;
-		guchar digest[16];
-		fu8_t nil = '\0';
-
-		/*
-		 * I'm not sure if we really need the empty append with the
-		 * new MD5 functions, so I'll leave it in, just in case.
-		 */
-		cipher = gaim_ciphers_find_cipher("md5");
-
-		context = gaim_cipher_context_new(cipher, NULL);
-		gaim_cipher_context_append(context, &nil, 0);
-		gaim_cipher_context_digest(context, 16, digest, NULL);
-		gaim_cipher_context_destroy(context);
-
-		aimbs_putraw(&fr->data, digest, 0x10);
-
-	} else {
-
-		/*
-		 * This data is correct for AIM 3.5.1670.
-		 *
-		 * Using these blocks is as close to "legal" as you can get
-		 * without using an AIM binary.
-		 *
-		 */
-		if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
-
-#if 1 /* with "AnrbnrAqhfzcd" */
-			aimbs_put32(&fr->data, 0x44a95d26);
-			aimbs_put32(&fr->data, 0xd2490423);
-			aimbs_put32(&fr->data, 0x93b8821f);
-			aimbs_put32(&fr->data, 0x51c54b01);
-#else /* no filename */
-			aimbs_put32(&fr->data, 0x1df8cbae);
-			aimbs_put32(&fr->data, 0x5523b839);
-			aimbs_put32(&fr->data, 0xa0e10db3);
-			aimbs_put32(&fr->data, 0xa46d3b39);
-#endif
-
-		} else if ((offset == 0x00001000) && (len == 0x00000000)) {
-
-			aimbs_put32(&fr->data, 0xd41d8cd9);
-			aimbs_put32(&fr->data, 0x8f00b204);
-			aimbs_put32(&fr->data, 0xe9800998);
-			aimbs_put32(&fr->data, 0xecf8427e);
-
-		} else
-			gaim_debug_warning("oscar", "sendmemblock: unknown hash request\n");
-
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0021 - Receive our extended status
- *
- * This is used for iChat's "available" messages, and maybe ICQ extended
- * status messages?  It's also used to tell the client whether or not it
- * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
- */
-static int aim_parse_extstatus(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t type;
-	fu8_t flags, length;
-
-	type = aimbs_get16(bs);
-	flags = aimbs_get8(bs);
-	length = aimbs_get8(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
-		switch (type) {
-		case 0x0000:
-		case 0x0001: { /* buddy icon checksum */
-			/* not sure what the difference between 1 and 0 is */
-			fu8_t *md5 = aimbs_getraw(bs, length);
-			ret = userfunc(sess, rx, type, flags, length, md5);
-			free(md5);
-			} break;
-		case 0x0002: { /* available message */
-			/* there is a second length that is just for the message */
-			char *msg = aimbs_getstr(bs, aimbs_get16(bs));
-			ret = userfunc(sess, rx, msg);
-			free(msg);
-			} break;
-		}
-	}
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return hostonline(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0005)
-		return redirect(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0007)
-		return rateresp(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000a)
-		return ratechange(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000b)
-		return serverpause(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000d)
-		return serverresume(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x000f)
-		return selfinfo(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0010)
-		return evilnotify(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0012)
-		return migrate(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0013)
-		return motd(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0018)
-		return hostversions(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x001f)
-		return memrequest(sess, mod, rx, snac, bs);
-	else if (snac->subtype == 0x0021)
-		return aim_parse_extstatus(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0001;
-	mod->version = 0x0003;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "service", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/snac.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/snac.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  *
  * Various SNAC-related dodads...
@@ -12,8 +32,7 @@
  *
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
 
 /*
  * Called from aim_session_init() to initialize the hash.
@@ -28,7 +47,7 @@
 	return;
 }
 
-faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen)
+faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
 {
 	aim_snac_t snac;
 
@@ -136,7 +155,7 @@
 	return;
 }
 
-faim_internal int aim_putsnac(aim_bstream_t *bs, fu16_t family, fu16_t subtype, fu16_t flags, aim_snacid_t snacid)
+faim_internal int aim_putsnac(aim_bstream_t *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid)
 {
 
 	aimbs_put16(bs, family);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/snactypes.h	Sat Feb 11 21:45:18 2006 +0000
@@ -0,0 +1,302 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/*
+ * AIM Callback Types
+ *
+ */
+#ifndef _SNACTYPES_H_
+#define _SNACTYPES_H_
+
+/*
+ * SNAC Families.
+ */
+#define AIM_CB_FAM_ACK          0x0000
+#define OSCAR_FAMILY_OSERVICE   0x0001
+#define OSCAR_FAMILY_LOCATE     0x0002
+#define OSCAR_FAMILY_BUDDY      0x0003
+#define OSCAR_FAMILY_ICBM       0x0004
+#define OSCAR_FAMILY_ADVERT     0x0005
+#define OSCAR_FAMILY_INVITE     0x0006
+#define OSCAR_FAMILY_ADMIN      0x0007
+#define OSCAR_FAMILY_POPUP      0x0008
+#define OSCAR_FAMILY_BOS        0x0009
+#define OSCAR_FAMILY_USERLOOKUP 0x000a
+#define OSCAR_FAMILY_STATS      0x000b
+#define OSCAR_FAMILY_TRANSLATE  0x000c
+#define OSCAR_FAMILY_CHATNAV    0x000d
+#define OSCAR_FAMILY_CHAT       0x000e
+#define OSCAR_FAMILY_ODIR       0x000f
+#define OSCAR_FAMILY_BART       0x0010
+#define OSCAR_FAMILY_FEEDBAG    0x0013
+#define OSCAR_FAMILY_ICQ        0x0015
+#define OSCAR_FAMILY_AUTH       0x0017
+#define OSCAR_FAMILY_ALERT      0x0018
+
+#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
+
+/*
+ * SNAC Family: Ack.
+ *
+ * Not really a family, but treating it as one really
+ * helps it fit into the libfaim callback structure better.
+ *
+ */
+#define AIM_CB_ACK_ACK 0x0001
+
+/*
+ * SNAC Family: General.
+ */
+#define OSCAR_SUBTYPE_OSERVICE_ERROR 0x0001
+#define OSCAR_SUBTYPE_OSERVICE_CLIENTREADY 0x0002
+#define OSCAR_SUBTYPE_OSERVICE_SERVERREADY 0x0003
+#define OSCAR_SUBTYPE_OSERVICE_SERVICEREQ 0x0004
+#define OSCAR_SUBTYPE_OSERVICE_REDIRECT 0x0005
+#define OSCAR_SUBTYPE_OSERVICE_RATEINFOREQ 0x0006
+#define OSCAR_SUBTYPE_OSERVICE_RATEINFO 0x0007
+#define OSCAR_SUBTYPE_OSERVICE_RATEINFOACK 0x0008
+#define OSCAR_SUBTYPE_OSERVICE_RATECHANGE 0x000a
+#define OSCAR_SUBTYPE_OSERVICE_SERVERPAUSE 0x000b
+#define OSCAR_SUBTYPE_OSERVICE_SERVERRESUME 0x000d
+#define OSCAR_SUBTYPE_OSERVICE_REQSELFINFO 0x000e
+#define OSCAR_SUBTYPE_OSERVICE_SELFINFO 0x000f
+#define OSCAR_SUBTYPE_OSERVICE_EVIL 0x0010
+#define OSCAR_SUBTYPE_OSERVICE_SETIDLE 0x0011
+#define OSCAR_SUBTYPE_OSERVICE_MIGRATIONREQ 0x0012
+#define OSCAR_SUBTYPE_OSERVICE_MOTD 0x0013
+#define OSCAR_SUBTYPE_OSERVICE_SETPRIVFLAGS 0x0014
+#define OSCAR_SUBTYPE_OSERVICE_WELLKNOWNURL 0x0015
+#define OSCAR_SUBTYPE_OSERVICE_NOP 0x0016
+#define OSCAR_SUBTYPE_OSERVICE_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Location Services.
+ */
+#define OSCAR_SUBTYPE_LOCATE_ERROR 0x0001
+#define OSCAR_SUBTYPE_LOCATE_REQRIGHTS 0x0002
+#define OSCAR_SUBTYPE_LOCATE_RIGHTSINFO 0x0003
+#define OSCAR_SUBTYPE_LOCATE_SETUSERINFO 0x0004
+#define OSCAR_SUBTYPE_LOCATE_REQUSERINFO 0x0005
+#define OSCAR_SUBTYPE_LOCATE_USERINFO 0x0006
+#define OSCAR_SUBTYPE_LOCATE_WATCHERSUBREQ 0x0007
+#define OSCAR_SUBTYPE_LOCATE_WATCHERNOT 0x0008
+#define OSCAR_SUBTYPE_LOCATE_GOTINFOBLOCK 0xfffd
+#define OSCAR_SUBTYPE_LOCATE_REQUESTINFOTIMEOUT 0xfffe
+#define OSCAR_SUBTYPE_LOCATE_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Buddy List Management Services.
+ */
+#define OSCAR_SUBTYPE_BUDDY_ERROR 0x0001
+#define OSCAR_SUBTYPE_BUDDY_REQRIGHTS 0x0002
+#define OSCAR_SUBTYPE_BUDDY_RIGHTSINFO 0x0003
+#define OSCAR_SUBTYPE_BUDDY_ADDBUDDY 0x0004
+#define OSCAR_SUBTYPE_BUDDY_REMBUDDY 0x0005
+#define OSCAR_SUBTYPE_BUDDY_REJECT 0x000a
+#define OSCAR_SUBTYPE_BUDDY_ONCOMING 0x000b
+#define OSCAR_SUBTYPE_BUDDY_OFFGOING 0x000c
+#define OSCAR_SUBTYPE_BUDDY_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Messaging Services.
+ */
+#define OSCAR_SUBTYPE_ICBM_ERROR 0x0001
+#define OSCAR_SUBTYPE_ICBM_PARAMINFO 0x0005
+#define OSCAR_SUBTYPE_ICBM_INCOMING 0x0007
+#define OSCAR_SUBTYPE_ICBM_EVIL 0x0009
+#define OSCAR_SUBTYPE_ICBM_MISSEDCALL 0x000a
+#define OSCAR_SUBTYPE_ICBM_CLIENTAUTORESP 0x000b
+#define OSCAR_SUBTYPE_ICBM_ACK 0x000c
+#define OSCAR_SUBTYPE_ICBM_MTN 0x0014
+#define OSCAR_SUBTYPE_ICBM_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Advertisement Services
+ */
+#define OSCAR_SUBTYPE_ADVERT_ERROR 0x0001
+#define OSCAR_SUBTYPE_ADVERT_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Invitation Services.
+ */
+#define OSCAR_SUBTYPE_INVITE_ERROR 0x0001
+#define OSCAR_SUBTYPE_INVITE_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Administrative Services.
+ */
+#define OSCAR_SUBTYPE_ADMIN_ERROR 0x0001
+#define OSCAR_SUBTYPE_ADMIN_INFOCHANGE_REPLY 0x0005
+#define OSCAR_SUBTYPE_ADMIN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Popup Messages
+ */
+#define OSCAR_SUBTYPE_POPUP_ERROR 0x0001
+#define OSCAR_SUBTYPE_POPUP_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Misc BOS Services.
+ */
+#define OSCAR_SUBTYPE_BOS_ERROR 0x0001
+#define OSCAR_SUBTYPE_BOS_RIGHTSQUERY 0x0002
+#define OSCAR_SUBTYPE_BOS_RIGHTS 0x0003
+#define OSCAR_SUBTYPE_BOS_DEFAULT 0xffff
+
+/*
+ * SNAC Family: User Lookup Services
+ */
+#define OSCAR_SUBTYPE_USERLOOKUP_ERROR 0x0001
+#define OSCAR_SUBTYPE_USERLOOKUP_DEFAULT 0xffff
+
+/*
+ * SNAC Family: User Status Services
+ */
+#define OSCAR_SUBTYPE_STATS_ERROR 0x0001
+#define OSCAR_SUBTYPE_STATS_SETREPORTINTERVAL 0x0002
+#define OSCAR_SUBTYPE_STATS_REPORTACK 0x0004
+#define OSCAR_SUBTYPE_STATS_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Translation Services
+ */
+#define OSCAR_SUBTYPE_TRANSLATE_ERROR 0x0001
+#define OSCAR_SUBTYPE_TRANSLATE_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Chat Navigation Services
+ */
+#define OSCAR_SUBTYPE_CHATNAV_ERROR 0x0001
+#define OSCAR_SUBTYPE_CHATNAV_CREATE 0x0008
+#define OSCAR_SUBTYPE_CHATNAV_INFO 0x0009
+#define OSCAR_SUBTYPE_CHATNAV_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Chat Services
+ */
+#define OSCAR_SUBTYPE_CHAT_ERROR 0x0001
+#define OSCAR_SUBTYPE_CHAT_ROOMINFOUPDATE 0x0002
+#define OSCAR_SUBTYPE_CHAT_USERJOIN 0x0003
+#define OSCAR_SUBTYPE_CHAT_USERLEAVE 0x0004
+#define OSCAR_SUBTYPE_CHAT_OUTGOINGMSG 0x0005
+#define OSCAR_SUBTYPE_CHAT_INCOMINGMSG 0x0006
+#define OSCAR_SUBTYPE_CHAT_DEFAULT 0xffff
+
+/*
+ * SNAC Family: "New" Search
+ */
+#define OSCAR_SUBTYPE_ODIR_ERROR 0x0001
+#define OSCAR_SUBTYPE_ODIR_SEARCH 0x0002
+#define OSCAR_SUBTYPE_ODIR_RESULTS 0x0003
+
+/*
+ * SNAC Family: Buddy icons
+ */
+#define OSCAR_SUBTYPE_BART_ERROR 0x0001
+#define OSCAR_SUBTYPE_BART_REQUEST 0x0004
+#define OSCAR_SUBTYPE_BART_RESPONSE 0x0005
+
+/*
+ * SNAC Family: Server-Stored Buddy Lists
+ */
+#define OSCAR_SUBTYPE_FEEDBAG_ERROR 0x0001
+#define OSCAR_SUBTYPE_FEEDBAG_REQRIGHTS 0x0002
+#define OSCAR_SUBTYPE_FEEDBAG_RIGHTSINFO 0x0003
+#define OSCAR_SUBTYPE_FEEDBAG_REQDATA 0x0004
+#define OSCAR_SUBTYPE_FEEDBAG_REQIFCHANGED 0x0005
+#define OSCAR_SUBTYPE_FEEDBAG_LIST 0x0006
+#define OSCAR_SUBTYPE_FEEDBAG_ACTIVATE 0x0007
+#define OSCAR_SUBTYPE_FEEDBAG_ADD 0x0008
+#define OSCAR_SUBTYPE_FEEDBAG_MOD 0x0009
+#define OSCAR_SUBTYPE_FEEDBAG_DEL 0x000A
+#define OSCAR_SUBTYPE_FEEDBAG_SRVACK 0x000E
+#define OSCAR_SUBTYPE_FEEDBAG_NOLIST 0x000F
+#define OSCAR_SUBTYPE_FEEDBAG_EDITSTART 0x0011
+#define OSCAR_SUBTYPE_FEEDBAG_EDITSTOP 0x0012
+#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTH 0x0014
+#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTH 0x0015
+#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREQ 0x0018
+#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREQ 0x0019
+#define OSCAR_SUBTYPE_FEEDBAG_SENDAUTHREP 0x001a
+#define OSCAR_SUBTYPE_FEEDBAG_RECVAUTHREP 0x001b
+#define OSCAR_SUBTYPE_FEEDBAG_ADDED 0x001c
+
+/*
+ * SNAC Family: ICQ
+ *
+ * Most of these are actually special.
+ */
+#define OSCAR_SUBTYPE_ICQ_ERROR 0x0001
+#define OSCAR_SUBTYPE_ICQ_OFFLINEMSG 0x00f0
+#define OSCAR_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE 0x00f1
+#define OSCAR_SUBTYPE_ICQ_INFO 0x00f2
+#define OSCAR_SUBTYPE_ICQ_ALIAS 0x00f3
+#define OSCAR_SUBTYPE_ICQ_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Authorizer
+ *
+ * Used only in protocol versions three and above.
+ *
+ */
+#define OSCAR_SUBTYPE_AUTH_ERROR 0x0001
+#define OSCAR_SUBTYPE_AUTH_LOGINREQEST 0x0002
+#define OSCAR_SUBTYPE_AUTH_LOGINRESPONSE 0x0003
+#define OSCAR_SUBTYPE_AUTH_AUTHREQ 0x0006
+#define OSCAR_SUBTYPE_AUTH_AUTHRESPONSE 0x0007
+#define OSCAR_SUBTYPE_AUTH_SECURID_REQUEST 0x000a
+#define OSCAR_SUBTYPE_AUTH_SECURID_RESPONSE 0x000b
+
+/*
+ * SNAC Family: Email
+ *
+ * Used for getting information on the email address
+ * associated with your screen name.
+ *
+ */
+#define OSCAR_SUBTYPE_ALERT_ERROR 0x0001
+#define OSCAR_SUBTYPE_ALERT_SENDCOOKIES 0x0006
+#define OSCAR_SUBTYPE_ALERT_MAILSTATUS 0x0007
+#define OSCAR_SUBTYPE_ALERT_INIT 0x0016
+
+/*
+ * SNAC Family: Internal Messages
+ *
+ * This isn't truly a SNAC family either, but using
+ * these, we can integrated non-SNAC services into
+ * the SNAC-centered libfaim callback structure.
+ *
+ */
+#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
+#define AIM_CB_SPECIAL_AUTHOTHER 0x0002
+#define AIM_CB_SPECIAL_CONNERR 0x0003
+#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
+#define AIM_CB_SPECIAL_FLAPVER 0x0005
+#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
+#define AIM_CB_SPECIAL_IMAGETRANSFER 0x0007
+#define AIM_CB_SPECIAL_MSGTIMEOUT 0x0008
+#define AIM_CB_SPECIAL_CONNDEAD 0x0009
+#define AIM_CB_SPECIAL_UNKNOWN 0xffff
+#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
+
+/* SNAC flags */
+#define AIM_SNACFLAGS_DESTRUCTOR 0x0001
+
+#endif /* _SNACTYPES_H_ */
--- a/src/protocols/oscar/ssi.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1988 +0,0 @@
-/*
- * Family 0x0013 - Server-Side/Stored Information.
- *
- * Relatively new facility that allows certain types of information, such as 
- * a user's buddy list, permit/deny list, and permit/deny preferences, to be 
- * stored on the server, so that they can be accessed from any client.
- *
- * We keep 2 copies of SSI data:
- * 1) An exact copy of what is stored on the AIM servers.
- * 2) A local copy that we make changes to, and then send diffs 
- *    between this and the exact copy to keep them in sync.
- *
- * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list 
- * that is given to them (i.e. they don't send SNACs).
- *
- * The SNAC sending and receiving functions are lower down in the file, and 
- * they're simpler.  They are in the order of the subtypes they deal with, 
- * starting with the request rights function (subtype 0x0002), then parse 
- * rights (subtype 0x0003), then--well, you get the idea.
- *
- * This is entirely too complicated.
- * You don't know the half of it.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-/**
- * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
- *
- * @param list A pointer to a pointer to the current list of items.
- * @param name A null terminated string containing the group name, or NULL 
- *        if you want to modify the master group.
- * @return Return a pointer to the modified item.
- */
-static struct aim_ssi_item *aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
-{
-	int newlen;
-	struct aim_ssi_item *cur, *group;
-
-	if (!list)
-		return NULL;
-
-	/* Find the group */
-	if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
-		return NULL;
-
-	/* Find the length for the new additional data */
-	newlen = 0;
-	if (group->gid == 0x0000) {
-		for (cur=list; cur; cur=cur->next)
-			if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
-				newlen += 2;
-	} else {
-		for (cur=list; cur; cur=cur->next)
-			if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
-				newlen += 2;
-	}
-
-	/* Build the new TLV list */
-	if (newlen > 0) {
-		fu8_t *newdata;
-
-		if (!(newdata = (fu8_t *)malloc((newlen)*sizeof(fu8_t))))
-			return NULL;
-		newlen = 0;
-		if (group->gid == 0x0000) {
-			for (cur=list; cur; cur=cur->next)
-				if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
-						newlen += aimutil_put16(newdata+newlen, cur->gid);
-		} else {
-			for (cur=list; cur; cur=cur->next)
-				if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
-						newlen += aimutil_put16(newdata+newlen, cur->bid);
-		}
-		aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
-
-		free(newdata);
-	}
-
-	return group;
-}
-
-/**
- * Locally add a new item to the given item list.
- *
- * @param list A pointer to a pointer to the current list of items.
- * @param name A null terminated string of the name of the new item, or NULL if the 
- *        item should have no name.
- * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
- * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
- * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
- * @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, fu16_t gid, fu16_t bid, fu16_t type, aim_tlvlist_t *data)
-{
-	int i;
-	struct aim_ssi_item *cur, *new;
-
-	if (!list)
-		return NULL;
-
-	if (!(new = (struct aim_ssi_item *)malloc(sizeof(struct aim_ssi_item))))
-		return NULL;
-
-	/* Set the name */
-	if (name) {
-		new->name = (char *)malloc((strlen(name)+1)*sizeof(char));
-		strcpy(new->name, name);
-	} else
-		new->name = NULL;
-
-	/* Set the group ID# and buddy ID# */
-	new->gid = gid;
-	new->bid = bid;
-	if (type == AIM_SSI_TYPE_GROUP) {
-		if ((new->gid == 0xFFFF) && name) {
-			do {
-				new->gid += 0x0001;
-				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
-					if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid))
-						i=1;
-			} while (i);
-		}
-	} else {
-		if (new->bid == 0xFFFF) {
-			do {
-				new->bid += 0x0001;
-				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
-					if ((cur->bid == new->bid) && (cur->gid == new->gid))
-						i=1;
-			} while (i);
-		}
-	}
-
-	/* Set the type */
-	new->type = type;
-
-	/* Set the TLV list */
-	new->data = aim_tlvlist_copy(data);
-
-	/* Add the item to the list in the correct numerical position.  Fancy, eh? */
-	if (*list) {
-		if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
-			new->next = *list;
-			*list = new;
-		} else {
-			struct aim_ssi_item *prev;
-			for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
-			new->next = prev->next;
-			prev->next = new;
-		}
-	} else {
-		new->next = *list;
-		*list = new;
-	}
-
-	return new;
-}
-
-/**
- * Locally delete an item from the given item list.
- *
- * @param list A pointer to a pointer to the current list of items.
- * @param del A pointer to the item you want to remove from the list.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
-{
-	if (!list || !(*list) || !del)
-		return -EINVAL;
-
-	/* Remove the item from the list */
-	if (*list == del) {
-		*list = (*list)->next;
-	} else {
-		struct aim_ssi_item *cur;
-		for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
-		if (cur->next)
-			cur->next = del->next;
-	}
-
-	/* Free the removed item */
-	free(del->name);
-	aim_tlvlist_free(&del->data);
-	free(del);
-
-	return 0;
-}
-
-/**
- * Compare two items to see if they have the same data.
- *
- * @param cur1 A pointer to a pointer to the first item.
- * @param cur2 A pointer to a pointer to the second item.
- * @return Return 0 if no differences, or a number if there are differences.
- */
-static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
-{
-	if (!cur1 || !cur2)
-		return 1;
-
-	if (cur1->data && !cur2->data)
-		return 2;
-
-	if (!cur1->data && cur2->data)
-		return 3;
-
-	if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
-			return 4;
-
-	if (cur1->name && !cur2->name)
-		return 5;
-
-	if (!cur1->name && cur2->name)
-		return 6;
-
-	if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
-		return 7;
-
-	if (cur1->gid != cur2->gid)
-		return 8;
-
-	if (cur1->bid != cur2->bid)
-		return 9;
-
-	if (cur1->type != cur2->type)
-		return 10;
-
-	return 0;
-}
-
-static int aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
-{
-	struct aim_ssi_item *cur;
-	for (cur=list; cur; cur=cur->next)
-		if (cur == item)
-			return 1;
-	return 0;
-}
-
-/**
- * Locally find an item given a group ID# and a buddy ID#.
- *
- * @param list A pointer to the current list of items.
- * @param gid The group ID# of the desired item.
- * @param bid The buddy ID# of the desired item.
- * @return Return a pointer to the item if found, else return NULL;
- */
-faim_export struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, fu16_t gid, fu16_t bid)
-{
-	struct aim_ssi_item *cur;
-	for (cur=list; cur; cur=cur->next)
-		if ((cur->gid == gid) && (cur->bid == bid))
-			return cur;
-	return NULL;
-}
-
-/**
- * Locally find an item given a group name, screen name, and type.  If group name 
- * and screen name are null, then just return the first item of the given type.
- *
- * @param list A pointer to the current list of items.
- * @param gn The group name of the desired item.
- * @param bn The buddy name of the desired item.
- * @param type The type of the desired item.
- * @return Return a pointer to the item if found, else return NULL.
- */
-faim_export struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, fu16_t type)
-{
-	struct aim_ssi_item *cur;
-	if (!list)
-		return NULL;
-
-	if (gn && sn) { /* For finding buddies in groups */
-		for (cur=list; cur; cur=cur->next)
-			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
-				struct aim_ssi_item *curg;
-				for (curg=list; curg; curg=curg->next)
-					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
-						return cur;
-			}
-
-	} else if (gn) { /* For finding groups */
-		for (cur=list; cur; cur=cur->next) {
-			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
-				return cur;
-			}
-		}
-
-	} else if (sn) { /* For finding permits, denies, and ignores */
-		for (cur=list; cur; cur=cur->next) {
-			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
-				return cur;
-			}
-		}
-
-	/* For stuff without names--permit deny setting, visibility mask, etc. */
-	} else for (cur=list; cur; cur=cur->next) {
-		if ((cur->type == type) && (!cur->name))
-			return cur;
-	}
-
-	return NULL;
-}
-
-/**
- * Check if the given buddy exists in any group in the buddy list.
- *
- * @param list A pointer to the current list of items.
- * @param sn The group name of the desired item.
- * @return Return a pointer to the name of the item if found, else return NULL;
- */
-faim_export struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
-{
-	struct aim_ssi_item *cur;
-	if (!list || !sn)
-		return NULL;
-	for (cur=list; cur; cur=cur->next)
-		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && (!aim_sncmp(cur->name, sn)))
-			return cur;
-	return NULL;
-}
-
-/**
- * Locally find the parent item of the given buddy name.
- *
- * @param list A pointer to the current list of items.
- * @param bn The buddy name of the desired item.
- * @return Return a pointer to the name of the item if found, else return NULL;
- */
-faim_export char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
-{
-	struct aim_ssi_item *cur, *curg;
-	if (!list || !sn)
-		return NULL;
-	if (!(cur = aim_ssi_itemlist_exists(list, sn)))
-		return NULL;
-	if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
-		return NULL;
-	return curg->name;
-}
-
-/**
- * Locally find the permit/deny setting item, and return the setting.
- *
- * @param list A pointer to the current list of items.
- * @return Return the current SSI permit deny setting, or 0 if no setting was found.
- */
-faim_export int aim_ssi_getpermdeny(struct aim_ssi_item *list)
-{
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
-	if (cur) {
-		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
-		if (tlv && tlv->value)
-			return aimutil_get8(tlv->value);
-	}
-	return 0;
-}
-
-/**
- * Locally find the presence flag item, and return the setting.  The returned setting is a 
- * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines 
- * in aim.h
- *
- * @param list A pointer to the current list of items.
- * @return Return the current visibility mask.
- */
-faim_export fu32_t aim_ssi_getpresence(struct aim_ssi_item *list)
-{
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
-	if (cur) {
-		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
-		if (tlv && tlv->length)
-			return aimutil_get32(tlv->value);
-	}
-	return 0xFFFFFFFF;
-}
-
-/**
- * Locally find the alias of the given buddy.
- *
- * @param list A pointer to the current list of items.
- * @param gn The group of the buddy.
- * @param sn The name of the buddy.
- * @return A pointer to a NULL terminated string that is the buddy's 
- *         alias, or NULL if the buddy has no alias.  You should free
- *         this returned value!
- */
-faim_export char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
-{
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
-	if (cur) {
-		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
-		if (tlv && tlv->length) {
-			char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
-			strncpy(alias, (char *)tlv->value, tlv->length);
-			alias[tlv->length] = 0;
-			return alias;
-		}
-	}
-	return NULL;
-}
-
-/**
- * Locally find the comment of the given buddy.
- *
- * @param list A pointer to the current list of items.
- * @param gn The group of the buddy.
- * @param sn The name of the buddy.
- * @return A pointer to a NULL terminated string that is the buddy's 
- *         comment, or NULL if the buddy has no comment.  You should free
- *         this returned value!
- */
-faim_export char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn)
-{
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
-	if (cur) {
-		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
-		if (tlv && tlv->length) {
-			char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
-			strncpy(alias, (char *)tlv->value, tlv->length);
-			alias[tlv->length] = 0;
-			return alias;
-		}
-	}
-	return NULL;
-}
-
-/**
- * Locally find if you are waiting for authorization for a buddy.
- *
- * @param list A pointer to the current list of items.
- * @param gn The group of the buddy.
- * @param sn The name of the buddy.
- * @return 1 if you are waiting for authorization; 0 if you are not
- */
-faim_export int aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
-{
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
-	if (cur) {
-		if (aim_tlv_gettlv(cur->data, 0x0066, 1))
-			return 1;
-	}
-	return 0;
-}
-
-/**
- * If there are changes, then create temporary items and 
- * call addmoddel.
- *
- * @param sess The oscar session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-static int aim_ssi_sync(aim_session_t *sess)
-{
-	struct aim_ssi_item *cur1, *cur2;
-	struct aim_ssi_tmp *cur, *new;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* If we're waiting for an ack, we shouldn't do anything else */
-	if (sess->ssi.waiting_for_ack)
-		return 0;
-
-	/*
-	 * Compare the 2 lists and create an aim_ssi_tmp for each difference.  
-	 * We should only send either additions, modifications, or deletions 
-	 * before waiting for an acknowledgement.  So first do deletions, then 
-	 * additions, then modifications.  Also, both the official and the local 
-	 * list should be in ascending numerical order for the group ID#s and the 
-	 * buddy ID#s, which makes things more efficient.  I think.
-	 */
-
-	/* Additions */
-	if (!sess->ssi.pending) {
-		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
-			if (!aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid)) {
-				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
-				new->action = AIM_CB_SSI_ADD;
-				new->ack = 0xffff;
-				new->name = NULL;
-				new->item = cur1;
-				new->next = NULL;
-				if (sess->ssi.pending) {
-					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
-					cur->next = new;
-				} else
-					sess->ssi.pending = new;
-			}
-		}
-	}
-
-	/* Deletions */
-	if (!sess->ssi.pending) {
-		for (cur1=sess->ssi.official; cur1; cur1=cur1->next) {
-			if (!aim_ssi_itemlist_find(sess->ssi.local, cur1->gid, cur1->bid)) {
-				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
-				new->action = AIM_CB_SSI_DEL;
-				new->ack = 0xffff;
-				new->name = NULL;
-				new->item = cur1;
-				new->next = NULL;
-				if (sess->ssi.pending) {
-					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
-					cur->next = new;
-				} else
-					sess->ssi.pending = new;
-			}
-		}
-	}
-
-	/* Modifications */
-	if (!sess->ssi.pending) {
-		for (cur1=sess->ssi.local; cur1; cur1=cur1->next) {
-			cur2 = aim_ssi_itemlist_find(sess->ssi.official, cur1->gid, cur1->bid);
-			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
-				new = (struct aim_ssi_tmp *)malloc(sizeof(struct aim_ssi_tmp));
-				new->action = AIM_CB_SSI_MOD;
-				new->ack = 0xffff;
-				new->name = NULL;
-				new->item = cur1;
-				new->next = NULL;
-				if (sess->ssi.pending) {
-					for (cur=sess->ssi.pending; cur->next; cur=cur->next);
-					cur->next = new;
-				} else
-					sess->ssi.pending = new;
-			}
-		}
-	}
-
-	/* We're out of stuff to do, so tell the AIM servers we're done and exit */
-	if (!sess->ssi.pending) {
-		aim_ssi_modend(sess);
-		return 0;
-	}
-
-	/* Make sure we don't send anything else between now 
-	 * and when we receive the ack for the following operation */
-	sess->ssi.waiting_for_ack = 1;
-
-	/* Now go mail off our data and wait 4 to 6 weeks */
-	aim_ssi_addmoddel(sess);
-
-	return 0;
-}
-
-/**
- * Free all SSI data.
- *
- * This doesn't remove it from the server, that's different.
- *
- * @param sess The oscar session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-static int aim_ssi_freelist(aim_session_t *sess)
-{
-	struct aim_ssi_item *cur, *del;
-	struct aim_ssi_tmp *curtmp, *deltmp;
-
-	cur = sess->ssi.official;
-	while (cur) {
-		del = cur;
-		cur = cur->next;
-		free(del->name);
-		aim_tlvlist_free(&del->data);
-		free(del);
-	}
-
-	cur = sess->ssi.local;
-	while (cur) {
-		del = cur;
-		cur = cur->next;
-		free(del->name);
-		aim_tlvlist_free(&del->data);
-		free(del);
-	}
-
-	curtmp = sess->ssi.pending;
-	while (curtmp) {
-		deltmp = curtmp;
-		curtmp = curtmp->next;
-		free(deltmp);
-	}
-
-	sess->ssi.numitems = 0;
-	sess->ssi.official = NULL;
-	sess->ssi.local = NULL;
-	sess->ssi.pending = NULL;
-	sess->ssi.timestamp = (time_t)0;
-
-	return 0;
-}
-
-/**
- * Delete all SSI data.
- *
- * @param sess The oscar session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_deletelist(aim_session_t *sess)
-{
-	struct aim_ssi_item *cur, *del;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Free the local list */
-	cur = sess->ssi.local;
-	while (cur) {
-		del = cur;
-		cur = cur->next;
-		free(del->name);
-		aim_tlvlist_free(&del->data);
-		free(del);
-	}
-	sess->ssi.local = NULL;
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * This "cleans" the ssi list.  It does the following:
- * 1) Makes sure all buddies, permits, and denies have names.
- * 2) Makes sure that all buddies are in a group that exist.
- * 3) Deletes any empty groups
- *
- * @param sess The oscar session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_cleanlist(aim_session_t *sess)
-{
-	struct aim_ssi_item *cur, *next;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Delete any buddies, permits, or denies with empty names. */
-	/* If there are any buddies directly in the master group, add them to a real group. */
-	/* DESTROY any buddies that are directly in the master group. */
-	/* Do the same for buddies that are in a non-existant group. */
-	/* This will kind of mess up if you hit the item limit, but this function isn't too critical */
-	cur = sess->ssi.local;
-	while (cur) {
-		next = cur->next;
-		if (!cur->name) {
-			if (cur->type == AIM_SSI_TYPE_BUDDY)
-				aim_ssi_delbuddy(sess, NULL, NULL);
-			else if (cur->type == AIM_SSI_TYPE_PERMIT)
-				aim_ssi_delpermit(sess, NULL);
-			else if (cur->type == AIM_SSI_TYPE_DENY)
-				aim_ssi_deldeny(sess, NULL);
-		} else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(sess->ssi.local, cur->gid, 0x0000)))) {
-			char *alias = aim_ssi_getalias(sess->ssi.local, NULL, cur->name);
-			aim_ssi_addbuddy(sess, cur->name, "orphans", alias, NULL, NULL, 0);
-			aim_ssi_delbuddy(sess, cur->name, NULL);
-			free(alias);
-		}
-		cur = next;
-	}
-
-	/* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
-	cur = sess->ssi.local;
-	while (cur) {
-		if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
-		{
-			struct aim_ssi_item *cur2, *next2;
-			cur2 = cur->next;
-			while (cur2) {
-				next2 = cur2->next;
-				if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!strcmp(cur->name, cur2->name))) {
-					aim_ssi_itemlist_del(&sess->ssi.local, cur2);
-				}
-				cur2 = next2;
-			}
-		}
-		cur = cur->next;
-	}
-
-	/* Check if there are empty groups and delete them */
-	cur = sess->ssi.local;
-	while (cur) {
-		next = cur->next;
-		if (cur->type == AIM_SSI_TYPE_GROUP) {
-			aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c8, 1);
-			if (!tlv || !tlv->length)
-				aim_ssi_itemlist_del(&sess->ssi.local, cur);
-		}
-		cur = next;
-	}
-
-	/* Check if the master group is empty */
-	if ((cur = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!cur->data))
-		aim_ssi_itemlist_del(&sess->ssi.local, cur);
-
-	/* If we've made any changes then sync our list with the server's */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Add a buddy to the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item.
- * @param group The group of the 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.
- */
-faim_export int aim_ssi_addbuddy(aim_session_t *sess, const char *name, const char *group, const char *alias, const char *comment, const char *smsnum, int needauth)
-{
-	struct aim_ssi_item *parent;
-	aim_tlvlist_t *data = NULL;
-
-	if (!sess || !name || !group)
-		return -EINVAL;
-
-	/* Find the parent */
-	if (!(parent = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
-		/* Find the parent's parent (the master group) */
-		if (!(parent = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)))
-			if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
-				return -ENOMEM;
-		/* Add the parent */
-		if (!(parent = aim_ssi_itemlist_add(&sess->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL)))
-			return -ENOMEM;
-
-		/* Modify the parent's parent (the master group) */
-		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
-	}
-
-	/* Create a TLV list for the new buddy */
-	if (needauth)
-		aim_tlvlist_add_noval(&data, 0x0066);
-	if (alias)
-		aim_tlvlist_add_str(&data, 0x0131, alias);
-	if (smsnum)
-		aim_tlvlist_add_str(&data, 0x013a, smsnum);
-	if (comment)
-		aim_tlvlist_add_str(&data, 0x013c, comment);
-
-	/* Add that bad boy */
-	aim_ssi_itemlist_add(&sess->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
-	aim_tlvlist_free(&data);
-
-	/* Modify the parent group */
-	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Add a permit buddy to the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item..
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_addpermit(aim_session_t *sess, const char *name)
-{
-
-	if (!sess || !name)
-		return -EINVAL;
-
-	/* Add that bad boy */
-	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_PERMIT, NULL);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Add a deny buddy to the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item..
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_adddeny(aim_session_t *sess, const char *name)
-{
-
-	if (!sess || !name)
-		return -EINVAL;
-
-	/* Add that bad boy */
-	aim_ssi_itemlist_add(&sess->ssi.local, name, 0x0000, 0xFFFF, AIM_SSI_TYPE_DENY, NULL);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Deletes a buddy from the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item, or NULL.
- * @param group The group of the item, or NULL.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_delbuddy(aim_session_t *sess, const char *name, const char *group)
-{
-	struct aim_ssi_item *del;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the buddy */
-	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
-		return -EINVAL;
-
-	/* Remove the item from the list */
-	aim_ssi_itemlist_del(&sess->ssi.local, del);
-
-	/* Modify the parent group */
-	aim_ssi_itemlist_rebuildgroup(sess->ssi.local, group);
-
-	/* Check if we should delete the parent group */
-	if ((del = aim_ssi_itemlist_finditem(sess->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)) && (!del->data)) {
-		aim_ssi_itemlist_del(&sess->ssi.local, del);
-
-		/* Modify the parent group */
-		aim_ssi_itemlist_rebuildgroup(sess->ssi.local, NULL);
-
-		/* Check if we should delete the parent's parent (the master group) */
-		if ((del = aim_ssi_itemlist_find(sess->ssi.local, 0x0000, 0x0000)) && (!del->data)) {
-			aim_ssi_itemlist_del(&sess->ssi.local, del);
-		}
-	}
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Deletes a permit buddy from the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item, or NULL.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_delpermit(aim_session_t *sess, const char *name)
-{
-	struct aim_ssi_item *del;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the item */
-	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_PERMIT)))
-		return -EINVAL;
-
-	/* Remove the item from the list */
-	aim_ssi_itemlist_del(&sess->ssi.local, del);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Deletes a deny buddy from the list.
- *
- * @param sess The oscar session.
- * @param name The name of the item, or NULL.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_deldeny(aim_session_t *sess, const char *name)
-{
-	struct aim_ssi_item *del;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the item */
-	if (!(del = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, name, AIM_SSI_TYPE_DENY)))
-		return -EINVAL;
-
-	/* Remove the item from the list */
-	aim_ssi_itemlist_del(&sess->ssi.local, del);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Move a buddy from one group to another group.  This basically just deletes the 
- * buddy and re-adds it.
- *
- * @param sess The oscar session.
- * @param oldgn The group that the buddy is currently in.
- * @param newgn The group that the buddy should be moved in to.
- * @param sn The name of the buddy to be moved.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_movebuddy(aim_session_t *sess, const char *oldgn, const char *newgn, const char *sn)
-{
-	char *alias = aim_ssi_getalias(sess->ssi.local, oldgn, sn);
-	aim_ssi_addbuddy(sess, sn, newgn, alias, NULL, NULL, aim_ssi_waitingforauth(sess->ssi.local, oldgn, sn));
-	aim_ssi_delbuddy(sess, sn, oldgn);
-	free(alias);
-	return 0;
-}
-
-/**
- * Change the alias stored on the server for a given buddy.
- *
- * @param sess The oscar session.
- * @param gn The group that the buddy is currently in.
- * @param sn The screen name of the buddy.
- * @param alias The new alias for the buddy, or NULL if you want to remove 
- *        a buddy's comment.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
-{
-	struct aim_ssi_item *tmp;
-
-	if (!sess || !gn || !sn)
-		return -EINVAL;
-
-	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
-		return -EINVAL;
-
-	/* Either add or remove the 0x0131 TLV from the TLV chain */
-	if ((alias != NULL) && (strlen(alias) > 0))
-		aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
-	else
-		aim_tlvlist_remove(&tmp->data, 0x0131);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Change the comment stored on the server for a given buddy.
- *
- * @param sess The oscar session.
- * @param gn The group that the buddy is currently in.
- * @param sn The screen name of the buddy.
- * @param alias The new comment for the buddy, or NULL if you want to remove 
- *        a buddy's comment.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_editcomment(aim_session_t *sess, const char *gn, const char *sn, const char *comment)
-{
-	struct aim_ssi_item *tmp;
-
-	if (!sess || !gn || !sn)
-		return -EINVAL;
-
-	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
-		return -EINVAL;
-
-	/* Either add or remove the 0x0131 TLV from the TLV chain */
-	if ((comment != NULL) && (strlen(comment) > 0))
-		aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
-	else
-		aim_tlvlist_remove(&tmp->data, 0x013c);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Rename a group.
- *
- * @param sess The oscar session.
- * @param oldgn The old group name.
- * @param newgn The new group name.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_rename_group(aim_session_t *sess, const char *oldgn, const char *newgn)
-{
-	struct aim_ssi_item *group;
-
-	if (!sess || !oldgn || !newgn)
-		return -EINVAL;
-
-	if (!(group = aim_ssi_itemlist_finditem(sess->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
-		return -EINVAL;
-
-	free(group->name);
-	group->name = (char *)malloc((strlen(newgn)+1)*sizeof(char));
-	strcpy(group->name, newgn);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Stores your permit/deny setting on the server, and starts using it.
- *
- * @param sess The oscar session.
- * @param permdeny Your permit/deny setting.  Can be one of the following:
- *        1 - Allow all users
- *        2 - Block all users
- *        3 - Allow only the users below
- *        4 - Block only the users below
- *        5 - Allow only users on my buddy list
- * @param vismask A bitmask of the class of users to whom you want to be 
- *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask)
-{
-	struct aim_ssi_item *tmp;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the PDINFO item, or add it if it does not exist */
-	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
-
-	/* Need to add the 0x00ca TLV to the TLV chain */
-	aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
-
-	/* Need to add the 0x00cb TLV to the TLV chain */
-	aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/**
- * Set buddy icon information
- *
- * @param sess The oscar session.
- * @param iconcsum The MD5 checksum of the icon you are using.
- * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen)
-{
-	struct aim_ssi_item *tmp;
-	fu8_t *csumdata;
-
-	if (!sess || !iconsum || !iconsumlen)
-		return -EINVAL;
-
-	/* Find the ICONINFO item, or add it if it does not exist */
-	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL);
-	}
-
-	/* Need to add the 0x00d5 TLV to the TLV chain */
-	if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t))))
-		return -ENOMEM;
-	csumdata[0] = 0x00;
-	csumdata[1] = 0x10;
-	memcpy(&csumdata[2], iconsum, iconsumlen);
-	aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);
-	free(csumdata);
-
-	/* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
-	aim_tlvlist_replace_noval(&tmp->data, 0x0131);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-	return 0;
-}
-
-/**
- * Remove a reference to a server stored buddy icon.  This will make your 
- * icon stop showing up to other people.
- *
- * @param sess The oscar session.
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_delicon(aim_session_t *sess)
-{
-	struct aim_ssi_item *tmp;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the ICONINFO item and delete it if it exists*/
-	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO)))
-		aim_ssi_itemlist_del(&sess->ssi.local, tmp);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-	return 0;
-}
-
-/**
- * Stores your setting for various SSI settings.  Whether you 
- * should show up as idle or not, etc.
- *
- * @param sess The oscar session.
- * @param presence I think it's a bitmask, but I only know what one of the bits is:
- *        0x00000002 - Hide wireless?
- *        0x00000400 - Allow others to see your idle time
- * @return Return 0 if no errors, otherwise return the error number.
- */
-faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) {
-	struct aim_ssi_item *tmp;
-
-	if (!sess)
-		return -EINVAL;
-
-	/* Find the PRESENCEPREFS item, or add it if it does not exist */
-	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
-
-	/* Need to add the x00c9 TLV to the TLV chain */
-	aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
-
-	/* Sync our local list with the server list */
-	aim_ssi_sync(sess);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0002 - Request SSI Rights.
- */
-faim_export int aim_ssi_reqrights(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
-}
-
-/*
- * Subtype 0x0003 - SSI Rights Information.
- */
-static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0, i;
-	aim_rxcallback_t userfunc;
-	aim_tlvlist_t *tlvlist;
-	aim_tlv_t *tlv;
-	aim_bstream_t bstream;
-	fu16_t *maxitems;
-
-	/* This SNAC is made up of a bunch of TLVs */
-	tlvlist = aim_tlvlist_read(bs);
-
-	/* TLV 0x0004 contains the maximum number of each item */
-	if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
-		aim_tlvlist_free(&tlvlist);
-		return 0;
-	}
-
-	aim_bstream_init(&bstream, tlv->value, tlv->length);
-
-	if (!(maxitems = (fu16_t *)malloc((tlv->length/2)*sizeof(fu16_t)))) {
-		aim_tlvlist_free(&tlvlist);
-		return 0;
-	}
-
-	for (i=0; i<(tlv->length/2); i++)
-		maxitems[i] = aimbs_get16(&bstream);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, tlv->length/2, maxitems);
-
-	aim_tlvlist_free(&tlvlist);
-	free(maxitems);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and 
- * revision number.
- * 
- */
-faim_export int aim_ssi_reqdata(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	/* Free any current data, just in case */
-	aim_ssi_freelist(sess);
-
-	return aim_genericreq_n_snacid(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQDATA);
-}
-
-/*
- * Subtype 0x0005 - Request SSI Data when you have a timestamp and revision 
- * number.
- *
- * The data will only be sent if it is newer than the posted local
- * timestamp and revision.
- * 
- * Note that the client should never increment the revision, only the server.
- * 
- */
-faim_export int aim_ssi_reqifchanged(aim_session_t *sess, time_t timestamp, fu16_t numitems)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, NULL, 0);
-
-	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQIFCHANGED, 0x0000, snacid);
-	aimbs_put32(&fr->data, timestamp);
-	aimbs_put16(&fr->data, numitems);
-
-	aim_tx_enqueue(sess, fr);
-
-	/* Free any current data, just in case */
-	aim_ssi_freelist(sess);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0006 - SSI Data.
- */
-static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu8_t fmtver; /* guess */
-	fu16_t namelen, gid, bid, type;
-	char *name;
-	aim_tlvlist_t *data;
-
-	fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
-	sess->ssi.numitems += aimbs_get16(bs); /* # of items in this SSI SNAC */
-
-	/* Read in the list */
-	while (aim_bstream_empty(bs) > 4) { /* last four bytes are timestamp */
-		if ((namelen = aimbs_get16(bs)))
-			name = aimbs_getstr(bs, namelen);
-		else
-			name = NULL;
-		gid = aimbs_get16(bs);
-		bid = aimbs_get16(bs);
-		type = aimbs_get16(bs);
-		data = aim_tlvlist_readlen(bs, aimbs_get16(bs));
-		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
-		free(name);
-		aim_tlvlist_free(&data);
-	}
-
-	/* Read in the timestamp */
-	sess->ssi.timestamp = aimbs_get32(bs);
-
-	if (!(snac->flags & 0x0001)) {
-		/* Make a copy of the list */
-		struct aim_ssi_item *cur;
-		for (cur=sess->ssi.official; cur; cur=cur->next)
-			aim_ssi_itemlist_add(&sess->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
-
-		sess->ssi.received_data = 1;
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, fmtver, sess->ssi.numitems, sess->ssi.official, sess->ssi.timestamp);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x0007 - SSI Activate Data.
- *
- * Should be sent after receiving 13/6 or 13/f to tell the server you
- * are ready to begin using the list.  It will promptly give you the
- * presence information for everyone in your list and put your permit/deny
- * settings into effect.
- * 
- */
-faim_export int aim_ssi_enable(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
-}
-
-/*
- * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
- *
- * Sends the SNAC to add, modify, or delete an item from the server-stored
- * information.  These 3 SNACs all have an identical structure.  The only
- * difference is the subtype that is set for the SNAC.
- * 
- */
-faim_export int aim_ssi_addmoddel(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int snaclen;
-	struct aim_ssi_tmp *cur;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sess->ssi.pending || !sess->ssi.pending->item)
-		return -EINVAL;
-
-	/* Calculate total SNAC size */
-	snaclen = 10; /* For family, subtype, flags, and SNAC ID */
-	for (cur=sess->ssi.pending; cur; cur=cur->next) {
-		snaclen += 10; /* For length, GID, BID, type, and length */
-		if (cur->item->name)
-			snaclen += strlen(cur->item->name);
-		if (cur->item->data)
-			snaclen += aim_tlvlist_size(&cur->item->data);
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, sess->ssi.pending->action, 0x0000, snacid);
-
-	for (cur=sess->ssi.pending; cur; cur=cur->next) {
-		aimbs_put16(&fr->data, cur->item->name ? strlen(cur->item->name) : 0);
-		if (cur->item->name)
-			aimbs_putstr(&fr->data, cur->item->name);
-		aimbs_put16(&fr->data, cur->item->gid);
-		aimbs_put16(&fr->data, cur->item->bid);
-		aimbs_put16(&fr->data, cur->item->type);
-		aimbs_put16(&fr->data, cur->item->data ? aim_tlvlist_size(&cur->item->data) : 0);
-		if (cur->item->data)
-			aim_tlvlist_write(&fr->data, &cur->item->data);
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0008 - Incoming SSI add.
- *
- * Sent by the server, for example, when someone is added to 
- * your "Recent Buddies" group.
- */
-static int parseadd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	char *name;
-	fu16_t len, gid, bid, type;
-	aim_tlvlist_t *data;
-
-	while (aim_bstream_empty(bs)) {
-		if ((len = aimbs_get16(bs)))
-			name = aimbs_getstr(bs, len);
-		else
-			name = NULL;
-		gid = aimbs_get16(bs);
-		bid = aimbs_get16(bs);
-		type = aimbs_get16(bs);
-		if ((len = aimbs_get16(bs)))
-			data = aim_tlvlist_readlen(bs, len);
-		else
-			data = NULL;
-
-		aim_ssi_itemlist_add(&sess->ssi.local, name, gid, bid, type, data);
-		aim_ssi_itemlist_add(&sess->ssi.official, name, gid, bid, type, data);
-		aim_tlvlist_free(&data);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, type, name);
-
-		free(name);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x0009 - Incoming SSI mod.
- *
- * XXX - It would probably be good for the client to actually do something when it gets this.
- */
-static int parsemod(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	char *name;
-	fu16_t len, gid, bid, type;
-	aim_tlvlist_t *data;
-	struct aim_ssi_item *item;
-
-	while (aim_bstream_empty(bs)) {
-		if ((len = aimbs_get16(bs)))
-			name = aimbs_getstr(bs, len);
-		else
-			name = NULL;
-		gid = aimbs_get16(bs);
-		bid = aimbs_get16(bs);
-		type = aimbs_get16(bs);
-		if ((len = aimbs_get16(bs)))
-			data = aim_tlvlist_readlen(bs, len);
-		else
-			data = NULL;
-
-		/* Replace the 2 local items with the given one */
-		if ((item = aim_ssi_itemlist_find(sess->ssi.local, gid, bid))) {
-			item->type = type;
-			free(item->name);
-			if (name) {
-				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
-			aim_tlvlist_free(&item->data);
-			item->data = aim_tlvlist_copy(data);
-		}
-
-		if ((item = aim_ssi_itemlist_find(sess->ssi.official, gid, bid))) {
-			item->type = type;
-			free(item->name);
-			if (name) {
-				item->name = (char *)malloc((strlen(name)+1)*sizeof(char));
-				strcpy(item->name, name);
-			} else
-				item->name = NULL;
-			aim_tlvlist_free(&item->data);
-			item->data = aim_tlvlist_copy(data);
-		}
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx);
-
-		free(name);
-		aim_tlvlist_free(&data);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x000a - Incoming SSI del.
- *
- * XXX - It would probably be good for the client to actually do something when it gets this.
- */
-static int parsedel(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t gid, bid;
-	struct aim_ssi_item *del;
-
-	while (aim_bstream_empty(bs)) {
-		aim_bstream_advance(bs, aimbs_get16(bs));
-		gid = aimbs_get16(bs);
-		bid = aimbs_get16(bs);
-		aimbs_get16(bs);
-		aim_bstream_advance(bs, aimbs_get16(bs));
-
-		if ((del = aim_ssi_itemlist_find(sess->ssi.local, gid, bid)))
-			aim_ssi_itemlist_del(&sess->ssi.local, del);
-		if ((del = aim_ssi_itemlist_find(sess->ssi.official, gid, bid)))
-			aim_ssi_itemlist_del(&sess->ssi.official, del);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x000e - SSI Add/Mod/Del Ack.
- *
- * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
- *
- */
-static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	struct aim_ssi_tmp *cur, *del;
-
-	/* Read in the success/failure flags from the ack SNAC */
-	cur = sess->ssi.pending;
-	while (cur && (aim_bstream_empty(bs)>0)) {
-		cur->ack = aimbs_get16(bs);
-		cur = cur->next;
-	}
-
-	/*
-	 * If outcome is 0, then add the item to the item list, or replace the other item, 
-	 * or remove the old item.  If outcome is non-zero, then remove the item from the 
-	 * local list, or unmodify it, or add it.
-	 */
-	for (cur=sess->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
-	if (cur->item) {
-		if (cur->ack) {
-			/* Our action was unsuccessful, so change the local list back to how it was */
-			if (cur->action == AIM_CB_SSI_ADD) {
-				/* Remove the item from the local list */
-				/* Make sure cur->item is still valid memory */
-				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
-					if (cur->item->name) {
-						cur->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
-						strcpy(cur->name, cur->item->name);
-					}
-					aim_ssi_itemlist_del(&sess->ssi.local, cur->item);
-				}
-				cur->item = NULL;
-
-			} else if (cur->action == AIM_CB_SSI_MOD) {
-				/* Replace the local item with the item from the official list */
-				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
-					struct aim_ssi_item *cur1;
-					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
-						free(cur->item->name);
-						if (cur1->name) {
-							cur->item->name = (char *)malloc((strlen(cur1->name)+1)*sizeof(char));
-							strcpy(cur->item->name, cur1->name);
-						} else
-							cur->item->name = NULL;
-						aim_tlvlist_free(&cur->item->data);
-						cur->item->data = aim_tlvlist_copy(cur1->data);
-					}
-				} else
-					cur->item = NULL;
-
-			} else if (cur->action == AIM_CB_SSI_DEL) {
-				/* Add the item back into the local list */
-				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item)) {
-					aim_ssi_itemlist_add(&sess->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
-				} else
-					cur->item = NULL;
-			}
-
-		} else {
-			/* Do the exact opposite */
-			if (cur->action == AIM_CB_SSI_ADD) {
-			/* Add the local item to the official list */
-				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
-					aim_ssi_itemlist_add(&sess->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
-				} else
-					cur->item = NULL;
-
-			} else if (cur->action == AIM_CB_SSI_MOD) {
-				/* Replace the official item with the item from the local list */
-				if (aim_ssi_itemlist_valid(sess->ssi.local, cur->item)) {
-					struct aim_ssi_item *cur1;
-					if ((cur1 = aim_ssi_itemlist_find(sess->ssi.official, cur->item->gid, cur->item->bid))) {
-						free(cur1->name);
-						if (cur->item->name) {
-							cur1->name = (char *)malloc((strlen(cur->item->name)+1)*sizeof(char));
-							strcpy(cur1->name, cur->item->name);
-						} else
-							cur1->name = NULL;
-						aim_tlvlist_free(&cur1->data);
-						cur1->data = aim_tlvlist_copy(cur->item->data);
-					}
-				} else
-					cur->item = NULL;
-
-			} else if (cur->action == AIM_CB_SSI_DEL) {
-				/* Remove the item from the official list */
-				if (aim_ssi_itemlist_valid(sess->ssi.official, cur->item))
-					aim_ssi_itemlist_del(&sess->ssi.official, cur->item);
-				cur->item = NULL;
-			}
-
-		}
-	} /* End if (cur->item) */
-	} /* End for loop */
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sess->ssi.pending);
-
-	/* Free all aim_ssi_tmp's with an outcome */
-	cur = sess->ssi.pending;
-	while (cur && (cur->ack != 0xffff)) {
-		del = cur;
-		cur = cur->next;
-		free(del->name);
-		free(del);
-	}
-	sess->ssi.pending = cur;
-
-	/* If we're not waiting for any more acks, then send more SNACs */
-	if (!sess->ssi.pending) {
-		sess->ssi.pending = NULL;
-		sess->ssi.waiting_for_ack = 0;
-		aim_ssi_sync(sess);
-	}
-
-	return ret;
-}
-
-/*
- * Subtype 0x000f - SSI Data Unchanged.
- *
- * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
- * posted local stamp/revision.
- *
- */
-static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-
-	sess->ssi.received_data = 1;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0011 - SSI Begin Data Modification.
- *
- * Tells the server you're going to start modifying data.
- * 
- */
-faim_export int aim_ssi_modbegin(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
-}
-
-/*
- * Subtype 0x0012 - SSI End Data Modification.
- *
- * Tells the server you're finished modifying data.
- *
- */
-faim_export int aim_ssi_modend(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)))
-		return -EINVAL;
-
-	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
-}
-
-/*
- * Subtype 0x0014 - Grant authorization
- *
- * Authorizes a contact so they can add you to their contact list.
- *
- */
-faim_export int aim_ssi_sendauth(aim_session_t *sess, char *sn, char *msg)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTH, 0x0000, snacid);
-
-	/* Screen name */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	/* Message (null terminated) */
-	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
-	if (msg) {
-		aimbs_putstr(&fr->data, msg);
-		aimbs_put8(&fr->data, 0x00);
-	}
-
-	/* Unknown */
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0015 - Receive an authorization grant
- */
-static int receiveauthgrant(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t tmp;
-	char *sn, *msg;
-
-	/* Read screen name */
-	if ((tmp = aimbs_get8(bs)))
-		sn = aimbs_getstr(bs, tmp);
-	else
-		sn = NULL;
-
-	/* Read message (null terminated) */
-	if ((tmp = aimbs_get16(bs)))
-		msg = aimbs_getstr(bs, tmp);
-	else
-		msg = NULL;
-
-	/* Unknown */
-	tmp = aimbs_get16(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sn, msg);
-
-	free(sn);
-	free(msg);
-
-	return ret;
-}
-
-/*
- * Subtype 0x0018 - Send authorization request
- *
- * Sends a request for authorization to the given contact.  The request will either be
- * granted, denied, or dropped.
- *
- */
-faim_export int aim_ssi_sendauthrequest(aim_session_t *sess, char *sn, const char *msg)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)+2+(msg ? strlen(msg)+1 : 0)+2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid);
-
-	/* Screen name */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	/* Message (null terminated) */
-	aimbs_put16(&fr->data, msg ? strlen(msg) : 0);
-	if (msg) {
-		aimbs_putstr(&fr->data, msg);
-		aimbs_put8(&fr->data, 0x00);
-	}
-
-	/* Unknown */
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x0019 - Receive an authorization request
- */
-static int receiveauthrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t tmp;
-	char *sn, *msg;
-
-	/* Read screen name */
-	if ((tmp = aimbs_get8(bs)))
-		sn = aimbs_getstr(bs, tmp);
-	else
-		sn = NULL;
-
-	/* Read message (null terminated) */
-	if ((tmp = aimbs_get16(bs)))
-		msg = aimbs_getstr(bs, tmp);
-	else
-		msg = NULL;
-
-	/* Unknown */
-	tmp = aimbs_get16(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sn, msg);
-
-	free(sn);
-	free(msg);
-
-	return ret;
-}
-
-/*
- * Subtype 0x001a - Send authorization reply
- *
- * Sends a reply to a request for authorization.  The reply can either 
- * grant authorization or deny authorization.
- *
- * if reply=0x00 then deny
- * if reply=0x01 then grant
- *
- */
-faim_export int aim_ssi_sendauthreply(aim_session_t *sess, char *sn, fu8_t reply, const char *msg)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, AIM_CB_FAM_SSI)) || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 1 + 2+(msg ? strlen(msg)+1 : 0) + 2)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid);
-
-	/* Screen name */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putstr(&fr->data, sn);
-
-	/* Grant or deny */
-	aimbs_put8(&fr->data, reply);
-
-	/* Message (null terminated) */
-	aimbs_put16(&fr->data, msg ? (strlen(msg)+1) : 0);
-	if (msg) {
-		aimbs_putstr(&fr->data, msg);
-		aimbs_put8(&fr->data, 0x00);
-	}
-
-	/* Unknown */
-	aimbs_put16(&fr->data, 0x0000);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
- * Subtype 0x001b - Receive an authorization reply
- * You get this bad boy when other people respond to the authorization 
- * request that you have previously sent them.
- */
-static int receiveauthreply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t tmp;
-	fu8_t reply;
-	char *sn, *msg;
-
-	/* Read screen name */
-	if ((tmp = aimbs_get8(bs)))
-		sn = aimbs_getstr(bs, tmp);
-	else
-		sn = NULL;
-
-	/* Read reply */
-	reply = aimbs_get8(bs);
-
-	/* Read message (null terminated) */
-	if ((tmp = aimbs_get16(bs)))
-		msg = aimbs_getstr(bs, tmp);
-	else
-		msg = NULL;
-
-	/* Unknown */
-	tmp = aimbs_get16(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sn, reply, msg);
-
-	free(sn);
-	free(msg);
-
-	return ret;
-}
-
-/*
- * Subtype 0x001c - Receive a message telling you someone added you to their list.
- */
-static int receiveadded(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t tmp;
-	char *sn;
-
-	/* Read screen name */
-	if ((tmp = aimbs_get8(bs)))
-		sn = aimbs_getstr(bs, tmp);
-	else
-		sn = NULL;
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, sn);
-
-	free(sn);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
-		return parserights(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_LIST)
-		return parsedata(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_ADD)
-		return parseadd(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_MOD)
-		return parsemod(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_DEL)
-		return parsedel(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_SRVACK)
-		return parseack(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_NOLIST)
-		return parsedataunchanged(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_RECVAUTH)
-		return receiveauthgrant(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_RECVAUTHREQ)
-		return receiveauthrequest(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_RECVAUTHREP)
-		return receiveauthreply(sess, mod, rx, snac, bs);
-	else if (snac->subtype == AIM_CB_SSI_ADDED)
-		return receiveadded(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
-{
-	aim_ssi_freelist(sess);
-}
-
-faim_internal int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = AIM_CB_FAM_SSI;
-	mod->version = 0x0004;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x0629;
-	mod->flags = 0;
-	strncpy(mod->name, "ssi", sizeof(mod->name));
-	mod->snachandler = snachandler;
-	mod->shutdown = ssi_shutdown;
-
-	return 0;
-}
--- a/src/protocols/oscar/stats.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * Family 0x000b - Statistics.
- *
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t interval;
-
-	interval = aimbs_get16(bs);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, interval);
-
-	return ret;
-}
-
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0002)
-		return reportinterval(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000b;
-	mod->version = 0x0001;
-	mod->toolid = 0x0104;
-	mod->toolversion = 0x0001;
-	mod->flags = 0;
-	strncpy(mod->name, "stats", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
--- a/src/protocols/oscar/tlv.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/tlv.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,8 +1,27 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
 
-#define FAIM_INTERNAL
-#include <aim.h>
 
-static aim_tlv_t *createtlv(fu16_t type, fu16_t length, fu8_t *value)
+#include "oscar.h"
+
+static aim_tlv_t *createtlv(guint16 type, guint16 length, guint8 *value)
 {
 	aim_tlv_t *ret;
 
@@ -50,7 +69,7 @@
 	aim_tlvlist_t *list = NULL, *cur;
 
 	while (aim_bstream_empty(bs) > 0) {
-		fu16_t type, length;
+		guint16 type, length;
 
 		type = aimbs_get16(bs);
 		length = aimbs_get16(bs);
@@ -130,12 +149,12 @@
  *        preceded by the number of TLVs.  So you can limit that with this.
  * @return Return the TLV chain read
  */
-faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num)
+faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, guint16 num)
 {
 	aim_tlvlist_t *list = NULL, *cur;
 
 	while ((aim_bstream_empty(bs) > 0) && (num != 0)) {
-		fu16_t type, length;
+		guint16 type, length;
 
 		type = aimbs_get16(bs);
 		length = aimbs_get16(bs);
@@ -199,12 +218,12 @@
  *        preceded by the length of the TLVs.  So you can limit that with this.
  * @return Return the TLV chain read
  */
-faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, fu16_t len)
+faim_internal aim_tlvlist_t *aim_tlvlist_readlen(aim_bstream_t *bs, guint16 len)
 {
 	aim_tlvlist_t *list = NULL, *cur;
 
 	while ((aim_bstream_empty(bs) > 0) && (len > 0)) {
-		fu16_t type, length;
+		guint16 type, length;
 
 		type = aimbs_get16(bs);
 		length = aimbs_get16(bs);
@@ -280,8 +299,8 @@
 	if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two))
 		return 1;
 
-	aim_bstream_init(&bs1, ((fu8_t *)malloc(aim_tlvlist_size(&one)*sizeof(fu8_t))), aim_tlvlist_size(&one));
-	aim_bstream_init(&bs2, ((fu8_t *)malloc(aim_tlvlist_size(&two)*sizeof(fu8_t))), aim_tlvlist_size(&two));
+	aim_bstream_init(&bs1, ((guint8 *)malloc(aim_tlvlist_size(&one)*sizeof(guint8))), aim_tlvlist_size(&one));
+	aim_bstream_init(&bs2, ((guint8 *)malloc(aim_tlvlist_size(&two)*sizeof(guint8))), aim_tlvlist_size(&two));
 
 	aim_tlvlist_write(&bs1, &one);
 	aim_tlvlist_write(&bs2, &two);
@@ -380,7 +399,7 @@
  * @param value String to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value)
+faim_internal int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
 {
 	aim_tlvlist_t *newtlv, *cur;
 
@@ -396,7 +415,7 @@
 		return 0;
 	}
 	if (newtlv->tlv->length > 0) {
-		newtlv->tlv->value = (fu8_t *)malloc(newtlv->tlv->length);
+		newtlv->tlv->value = (guint8 *)malloc(newtlv->tlv->length);
 		memcpy(newtlv->tlv->value, value, newtlv->tlv->length);
 	}
 
@@ -419,9 +438,9 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value)
+faim_internal int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
 {
-	fu8_t v8[1];
+	guint8 v8[1];
 
 	aimutil_put8(v8, value);
 
@@ -436,9 +455,9 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value)
+faim_internal int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value)
 {
-	fu8_t v16[2];
+	guint8 v16[2];
 
 	aimutil_put16(v16, value);
 
@@ -453,9 +472,9 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value)
+faim_internal int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
 {
-	fu8_t v32[4];
+	guint8 v32[4];
 
 	aimutil_put32(v32, value);
 
@@ -470,9 +489,9 @@
  * @param value Value to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const fu16_t type, const char *value)
+faim_internal int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value)
 {
-	return aim_tlvlist_add_raw(list, type, strlen(value), (fu8_t *)value);
+	return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
 }
 
 /**
@@ -491,9 +510,9 @@
  * @param caps Bitfield of capability flags to send
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps)
+faim_internal int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps)
 {
-	fu8_t buf[16*16]; /* XXX icky fixed length buffer */
+	guint8 buf[16*16]; /* XXX icky fixed length buffer */
 	aim_bstream_t bs;
 
 	if (!caps)
@@ -513,9 +532,9 @@
  * @param type TLV type to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *userinfo)
+faim_internal int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo)
 {
-	fu8_t buf[1024]; /* bleh */
+	guint8 buf[1024]; /* bleh */
 	aim_bstream_t bs;
 
 	aim_bstream_init(&bs, buf, sizeof(buf));
@@ -534,9 +553,9 @@
  * @param instance The instance.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance)
+faim_internal int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
 {
-	fu8_t *buf;
+	guint8 *buf;
 	int len;
 	aim_bstream_t bs;
 
@@ -566,7 +585,7 @@
  * @param type TLV type to add.
  * @return The size of the value added.
  */
-faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const fu16_t type)
+faim_internal int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type)
 {
 	return aim_tlvlist_add_raw(list, type, 0, NULL);
 }
@@ -587,9 +606,9 @@
  *         0 is returned if there was an error or if the destination
  *         TLV chain has length 0.
  */
-faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl)
+faim_internal int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
 {
-	fu8_t *buf;
+	guint8 *buf;
 	int buflen;
 	aim_bstream_t bs;
 
@@ -623,7 +642,7 @@
  * @param value String to add.
  * @return The length of the TLV.
  */
-faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value)
+faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
 {
 	aim_tlvlist_t *cur;
 
@@ -637,7 +656,7 @@
 	free(cur->tlv->value);
 	cur->tlv->length = length;
 	if (cur->tlv->length > 0) {
-		cur->tlv->value = (fu8_t *)malloc(cur->tlv->length);
+		cur->tlv->value = (guint8 *)malloc(cur->tlv->length);
 		memcpy(cur->tlv->value, value, cur->tlv->length);
 	} else
 		cur->tlv->value = NULL;
@@ -655,7 +674,7 @@
  * @param str String to add.
  * @return The length of the TLV.
  */
-faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const fu16_t type, const char *str)
+faim_internal int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str)
 {
 	return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
 }
@@ -669,7 +688,7 @@
  * @param type TLV type.
  * @return The length of the TLV.
  */
-faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type)
+faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type)
 {
 	return aim_tlvlist_replace_raw(list, type, 0, NULL);
 }
@@ -684,9 +703,9 @@
  * @param value 8 bit value to add.
  * @return The length of the TLV.
  */
-faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value)
+faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
 {
-	fu8_t v8[1];
+	guint8 v8[1];
 
 	aimutil_put8(v8, value);
 
@@ -703,9 +722,9 @@
  * @param value 32 bit value to add.
  * @return The length of the TLV.
  */
-faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value)
+faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
 {
-	fu8_t v32[4];
+	guint8 v32[4];
 
 	aimutil_put32(v32, value);
 
@@ -719,7 +738,7 @@
  * @param list Desination chain (%NULL pointer if empty).
  * @param type TLV type.
  */
-faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type)
+faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type)
 {
 	aim_tlvlist_t *del;
 
@@ -795,7 +814,7 @@
  * @param nth Index of TLV of type to get.
  * @return The TLV you were looking for, or NULL if one could not be found.
  */
-faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlvlist_t *cur;
 	int i;
@@ -821,7 +840,7 @@
  * @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.
  */
-faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlvlist_t *cur;
 	int i;
@@ -848,7 +867,7 @@
  *         not be found.  This is a dynamic buffer and must be freed by the
  *         caller.
  */
-faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 	char *newstr;
@@ -873,7 +892,7 @@
  * @return The value the TLV you were looking for, or 0 if one could 
  *         not be found.
  */
-faim_internal fu8_t aim_tlv_get8(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
@@ -892,7 +911,7 @@
  * @return The value the TLV you were looking for, or 0 if one could 
  *         not be found.
  */
-faim_internal fu16_t aim_tlv_get16(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
@@ -911,7 +930,7 @@
  * @return The value the TLV you were looking for, or 0 if one could 
  *         not be found.
  */
-faim_internal fu32_t aim_tlv_get32(aim_tlvlist_t *list, const fu16_t type, const int nth)
+faim_internal guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth)
 {
 	aim_tlv_t *tlv;
 
--- a/src/protocols/oscar/translate.c	Sat Feb 11 19:16:38 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * Family 0x000c - Translation.
- *
- * I have no idea why this group was issued.  I have never seen anything
- * that uses it.  From what I remember, the last time I tried to poke at
- * the server with this group, it whined about not supporting it.
- *
- * But we advertise it anyway, because its fun.
- * 
- */
-
-#define FAIM_INTERNAL
-#include <aim.h>
-
-faim_internal int translate_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x000c;
-	mod->version = 0x0001;
-	mod->toolid = 0x0104;
-	mod->toolversion = 0x0001;
-	mod->flags = 0;
-	strncpy(mod->name, "translate", sizeof(mod->name));
-	mod->snachandler = NULL;
-
-	return 0;
-}
--- a/src/protocols/oscar/txqueue.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/txqueue.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * txqueue.c
  *
@@ -5,8 +25,8 @@
  *
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
+#include "peer.h"
 
 #ifndef _WIN32
 #include <sys/socket.h>
@@ -26,7 +46,7 @@
  * chan = channel for FLAP, hdrtype for OFT
  *
  */
-faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen)
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint16 chan, int datalen)
 {
 	aim_frame_t *fr;
 
@@ -61,7 +81,7 @@
 		gaim_debug_misc("oscar", "tx_new: unknown framing\n");
 
 	if (datalen > 0) {
-		fu8_t *data;
+		guint8 *data;
 
 		if (!(data = (unsigned char *)malloc(datalen))) {
 			aim_frame_destroy(fr);
@@ -262,7 +282,7 @@
 static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
 {
 	aim_bstream_t bs;
-	fu8_t *bs_raw;
+	guint8 *bs_raw;
 	int payloadlen, err = 0, bslen;
 
 	payloadlen = aim_bstream_curpos(&fr->data);
@@ -298,7 +318,7 @@
 static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr)
 {
 	aim_bstream_t bs;
-	fu8_t *bs_raw;
+	guint8 *bs_raw;
 	int payloadlen, err = 0, bslen;
 
 	payloadlen = aim_bstream_curpos(&fr->data);
--- a/src/protocols/oscar/util.c	Sat Feb 11 19:16:38 2006 +0000
+++ b/src/protocols/oscar/util.c	Sat Feb 11 21:45:18 2006 +0000
@@ -1,3 +1,23 @@
+/*
+ * Gaim's oscar protocol plugin
+ * This file is the legal property of its developers.
+ * Please see the AUTHORS file distributed alongside this file.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
 /*
  * A little bit of this
  * A little bit of that
@@ -5,8 +25,7 @@
  * Now we're up to bat
  */
 
-#define FAIM_INTERNAL
-#include <aim.h>
+#include "oscar.h"
 #include <ctype.h>
 
 #ifdef _WIN32
@@ -109,9 +128,9 @@
  * Calculate the checksum of a given icon.
  *
  */
-faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen)
+faim_export guint16 aimutil_iconsum(const guint8 *buf, int buflen)
 {
-	fu32_t sum;
+	guint32 sum;
 	int i;
 
 	for (i=0, sum=0; i+1<buflen; i+=2)