changeset 2246:933346315b9b

[gaim-migrate @ 2256] heh. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sun, 09 Sep 2001 10:07:14 +0000
parents 31157c54fe6e
children f721ae9582d5
files ChangeLog src/conversation.c src/protocols/oscar/BUGS src/protocols/oscar/CHANGES src/protocols/oscar/admin.c src/protocols/oscar/adverts.c src/protocols/oscar/aim.h src/protocols/oscar/aim_internal.h src/protocols/oscar/auth.c src/protocols/oscar/bos.c src/protocols/oscar/buddylist.c src/protocols/oscar/chat.c src/protocols/oscar/chatnav.c src/protocols/oscar/conn.c src/protocols/oscar/faimconfig.h src/protocols/oscar/ft.c src/protocols/oscar/im.c src/protocols/oscar/info.c src/protocols/oscar/login.c src/protocols/oscar/meta.c src/protocols/oscar/misc.c src/protocols/oscar/msgcookie.c src/protocols/oscar/oscar.c src/protocols/oscar/rxhandlers.c src/protocols/oscar/rxqueue.c src/protocols/oscar/search.c src/protocols/oscar/snac.c src/protocols/oscar/stats.c src/protocols/oscar/tlv.c src/protocols/oscar/txqueue.c src/protocols/oscar/util.c src/prpl.h
diffstat 32 files changed, 7460 insertions(+), 7618 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Sep 09 06:33:54 2001 +0000
+++ b/ChangeLog	Sun Sep 09 10:07:14 2001 +0000
@@ -11,6 +11,7 @@
 	* Added a spiffy Help button
 	* Wrote a plugin for all those people who miss having the
 	  chat rooms in their buddy lists (chatlist.so)
+	* Updated libfaim
 
 version 0.43 (09/06/2001):
 	* Updated German Translation (thanks Daniel Seifert)
--- a/src/conversation.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/conversation.c	Sun Sep 09 10:07:14 2001 +0000
@@ -415,6 +415,13 @@
 		gtkspell_detach(GTK_TEXT(c->entry));
 
 	if (!c->is_chat) {
+		GSList *cn = connections;
+		while (cn) {
+			struct gaim_connection *gc = cn->data;
+			cn = cn->next;
+			if (gc->prpl->convo_closed)
+				(*gc->prpl->convo_closed)(gc, c->name);
+		}
 		remove_icon(c);
 		remove_checkbox(c);
 		if (display_options & OPT_DISP_ONE_WINDOW) {
--- a/src/protocols/oscar/BUGS	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/BUGS	Sun Sep 09 10:07:14 2001 +0000
@@ -3,35 +3,14 @@
 -----
   - Needs a bit of cleaning
 
-aim_auth.c
-----------
-  - Login doesn't take advantage of aim_conn constructs where it could
-  - Login should allow for multiple BOS connections
-
-aim_chat.c
-----------
-  - Needs to be implemented.
-
-aim_chatnav.c
--------------
-  - Needs to be implemented.
-
-aim_conn.c
-----------
-  - Does not work with proxies.
-
-aim_search.c
+search.c
 ------------
   - Still need aim_usersearch_name()
 
-aim_snac.c
+snac.c
 ----------
   - Should implement better SNAC handling
 
-aim_tlv.c
----------
-  - Newer TLV bulk-read routines most likely have leakage somewhere.
-
-aim_rxhandlers.c
+rxhandlers.c
 ----------------
   - Need a better solution than sleep()
--- a/src/protocols/oscar/CHANGES	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/CHANGES	Sun Sep 09 10:07:14 2001 +0000
@@ -1,6 +1,91 @@
 
 No release numbers
 ------------------
+ - Sat Sep  8 21:26:27 PDT 2001
+  - Minor bug directim_connect
+
+ - Sat Sep  8 20:18:34 PDT 2001
+  - Split up the ICBM Channel 2 handlers a bit more
+  - Add a stub parser for recieving buddy lists.  It will stay that way until
+      someone decides on a good API for passing lists like this to the client.
+      In case someone does, the same standard will be used for setbuddylist and
+      the as yet nonexistant support for server-side buddy lists.
+  - Make infochange work again. I still don't like it, but I don't want to 
+      fix it.
+
+ - Sat Sep  8 19:05:05 PDT 2001
+  - Redo ICQ login
+  - Fix search by email (oops).
+
+ - Sat Sep  8 17:07:09 PDT 2001
+  - Fix directim so you can do it more than once without crashing.
+   - This removes the connect/disconnect callbacks.  They were pointless, as
+       you can get the same information at a better spot by looking for when
+       aim_getcommand returns -1, just like is done for FLAP connections.
+   - (This was causing aim_conn_kill to be called twice for the same connect,
+       once in the client callback, and once more when aim_getcommand returned
+       the -1).
+  - Add aim_conn_(close|kill)_rend().  This is for destroying the cookie and
+     the internal data sections in one spot.
+  - Fix a bug in aim_connrst (and hence aim_logout()) that caused intdata/priv
+     to not be freed in those cases.  Evil.
+
+ - Sat Sep  8 07:32:27 PDT 2001
+  - Clean up ft.c slightly.  Direct IM now works.  In both directions.
+   - This could still use a lot more help.  It should use bstreams more.
+
+ - Sat Sep  8 00:55:46 PDT 2001
+  - Chatnav loop fix. (Err. I shouldn't code when I'm that tired.)
+  - Remove some things from BUGS
+  - Remove USE_MACROS from faimconfig.h.  Not used, and frivelous when it was.
+
+ - Fri Sep  7 21:18:51 PDT 2001
+  - Make icon field names uniform
+  - Add AIM_IMFLAGS_CUSTOMFEATURES.  This allows the client to send/recieve
+     the field in IMs that show client information.
+    - This can be used to identify other open source OSCAR clients, if any
+        one is interested.
+  - Increase the size of args->icbmflags to 32bits
+  - Make sure that extended-only flags are not set for aim_send_im()
+
+ - Fri Sep  7 19:59:43 PDT 2001
+  - Fix buddyicons. Yay! (checksums are 32bits all the time. duh.)
+
+ - Mon Sep  3 18:48:26 PDT 2001
+  - Reformat everything to use real tabs (and to my latest coding style)
+  - Abstract out the numerical data types to fu8/16/32_t for portability.
+  - AIM_FRAMETYPE_OSCAR -> AIM_FRAMETYPE_FLAP.  This makes more sense.
+  - aim_conn_t's FLAP sequence number was a signed int.  Oops.
+  - Get rid of the 'struct' on all types suffixed with _t.  That's been 
+     annoying me for a while.  They're all real typedefs now.
+     - Yes, client people are going to be rather annoyed by this.  But it 
+         doesn't stop there.  Keep reading.
+  - Make the 'struct aim_rxcblist_t' type local to rxhandlers.c.
+  - Combine the command_tx_struct and command_rx_struct into aim_frame_t.
+     - Mostly aim_frame_t is command_rx_struct.  For command_tx_struct, the
+         same structure is used, except where ->sent was, ->handled is now 
+         used.
+     - This makes things a lot easier, everywhere.
+     - Get rid of ->lock.  Pointless.  If libfaim is ever preemptible, it 
+         has much more important problems than that.
+  - Welcome to aim_bstream_t.  No more direct buffer accesses.  Anywhere.
+     - In fact I plan on getting rid of the aimutil macros completly.
+     - This isn't complete yet.  It will be better later.  Believe me.  Maybe
+         even make more sense.  
+     - More advanced and configurable boundschecking is coming, too.
+  - Clean up lots of stuff, everywhere.
+  - Rearrange the implementation of the TLV functions, saving lots of code.
+      - I'm rather pleased with the way some things got implemented with this,
+          particularly in places where TLVs contain TLVs.
+  - Get rid of aim_puttlv_() functions.  Those were gross. Use tlvchains.
+  - XOR login is temporarily broken.  I'll fix it someday. ("Someone" needs it.)
+  - Fix the return values of most everything -- OSCAR functions should all 
+      return 0 on sucess, negative errno on failure.
+  - There are several things braced with #ifdef MID_REWROTE_ALL_THE_CRAP.
+      Consider that code broken and nonfunctional for now.
+  - I think I may have broken buddy icons.  Remind me to fix that.
+  - Renovate faimtest substantially.  Reformat, split up, update to new types.
+
  - Wed Aug 29 16:59:24 PDT 2001
   - Pass up entire icon triplet (checksum/length/timestamp) in all cases
      that it is recieved.
--- a/src/protocols/oscar/admin.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/admin.c	Sun Sep 09 10:07:14 2001 +0000
@@ -3,180 +3,163 @@
 #include <aim.h>
 
 /* called for both reply and change-reply */
-static int infochange(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  int i;
 
-  /*
-   * struct {
-   *    unsigned short perms;
-   *    unsigned short tlvcount;
-   *    aim_tlv_t tlvs[tlvcount];
-   *  } admin_info[n];
-   */
-  for (i = 0; i < datalen; ) {
-    int perms, tlvcount;
+	/*
+	 * struct {
+	 *    unsigned short perms;
+	 *    unsigned short tlvcount;
+	 *    aim_tlv_t tlvs[tlvcount];
+	 *  } admin_info[n];
+	 */
+	while (aim_bstream_empty(bs)) {
+		fu16_t perms, tlvcount;
 
-    perms = aimutil_get16(data+i);
-    i += 2;
+		perms = aimbs_get16(bs);
+		tlvcount = aimbs_get16(bs);
 
-    tlvcount = aimutil_get16(data+i);
-    i += 2;
-
-    while (tlvcount) {
-      aim_rxcallback_t userfunc;
-      struct aim_tlv_t *tlv;
-      int str = 0;
+		while (tlvcount && aim_bstream_empty(bs)) {
+			aim_rxcallback_t userfunc;
+			fu16_t type, len;
+			fu8_t *val;
+			int str = 0;
 
-      if ((aimutil_get16(data+i) == 0x0011) ||
-	  (aimutil_get16(data+i) == 0x0004))
-	str = 1;
+			type = aimbs_get16(bs);
+			len = aimbs_get16(bs);
 
-      if (str)
-	tlv = aim_grabtlvstr(data+i);
-      else
-	tlv = aim_grabtlv(data+i);
+			if ((type == 0x0011) || (type == 0x0004))
+				str = 1;
 
-      /* XXX fix so its only called once for the entire packet */
-      if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-	userfunc(sess, rx, perms, tlv->type, tlv->length, tlv->value, str);
+			if (str)
+				val = aimbs_getstr(bs, len);
+			else
+				val = aimbs_getraw(bs, len);
 
-      if (tlv)
-	i += 2+2+tlv->length;
+			/* XXX fix so its only called once for the entire packet */
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, type, len, val, str);
+
+			free(val);
 
-      if (tlv && tlv->value)
-	free(tlv->value);
-      if (tlv)
-	free(tlv);
+			tlvcount--;
+		}
+	}
 
-      tlvcount--;
-    }
-  }
-
-  return 1;
+	return 1;
 }
 
-static int accountconfirm(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int accountconfirm(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 status;
+	aim_rxcallback_t userfunc;
+	fu16_t status;
 
-  status = aimutil_get16(data);
+	status = aimbs_get16(bs);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    return userfunc(sess, rx, status);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, status);
 
-  return 0;
+	return 0;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if (snac->subtype == 0x0007)
-    return accountconfirm(sess, mod, rx, snac, data, datalen);
+	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;
+	return 0;
 }
 
-faim_internal int admin_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int admin_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x0007;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "admin", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x0007;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "admin", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
 
-faim_export unsigned long aim_auth_clientready(struct aim_session_t *sess,
-					       struct aim_conn_t *conn)
+faim_export int aim_auth_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct aim_tool_version tools[] = {
-    {0x0001, 0x0003,    AIM_TOOL_NEWWIN, 0x0361},
-    {0x0007, 0x0001,    AIM_TOOL_NEWWIN, 0x0361},
-  };
-  int i,j;
-  struct command_tx_struct *newpacket;
-  int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
+	static const struct aim_tool_version tools[] = {
+		{0x0001, 0x0003,    AIM_TOOL_NEWWIN, 0x0361},
+		{0x0007, 0x0001,    AIM_TOOL_NEWWIN, 0x0361},
+	};
+	int j;
+	aim_frame_t *tx;
+	int toolcount = sizeof(tools) / sizeof(struct aim_tool_version);
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
-    return -1;
-
-  newpacket->lock = 1;
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 1152)))
+		return -ENOMEM;
 
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0001, 0x0002, 0x0000, snacid);
 
-  for (j = 0; j < toolcount; j++) {
-    i += aimutil_put16(newpacket->data+i, tools[j].group);
-    i += aimutil_put16(newpacket->data+i, tools[j].version);
-    i += aimutil_put16(newpacket->data+i, tools[j].tool);
-    i += aimutil_put16(newpacket->data+i, tools[j].toolversion);
-  }
+	for (j = 0; j < toolcount; j++) {
+		aimbs_put16(&tx->data, tools[j].group);
+		aimbs_put16(&tx->data, tools[j].version);
+		aimbs_put16(&tx->data, tools[j].tool);
+		aimbs_put16(&tx->data, tools[j].toolversion);
+	}
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
+	aim_tx_enqueue(sess, tx);
 
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
+	return 0;
 }
 
-faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *sess,
-						struct aim_conn_t *conn, 
-						char *new, char *current)
+faim_export int aim_auth_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *tx;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+4+strlen(current)+4+strlen(new))))
-    return -1;
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
 
-  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	/* new password TLV t(0002) */
+	aim_addtlvtochain_raw(&tl, 0x0002, strlen(newpw), newpw);
 
-  /* new password TLV t(0002) */
-  i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(new), new);
+	/* current password TLV t(0012) */
+	aim_addtlvtochain_raw(&tl, 0x0012, strlen(curpw), curpw);
 
-  /* current password TLV t(0012) */
-  i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(current), current);
+	aim_writetlvchain(&tx->data, &tl);
+	aim_freetlvchain(&tl);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, tx);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
-faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess,
-					       struct aim_conn_t *conn)
+faim_export int aim_auth_setversions(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*2))))
-    return -1;
-
-  newpacket->lock = 1;
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 18)))
+		return -ENOMEM;
 
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0001, 0x0017, 0x0000, snacid);
 
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
+	aimbs_put16(&tx->data, 0x0001);
+	aimbs_put16(&tx->data, 0x0003);
 
-  i += aimutil_put16(newpacket->data+i, 0x0007);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&tx->data, 0x0007);
+	aimbs_put16(&tx->data, 0x0001);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, tx);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
 /*
@@ -187,10 +170,9 @@
  * get the TRIAL flag removed from your account.
  *
  */
-faim_export unsigned long aim_auth_reqconfirm(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
+faim_export int aim_auth_reqconfirm(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
+	return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
 }
 
 /*
@@ -199,49 +181,43 @@
  * The only known valid tag is 0x0011 (email address).
  *
  */ 
-faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess,
-					   struct aim_conn_t *conn,
-					   unsigned short info)
+faim_export int aim_auth_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4)))
-    return -1;
-
-  newpacket->lock = 1;
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
+		return -ENOMEM;
 
-  i = aim_putsnac(newpacket->data, 0x0007, 0x0002, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0);
-
-  i += aimutil_put16(newpacket->data+i, info);
-  i += aimutil_put16(newpacket->data+i, 0x0000);
+	snacid = aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0002, 0x0000, snacid);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aimbs_put16(&tx->data, info);
+	aimbs_put16(&tx->data, 0x0000);
 
-  return sess->snac_nextid;
+	aim_tx_enqueue(sess, tx);
+
+	return 0;
 }
 
-faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess,
-					    struct aim_conn_t *conn, 
-					    char *newemail)
+faim_export int aim_auth_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(newemail))))
-    return -1;
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
 
-  i += aim_puttlv_str(newpacket->data+i, 0x0011, strlen(newemail), newemail);
+	aim_addtlvtochain_raw(&tl, 0x0011, strlen(newemail), newemail);
+	
+	aim_writetlvchain(&tx->data, &tl);
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, tx);
 
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
+	return 0;
 }
--- a/src/protocols/oscar/adverts.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/adverts.c	Sun Sep 09 10:07:14 2001 +0000
@@ -6,38 +6,34 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
-faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
+faim_export int aim_ads_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 0x1a)))
+		return -ENOMEM;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x1a)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0002);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0013);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0013);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0005);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0001);
 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return (sess->snac_nextid++);
+	return 0;
 }
 
-faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess,
-					     struct aim_conn_t *conn)
+faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
+	return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
 }
--- a/src/protocols/oscar/aim.h	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/aim.h	Sun Sep 09 10:07:14 2001 +0000
@@ -39,6 +39,13 @@
 #include <sys/socket.h>
 #endif
 
+/* XXX adjust these based on autoconf-detected platform */
+typedef unsigned char fu8_t;
+typedef unsigned short fu16_t;
+typedef unsigned long fu32_t;
+typedef fu32_t aim_snacid_t;
+typedef fu16_t flap_seqnum_t;
+
 #ifdef FAIM_USEPTHREADS
 #include <pthread.h>
 #define faim_mutex_t pthread_mutex_t 
@@ -53,14 +60,20 @@
  * means we don't have to do real locking.  The 
  * macros below do nothing really.  They're a joke.
  * But they get it to compile.
+ * 
+ * XXX NOTE that locking hasn't really been tested in a long time,
+ * and most code written after dec2000 --is not thread safe--.  You'll
+ * want to audit locking use before you use less-than-library level
+ * concurrency.
+ *
  */
-#define faim_mutex_t char
+#define faim_mutex_t fu8_t 
 #define faim_mutex_init(x) *x = 0
 #define faim_mutex_lock(x) while(*x != 0) {/* spin */}; *x = 1;
 #define faim_mutex_unlock(x) while(*x != 0) {/* spin spin spin */}; *x = 0;
 #define faim_mutex_destroy(x) while(*x != 0) {/* spiiiinnn */}; *x = 0;
 #elif defined(FAIM_USENOPLOCKS)
-#define faim_mutex_t char
+#define faim_mutex_t fu8_t 
 #define faim_mutex_init(x)
 #define faim_mutex_lock(x)
 #define faim_mutex_unlock(x)
@@ -164,39 +177,39 @@
  *
  */
 struct client_info_s {
-  char clientstring[100]; /* arbitrary size */
-  int major;
-  int minor;
-  int build;
-  char country[3];
-  char lang[3];
-  int major2;
-  int minor2;
-  long unknown;
+	char clientstring[100]; /* arbitrary size */
+	int major;
+	int minor;
+	int build;
+	char country[3];
+	char lang[3];
+	int major2;
+	int minor2;
+	long unknown;
 };
 
 #define AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 { \
-  "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
-  0x0003, \
-  0x0005, \
-  0x0686, \
-  "us", \
-  "en", \
-  0x0004, \
-  0x0000, \
-  0x0000002a, \
+	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
+	0x0003, \
+	0x0005, \
+	0x0686, \
+	"us", \
+	"en", \
+	0x0004, \
+	0x0000, \
+	0x0000002a, \
 }
 
 #define AIM_CLIENTINFO_KNOWNGOOD_4_1_2010 { \
-  "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
-  0x0004, \
-  0x0001, \
-  0x07da, \
-  "us", \
-  "en", \
-  0x0004, \
-  0x0000, \
-  0x0000004b, \
+	  "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
+	  0x0004, \
+	  0x0001, \
+	  0x07da, \
+	  "us", \
+	  "en", \
+	  0x0004, \
+	  0x0000, \
+	  0x0000004b, \
 }
 
 /*
@@ -224,7 +237,7 @@
 #define AIM_CONN_TYPE_CHATNAV       0x000d
 
 /* they start getting arbitrary in rendezvous stuff =) */
-#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak OSCAR! */
+#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak FLAP! */
 #define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
 
 /*
@@ -245,145 +258,155 @@
 #define AIM_CONN_STATUS_CONNERR     0x0080
 #define AIM_CONN_STATUS_INPROGRESS  0x0100
 
-#define AIM_FRAMETYPE_OSCAR 0x0000
-#define AIM_FRAMETYPE_OFT 0x0001
+#define AIM_FRAMETYPE_FLAP 0x0000
+#define AIM_FRAMETYPE_OFT  0x0001
 
-struct aim_conn_t {
-  int fd;
-  unsigned short type;
-  unsigned short subtype;
-  int seqnum;
-  int status;
-  void *priv; /* misc data the client may want to store */
-  time_t lastactivity; /* time of last transmit */
-  int forcedlatency; 
-  struct aim_rxcblist_t *handlerlist;
-  faim_mutex_t active; /* lock around read/writes */
-  faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */
-  void *sessv;
-  struct aim_conn_t *next;
-};
+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;
+	faim_mutex_t active; /* lock around read/writes */
+	faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */
+	void *sessv; /* pointer to parent session */
+	struct aim_conn_s *next;
+} aim_conn_t;
 
-/* struct for incoming commands */
-struct command_rx_struct {
-  unsigned char hdrtype; /* defines which piece of the union to use */
-  union {
-    struct { 
-      char type;
-      unsigned short seqnum;     
-    } oscar;
-    struct {
-      unsigned short type;
-      unsigned char magic[4]; /* ODC2 OFT2 */
-      unsigned short hdr2len;
-      unsigned char *hdr2; /* rest of bloated header */
-    } oft;
-  } hdr;
-  unsigned short commandlen;         /* total payload length */
-  unsigned char *data;             /* packet data (from 7 byte on) */
-  unsigned char lock;               /* 0 = open, !0 = locked */
-  unsigned char handled;            /* 0 = new, !0 = been handled */
-  unsigned char nofree;		    /* 0 = free data on purge, 1 = only unlink */
-  struct aim_conn_t *conn;  /* the connection it came in on... */
-  struct command_rx_struct *next; /* ptr to next struct in list */
-};
+/*
+ * 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;
+	fu16_t len;
+	fu16_t offset;
+} aim_bstream_t;
 
-/* struct for outgoing commands */
-struct command_tx_struct {
-  unsigned char hdrtype; /* defines which piece of the union to use */
-  union {
-    struct {
-      unsigned char type;
-      unsigned short seqnum;
-    } oscar;
-    struct {
-      unsigned short type;
-      unsigned char magic[4]; /* ODC2 OFT2 */
-      unsigned short hdr2len;
-      unsigned char *hdr2;
-    } oft;
-  } hdr;
-  u_int commandlen;         
-  u_char *data;      
-  u_int lock;               /* 0 = open, !0 = locked */
-  u_int sent;               /* 0 = pending, !0 = has been sent */
-  struct aim_conn_t *conn; 
-  struct command_tx_struct *next; /* ptr to next struct in list */
-};
+typedef struct aim_frame_s {
+	fu8_t hdrtype; /* defines which piece of the union to use */
+	union {
+		struct { 
+			fu8_t type;
+			flap_seqnum_t seqnum;     
+		} flap;
+		struct {
+			fu16_t type;
+			fu8_t magic[4]; /* ODC2 OFT2 */
+			fu16_t hdr2len;
+			fu8_t *hdr2; /* rest of bloated header */
+		} oft;
+	} hdr;
+	aim_bstream_t data;	/* payload stream */
+	fu8_t handled;		/* 0 = new, !0 = been handled */
+	fu8_t nofree;		/* 0 = free data on purge, 1 = only unlink */
+	aim_conn_t *conn;  /* the connection it came in on... */
+	struct aim_frame_s *next;
+} aim_frame_t;
+
+typedef struct aim_msgcookie_s {
+	unsigned char cookie[8];
+	int type;
+	void *data;
+	time_t addtime;
+	struct aim_msgcookie_s *next;
+} aim_msgcookie_t;
 
 /*
  * AIM Session: The main client-data interface.  
  *
  */
-struct aim_session_t {
+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;
 
-  /* ---- 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;
+	faim_mutex_t connlistlock;
+
+	/*
+	 * 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; 
 
-  /* ---- Internal Use Only ------------------------ */
-  /* 
-   * Connection information
-   */
-  struct aim_conn_t *connlist;
-  faim_mutex_t connlistlock;
-  
-  /* 
-   * TX/RX queues 
-   */
-  struct command_tx_struct *queue_outgoing;   
-  struct command_rx_struct *queue_incoming; 
-  
-  /*
-   * Tx Enqueuing function
-   */
-  int (*tx_enqueue)(struct aim_session_t *, struct command_tx_struct *);
+	/*
+	 * 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 *);
+
+	/*
+	 * This is a dreadful solution to the what-room-are-we-joining
+	 * problem.  (There's no connection between the service
+	 * request and the resulting redirect.)
+	 */ 
+	char *pendingjoin;
+	fu16_t pendingjoinexchange;
 
-  /*
-   * This is a dreadful solution to the what-room-are-we-joining
-   * problem.  (There's no connection between the service
-   * request and the resulting redirect.)
-   */ 
-  char *pendingjoin;
-  unsigned short pendingjoinexchange;
+	/*
+	 * Outstanding snac handling 
+	 *
+	 * XXX: Should these be per-connection? -mid
+	 */
+	void *snac_hash[FAIM_SNAC_HASH_SIZE];
+	faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE];
+	aim_snacid_t snacid_next;
 
-  /*
-   * Outstanding snac handling 
-   *
-   * XXX: Should these be per-connection? -mid
-   */
-  struct aim_snac_t *snac_hash[FAIM_SNAC_HASH_SIZE];
-  faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE];
-  unsigned long snac_nextid;
+	struct {
+		char server[128];
+		char username[128];
+		char password[128];
+	} socksproxy;
+
+	fu32_t flags; /* AIM_SESS_FLAGS_ */
 
-  struct {
-    char server[128];
-    char username[128];
-    char password[128];
-  } socksproxy;
-
-  unsigned long flags;
+	int debug;
+	void (*debugcb)(struct aim_session_s *sess, int level, const char *format, va_list va); /* same as faim_debugging_callback_t */
 
-  int debug;
-  void (*debugcb)(struct aim_session_t *sess, int level, const char *format, va_list va); /* same as faim_debugging_callback_t */
+	aim_msgcookie_t *msgcookies;
 
-  struct aim_msgcookie_t *msgcookies;
-
-  void *modlistv;
-};
+	void *modlistv;
+} aim_session_t;
 
 /* Values for sess->flags */
 #define AIM_SESS_FLAGS_SNACLOGIN       0x00000001
@@ -394,19 +417,19 @@
  * AIM User Info, Standard Form.
  */
 struct aim_userinfo_s {
-  char sn[MAXSNLEN+1];
-  u_short warnlevel;
-  u_short idletime;
-  u_short flags;
-  u_long membersince;
-  u_long onlinesince;
-  u_long sessionlen;  
-  u_short capabilities;
-  struct {
-    unsigned short status;
-    unsigned int ipaddr;
-    char crap[0x25]; /* until we figure it out... */
-  } icqinfo;
+	char sn[MAXSNLEN+1];
+	fu16_t warnlevel;
+	fu16_t idletime;
+	fu16_t flags;
+	fu32_t membersince;
+	fu32_t onlinesince;
+	fu32_t sessionlen;  
+	fu16_t capabilities;
+	struct {
+		fu16_t status;
+		fu32_t ipaddr;
+		fu8_t crap[0x25]; /* until we figure it out... */
+	} icqinfo;
 };
 
 #define AIM_FLAG_UNCONFIRMED 	0x0001 /* "damned transients" */
@@ -427,112 +450,114 @@
  */
 
 /* Generic TLV structure. */
-struct aim_tlv_t {
-  u_short type;
-  u_short length;
-  u_char *value;
-};
+typedef struct aim_tlv_s {
+	fu16_t type;
+	fu16_t length;
+	fu8_t *value;
+} aim_tlv_t;
 
 /* List of above. */
-struct aim_tlvlist_t {
-  struct aim_tlv_t *tlv;
-  struct aim_tlvlist_t *next;
-};
+typedef struct aim_tlvlist_s {
+	aim_tlv_t *tlv;
+	struct aim_tlvlist_s *next;
+} aim_tlvlist_t;
 
 /* TLV-handling functions */
-faim_internal struct aim_tlvlist_t *aim_readtlvchain(const unsigned char *buf, const int maxlen);
-faim_internal void aim_freetlvchain(struct aim_tlvlist_t **list);
-faim_internal struct aim_tlv_t *aim_grabtlv(const unsigned char *src);
-faim_internal struct aim_tlv_t *aim_grabtlvstr(const unsigned char *src);
-faim_internal struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, const unsigned short, const int);
-faim_internal char *aim_gettlv_str(struct aim_tlvlist_t *, const unsigned short, const int);
-faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, const unsigned short type, const int num);
-faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, const unsigned short type, const int num);
-faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, const unsigned short type, const int num);
-faim_internal int aim_puttlv (unsigned char *dest, struct aim_tlv_t *newtlv);
-faim_internal struct aim_tlv_t *aim_createtlv(void);
-faim_internal int aim_freetlv(struct aim_tlv_t **oldtlv);
-faim_internal int aim_puttlv_8(unsigned char *buf, const unsigned short t, const unsigned char  v);
-faim_internal int aim_puttlv_16(unsigned char *, const unsigned short, const unsigned short);
-faim_internal int aim_puttlv_32(unsigned char *, const unsigned short, const unsigned long);
-faim_internal int aim_puttlv_str(u_char *buf, const unsigned short t, const int l, const char *v);
-faim_internal int aim_writetlvchain(unsigned char *buf, const int buflen, struct aim_tlvlist_t **list);
-faim_internal int aim_addtlvtochain16(struct aim_tlvlist_t **list, const unsigned short type, const unsigned short val);
-faim_internal int aim_addtlvtochain32(struct aim_tlvlist_t **list, const unsigned short type, const unsigned long val);
-faim_internal int aim_addtlvtochain_str(struct aim_tlvlist_t **list, const unsigned short type, const char *str, const int len);
-faim_internal int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, const unsigned short type, const unsigned short caps);
-faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, const unsigned short type);
-faim_internal int aim_counttlvchain(struct aim_tlvlist_t **list);
+
+#if 0
+/* Very, very raw TLV handling. */
+faim_internal int aim_puttlv_8(fu8_t *buf, const fu16_t t, const fu8_t v);
+faim_internal int aim_puttlv_16(fu8_t *buf, const fu16_t t, const fu16_t v);
+faim_internal int aim_puttlv_32(fu8_t *buf, const fu16_t t, const fu32_t v);
+faim_internal int aim_puttlv_raw(fu8_t *buf, const fu16_t t, const fu16_t l, const fu8_t *v);
+#endif
+
+/* TLV list handling. */
+faim_internal aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs);
+faim_internal void aim_freetlvchain(aim_tlvlist_t **list);
+faim_internal aim_tlv_t *aim_gettlv(aim_tlvlist_t *, fu16_t t, const int n);
+faim_internal char *aim_gettlv_str(aim_tlvlist_t *, const fu16_t t, const int n);
+faim_internal fu8_t aim_gettlv8(aim_tlvlist_t *list, const fu16_t type, const int num);
+faim_internal fu16_t aim_gettlv16(aim_tlvlist_t *list, const fu16_t t, const int n);
+faim_internal fu32_t aim_gettlv32(aim_tlvlist_t *list, const fu16_t t, const int n);
+faim_internal int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
+faim_internal int aim_addtlvtochain16(aim_tlvlist_t **list, const fu16_t t, const fu16_t v);
+faim_internal int aim_addtlvtochain32(aim_tlvlist_t **list, const fu16_t type, const fu32_t v);
+faim_internal int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t t, const fu16_t l, const fu8_t *v);
+faim_internal int aim_addtlvtochain_caps(aim_tlvlist_t **list, const fu16_t t, const fu16_t caps);
+faim_internal int aim_addtlvtochain_noval(aim_tlvlist_t **list, const fu16_t type);
+faim_internal int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl);
+faim_internal int aim_counttlvchain(aim_tlvlist_t **list);
+faim_export int aim_sizetlvchain(aim_tlvlist_t **list);
 #endif /* FAIM_INTERNAL */
 
 /*
- * Get command from connections / Dispatch commands
- * already in queue.
+ * 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(struct aim_session_t *, struct aim_conn_t *);
-int aim_rxdispatch(struct aim_session_t *);
+faim_export int aim_get_command(aim_session_t *, aim_conn_t *);
 
-faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
+/*
+ * Dispatch commands that are in the rx queue.
+ */
+faim_export void aim_rxdispatch(aim_session_t *);
 
-faim_export int aim_logoff(struct 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) || defined(FAIM_INTERNAL_INSANE)
 /* the library should never call aim_conn_kill */
-faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn);
+faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
 #endif
 
-typedef int (*aim_rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...);
+typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...);
 
 /* aim_login.c */
-faim_export int aim_sendflapver(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn);
-faim_export int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *, char *key);
+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);
 faim_export int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest);
-faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, int errorcode, char *errorurl, char *bosip, char *cookie, char *email, int regstatus);
+faim_export int aim_sendauthresp(aim_session_t *sess, aim_conn_t *conn, const char *sn, int errorcode, const char *errorurl, const char *bosip, const char *cookie, const char *email, int regstatus);
 faim_export int aim_gencookie(unsigned char *buf);
-faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie);
-faim_export void aim_purge_rxqueue(struct aim_session_t *);
+faim_export int aim_sendserverready(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_sendredirect(aim_session_t *sess, aim_conn_t *conn, fu16_t servid, const char *ip, const char *cookie);
+faim_export void aim_purge_rxqueue(aim_session_t *);
 
 #define AIM_TX_QUEUED    0 /* default */
 #define AIM_TX_IMMEDIATE 1
 #define AIM_TX_USER      2
-faim_export int aim_tx_setenqueue(struct aim_session_t *sess, int what,  int (*func)(struct aim_session_t *, struct command_tx_struct *));
+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(struct aim_session_t *);
-faim_export void aim_tx_purgequeue(struct aim_session_t *);
+faim_export int aim_tx_flushqueue(aim_session_t *);
+faim_export void aim_tx_purgequeue(aim_session_t *);
 
-struct aim_rxcblist_t {
-  u_short family;
-  u_short type;
-  aim_rxcallback_t handler;
-  u_short flags;
-  struct aim_rxcblist_t *next;
-};
+faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval);
 
-faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval);
-
-faim_export int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, aim_rxcallback_t newhandler, u_short flags);
-faim_export int aim_clearhandlers(struct aim_conn_t *conn);
+faim_export int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, u_short family, u_short type, aim_rxcallback_t newhandler, u_short flags);
+faim_export int aim_clearhandlers(aim_conn_t *conn);
 
-faim_export struct aim_session_t *aim_conn_getsess(struct aim_conn_t *conn);
-faim_export void aim_conn_close(struct aim_conn_t *deadconn);
-faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest);
-faim_export int aim_conngetmaxfd(struct aim_session_t *);
-faim_export struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *);
-faim_export int aim_conn_isready(struct aim_conn_t *);
-faim_export int aim_conn_setstatus(struct aim_conn_t *, int);
-faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_conn_isconnecting(struct aim_conn_t *conn);
+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, const char *dest);
+faim_export int aim_conngetmaxfd(aim_session_t *);
+faim_export aim_conn_t *aim_select(aim_session_t *, struct timeval *, int *);
+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);
 
-typedef void (*faim_debugging_callback_t)(struct aim_session_t *sess, int level, const char *format, va_list va);
-faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t);
-faim_export void aim_session_init(struct aim_session_t *, unsigned long flags, int debuglevel);
-faim_export void aim_session_kill(struct aim_session_t *);
-faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password);
-faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type);
-faim_export struct aim_conn_t *aim_getconn_type_all(struct aim_session_t *, int type);
-faim_export struct aim_conn_t *aim_getconn_fd(struct aim_session_t *, int fd);
+typedef void (*faim_debugging_callback_t)(aim_session_t *sess, int level, const char *format, va_list va);
+faim_export int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t);
+faim_export void aim_session_init(aim_session_t *, unsigned long flags, int debuglevel);
+faim_export void aim_session_kill(aim_session_t *);
+faim_export void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password);
+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);
 
 /* aim_misc.c */
 
@@ -546,36 +571,29 @@
 
 #define AIM_WARN_ANON                     0x01
 
-faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, const char *destsn, unsigned long flags);
-faim_export unsigned long aim_bos_nop(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *);
-faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess, struct aim_conn_t *conn, const char *profile, const char *awaymsg, unsigned short caps);
-faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short);
-faim_export unsigned long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_auth_reqconfirm(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short info);
-faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess, struct aim_conn_t *conn, char *newemail);
-faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy);
-faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy);
-faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long status);
+faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
+faim_export int aim_bos_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_setidle(aim_session_t *, aim_conn_t *, fu32_t);
+faim_export int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *);
+faim_export int aim_bos_setbuddylist(aim_session_t *, aim_conn_t *, const char *);
+faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu16_t caps);
+faim_export int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, fu32_t mask);
+faim_export int aim_bos_clientready(aim_session_t *, aim_conn_t *);
+faim_export int aim_bos_reqrate(aim_session_t *, aim_conn_t *);
+faim_export int aim_bos_ackrateresp(aim_session_t *, aim_conn_t *);
+faim_export int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, fu32_t);
+faim_export int aim_bos_reqpersonalinfo(aim_session_t *, aim_conn_t *);
+faim_export int aim_bos_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_bos_reqbuddyrights(aim_session_t *, aim_conn_t *);
+faim_export int aim_bos_reqlocaterights(aim_session_t *, aim_conn_t *);
+faim_export int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, 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);
+faim_export int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy);
+faim_export int aim_icq_setstatus(aim_session_t *sess, aim_conn_t *conn, fu32_t status);
 
-faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *);
-
-/* aim_rxhandlers.c */
-faim_export int aim_rxdispatch(struct aim_session_t *);
+faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *);
 
 #define AIM_CLIENTTYPE_UNKNOWN  0x0000
 #define AIM_CLIENTTYPE_MC       0x0001
@@ -588,49 +606,44 @@
 #define AIM_RATE_CODE_WARNING    0x0002
 #define AIM_RATE_CODE_LIMIT      0x0003
 #define AIM_RATE_CODE_CLEARLIMIT 0x0004
-faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_ads_clientready(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
 
 /* aim_im.c */
-struct aim_directim_priv {
-  unsigned char cookie[8];
-  char sn[MAXSNLEN+1];
-  char ip[30];
-};
 
 struct aim_fileheader_t {
 #if 0
-  char  magic[4];            /* 0 */
-  short hdrlen;           /* 4 */
-  short hdrtype;             /* 6 */
+	char  magic[4];		/* 0 */
+	short hdrlen; 		/* 4 */
+	short hdrtype;		/* 6 */
 #endif
-  char  bcookie[8];       /* 8 */
-  short encrypt;          /* 16 */
-  short compress;         /* 18 */
-  short totfiles;         /* 20 */
-  short filesleft;        /* 22 */
-  short totparts;         /* 24 */
-  short partsleft;        /* 26 */
-  long  totsize;          /* 28 */
-  long  size;             /* 32 */
-  long  modtime;          /* 36 */
-  long  checksum;         /* 40 */
-  long  rfrcsum;          /* 44 */
-  long  rfsize;           /* 48 */
-  long  cretime;          /* 52 */
-  long  rfcsum;           /* 56 */
-  long  nrecvd;           /* 60 */
-  long  recvcsum;         /* 64 */
-  char  idstring[32];     /* 68 */
-  char  flags;            /* 100 */
-  char  lnameoffset;      /* 101 */
-  char  lsizeoffset;      /* 102 */
-  char  dummy[69];        /* 103 */
-  char  macfileinfo[16];  /* 172 */
-  short nencode;          /* 188 */
-  short nlanguage;        /* 190 */
-  char  name[64];         /* 192 */
-                          /* 256 */
+	char  bcookie[8];       /* 8 */
+	short encrypt;          /* 16 */
+	short compress;         /* 18 */
+	short totfiles;         /* 20 */
+	short filesleft;        /* 22 */
+	short totparts;         /* 24 */
+	short partsleft;        /* 26 */
+	long  totsize;          /* 28 */
+	long  size;             /* 32 */
+	long  modtime;          /* 36 */
+	long  checksum;         /* 40 */
+	long  rfrcsum;          /* 44 */
+	long  rfsize;           /* 48 */
+	long  cretime;          /* 52 */
+	long  rfcsum;           /* 56 */
+	long  nrecvd;           /* 60 */
+	long  recvcsum;         /* 64 */
+	char  idstring[32];     /* 68 */
+	char  flags;            /* 100 */
+	char  lnameoffset;      /* 101 */
+	char  lsizeoffset;      /* 102 */
+	char  dummy[69];        /* 103 */
+	char  macfileinfo[16];  /* 172 */
+	short nencode;          /* 188 */
+	short nlanguage;        /* 190 */
+	char  name[64];         /* 192 */
+				/* 256 */
 };
 
 struct aim_filetransfer_priv {
@@ -647,52 +660,73 @@
 	unsigned short instance;
 };
 
-#define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */
-#define AIM_IMFLAGS_ACK  0x02 /* request a receipt notice */
-#define AIM_IMFLAGS_UNICODE    0x04
-#define AIM_IMFLAGS_ISO_8859_1 0x08
-#define AIM_IMFLAGS_BUDDYREQ   0x10 /* buddy icon requested */
-#define AIM_IMFLAGS_HASICON    0x20 /* already has icon (timestamp included) */
-#define AIM_IMFLAGS_SUBENC_MACINTOSH	0x40 /* damn that Steve Jobs! */
+#define AIM_IMFLAGS_AWAY		0x0001 /* mark as an autoreply */
+#define AIM_IMFLAGS_ACK			0x0002 /* request a receipt notice */
+#define AIM_IMFLAGS_UNICODE		0x0004
+#define AIM_IMFLAGS_ISO_8859_1		0x0008
+#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
 
 struct aim_sendimext_args {
+
+	/* These are _required_ */
 	const char *destsn;
-	unsigned short flags;
+	fu32_t flags;
 	const char *msg;
 	int msglen;
-	int iconlen;
+
+	/* Only used if AIM_IMFLAGS_HASICON is set */
+	fu32_t iconlen;
 	time_t iconstamp;
-	unsigned short iconsum;
+	fu32_t iconsum;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
+	fu8_t *features;
+	fu8_t featureslen;
 };
 
 struct aim_incomingim_ch1_args {
+
+	/* Always provided */
 	char *msg;
 	int msglen;
-	unsigned long icbmflags;
-	unsigned short flag1;
-	unsigned short flag2;
-	int finlen;
-	unsigned char fingerprint[10];
+	fu32_t icbmflags;
+	fu16_t flag1;
+	fu16_t flag2;
+
+	/* Only provided if AIM_IMFLAGS_HASICON is set */
 	time_t iconstamp;
-	unsigned long iconlength;
-	unsigned long iconchecksum;  
-	int extdatalen;
-	unsigned char *extdata;
+	fu32_t iconlen;
+	fu32_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;
 };
 
 struct aim_incomingim_ch2_args {
-	unsigned short reqclass;
-	unsigned short status;
+	fu8_t cookie[8];
+	fu16_t reqclass;
+	fu16_t status;
 	union {
 		struct {
-			unsigned long checksum;
-			unsigned int length;
+			fu32_t checksum;
+			fu32_t length;
 			time_t timestamp;
-			unsigned char *icon;
+			fu8_t *icon;
 		} icon;
 		struct {
 		} voice;
-		struct aim_directim_priv *directim;
+		struct {
+			fu8_t ip[22]; /* xxx.xxx.xxx.xxx:xxxxx\0 */
+		} imimage;
 		struct {
 			char *msg;
 			char *encoding;
@@ -708,18 +742,19 @@
 	} info;
 };
 
-faim_export int aim_send_im_ext(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_sendimext_args *args);
-faim_export int aim_send_im(struct aim_session_t *, struct aim_conn_t *, const char *destsn, unsigned short flags, const char *msg);
-faim_export int aim_send_icon(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn, const unsigned char *icon, int iconlen, time_t stamp, unsigned short iconsum);
-faim_export unsigned short aim_iconsum(const unsigned char *buf, int buflen);
-faim_export int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *destsn);
-faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *);
+faim_export int aim_send_im_ext(aim_session_t *sess, aim_conn_t *conn, struct aim_sendimext_args *args);
+faim_export int aim_send_im(aim_session_t *, aim_conn_t *, const char *destsn, unsigned short flags, const char *msg);
+faim_export int aim_send_icon(aim_session_t *sess, aim_conn_t *conn, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu32_t iconsum);
+faim_export fu32_t aim_iconsum(const fu8_t *buf, int buflen);
+faim_export int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg);
+faim_export const char *aim_directim_getsn(aim_conn_t *conn);
+faim_export aim_conn_t *aim_directim_initiate(aim_session_t *, aim_conn_t *, const char *destsn);
+faim_export aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const fu8_t *cookie);
 
-faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn);
-faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size);
-faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn);
+faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size);
+faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn);
 
 /* aim_info.c */
 #define AIM_CAPS_BUDDYICON      0x0001
@@ -734,29 +769,21 @@
 #define AIM_CAPS_GAMES2         0x0200
 #define AIM_CAPS_LAST           0x8000
 
-faim_export int aim_0002_000b(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn);
+faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn);
 
 #define AIM_SENDMEMBLOCK_FLAG_ISREQUEST  0
 #define AIM_SENDMEMBLOCK_FLAG_ISHASH     1
 
-faim_export int aim_sendmemblock(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long offset, unsigned long len, const unsigned char *buf, unsigned char flag);
+faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, unsigned long offset, unsigned long len, const unsigned char *buf, unsigned char flag);
 
 #define AIM_GETINFO_GENERALINFO 0x00001
 #define AIM_GETINFO_AWAYMESSAGE 0x00003
 
-struct aim_msgcookie_t {
-  unsigned char cookie[8];
-  int type;
-  void *data;
-  time_t addtime;
-  struct aim_msgcookie_t *next;
-};
-
 struct aim_invite_priv {
-  char *sn;
-  char *roomname;
-  int exchange;
-  int instance;
+	char *sn;
+	char *roomname;
+	fu16_t exchange;
+	fu16_t instance;
 };
 
 #define AIM_COOKIETYPE_UNKNOWN  0x00
@@ -776,17 +803,17 @@
 #define AIM_COOKIETYPE_OFTIMAGE 0x14
 #define AIM_COOKIETYPE_OFTICON  0x15
 
-faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur);
+faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
 
 #define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
 #define AIM_TRANSFER_DENY_DECLINE 0x0001
 #define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
-faim_export int aim_denytransfer(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sender, const char *cookie, unsigned short code);
-faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *cookie, char *ip, unsigned short listingfiles, unsigned short listingtotsize, unsigned short listingsize, unsigned int listingchecksum, unsigned short rendid);
+faim_export int aim_denytransfer(aim_session_t *sess, aim_conn_t *conn, const char *sender, const char *cookie, unsigned short code);
+faim_export aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, const fu8_t *cookie, const fu8_t *ip, fu16_t listingfiles, fu16_t listingtotsize, fu16_t listingsize, fu32_t listingchecksum, fu16_t rendid);
 
-faim_export int aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *, unsigned short);
-faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info);
-faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
+faim_export int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, unsigned short);
+faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *info);
+faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
 
 #define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
 #define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
@@ -819,79 +846,78 @@
 	unsigned long minmsginterval; /* in milliseconds? */
 };
 
-faim_export unsigned long aim_reqicbmparams(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_seticbmparam(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_icbmparameters *params);
+faim_export int aim_reqicbmparams(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_seticbmparam(aim_session_t *sess, aim_conn_t *conn, struct aim_icbmparameters *params);
 
 
-/* aim_auth.c */
-faim_export int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *);
-faim_export u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *);
+/* auth.c */
+faim_export int aim_auth_sendcookie(aim_session_t *, aim_conn_t *, const fu8_t *);
+
+faim_export int aim_auth_clientready(aim_session_t *, aim_conn_t *);
+faim_export int aim_auth_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw);
+faim_export int aim_auth_setversions(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_auth_reqconfirm(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_auth_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info);
+faim_export int aim_auth_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
 
 /* aim_buddylist.c */
-faim_export unsigned long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export unsigned long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
+faim_export int aim_add_buddy(aim_session_t *, aim_conn_t *, const char *);
+faim_export int aim_remove_buddy(aim_session_t *, aim_conn_t *, const char *);
 
 /* aim_search.c */
-faim_export u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *);
+faim_export int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
 
 struct aim_chat_exchangeinfo {
-  u_short number;
-  char *name;
-  char *charset1;
-  char *lang1;
-  char *charset2;
-  char *lang2;
+	fu16_t number;
+	char *name;
+	char *charset1;
+	char *lang1;
+	char *charset2;
+	char *lang2;
 };
 
 #define AIM_CHATFLAGS_NOREFLECT 0x0001
 #define AIM_CHATFLAGS_AWAY      0x0002
-faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short flags, const char *msg, int msglen);
-faim_export unsigned long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname);
-faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname);
-faim_export char *aim_chat_getname(struct aim_conn_t *conn);
-faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name);
+faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen);
+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_clientready(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_chat_attachname(aim_conn_t *conn, const char *roomname);
+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 unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn);
+faim_export int aim_chatnav_clientready(aim_session_t *sess, aim_conn_t *conn);
 
-faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance);
+faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance);
 
-faim_export u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange);
-faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name);
+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);
 
 /* aim_util.c */
-#ifdef AIMUTIL_USEMACROS
 /*
  * 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) = (u_char)(data)&0xff),1)
 #define aimutil_get8(buf) ((*(buf))&0xff)
 #define aimutil_put16(buf, data) ( \
-                                  (*(buf) = (u_char)((data)>>8)&0xff), \
-				  (*((buf)+1) = (u_char)(data)&0xff),  \
-				  2)
+		(*(buf) = (u_char)((data)>>8)&0xff), \
+		(*((buf)+1) = (u_char)(data)&0xff),  \
+		2)
 #define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
 #define aimutil_put32(buf, data) ( \
-                                  (*((buf)) = (u_char)((data)>>24)&0xff), \
-				  (*((buf)+1) = (u_char)((data)>>16)&0xff), \
-				  (*((buf)+2) = (u_char)((data)>>8)&0xff), \
-				  (*((buf)+3) = (u_char)(data)&0xff), \
-                                  4)
+		(*((buf)) = (u_char)((data)>>24)&0xff), \
+		(*((buf)+1) = (u_char)((data)>>16)&0xff), \
+		(*((buf)+2) = (u_char)((data)>>8)&0xff), \
+		(*((buf)+3) = (u_char)(data)&0xff), \
+		4)
 #define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
-                            (((*((buf)+1))<<16)&0x00ff0000) + \
-                            (((*((buf)+2))<< 8)&0x0000ff00) + \
-                            (((*((buf)+3)    )&0x000000ff)))
-#else
-#warning Not using aimutil macros.  May have performance problems.
-int aimutil_put8(u_char *, u_char);
-u_char aimutil_get8(u_char *buf);
-int aimutil_put16(u_char *, u_short);
-u_short aimutil_get16(u_char *);
-int aimutil_put32(u_char *, u_long);
-u_long aimutil_get32(u_char *);
-#endif
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
 
 faim_export int aimutil_putstr(u_char *, const char *, int);
 faim_export int aimutil_tokslen(char *toSearch, int index, char dl);
--- a/src/protocols/oscar/aim_internal.h	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/aim_internal.h	Sun Sep 09 10:07:14 2001 +0000
@@ -8,88 +8,109 @@
 #define __AIM_INTERNAL_H__ 1
 
 typedef struct {
-  unsigned short family;
-  unsigned short subtype;
-  unsigned short flags;
-  unsigned long id;
+	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 {
-  unsigned short family;
-  unsigned short flags;
-  unsigned short version;
-  char name[AIM_MODULENAME_MAXLEN+1];
-  int (*snachandler)(struct aim_session_t *sess, struct aim_module_s *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen);
-  void (*shutdown)(struct aim_session_t *sess, struct aim_module_s *mod);
-  void *priv;
-  struct aim_module_s *next;
+	unsigned short family;
+	unsigned short flags;
+	unsigned short version;
+	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(struct aim_session_t *sess, int (*modfirst)(struct aim_session_t *, aim_module_t *));
-faim_internal void aim__shutdownmodules(struct aim_session_t *sess);
+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 int buddylist_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int admin_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int bos_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int search_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int stats_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int auth_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int msg_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int misc_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int chatnav_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int chat_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int locate_modfirst(struct aim_session_t *sess, aim_module_t *mod);
-faim_internal int general_modfirst(struct aim_session_t *sess, aim_module_t *mod);
+faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int admin_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 general_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)
 
-faim_internal unsigned long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype);
-faim_internal unsigned long aim_genericreq_n_snacid(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype);
-faim_internal unsigned long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *);
-faim_internal unsigned long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *);
-
-faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
 faim_internal int aim_recv(int fd, void *buf, size_t count);
-
-faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count);
+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, 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 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_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_putraw(aim_bstream_t *bs, const fu8_t *v, int len);
+faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len);
 
-faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur);
-faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *);
-faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned char framing, int chan, int datalen);
-faim_internal int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *);
-faim_internal int aim_tx_printqueue(struct aim_session_t *);
-faim_internal int aim_tx_cleanqueue(struct aim_session_t *, struct aim_conn_t *);
+faim_internal int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn);
 
-faim_internal aim_rxcallback_t aim_callhandler(struct aim_session_t *sess, struct aim_conn_t *conn, u_short family, u_short type);
-faim_internal int aim_callhandler_noparam(struct aim_session_t *sess, struct aim_conn_t *conn, u_short family, u_short type, struct command_rx_struct *ptr);
+faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
+faim_internal flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *);
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu8_t chan, int datalen);
+faim_internal void aim_frame_destroy(aim_frame_t *);
+faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
+faim_internal int aim_tx_printqueue(aim_session_t *);
+faim_internal void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);
+
+faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, u_short family, u_short 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);
 
 /*
  * Generic SNAC structure.  Rarely if ever used.
  */
-struct aim_snac_t {
-  u_long id;
-  u_short family;
-  u_short type;
-  u_short flags;
-  void *data;
-  time_t issuetime;
-  struct aim_snac_t *next;
-};
-faim_internal void aim_initsnachash(struct aim_session_t *sess);
-faim_internal unsigned long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac);
-faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, const unsigned short family, const unsigned short type, const unsigned short flags, const void *data, const int datalen);
-faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id);
-faim_internal int aim_cleansnacs(struct aim_session_t *, int maxage);
-faim_internal int aim_putsnac(u_char *, int, int, int, u_long);
+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;
 
-faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess, struct aim_conn_t *src);
+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 void aim_cleansnacs(aim_session_t *, int maxage);
+faim_internal int aim_putsnac(aim_bstream_t *, fu16_t family, fu16_t type, fu16_t flags, aim_snacid_t id);
+
+faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
+faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
 
 faim_internal int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *);
-faim_internal int aim_listenestablish(u_short);
-faim_internal int aim_tx_destroy(struct command_tx_struct *);
 
-faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *, ...);
+faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
 
 /* these are used by aim_*_clientready */
 #define AIM_TOOL_JAVA   0x0001
@@ -100,35 +121,35 @@
 #define AIM_TOOL_MACPPC 0x0006
 #define AIM_TOOL_NEWWIN 0x0010
 struct aim_tool_version {
-  unsigned short group;
-  unsigned short version;
-  unsigned short tool;
-  unsigned short toolversion;
+	fu16_t group;
+	fu16_t version;
+	fu16_t tool;
+	fu16_t toolversion;
 };
 
-faim_internal int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal fu16_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len);
+faim_internal int aim_putcap(aim_bstream_t *bs, fu16_t caps);
 
-faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen);
-faim_internal int aim_putcap(unsigned char *capblock, int buflen, unsigned short caps);
+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);
 
-faim_internal int aim_cachecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
-faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type);
-faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *, int, void *);
-faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *, const unsigned char *, const int);
-faim_internal int aim_freecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
-faim_internal int aim_msgcookie_gettype(int reqclass);
-faim_internal int aim_cookie_free(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
+faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, struct aim_userinfo_s *);
+faim_internal int aim_putuserinfo(aim_bstream_t *bs, struct aim_userinfo_s *info);
+
+faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo);
 
-faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *, struct aim_userinfo_s *);
-faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info);
+faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...);
 
-faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo);
+faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn);
+faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn);
 
-faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...);
 
 #ifndef FAIM_INTERNAL_INSANE
-/* why the hell wont cpp let you use #error inside #define's? */
-/* isn't it single-pass? so the #error would get passed to the compiler --jbm */
 #define printf() printf called inside libfaim
 #define sprintf() unbounded sprintf used inside libfaim
 #endif
--- a/src/protocols/oscar/auth.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/auth.c	Sun Sep 09 10:07:14 2001 +0000
@@ -1,33 +1,30 @@
 /*
-  aim_auth.c
-
-  Deals with the authorizer.
-
+ *  aim_auth.c
+ *
+ * Deals with the authorizer.
+ *
  */
 
 #define FAIM_INTERNAL
 #include <aim.h> 
 
 /* this just pushes the passed cookie onto the passed connection -- NO SNAC! */
-faim_export int aim_auth_sendcookie(struct aim_session_t *sess, 
-				    struct aim_conn_t *conn, 
-				    unsigned char *chipsahoy)
+faim_export int aim_auth_sendcookie(aim_session_t *sess, aim_conn_t *conn, const fu8_t *chipsahoy)
 {
-  struct command_tx_struct *newpacket;
-  int curbyte=0;
-  
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 4+2+2+AIM_COOKIELEN)))
-    return -1;
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0001, 4+2+2+AIM_COOKIELEN)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
+	aimbs_put32(&fr->data, 0x00000001);
+	aim_addtlvtochain_raw(&tl, 0x0006, AIM_COOKIELEN, chipsahoy);	
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
 
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
-  curbyte += aimutil_put16(newpacket->data+curbyte, AIM_COOKIELEN);
-  memcpy(newpacket->data+curbyte, chipsahoy, AIM_COOKIELEN);
+	aim_tx_enqueue(sess, fr);
 
-  return aim_tx_enqueue(sess, newpacket);
+	return 0;
 }
 
 /*
@@ -39,137 +36,126 @@
  * its nonzero, there was an error.
  *
  */
-static int parse(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_tlvlist_t *tlvlist;
-  int ret = 0;
-  aim_rxcallback_t userfunc;
-  char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
-  unsigned char *cookie = NULL;
-  int errorcode = 0, regstatus = 0;
-  int latestbuild = 0, latestbetabuild = 0;
-  char *latestrelease = NULL, *latestbeta = NULL;
-  char *latestreleaseurl = NULL, *latestbetaurl = NULL;
-  char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
+	aim_tlvlist_t *tlvlist;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
+	unsigned char *cookie = NULL;
+	int errorcode = 0, regstatus = 0;
+	int latestbuild = 0, latestbetabuild = 0;
+	char *latestrelease = NULL, *latestbeta = NULL;
+	char *latestreleaseurl = NULL, *latestbetaurl = NULL;
+	char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
 
-  /*
-   * Read block of TLVs.  All further data is derived
-   * from what is parsed here.
-   *
-   */
-  tlvlist = aim_readtlvchain(data, datalen);
+	/*
+	 * Read block of TLVs.  All further data is derived
+	 * from what is parsed here.
+	 */
+	tlvlist = aim_readtlvchain(bs);
 
-  /*
-   * No matter what, we should have a screen name.
-   */
-  memset(sess->sn, 0, sizeof(sess->sn));
-  if (aim_gettlv(tlvlist, 0x0001, 1)) {
-    sn = aim_gettlv_str(tlvlist, 0x0001, 1);
-    strncpy(sess->sn, sn, sizeof(sess->sn));
-  }
+	/*
+	 * No matter what, we should have a screen name.
+	 */
+	memset(sess->sn, 0, sizeof(sess->sn));
+	if (aim_gettlv(tlvlist, 0x0001, 1)) {
+		sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+		strncpy(sess->sn, sn, sizeof(sess->sn));
+	}
 
-  /*
-   * Check for an error code.  If so, we should also
-   * have an error url.
-   */
-  if (aim_gettlv(tlvlist, 0x0008, 1)) 
-    errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
-  if (aim_gettlv(tlvlist, 0x0004, 1))
-    errurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+	/*
+	 * Check for an error code.  If so, we should also
+	 * have an error url.
+	 */
+	if (aim_gettlv(tlvlist, 0x0008, 1)) 
+		errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
+	if (aim_gettlv(tlvlist, 0x0004, 1))
+		errurl = aim_gettlv_str(tlvlist, 0x0004, 1);
 
-  /*
-   * BOS server address.
-   */
-  if (aim_gettlv(tlvlist, 0x0005, 1))
-    bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
+	/*
+	 * BOS server address.
+	 */
+	if (aim_gettlv(tlvlist, 0x0005, 1))
+		bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
 
-  /*
-   * Authorization cookie.
-   */
-  if (aim_gettlv(tlvlist, 0x0006, 1)) {
-    struct aim_tlv_t *tmptlv;
+	/*
+	 * Authorization cookie.
+	 */
+	if (aim_gettlv(tlvlist, 0x0006, 1)) {
+		aim_tlv_t *tmptlv;
 
-    tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+		tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
 
-    if ((cookie = malloc(tmptlv->length)))
-      memcpy(cookie, tmptlv->value, tmptlv->length);
-  }
+		if ((cookie = malloc(tmptlv->length)))
+			memcpy(cookie, tmptlv->value, tmptlv->length);
+	}
 
-  /*
-   * The email address attached to this account
-   *   Not available for ICQ logins.
-   */
-  if (aim_gettlv(tlvlist, 0x0011, 1))
-    email = aim_gettlv_str(tlvlist, 0x0011, 1);
+	/*
+	 * The email address attached to this account
+	 *   Not available for ICQ logins.
+	 */
+	if (aim_gettlv(tlvlist, 0x0011, 1))
+		email = aim_gettlv_str(tlvlist, 0x0011, 1);
 
-  /*
-   * The registration status.  (Not real sure what it means.)
-   *   Not available for ICQ 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.
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0013, 1))
-    regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
+	/*
+	 * The registration status.  (Not real sure what it means.)
+	 *   Not available for ICQ 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.
+	 *
+	 */
+	if (aim_gettlv(tlvlist, 0x0013, 1))
+		regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
 
-  if (aim_gettlv(tlvlist, 0x0040, 1))
-    latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1);
-  if (aim_gettlv(tlvlist, 0x0041, 1))
-    latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1);
-  if (aim_gettlv(tlvlist, 0x0042, 1))
-    latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1);
-  if (aim_gettlv(tlvlist, 0x0043, 1))
-    latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1);
-  if (aim_gettlv(tlvlist, 0x0048, 1))
-    ; /* no idea what this is */
+	if (aim_gettlv(tlvlist, 0x0040, 1))
+		latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1);
+	if (aim_gettlv(tlvlist, 0x0041, 1))
+		latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1);
+	if (aim_gettlv(tlvlist, 0x0042, 1))
+		latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1);
+	if (aim_gettlv(tlvlist, 0x0043, 1))
+		latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1);
+	if (aim_gettlv(tlvlist, 0x0048, 1))
+		; /* no idea what this is */
 
-  if (aim_gettlv(tlvlist, 0x0044, 1))
-    latestbuild = aim_gettlv32(tlvlist, 0x0044, 1);
-  if (aim_gettlv(tlvlist, 0x0045, 1))
-    latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1);
-  if (aim_gettlv(tlvlist, 0x0046, 1))
-    latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1);
-  if (aim_gettlv(tlvlist, 0x0047, 1))
-    latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1);
-  if (aim_gettlv(tlvlist, 0x0049, 1))
-    ; /* no idea what this is */
+	if (aim_gettlv(tlvlist, 0x0044, 1))
+		latestbuild = aim_gettlv32(tlvlist, 0x0044, 1);
+	if (aim_gettlv(tlvlist, 0x0045, 1))
+		latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1);
+	if (aim_gettlv(tlvlist, 0x0046, 1))
+		latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1);
+	if (aim_gettlv(tlvlist, 0x0047, 1))
+		latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1);
+	if (aim_gettlv(tlvlist, 0x0049, 1))
+		; /* no idea what this is */
 
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac?snac->family:0x0017, snac?snac->subtype:0x0003)))
-    ret = userfunc(sess, rx, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
-
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) {
+		/* XXX return as a struct? */
+		ret = userfunc(sess, rx, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
+	}
 
-  if (sn)
-    free(sn);
-  if (bosip)
-    free(bosip);
-  if (errurl)
-    free(errurl);
-  if (email)
-    free(email);
-  if (cookie)
-    free(cookie);
-  if (latestrelease)
-    free(latestrelease);
-  if (latestreleaseurl)
-    free(latestreleaseurl);
-  if (latestbeta)
-    free(latestbeta);
-  if (latestbetaurl)
-    free(latestbetaurl);
-  if (latestreleaseinfo)
-    free(latestreleaseinfo);
-  if (latestbetainfo)
-    free(latestbetainfo);
+	free(sn);
+	free(bosip);
+	free(errurl);
+	free(email);
+	free(cookie);
+	free(latestrelease);
+	free(latestreleaseurl);
+	free(latestbeta);
+	free(latestbetaurl);
+	free(latestreleaseinfo);
+	free(latestbetainfo);
 
-  aim_freetlvchain(&tlvlist);
+	aim_freetlvchain(&tlvlist);
 
-  return ret;
+	return ret;
 }
 
 /*
@@ -179,46 +165,43 @@
  * Calls the client, which should then use the value to call aim_send_login.
  *
  */
-static int keyparse(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  unsigned char *key;
-  int keylen;
-  int ret = 1;
-  aim_rxcallback_t userfunc;
+	int keylen, ret = 1;
+	aim_rxcallback_t userfunc;
+	char *keystr;
 
-  keylen = aimutil_get16(data);
-  if (!(key = malloc(keylen+1)))
-    return ret;
-  memcpy(key, data+2, keylen);
-  key[keylen] = '\0';
-  
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, (char *)key);
+	keylen = aimbs_get16(bs);
+	keystr = aimbs_getstr(bs, keylen);
 
-  free(key);  
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, keystr);
 
-  return ret;
+	free(keystr); 
+
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if (snac->subtype == 0x0007)
-    return keyparse(sess, mod, rx, snac, data, datalen);
+	if (snac->subtype == 0x0003)
+		return parse(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return keyparse(sess, mod, rx, snac, bs);
 
-  return 0;
+	return 0;
 }
 
-faim_internal int auth_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+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->family = 0x0017;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "auth", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
+
--- a/src/protocols/oscar/bos.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/bos.c	Sun Sep 09 10:07:14 2001 +0000
@@ -12,64 +12,62 @@
  * a bitwise OR of all the user classes you want to see you.
  *
  */
-faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *sess,
-					       struct aim_conn_t *conn, 
-					       u_long mask)
+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);
+	return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
 }
 
-static int rights(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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;
-  int ret = 0;
-  struct aim_tlvlist_t *tlvlist;
-  unsigned short maxpermits = 0, maxdenies = 0;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	fu16_t maxpermits = 0, maxdenies = 0;
+	int ret = 0;
 
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(data, datalen)))
-    return 0;
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_readtlvchain(bs);
 
-  /*
-   * TLV type 0x0001: Maximum number of buddies on permit list.
-   */
-  if (aim_gettlv(tlvlist, 0x0001, 1))
-    maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
+	/*
+	 * TLV type 0x0001: Maximum number of buddies on permit list.
+	 */
+	if (aim_gettlv(tlvlist, 0x0001, 1))
+		maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
 
-  /*
-   * TLV type 0x0002: Maximum number of buddies on deny list.
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0002, 1)) 
-    maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
-  
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, maxpermits, maxdenies);
+	/*
+	 * TLV type 0x0002: Maximum number of buddies on deny list.
+	 */
+	if (aim_gettlv(tlvlist, 0x0002, 1)) 
+		maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
 
-  aim_freetlvchain(&tlvlist);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxpermits, maxdenies);
 
-  return ret;  
+	aim_freetlvchain(&tlvlist);
+
+	return ret;  
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
 
-  return 0;
+	return 0;
 }
 
-faim_internal int bos_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int bos_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x0009;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "bos", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x0009;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "bos", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
+
+
--- a/src/protocols/oscar/buddylist.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/buddylist.c	Sun Sep 09 10:07:14 2001 +0000
@@ -11,76 +11,77 @@
  * it is still in a format parsable by extractuserinfo.
  *
  */
-static int buddychange(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_userinfo_s userinfo;
-  aim_rxcallback_t userfunc;
+	struct aim_userinfo_s userinfo;
+	aim_rxcallback_t userfunc;
 
-  aim_extractuserinfo(sess, data, &userinfo);
+	aim_extractuserinfo(sess, bs, &userinfo);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    return userfunc(sess, rx, &userinfo);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &userinfo);
 
-  return 0;
+	return 0;
 }
 
-static int rights(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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;
-  struct aim_tlvlist_t *tlvlist;
-  unsigned short maxbuddies = 0, maxwatchers = 0;
-  int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	fu16_t maxbuddies = 0, maxwatchers = 0;
+	int ret = 0;
 
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(data, datalen)))
-    return 0;
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_readtlvchain(bs);
+
+	/*
+	 * TLV type 0x0001: Maximum number of buddies.
+	 */
+	if (aim_gettlv(tlvlist, 0x0001, 1))
+		maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1);
 
-  /*
-   * TLV type 0x0001: Maximum number of buddies.
-   */
-  if (aim_gettlv(tlvlist, 0x0001, 1))
-    maxbuddies = aim_gettlv16(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_gettlv(tlvlist, 0x0002, 1))
+		maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
 
-  /*
-   * TLV type 0x0002: Maximum number of watchers.
-   *
-   * XXX: what the hell is a watcher? 
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0002, 1))
-    maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
-  
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, maxbuddies, maxwatchers);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxbuddies, maxwatchers);
 
-  aim_freetlvchain(&tlvlist);
+	aim_freetlvchain(&tlvlist);
 
-  return ret;  
+	return ret;  
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
-    return buddychange(sess, mod, rx, snac, data, datalen);
+	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;
+	return 0;
 }
 
-faim_internal int buddylist_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x0003;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "buddylist", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x0003;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "buddylist", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
 
 /*
@@ -91,30 +92,26 @@
  * XXX this should just be an extension of setbuddylist()
  *
  */
-faim_export unsigned long aim_add_buddy(struct aim_session_t *sess,
-					struct aim_conn_t *conn, 
-					char *sn )
+faim_export int aim_add_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
-   struct command_tx_struct *newpacket;
-   int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-   if(!sn)
-     return -1;
+	if (!sn || !strlen(sn))
+		return -EINVAL;
 
-   if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
-     return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
 
-   newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
 
-   i = aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, sess->snac_nextid);
-   i += aimutil_put8(newpacket->data+i, strlen(sn));
-   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-   aim_tx_enqueue(sess, newpacket );
+	aim_tx_enqueue(sess, fr);
 
-   aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
-
-   return sess->snac_nextid;
+	return 0;
 }
 
 /*
@@ -122,30 +119,25 @@
  * the same as setbuddylist() but with a different snac subtype).
  *
  */
-faim_export unsigned long aim_remove_buddy(struct aim_session_t *sess,
-					   struct aim_conn_t *conn, 
-					   char *sn )
+faim_export int aim_remove_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
-   struct command_tx_struct *newpacket;
-   int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-   if(!sn)
-     return -1;
-
-   if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
-     return -1;
+	if (!sn || !strlen(sn))
+		return -EINVAL;
 
-   newpacket->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+1+strlen(sn))))
+		return -ENOMEM;
 
-   i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid);
+	snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid);
 
-   i += aimutil_put8(newpacket->data+i, strlen(sn));
-   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-   aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-   aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
-
-   return sess->snac_nextid;
+	return 0;
 }
 
--- a/src/protocols/oscar/chat.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/chat.c	Sun Sep 09 10:07:14 2001 +0000
@@ -8,47 +8,50 @@
 #define FAIM_INTERNAL
 #include <aim.h> 
 
-faim_export char *aim_chat_getname(struct aim_conn_t *conn)
+faim_export char *aim_chat_getname(aim_conn_t *conn)
 {
-  if (!conn)
-    return NULL;
-  if (conn->type != AIM_CONN_TYPE_CHAT)
-    return NULL;
+	
+	if (!conn)
+		return NULL;
+	
+	if (conn->type != AIM_CONN_TYPE_CHAT)
+		return NULL;
 
-  return (char *)conn->priv; /* yuck ! */
+	return (char *)conn->priv; /* yuck ! */
 }
 
-faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
+faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
 {
-  struct aim_conn_t *cur;
-  
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->type != AIM_CONN_TYPE_CHAT)
-      continue;
-    if (!cur->priv) {
-      faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
-      continue;
-    }
-    if (strcmp((char *)cur->priv, name) == 0)
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	aim_conn_t *cur;
 
-  return cur;
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if (cur->type != AIM_CONN_TYPE_CHAT)
+			continue;
+		if (!cur->priv) {
+			faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
+			continue;
+		}
+		if (strcmp((char *)cur->priv, name) == 0)
+			break;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
+
+	return cur;
 }
 
-faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
+faim_export int aim_chat_attachname(aim_conn_t *conn, const char *roomname)
 {
-  if (!conn || !roomname)
-    return -1;
+
+	if (!conn || !roomname)
+		return -EINVAL;
 
-  if (conn->priv)
-    free(conn->priv);
+	if (conn->priv)
+		free(conn->priv);
 
-  conn->priv = strdup(roomname);
+	conn->priv = strdup(roomname);
 
-  return 0;
+	return 0;
 }
 
 /*
@@ -63,331 +66,317 @@
  *
  * XXX convert this to use tlvchains 
  */
-faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess,
-					   struct aim_conn_t *conn, 
-					   unsigned short flags,
-					   const char *msg,
-					   int msglen)
+faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen)
 {   
+	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;
 
-  int curbyte,i;
-  struct command_tx_struct *newpacket;
-  struct aim_msgcookie_t *cookie;
+	if (!sess || !conn || !msg || (msglen <= 0))
+		return 0;
 
-  if (!sess || !conn || !msg || (msglen <= 0))
-    return 0;
-  
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
 
-  newpacket->lock = 1; /* lock struct */
+	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
+
 
-  curbyte  = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte, 
-			 0x000e, 0x0005, 0x0000, sess->snac_nextid);
+	/* 
+	 * Generate a random message cookie.
+	 *
+	 * XXX mkcookie should generate the cookie and cache it in one
+	 * operation to preserve uniqueness.
+	 *
+	 */
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimutil_put8(ckstr+i, (fu8_t) rand());
 
-  /* 
-   * Generate a random message cookie 
-   */
-  for (i=0;i<8;i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand());
+	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
+	cookie->data = strdup(conn->priv); /* chat hack dependent */
+
+	aim_cachecookie(sess, cookie);
 
-  cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL);
-  cookie->data = strdup(conn->priv); /* chat hack dependent */
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimbs_put8(&fr->data, ckstr[i]);
 
-  aim_cachecookie(sess, cookie);
 
-  /*
-   * Channel ID. 
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+	/*
+	 * Channel ID. 
+	 */
+	aimbs_put16(&fr->data, 0x0003);
+
+
+	/*
+	 * Type 1: Flag meaning this message is destined to the room.
+	 */
+	aim_addtlvtochain_noval(&otl, 0x0001);
 
-  /*
-   * Type 1: Flag meaning this message is destined to the room.
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  
-  /*
-   * Type 6: Reflect
-   */
-  if (!(flags & AIM_CHATFLAGS_NOREFLECT)) {
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  }
+	/*
+	 * Type 6: Reflect
+	 */
+	if (!(flags & AIM_CHATFLAGS_NOREFLECT))
+		aim_addtlvtochain_noval(&otl, 0x0006);
+
+	/*
+	 * Type 7: Autoresponse
+	 */
+	if (flags & AIM_CHATFLAGS_AWAY)
+		aim_addtlvtochain_noval(&otl, 0x0007);
 
-  /*
-   * Type 7: Autoresponse
-   */
-  if (flags & AIM_CHATFLAGS_AWAY) {
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0007);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  }
+	/*
+	 * SubTLV: Type 1: Message
+	 */
+	aim_addtlvtochain_raw(&itl, 0x0001, strlen(msg), msg);
+
+	/*
+	 * Type 5: Message block.  Contains more TLVs.
+	 *
+	 * This could include other information... We just
+	 * put in a message TLV however.  
+	 * 
+	 */
+	aim_addtlvtochain_frozentlvlist(&otl, 0x0005, &itl);
 
-  /*
-   * Type 5: Message block.  Contains more TLVs.
-   *
-   * This could include other information... We just
-   * put in a message TLV however.  
-   * 
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4);
+	aim_writetlvchain(&fr->data, &otl);
+	
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&otl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance)
+{
+	fu8_t *buf;
+	int buflen;
+	aim_bstream_t bs;
 
-  /*
-   * SubTLV: Type 1: Message
-   */
-  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg);
-  
-  newpacket->commandlen = curbyte;
+	buflen = 2 + 1 + strlen(roomname) + 2;
+	
+	if (!(buf = malloc(buflen)))
+		return 0;
+
+	aim_bstream_init(&bs, buf, buflen);
 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aimbs_put16(&bs, exchange);
+	aimbs_put8(&bs, strlen(roomname));
+	aimbs_putraw(&bs, roomname, strlen(roomname));
+	aimbs_put16(&bs, instance);
 
-  return (sess->snac_nextid++);
+	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+	free(buf);
+
+	return 0;
 }
 
 /*
- * 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.
- *
+ * 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 unsigned long aim_chat_join(struct aim_session_t *sess,
-					struct aim_conn_t *conn, 
-					u_short exchange,
-					const char *roomname)
+faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	
+	if (!sess || !conn || !roomname || !strlen(roomname))
+		return -EINVAL;
 
-  if (!sess || !conn || !roomname)
-    return 0;
-  
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+9+strlen(roomname)+2)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+9+strlen(roomname)+2)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-  
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);
 
-  i+= aimutil_put16(newpacket->data+i, 0x000e);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1);
+	aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
 
-  /* 
-   * this is techinally a TLV, but we can't use normal functions
-   * because we need the extraneous nulls and other weird things.
-   */
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
-  i+= aimutil_put16(newpacket->data+i, exchange);
-  i+= aimutil_put8(newpacket->data+i, strlen(roomname));
-  i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
-  i+= aimutil_put16(newpacket->data+i, 0x0000); /* instance? */
+	/*
+	 * Requesting service chat (0x000e)
+	 */
+	aimbs_put16(&fr->data, 0x000e);
+
+	aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
 
-  /*
-   * Chat hack.
-   *
-   * XXX: A problem occurs here if we request a channel
-   *      join but it fails....pendingjoin will be nonnull
-   *      even though the channel is never going to get a
-   *      redirect!
-   *
-   */
-  sess->pendingjoin = strdup(roomname);
-  sess->pendingjoinexchange = exchange;
+	/*
+	 * Chat hack.
+	 *
+	 * XXX: A problem occurs here if we request a channel
+	 *      join but it fails....pendingjoin will be nonnull
+	 *      even though the channel is never going to get a
+	 *      redirect!
+	 *
+	 */
+	sess->pendingjoin = strdup(roomname);
+	sess->pendingjoinexchange = exchange;
 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1);
-
-  return sess->snac_nextid;
+	return 0; 
 }
 
-faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo)
+faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
 {
-  int namelen = 0;
-  int i = 0;
+	int namelen;
 
-  if (!buf || !outinfo)
-    return 0;
-
-  outinfo->exchange = aimutil_get16(buf+i);
-  i += 2;
+	if (!bs || !outinfo)
+		return 0;
 
-  namelen = aimutil_get8(buf+i);
-  i += 1;
+	outinfo->exchange = aimbs_get16(bs);
+	namelen = aimbs_get8(bs);
+	outinfo->name = aimbs_getstr(bs, namelen);
+	outinfo->instance = aimbs_get16(bs);
 
-  outinfo->name = (char *)malloc(namelen+1);
-  memcpy(outinfo->name, buf+i, namelen);
-  outinfo->name[namelen] = '\0';
-  i += namelen;
-
-  outinfo->instance = aimutil_get16(buf+i);
-  i += 2;
-  
-  return i;
+	return 0;
 }
 
-faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess,
-					       struct aim_conn_t *conn)
+faim_export int aim_chat_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-  int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 0x20)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
 
-  i+= aimutil_put16(newpacket->data+i, 0x000e);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&fr->data, 0x000e);
+	aimbs_put16(&fr->data, 0x0001);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0004);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0001);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0003);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0003);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0004);
-  i+= aimutil_put16(newpacket->data+i, 0x0686);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0686);
 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return (sess->snac_nextid++);
+	return 0;
 }
 
-faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
+faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name)
 {
-  struct aim_conn_t *conn;
+	aim_conn_t *conn;
 
-  if ((conn = aim_chat_getconn(sess, name)))
-    aim_conn_close(conn);
+	if (!(conn = aim_chat_getconn(sess, name)))
+		return -ENOENT;
 
-  if (!conn)
-    return -1;
-  return 0;
+	aim_conn_close(conn);
+
+	return 0;
 }
 
 /*
  * conn must be a BOS connection!
  */
-faim_export unsigned long aim_chat_invite(struct aim_session_t *sess,
-					  struct aim_conn_t *conn,
-					  char *sn,
-					  char *msg,
-					  u_short exchange,
-					  char *roomname,
-					  u_short instance)
+faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance)
 {
-  struct command_tx_struct *newpacket;
-  int i,curbyte=0;
-  struct aim_msgcookie_t *cookie;
-  struct aim_invite_priv *priv;
+	int i;
+	aim_frame_t *fr;
+	aim_msgcookie_t *cookie;
+	struct aim_invite_priv *priv;
+	fu8_t ckstr[8];
+	aim_snacid_t snacid;
+	aim_tlvlist_t *otl = NULL, *itl = NULL;
+	fu8_t *hdr;
+	int hdrlen;
+	aim_bstream_t hdrbs;
+	
+	if (!sess || !conn || !sn || !msg || !roomname)
+		return -EINVAL;
 
-  if (!sess || !conn || !sn || !msg || !roomname)
-    return -1;
-
-  if (conn->type != AIM_CONN_TYPE_BOS)
-    return -1;
+	if (conn->type != AIM_CONN_TYPE_BOS)
+		return -EINVAL;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+		return -ENOMEM;
 
-  /*
-   * Cookie
-   */
-  for (i=0;i<8;i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
 
-  /* XXX this should get uncached by the unwritten 'invite accept' handler */
-  if(!(priv = calloc(sizeof(struct aim_invite_priv), 1)))
-    return -1;
-  priv->sn = strdup(sn);
-  priv->roomname = strdup(roomname);
-  priv->exchange = exchange;
-  priv->instance = instance;
+	/*
+	 * Cookie
+	 */
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimutil_put8(ckstr, (fu8_t) rand());
 
-  if(!(cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_INVITE, priv)))
-    return -1;
-  aim_cachecookie(sess, cookie);
+	/* 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;
+	}
 
-  /*
-   * Channel (2)
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+	if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv)))
+		aim_cachecookie(sess, cookie);
+	else
+		free(priv);
 
-  /*
-   * Dest sn
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimbs_put8(&fr->data, ckstr[i]);
 
-  /*
-   * TLV t(0005)
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02);
-  
-  /* 
-   * Unknown info
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  
-  /*
-   * TLV t(000a) -- Unknown
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-  
-  /*
-   * TLV t(000f) -- Unknown
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  
-  /*
-   * TLV t(000c) -- Invitation message
-   */
-  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg);
+
+	/*
+	 * Channel (2)
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-  /*
-   * TLV t(2711) -- Container for room information 
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2);
-  curbyte += aimutil_put16(newpacket->data+curbyte, exchange);
-  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname));
-  curbyte += aimutil_put16(newpacket->data+curbyte, instance);
+	/*
+	 * 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, ckstr, sizeof(ckstr)); /* I think... */
+	aim_putcap(&hdrbs, AIM_CAPS_CHAT);
 
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+	aim_addtlvtochain_noval(&itl, 0x000f);
+	aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), msg);
+	aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance);
+	aim_writetlvchain(&hdrbs, &itl);
+	
+	aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
 
-  return (sess->snac_nextid++);
+	aim_writetlvchain(&fr->data, &otl);
+
+	free(hdr);
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&otl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
 /*
@@ -399,43 +388,38 @@
  *
  * SNAC 000e/0002
  */
-static int infoupdate(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	struct aim_userinfo_s *userinfo = NULL;
-	aim_rxcallback_t userfunc=NULL;	
-	int ret = 0, i = 0;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
 	int usercount = 0;
-	unsigned char detaillevel = 0;
+	fu8_t detaillevel = 0;
 	char *roomname = NULL;
 	struct aim_chat_roominfo roominfo;
-	unsigned short tlvcount = 0;
-	struct aim_tlvlist_t *tlvlist;
+	fu16_t tlvcount = 0;
+	aim_tlvlist_t *tlvlist;
 	char *roomdesc = NULL;
-	unsigned short unknown_c9 = 0;
-	unsigned long creationtime = 0;
-	unsigned short maxmsglen = 0, maxvisiblemsglen = 0;
-	unsigned short unknown_d2 = 0, unknown_d5 = 0;
+	fu16_t unknown_c9 = 0;
+	fu32_t creationtime = 0;
+	fu16_t maxmsglen = 0, maxvisiblemsglen = 0;
+	fu16_t unknown_d2 = 0, unknown_d5 = 0;
 
-	i += aim_chat_readroominfo(data+i, &roominfo);
+	aim_chat_readroominfo(bs, &roominfo);
 
-	detaillevel = aimutil_get8(data+i);
-	i++;
+	detaillevel = aimbs_get8(bs);
 
 	if (detaillevel != 0x02) {
-		if (detaillevel == 0x01)
-			faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level 1 not supported\n");
-		else
-			faimdprintf(sess, 0, "faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
+		faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
 		return 1;
 	}
 
-	tlvcount = aimutil_get16(data+i);
-	i += 2;
+	tlvcount = aimbs_get16(bs);
 
 	/*
 	 * Everything else are TLVs.
 	 */ 
-	tlvlist = aim_readtlvchain(data+i, datalen-i);
+	tlvlist = aim_readtlvchain(bs);
 
 	/*
 	 * TLV type 0x006a is the room name in Human Readable Form.
@@ -454,15 +438,18 @@
 	 */
 	if (aim_gettlv(tlvlist, 0x0073, 1)) {	
 		int curoccupant = 0;
-		struct aim_tlv_t *tmptlv;
+		aim_tlv_t *tmptlv;
+		aim_bstream_t occbs;
 
 		tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
 
 		/* Allocate enough userinfo structs for all occupants */
 		userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));
 
-		for (i = 0; curoccupant < usercount; )
-			i += aim_extractuserinfo(sess, tmptlv->value+i, &userinfo[curoccupant++]);
+		aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
+
+		while (curoccupant < usercount)
+			aim_extractuserinfo(sess, &occbs, &userinfo[curoccupant++]);
 	}
 
 	/* 
@@ -563,24 +550,24 @@
 	return ret;
 }
 
-static int userlistchange(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_userinfo_s *userinfo = NULL;
-  aim_rxcallback_t userfunc;
-  int i = 0, curcount = 0, ret = 0;
+	struct aim_userinfo_s *userinfo = NULL;
+	aim_rxcallback_t userfunc;
+	int curcount = 0, ret = 0;
 
-  while (i < datalen) {
-    curcount++;
-    userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
-    i += aim_extractuserinfo(sess, data+i, &userinfo[curcount-1]);
-  }
+	while (aim_bstream_empty(bs)) {
+		curcount++;
+		userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
+		aim_extractuserinfo(sess, bs, &userinfo[curcount-1]);
+	}
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, curcount, userinfo);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, curcount, userinfo);
 
-  free(userinfo);
+	free(userinfo);
 
-  return ret;
+	return ret;
 }
 
 /*
@@ -606,118 +593,124 @@
  *       possibly others
  *  
  */
-static int incomingmsg(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_userinfo_s userinfo;
-  aim_rxcallback_t userfunc=NULL;	
-  int ret = 0, i = 0;
-  unsigned char cookie[8];
-  int channel;
-  struct aim_tlvlist_t *outerlist;
-  char *msg = NULL;
-  struct aim_msgcookie_t *ck;
+	struct aim_userinfo_s userinfo;
+	aim_rxcallback_t userfunc;	
+	int ret = 0;
+	fu8_t *cookie;
+	fu16_t channel;
+	aim_tlvlist_t *otl;
+	char *msg = NULL;
+	aim_msgcookie_t *ck;
 
-  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+	memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
 
-  /*
-   * ICBM Cookie.  Cache it.
-   */ 
-  memcpy(cookie, data, 8);
-  i += 8;
+	/*
+	 * ICBM Cookie.  Uncache it.
+	 */
+	cookie = aimbs_getraw(bs, 8);
 
-  if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
-    if (ck->data)
-      free(ck->data);
-    free(ck);
-  }
+	if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+		free(ck->data);
+		free(ck);
+	}
 
-  /*
-   * Channel ID
-   *
-   * Channels 1 and 2 are implemented in the normal ICBM
-   * parser.
-   *
-   * We only do channel 3 here.
-   *
-   */
-  channel = aimutil_get16(data+i);
-  i += 2;
+	/*
+	 * Channel ID
+	 *
+	 * Channels 1 and 2 are implemented in the normal ICBM
+	 * parser.
+	 *
+	 * We only do channel 3 here.
+	 *
+	 */
+	channel = aimbs_get16(bs);
 
-  if (channel != 0x0003) {
-    faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
-    return 0;
-  }
+	if (channel != 0x0003) {
+		faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
+		return 0;
+	}
+
+	/*
+	 * Start parsing TLVs right away. 
+	 */
+	otl = aim_readtlvchain(bs);
 
-  /*
-   * Start parsing TLVs right away. 
-   */
-  outerlist = aim_readtlvchain(data+8+2, datalen-8-2);
-  
-  /*
-   * Type 0x0003: Source User Information
-   */
-  if (aim_gettlv(outerlist, 0x0003, 1)) {
-    struct aim_tlv_t *userinfotlv;
-    
-    userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
-    aim_extractuserinfo(sess, userinfotlv->value, &userinfo);
-  }
+	/*
+	 * Type 0x0003: Source User Information
+	 */
+	if (aim_gettlv(otl, 0x0003, 1)) {
+		aim_tlv_t *userinfotlv;
+		aim_bstream_t tbs;
+
+		userinfotlv = aim_gettlv(otl, 0x0003, 1);
 
-  /*
-   * Type 0x0001: Unknown.
-   */
-  if (aim_gettlv(outerlist, 0x0001, 1))
-    ;
+		aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
+		aim_extractuserinfo(sess, &tbs, &userinfo);
+	}
+
+	/*
+	 * Type 0x0001: If present, it means it was a message to the 
+	 * room (as opposed to a whisper).
+	 */
+	if (aim_gettlv(otl, 0x0001, 1))
+		;
 
-  /*
-   * Type 0x0005: Message Block.  Conains more TLVs.
-   */
-  if (aim_gettlv(outerlist, 0x0005, 1)) {
-    struct aim_tlvlist_t *innerlist;
-    struct aim_tlv_t *msgblock;
+	/*
+	 * Type 0x0005: Message Block.  Conains more TLVs.
+	 */
+	if (aim_gettlv(otl, 0x0005, 1)) {
+		aim_tlvlist_t *itl;
+		aim_tlv_t *msgblock;
+		aim_bstream_t tbs;
+
+		msgblock = aim_gettlv(otl, 0x0005, 1);
+		aim_bstream_init(&tbs, msgblock->value, msgblock->length);
+		itl = aim_readtlvchain(&tbs);
 
-    msgblock = aim_gettlv(outerlist, 0x0005, 1);
-    innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
-      
-    /* 
-     * Type 0x0001: Message.
-     */	
-    if (aim_gettlv(innerlist, 0x0001, 1))
-      msg = aim_gettlv_str(innerlist, 0x0001, 1);
+		/* 
+		 * Type 0x0001: Message.
+		 */	
+		if (aim_gettlv(itl, 0x0001, 1))
+			msg = aim_gettlv_str(itl, 0x0001, 1);
+
+		aim_freetlvchain(&itl); 
+	}
 
-    aim_freetlvchain(&innerlist); 
-  }
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo, msg);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, &userinfo, msg);
+	free(cookie);
+	free(msg);
+	aim_freetlvchain(&otl);
 
-  free(msg);
-  aim_freetlvchain(&outerlist);
-
-  return ret;
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
-    return userlistchange(sess, mod, rx, snac, data, datalen);
-  else if (snac->subtype == 0x0006)
-    return incomingmsg(sess, mod, rx, snac, data, datalen);
+	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 incomingmsg(sess, mod, rx, snac, bs);
 
-  return 0;
+	return 0;
 }
 
-faim_internal int chat_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x000e;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "chat", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x000e;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "chat", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
+
+
--- a/src/protocols/oscar/chatnav.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/chatnav.c	Sun Sep 09 10:07:14 2001 +0000
@@ -13,400 +13,387 @@
 /*
  * conn must be a chatnav connection!
  */
-faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess,
-						struct aim_conn_t *conn)
+faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
 {
-
-  return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
+	return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
 }
 
-faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess,
-						  struct aim_conn_t *conn)
+faim_export int aim_chatnav_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket; 
-  int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x20)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 0x20)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
 
-  i+= aimutil_put16(newpacket->data+i, 0x000d);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&fr->data, 0x000d);
+	aimbs_put16(&fr->data, 0x0001);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0004);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0001);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0003);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0003);
 
-  i+= aimutil_put16(newpacket->data+i, 0x0004);
-  i+= aimutil_put16(newpacket->data+i, 0x0686);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0686);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return (sess->snac_nextid++);
+	return 0;
 }
 
-faim_export unsigned long aim_chatnav_createroom(struct aim_session_t *sess,
-						 struct aim_conn_t *conn,
-						 char *name, 
-						 u_short exchange)
+faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange)
 {
-  struct command_tx_struct *newpacket; 
-  int i;
+	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+12+strlen("invite")+strlen(name))))
+		return -ENOMEM;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+12+strlen("invite")+strlen(name))))
-    return -1;
-
-  newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
 
-  i = aim_putsnac(newpacket->data, 0x000d, 0x0008, 0x0000, sess->snac_nextid);
+	/* exchange */
+	aimbs_put16(&fr->data, exchange);
 
-  /* exchange */
-  i+= aimutil_put16(newpacket->data+i, exchange);
+	/* room cookie */
+	aimbs_put8(&fr->data, strlen("invite"));
+	aimbs_putraw(&fr->data, "invite", strlen("invite"));
 
-  /* room cookie */
-  i+= aimutil_put8(newpacket->data+i, strlen("invite"));
-  i+= aimutil_putstr(newpacket->data+i, "invite", strlen("invite"));
+	/* 
+	 * instance
+	 * 
+	 * Setting this to 0xffff apparently assigns the last instance.
+	 *
+	 */
+	aimbs_put16(&fr->data, 0xffff);
+
+	/* detail level */
+	aimbs_put8(&fr->data, 0x01);
 
-  /* instance */
-  i+= aimutil_put16(newpacket->data+i, 0xffff);
-  
-  /* detail level */
-  i+= aimutil_put8(newpacket->data+i, 0x01);
-  
-  /* tlvcount */
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
+	/* room name */
+	aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), name);
 
-  /* room name */
-  i+= aim_puttlv_str(newpacket->data+i, 0x00d3, strlen(name), name);
+	/* tlvcount */
+	aimbs_put16(&fr->data, aim_counttlvchain(&tl));
+	aim_writetlvchain(&fr->data, &tl);
 
-  aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+	aim_freetlvchain(&tl);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
-static int parseinfo_perms(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen, struct aim_snac_t *snac2)
+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_tlvlist_t *tlvlist;
-  struct aim_chat_exchangeinfo *exchanges = NULL;
-  int curexchange = 0;
-  struct aim_tlv_t *exchangetlv;
-  unsigned char maxrooms = 0;
-  struct aim_tlvlist_t *innerlist;
+	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_readtlvchain(data, datalen);
+	tlvlist = aim_readtlvchain(bs);
 
-  /* 
-   * Type 0x0002: Maximum concurrent rooms.
-   */ 
-  if (aim_gettlv(tlvlist, 0x0002, 1))
-    maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
+	/* 
+	 * Type 0x0002: Maximum concurrent rooms.
+	 */ 
+	if (aim_gettlv(tlvlist, 0x0002, 1))
+		maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
 
-  /* 
-   * Type 0x0003: Exchange information
-   *
-   * There can be any number of these, each one
-   * representing another exchange.  
-   * 
-   */
-  curexchange = 0;
-  while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))) {
-    curexchange++;
-    exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
-    /* exchange number */
-    exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value);
-    innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2);
+	/* 
+	 * Type 0x0003: Exchange information
+	 *
+	 * There can be any number of these, each one
+	 * representing another exchange.  
+	 * 
+	 */
+	for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
+		aim_bstream_t tbs;
 
-    /* 
-     * Type 0x000d: Unknown.
-     */
-    if (aim_gettlv(innerlist, 0x000d, 1))
-      ;
+		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_readtlvchain(&tbs);
 
-    /* 
-     * Type 0x0004: Unknown
-     */
-    if (aim_gettlv(innerlist, 0x0004, 1))
-      ;
+		/* 
+		 * Type 0x000d: Unknown.
+		 */
+		if (aim_gettlv(innerlist, 0x000d, 1))
+			;
 
-    /* 
-     * Type 0x0002: Unknown
-     */
-    if (aim_gettlv(innerlist, 0x0002, 1)) {
-      unsigned short classperms;
+		/* 
+		 * Type 0x0004: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x0004, 1))
+			;
 
-      classperms = aim_gettlv16(innerlist, 0x0002, 1);
-		
-      faimdprintf(sess, 1, "faim: class permissions %x\n", classperms);
-    }
+		/* 
+		 * Type 0x0002: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x0002, 1)) {
+			fu16_t classperms;
 
-    /*
-     * Type 0x00c9: Unknown
-     */ 
-    if (aim_gettlv(innerlist, 0x00c9, 1))
-      ;
-	      
-    /*
-     * Type 0x00ca: Creation Date 
-     */
-    if (aim_gettlv(innerlist, 0x00ca, 1))
-      ;
-	      
-    /*
-     * Type 0x00d0: Mandatory Channels?
-     */
-    if (aim_gettlv(innerlist, 0x00d0, 1))
-      ;
+			classperms = aim_gettlv16(innerlist, 0x0002, 1);
+			
+			faimdprintf(sess, 1, "faim: class permissions %x\n", classperms);
+		}
 
-    /*
-     * Type 0x00d1: Maximum Message length
-     */
-    if (aim_gettlv(innerlist, 0x00d1, 1))
-      ;
+		/*
+		 * Type 0x00c9: Unknown
+		 */ 
+		if (aim_gettlv(innerlist, 0x00c9, 1))
+			;
+		      
+		/*
+		 * Type 0x00ca: Creation Date 
+		 */
+		if (aim_gettlv(innerlist, 0x00ca, 1))
+			;
+		      
+		/*
+		 * Type 0x00d0: Mandatory Channels?
+		 */
+		if (aim_gettlv(innerlist, 0x00d0, 1))
+			;
 
-    /*
-     * Type 0x00d2: Maximum Occupancy?
-     */
-    if (aim_gettlv(innerlist, 0x00d2, 1))	
-      ;	
+		/*
+		 * Type 0x00d1: Maximum Message length
+		 */
+		if (aim_gettlv(innerlist, 0x00d1, 1))
+			;
 
-    /*
-     * Type 0x00d3: Exchange Name
-     */
-    if (aim_gettlv(innerlist, 0x00d3, 1))	
-      exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
-    else
-      exchanges[curexchange-1].name = NULL;
+		/*
+		 * Type 0x00d2: Maximum Occupancy?
+		 */
+		if (aim_gettlv(innerlist, 0x00d2, 1))	
+			;
 
-    /*
-     * Type 0x00d5: Creation Permissions
-     *
-     * 0  Creation not allowed
-     * 1  Room creation allowed
-     * 2  Exchange creation allowed
-     * 
-     */
-    if (aim_gettlv(innerlist, 0x00d5, 1)) {
-      unsigned char createperms;
+		/*
+		 * Type 0x00d3: Exchange Name
+		 */
+		if (aim_gettlv(innerlist, 0x00d3, 1))	
+			exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
+		else
+			exchanges[curexchange-1].name = NULL;
 
-      createperms = aim_gettlv8(innerlist, 0x00d5, 1);
-    }
+		/*
+		 * Type 0x00d5: Creation Permissions
+		 *
+		 * 0  Creation not allowed
+		 * 1  Room creation allowed
+		 * 2  Exchange creation allowed
+		 * 
+		 */
+		if (aim_gettlv(innerlist, 0x00d5, 1)) {
+			fu8_t createperms;
+
+			createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+		}
 
-    /*
-     * Type 0x00d6: Character Set (First Time)
-     */	      
-    if (aim_gettlv(innerlist, 0x00d6, 1))	
-      exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
-    else
-      exchanges[curexchange-1].charset1 = NULL;
-	      
-    /*
-     * Type 0x00d7: Language (First Time)
-     */	      
-    if (aim_gettlv(innerlist, 0x00d7, 1))	
-      exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
-    else
-      exchanges[curexchange-1].lang1 = NULL;
+		/*
+		 * Type 0x00d6: Character Set (First Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d6, 1))	
+			exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
+		else
+			exchanges[curexchange-1].charset1 = NULL;
+		      
+		/*
+		 * Type 0x00d7: Language (First Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d7, 1))	
+			exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
+		else
+			exchanges[curexchange-1].lang1 = NULL;
 
-    /*
-     * Type 0x00d8: Character Set (Second Time)
-     */	      
-    if (aim_gettlv(innerlist, 0x00d8, 1))	
-      exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
-    else
-      exchanges[curexchange-1].charset2 = NULL;
+		/*
+		 * Type 0x00d8: Character Set (Second Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d8, 1))	
+			exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
+		else
+			exchanges[curexchange-1].charset2 = NULL;
 
-    /*
-     * Type 0x00d9: Language (Second Time)
-     */	      
-    if (aim_gettlv(innerlist, 0x00d9, 1))	
-      exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
-    else
-      exchanges[curexchange-1].lang2 = NULL;
-	      
-    aim_freetlvchain(&innerlist);
-  }
+		/*
+		 * Type 0x00d9: Language (Second Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d9, 1))	
+			exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
+		else
+			exchanges[curexchange-1].lang2 = NULL;
+		      
+		aim_freetlvchain(&innerlist);
+	}
 
-  /*
-   * Call client.
-   */
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
-  curexchange--;
-  while(curexchange >= 0) {
-    free(exchanges[curexchange].name);
-    free(exchanges[curexchange].charset1);
-    free(exchanges[curexchange].lang1);
-    free(exchanges[curexchange].charset2);
-    free(exchanges[curexchange].lang2);
-    curexchange--;
-  }
-  free(exchanges);
-  aim_freetlvchain(&tlvlist);
+	/*
+	 * Call client.
+	 */
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
 
-  return ret;
+	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_freetlvchain(&tlvlist);
+
+	return ret;
 }
 
-static int parseinfo_create(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen, struct aim_snac_t *snac2)
+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;
-  struct aim_tlvlist_t *tlvlist, *innerlist;
-  char *ck = NULL, *fqcn = NULL, *name = NULL;
-  unsigned short exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
-  unsigned long createtime = 0;
-  unsigned char createperms = 0;
-  int i = 0, cklen;
-  struct aim_tlv_t *bigblock;
-  int ret = 0;
+	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;
 
-  if (!(tlvlist = aim_readtlvchain(data, datalen))) {
-    faimdprintf(sess, 0, "unable to read top tlv in create room response\n");
-    return 0;
-  }
+	tlvlist = aim_readtlvchain(bs);
 
-  if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
-    faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n");
-    aim_freetlvchain(&tlvlist);
-    return 0;
-  }
+	if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
+		faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n");
+		aim_freetlvchain(&tlvlist);
+		return 0;
+	}
 
-  exchange = aimutil_get16(bigblock->value+i);
-  i += 2;
+	aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
 
-  cklen = aimutil_get8(bigblock->value+i);
-  i++;
+	exchange = aimbs_get16(&bbbs);
+	cklen = aimbs_get8(&bbbs);
+	ck = aimbs_getstr(&bbbs, cklen);
+	instance = aimbs_get16(&bbbs);
+	detaillevel = aimbs_get8(&bbbs);
 
-  ck = malloc(cklen+1);
-  memcpy(ck, bigblock->value+i, cklen);
-  ck[cklen] = '\0';
-  i += cklen;
-
-  instance = aimutil_get16(bigblock->value+i);
-  i += 2;
+	if (detaillevel != 0x02) {
+		faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", detaillevel);
+		aim_freetlvchain(&tlvlist);
+		free(ck);
+		return 0;
+	}
 
-  if (aimutil_get8(bigblock->value+i) != 0x02) {
-    faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i));
-    aim_freetlvchain(&tlvlist);
-    free(ck);
-    return 0;
-  }
-  i += 1;
-      
-  unknown = aimutil_get16(bigblock->value+i);
-  i += 2;
+	unknown = aimbs_get16(&bbbs);
+
+	innerlist = aim_readtlvchain(&bbbs);
+
+	if (aim_gettlv(innerlist, 0x006a, 1))
+		fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
 
-  if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) {
-    faimdprintf(sess, 0, "unable to read inner tlv chain in create room response\n");
-    aim_freetlvchain(&tlvlist);
-    free(ck);
-    return 0;
-  }
+	if (aim_gettlv(innerlist, 0x00c9, 1))
+		flags = aim_gettlv16(innerlist, 0x00c9, 1);
 
-  if (aim_gettlv(innerlist, 0x006a, 1))
-    fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
+	if (aim_gettlv(innerlist, 0x00ca, 1))
+		createtime = aim_gettlv32(innerlist, 0x00ca, 1);
 
-  if (aim_gettlv(innerlist, 0x00c9, 1))
-    flags = aim_gettlv16(innerlist, 0x00c9, 1);
+	if (aim_gettlv(innerlist, 0x00d1, 1))
+		maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
 
-  if (aim_gettlv(innerlist, 0x00ca, 1))
-    createtime = aim_gettlv32(innerlist, 0x00ca, 1);
-
-  if (aim_gettlv(innerlist, 0x00d1, 1))
-    maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
+	if (aim_gettlv(innerlist, 0x00d2, 1))
+		maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
 
-  if (aim_gettlv(innerlist, 0x00d2, 1))
-    maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
+	if (aim_gettlv(innerlist, 0x00d3, 1))
+		name = aim_gettlv_str(innerlist, 0x00d3, 1);
 
-  if (aim_gettlv(innerlist, 0x00d3, 1))
-    name = aim_gettlv_str(innerlist, 0x00d3, 1);
+	if (aim_gettlv(innerlist, 0x00d5, 1))
+		createperms = aim_gettlv8(innerlist, 0x00d5, 1);
 
-  if (aim_gettlv(innerlist, 0x00d5, 1))
-    createperms = aim_gettlv8(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);
+	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_freetlvchain(&innerlist);
-  aim_freetlvchain(&tlvlist);
+	free(ck);
+	free(name);
+	free(fqcn);
+	aim_freetlvchain(&innerlist);
+	aim_freetlvchain(&tlvlist);
 
-  return ret;
+	return ret;
 }
 
 /*
- * Since multiple things can trigger this callback,
- * we must lookup the snacid to determine the original
- * snac subtype that was called.
+ * Since multiple things can trigger this callback, we must lookup the 
+ * snacid to determine the original snac subtype that was called.
  */
-static int parseinfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_snac_t *snac2;
-  int ret = 0;
+	aim_snac_t *snac2;
+	int ret = 0;
 
-  if (!(snac2 = aim_remsnac(sess, snac->id))) {
-    faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id);
-    return 0;
-  }
-  
-  if (snac2->family != 0x000d) {
-    faimdprintf(sess, 0, "faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac2->family);
-    return 0;
-  }
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snac->id);
+		return 0;
+	}
+
+	if (snac2->family != 0x000d) {
+		faimdprintf(sess, 0, "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, data, datalen, snac2);
-  else if (snac2->type == 0x0003) /* request exchange info */
-    faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n");
-  else if (snac2->type == 0x0004) /* request room info */
-    faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n");
-  else if (snac2->type == 0x0005) /* request more room info */
-    faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n");
-  else if (snac2->type == 0x0006) /* request occupant list */
-    faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n");
-  else if (snac2->type == 0x0007) /* search for a room */
-    faimdprintf(sess, 0, "chatnav_parse_info: search results\n");
-  else if (snac2->type == 0x0008) /* create room */
-    ret = parseinfo_create(sess, mod, rx, snac, data, datalen, snac2);
-  else
-    faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
+	/*
+	 * 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 */
+		faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n");
+	else if (snac2->type == 0x0004) /* request room info */
+		faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n");
+	else if (snac2->type == 0x0005) /* request more room info */
+		faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n");
+	else if (snac2->type == 0x0006) /* request occupant list */
+		faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n");
+	else if (snac2->type == 0x0007) /* search for a room */
+		faimdprintf(sess, 0, "chatnav_parse_info: search results\n");
+	else if (snac2->type == 0x0008) /* create room */
+		ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
+	else
+		faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
 
-  if (snac2)
-    free(snac2->data);
-  free(snac2);
-  
-  return ret;
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
+	if (snac->subtype == 0x0009)
+		return parseinfo(sess, mod, rx, snac, bs);
 
-  return 0;
+	return 0;
 }
 
-faim_internal int chatnav_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x000d;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "chatnav", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x000d;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "chatnav", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
--- a/src/protocols/oscar/conn.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/conn.c	Sun Sep 09 10:07:14 2001 +0000
@@ -15,6 +15,35 @@
 #include <netinet/in.h>
 #endif
 
+static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
+{
+
+	aim_rxqueue_cleanbyconn(sess, *deadconn);
+	aim_tx_cleanqueue(sess, *deadconn);
+
+	if ((*deadconn)->fd != -1) 
+		aim_conn_close(*deadconn);
+
+	/*
+	 * XXX ->priv should never be touched by the library. I know
+	 * it used to be, but I'm getting rid of all that.  Use
+	 * ->internal instead.
+	 */
+	if ((*deadconn)->priv)
+		free((*deadconn)->priv);
+
+	/*
+	 * This will free ->internal if it necessary...
+	 */
+	if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
+		aim_conn_kill_rend(sess, *deadconn);
+
+	free(*deadconn);
+	deadconn = NULL;
+
+	return;
+}
+
 /**
  * aim_connrst - Clears out connection list, killing remaining connections.
  * @sess: Session to be cleared
@@ -22,21 +51,25 @@
  * Clears out the connection list and kills any connections left.
  *
  */
-static void aim_connrst(struct aim_session_t *sess)
+static void aim_connrst(aim_session_t *sess)
 {
-  faim_mutex_init(&sess->connlistlock);
-  if (sess->connlist) {
-    struct aim_conn_t *cur = sess->connlist, *tmp;
+
+	faim_mutex_init(&sess->connlistlock);
+
+	if (sess->connlist) {
+		aim_conn_t *cur = sess->connlist, *tmp;
 
-    while(cur) {
-      tmp = cur->next;
-      aim_conn_close(cur);
-      free(cur);
-      cur = tmp;
-    }
-  }
-  sess->connlist = NULL;
-  return;
+		while (cur) {
+			tmp = cur->next;
+			aim_conn_close(cur);
+			connkill_real(sess, &cur);
+			cur = tmp;
+		}
+	}
+
+	sess->connlist = NULL;
+
+	return;
 }
 
 /**
@@ -46,23 +79,24 @@
  * Initializes and/or resets a connection structure.
  *
  */
-static void aim_conn_init(struct aim_conn_t *deadconn)
+static void aim_conn_init(aim_conn_t *deadconn)
 {
-  if (!deadconn)
-    return;
+
+	if (!deadconn)
+		return;
 
-  deadconn->fd = -1;
-  deadconn->subtype = -1;
-  deadconn->type = -1;
-  deadconn->seqnum = 0;
-  deadconn->lastactivity = 0;
-  deadconn->forcedlatency = 0;
-  deadconn->handlerlist = NULL;
-  deadconn->priv = NULL;
-  faim_mutex_init(&deadconn->active);
-  faim_mutex_init(&deadconn->seqnum_lock);
-  
-  return;
+	deadconn->fd = -1;
+	deadconn->subtype = -1;
+	deadconn->type = -1;
+	deadconn->seqnum = 0;
+	deadconn->lastactivity = 0;
+	deadconn->forcedlatency = 0;
+	deadconn->handlerlist = NULL;
+	deadconn->priv = NULL;
+	faim_mutex_init(&deadconn->active);
+	faim_mutex_init(&deadconn->seqnum_lock);
+
+	return;
 }
 
 /**
@@ -72,28 +106,20 @@
  * Allocate a new empty connection structure.
  *
  */
-static struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
+static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
 {
-  struct aim_conn_t *newconn, *cur;
-
-  if (!(newconn = malloc(sizeof(struct aim_conn_t)))) 	
-    return NULL;
-
-  memset(newconn, 0, sizeof(struct aim_conn_t));
-  aim_conn_init(newconn);
-  newconn->next = NULL;
+	aim_conn_t *newconn;
 
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL)
-    sess->connlist = newconn;
-  else {
-    for (cur = sess->connlist; cur->next; cur = cur->next)
-      ;
-    cur->next = newconn;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	if (!(newconn = malloc(sizeof(aim_conn_t)))) 	
+		return NULL;
+	memset(newconn, 0, sizeof(aim_conn_t));
 
-  return newconn;
+	aim_conn_init(newconn);
+
+	newconn->next = sess->connlist;
+	sess->connlist = newconn;
+
+	return newconn;
 }
 
 /**
@@ -105,44 +131,27 @@
  * called from within libfaim.
  *
  */
-faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
+faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
 {
-  struct aim_conn_t *cur;
+	aim_conn_t *cur, **prev;
 
-  if (!deadconn || !*deadconn)	
-    return;
-
-  aim_tx_cleanqueue(sess, *deadconn);
+	if (!deadconn || !*deadconn)	
+		return;
 
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL)
-    ;
-  else if (sess->connlist->next == NULL) {
-    if (sess->connlist == *deadconn)
-      sess->connlist = NULL;
-  } else {
-    cur = sess->connlist;
-    while (cur->next) {
-      if (cur->next == *deadconn) {
-	cur->next = cur->next->next;
-	break;
-      }
-      cur = cur->next;
-    }
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	for (prev = &sess->connlist; (cur = *prev); ) {
+		if (cur == *deadconn) {
+			*prev = cur->next;
+			break;
+		}
+		prev = &cur->next;
+	}
 
-  /* XXX: do we need this for txqueue too? */
-  aim_rxqueue_cleanbyconn(sess, *deadconn);
+	if (!cur)
+		return; /* oops */
 
-  if ((*deadconn)->fd != -1) 
-    aim_conn_close(*deadconn);
-  if ((*deadconn)->priv)
-    free((*deadconn)->priv);
-  free(*deadconn);
-  deadconn = NULL;
+	connkill_real(sess, &cur);
 
-  return;
+	return;
 }
 
 /**
@@ -153,21 +162,24 @@
  *
  * This leaves everything untouched except for clearing the 
  * handler list and setting the fd to -1 (used to recognize
- * dead connections).
+ * dead connections).  It will also remove cookies if necessary.
  *
  */
-faim_export void aim_conn_close(struct aim_conn_t *deadconn)
+faim_export void aim_conn_close(aim_conn_t *deadconn)
 {
 
-  faim_mutex_destroy(&deadconn->active);
-  faim_mutex_destroy(&deadconn->seqnum_lock);
-  if (deadconn->fd >= 3)
-    close(deadconn->fd);
-  deadconn->fd = -1;
-  if (deadconn->handlerlist)
-    aim_clearhandlers(deadconn);
+	faim_mutex_destroy(&deadconn->active);
+	faim_mutex_destroy(&deadconn->seqnum_lock);
+	if (deadconn->fd >= 3)
+		close(deadconn->fd);
+	deadconn->fd = -1;
+	if (deadconn->handlerlist)
+		aim_clearhandlers(deadconn);
+	if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS)
+		aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn);
 
-  return;
+
+	return;
 }
 
 /**
@@ -180,50 +192,48 @@
  * type found.
  *
  */
-faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
-						int type)
+faim_export aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
 {
-  struct aim_conn_t *cur;
+	aim_conn_t *cur;
 
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if ((cur->type == type) && 
+				!(cur->status & AIM_CONN_STATUS_INPROGRESS))
+			break;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  return cur;
+	return cur;
 }
 
-faim_export struct aim_conn_t *aim_getconn_type_all(struct aim_session_t *sess,
-						    int type)
+faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
 {
-  struct aim_conn_t *cur;
+	aim_conn_t *cur;
 
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->type == type)
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if (cur->type == type)
+			break;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  return cur;
+	return cur;
 }
 
 /* If you pass -1 for the fd, you'll get what you ask for.  Gibberish. */
-faim_export struct aim_conn_t *aim_getconn_fd(struct aim_session_t *sess,
-					      int fd)
+faim_export aim_conn_t *aim_getconn_fd(aim_session_t *sess, int fd)
 {
-  struct aim_conn_t *cur;
+	aim_conn_t *cur;
 
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd == fd)
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if (cur->fd == fd)
+			break;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  return cur;
+	return cur;
 }
 
 /**
@@ -237,161 +247,162 @@
  * proxy settings, if present.  If no proxy is configured for
  * this session, the connection is done directly.
  *
+ * XXX this is really awful.
+ *
  */
-static int aim_proxyconnect(struct aim_session_t *sess, 
-			    char *host, unsigned short port,
-			    int *statusret)
+static int aim_proxyconnect(aim_session_t *sess, const char *host, fu16_t port, fu32_t *statusret)
 {
-  int fd = -1;
+	int fd = -1;
 
-  if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
-    int i;
-    unsigned char buf[512];
-    struct sockaddr_in sa;
-    struct hostent *hp;
-    char *proxy;
-    unsigned short proxyport = 1080;
+	if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
+		int i;
+		unsigned char buf[512];
+		struct sockaddr_in sa;
+		struct hostent *hp;
+		char *proxy;
+		unsigned short proxyport = 1080;
 
-    for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
-      if (sess->socksproxy.server[i] == ':') {
-	proxyport = atoi(&(sess->socksproxy.server[i+1]));
-	break;
-      }
-    }
-    proxy = (char *)malloc(i+1);
-    strncpy(proxy, sess->socksproxy.server, i);
-    proxy[i] = '\0';
+		for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
+			if (sess->socksproxy.server[i] == ':') {
+				proxyport = atoi(&(sess->socksproxy.server[i+1]));
+				break;
+			}
+		}
+
+		proxy = (char *)malloc(i+1);
+		strncpy(proxy, sess->socksproxy.server, i);
+		proxy[i] = '\0';
 
-    if (!(hp = gethostbyname(proxy))) {
-      faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n");
-      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
-      return -1;
-    }
-    free(proxy);
+		if (!(hp = gethostbyname(proxy))) {
+			faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n");
+			*statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+			return -1;
+		}
+		free(proxy);
+
+		memset(&sa.sin_zero, 0, 8);
+		sa.sin_port = htons(proxyport);
+		memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+		sa.sin_family = hp->h_addrtype;
 
-    memset(&sa.sin_zero, 0, 8);
-    sa.sin_port = htons(proxyport);
-    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
-    sa.sin_family = hp->h_addrtype;
-  
-    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
-    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
-      faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n");
-      close(fd);
-      return -1;
-    }
+		fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+		if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+			faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n");
+			close(fd);
+			return -1;
+		}
 
-    i = 0;
-    buf[0] = 0x05; /* SOCKS version 5 */
-    if (strlen(sess->socksproxy.username)) {
-      buf[1] = 0x02; /* two methods */
-      buf[2] = 0x00; /* no authentication */
-      buf[3] = 0x02; /* username/password authentication */
-      i = 4;
-    } else {
-      buf[1] = 0x01;
-      buf[2] = 0x00;
-      i = 3;
-    }
+		i = 0;
+		buf[0] = 0x05; /* SOCKS version 5 */
+		if (strlen(sess->socksproxy.username)) {
+			buf[1] = 0x02; /* two methods */
+			buf[2] = 0x00; /* no authentication */
+			buf[3] = 0x02; /* username/password authentication */
+			i = 4;
+		} else {
+			buf[1] = 0x01;
+			buf[2] = 0x00;
+			i = 3;
+		}
 
-    if (write(fd, buf, i) < i) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
+		if (write(fd, buf, i) < i) {
+			*statusret = errno;
+			close(fd);
+			return -1;
+		}
 
-    if (read(fd, buf, 2) < 2) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
+		if (read(fd, buf, 2) < 2) {
+			*statusret = errno;
+			close(fd);
+			return -1;
+		}
 
-    if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
-      *statusret = EINVAL;
-      close(fd);
-      return -1;
-    }
+		if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+			*statusret = EINVAL;
+			close(fd);
+			return -1;
+		}
 
-    /* check if we're doing username authentication */
-    if (buf[1] == 0x02) {
-      i  = aimutil_put8(buf, 0x01); /* version 1 */
-      i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
-      i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
-      i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
-      i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
-      if (write(fd, buf, i) < i) {
-	*statusret = errno;
-	close(fd);
-	return -1;
-      }
-      if (read(fd, buf, 2) < 2) {
-	*statusret = errno;
-	close(fd);
-	return -1;
-      }
-      if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
-	*statusret = EINVAL;
-	close(fd);
-	return -1;
-      }
-    }
+		/* check if we're doing username authentication */
+		if (buf[1] == 0x02) {
+			i  = aimutil_put8(buf, 0x01); /* version 1 */
+			i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
+			i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
+			i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
+			i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
+			if (write(fd, buf, i) < i) {
+				*statusret = errno;
+				close(fd);
+				return -1;
+			}
+			if (read(fd, buf, 2) < 2) {
+				*statusret = errno;
+				close(fd);
+				return -1;
+			}
+			if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+				*statusret = EINVAL;
+				close(fd);
+				return -1;
+			}
+		}
 
-    i  = aimutil_put8(buf, 0x05);
-    i += aimutil_put8(buf+i, 0x01); /* CONNECT */
-    i += aimutil_put8(buf+i, 0x00); /* reserved */
-    i += aimutil_put8(buf+i, 0x03); /* address type: host name */
-    i += aimutil_put8(buf+i, strlen(host));
-    i += aimutil_putstr(buf+i, host, strlen(host));
-    i += aimutil_put16(buf+i, port);
+		i  = aimutil_put8(buf, 0x05);
+		i += aimutil_put8(buf+i, 0x01); /* CONNECT */
+		i += aimutil_put8(buf+i, 0x00); /* reserved */
+		i += aimutil_put8(buf+i, 0x03); /* address type: host name */
+		i += aimutil_put8(buf+i, strlen(host));
+		i += aimutil_putstr(buf+i, host, strlen(host));
+		i += aimutil_put16(buf+i, port);
 
-    if (write(fd, buf, i) < i) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-    if (read(fd, buf, 10) < 10) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-    if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
-      *statusret = EINVAL;
-      close(fd);
-      return -1;
-    }
+		if (write(fd, buf, i) < i) {
+			*statusret = errno;
+			close(fd);
+			return -1;
+		}
+		if (read(fd, buf, 10) < 10) {
+			*statusret = errno;
+			close(fd);
+			return -1;
+		}
+		if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+			*statusret = EINVAL;
+			close(fd);
+			return -1;
+		}
 
-  } else { /* connecting directly */
-    struct sockaddr_in sa;
-    struct hostent *hp;
+	} else { /* connecting directly */
+		struct sockaddr_in sa;
+		struct hostent *hp;
 
-    if (!(hp = gethostbyname(host))) {
-      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
-      return -1;
-    }
+		if (!(hp = gethostbyname(host))) {
+			*statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+			return -1;
+		}
 
-    memset(&sa, 0, sizeof(struct sockaddr_in));
-    sa.sin_port = htons(port);
-    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
-    sa.sin_family = hp->h_addrtype;
-  
-    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+		memset(&sa, 0, sizeof(struct sockaddr_in));
+		sa.sin_port = htons(port);
+		memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+		sa.sin_family = hp->h_addrtype;
+
+		fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
 
-    if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
-      fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
+		if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
+			fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
 
-    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
-      if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
-	if ((errno == EINPROGRESS) || (errno == EINTR)) {
-	  if (statusret)
-	    *statusret |= AIM_CONN_STATUS_INPROGRESS;
-	  return fd;
+		if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+			if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
+				if ((errno == EINPROGRESS) || (errno == EINTR)) {
+					if (statusret)
+						*statusret |= AIM_CONN_STATUS_INPROGRESS;
+					return fd;
+				}
+			}
+			close(fd);
+			fd = -1;
+		}
 	}
-      }
-      close(fd);
-      fd = -1;
-    }
-  }
-  return fd;
+	return fd;
 }
 
 /**
@@ -407,35 +418,29 @@
  * This function returns a pointer to the new aim_conn_t, or %NULL on
  * error
  */
-faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess,
-					       struct aim_conn_t *src)
+faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
 {
-  struct aim_conn_t *conn;
-    struct aim_rxcblist_t *cur;
+	aim_conn_t *conn;
 
-  if (!(conn = aim_conn_getnext(sess)))
-    return NULL;
+	if (!(conn = aim_conn_getnext(sess)))
+		return NULL;
 
-  faim_mutex_lock(&conn->active);
+	faim_mutex_lock(&conn->active);
 
-  conn->fd = src->fd;
-  conn->type = src->type;
-  conn->subtype = src->subtype;
-  conn->seqnum = src->seqnum;
-  conn->priv = src->priv;
-  conn->lastactivity = src->lastactivity;
-  conn->forcedlatency = src->forcedlatency;
-  conn->sessv = src->sessv;
+	conn->fd = src->fd;
+	conn->type = src->type;
+	conn->subtype = src->subtype;
+	conn->seqnum = src->seqnum;
+	conn->priv = src->priv;
+	conn->internal = src->internal;
+	conn->lastactivity = src->lastactivity;
+	conn->forcedlatency = src->forcedlatency;
+	conn->sessv = src->sessv;
+	aim_clonehandlers(sess, conn, src);
 
-  /* clone handler list */
-  for (cur = src->handlerlist; cur; cur = cur->next) {
-    aim_conn_addhandler(sess, conn, cur->family, cur->type, 
-			cur->handler, cur->flags);
-  }
+	faim_mutex_unlock(&conn->active);
 
-  faim_mutex_unlock(&conn->active);
-
-  return conn;
+	return conn;
 }
 
 /**
@@ -452,63 +457,62 @@
  * FIXME: Return errors in a more sane way.
  *
  */
-faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
-					   int type, char *dest)
+faim_export aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
 {
-  struct aim_conn_t *connstruct;
-  int ret;
-  u_short port = FAIM_LOGIN_PORT;
-  char *host = NULL;
-  int i=0;
+	aim_conn_t *connstruct;
+	fu16_t port = FAIM_LOGIN_PORT;
+	char *host;
+	int i, ret;
 
-  if ((connstruct=aim_conn_getnext(sess))==NULL)
-    return NULL;
+	if (!(connstruct = aim_conn_getnext(sess)))
+		return NULL;
 
-  faim_mutex_lock(&connstruct->active);
+	faim_mutex_lock(&connstruct->active);
 
-  connstruct->sessv = (void *)sess;
-  connstruct->type = type;
+	connstruct->sessv = (void *)sess;
+	connstruct->type = type;
 
-  if (!dest) { /* just allocate a struct */
-    connstruct->fd = -1;
-    connstruct->status = 0;
-    faim_mutex_unlock(&connstruct->active);
-    return connstruct;
-  }
+	if (!dest) { /* just allocate a struct */
+		connstruct->fd = -1;
+		connstruct->status = 0;
+		faim_mutex_unlock(&connstruct->active);
+		return connstruct;
+	}
 
-  /* 
-   * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
-   * colon, in the BOS redirect.  This fatally breaks all previous 
-   * libfaims.  Bad, bad AOL.
-   *
-   * We put this here to catch every case. 
-   *
-   */
+	/* 
+	 * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
+	 * colon, in the BOS redirect.  This fatally breaks all previous 
+	 * libfaims.  Bad, bad AOL.
+	 *
+	 * We put this here to catch every case. 
+	 *
+	 */
+
+	for(i = 0; i < (int)strlen(dest); i++) {
+		if (dest[i] == ':') {
+			port = atoi(&(dest[i+1]));
+			break;
+		}
+	}
 
-  for(i=0;i<(int)strlen(dest);i++) {
-    if (dest[i] == ':') {
-      port = atoi(&(dest[i+1]));
-      break;
-    }
-  }
-  host = (char *)malloc(i+1);
-  strncpy(host, dest, i);
-  host[i] = '\0';
+	host = (char *)malloc(i+1);
+	strncpy(host, dest, i);
+	host[i] = '\0';
 
-  if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
-    connstruct->fd = -1;
-    connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
-    free(host);
-    faim_mutex_unlock(&connstruct->active);
-    return connstruct;
-  } else
-    connstruct->fd = ret;
-  
-  faim_mutex_unlock(&connstruct->active);
+	if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
+		connstruct->fd = -1;
+		connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
+		free(host);
+		faim_mutex_unlock(&connstruct->active);
+		return connstruct;
+	} else
+		connstruct->fd = ret;
 
-  free(host);
+	faim_mutex_unlock(&connstruct->active);
 
-  return connstruct;
+	free(host);
+
+	return connstruct;
 }
 
 /**
@@ -519,19 +523,19 @@
  * connections in @sess.
  *
  */
-faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
+faim_export int aim_conngetmaxfd(aim_session_t *sess)
 {
-  int j = 0;
-  struct aim_conn_t *cur;
+	int j;
+	aim_conn_t *cur;
 
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd > j)
-      j = cur->fd;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist, j = 0; cur; cur = cur->next) {
+		if (cur->fd > j)
+			j = cur->fd;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  return j;
+	return j;
 }
 
 /**
@@ -543,18 +547,20 @@
  * zero otherwise.
  *
  */
-faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct aim_conn_t *cur;
+	aim_conn_t *cur;
 
-  faim_mutex_lock(&sess->connlistlock);
-  for(cur = sess->connlist; cur; cur = cur->next)
-    if(cur == conn) {
-      faim_mutex_unlock(&sess->connlistlock);
-      return 1;
-    }
-  faim_mutex_unlock(&sess->connlistlock);
-  return 0;
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if (cur == conn) {
+			faim_mutex_unlock(&sess->connlistlock);
+			return 1;
+		}
+	}
+	faim_mutex_unlock(&sess->connlistlock);
+
+	return 0;
 }
 
 /**
@@ -575,126 +581,80 @@
  * XXX: we could probably stand to do a little courser locking here.
  *
  */ 
-faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
-					  struct timeval *timeout, 
-					  int *status)
+faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, int *status)
 {
-  struct aim_conn_t *cur;
-  fd_set fds, wfds;
-  int maxfd = 0;
-  int i, haveconnecting = 0;
+	aim_conn_t *cur;
+	fd_set fds, wfds;
+	int maxfd, i, haveconnecting = 0;
 
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL) {
-    faim_mutex_unlock(&sess->connlistlock);
-    *status = -1;
-    return NULL;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	if (!sess->connlist) {
+		faim_mutex_unlock(&sess->connlistlock);
+		*status = -1;
+		return NULL;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  FD_ZERO(&fds);
-  FD_ZERO(&wfds);
-  maxfd = 0;
+	FD_ZERO(&fds);
+	FD_ZERO(&wfds);
 
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd == -1) {
-      /* don't let invalid/dead connections sit around */
-      *status = 2;
-      faim_mutex_unlock(&sess->connlistlock);
-      return cur;
-    } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
-      FD_SET(cur->fd, &wfds);
-      
-      haveconnecting++;
-    }
-    FD_SET(cur->fd, &fds);
-    if (cur->fd > maxfd)
-      maxfd = cur->fd;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
+	faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist, maxfd = 0; cur; cur = cur->next) {
+		if (cur->fd == -1) {
+			/* don't let invalid/dead connections sit around */
+			*status = 2;
+			faim_mutex_unlock(&sess->connlistlock);
+			return cur;
+		} else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
+			FD_SET(cur->fd, &wfds);
 
-  /* 
-   * If we have data waiting to be sent, return
-   *
-   * We have to not do this if theres at least one
-   * connection thats still connecting, since that connection
-   * may have queued data and this return would prevent
-   * the connection from ever completing!  This is a major
-   * inadequacy of the libfaim way of doing things.  It means
-   * that nothing can transmit as long as there's connecting
-   * sockets. Evil.
-   *
-   * But its still better than having blocking connects.
-   *
-   */
-  if (!haveconnecting && (sess->queue_outgoing != NULL)) {
-    *status = 1;
-    return NULL;
-  } 
+			haveconnecting++;
+		}
+		FD_SET(cur->fd, &fds);
+		if (cur->fd > maxfd)
+			maxfd = cur->fd;
+	}
+	faim_mutex_unlock(&sess->connlistlock);
 
-  if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
-    faim_mutex_lock(&sess->connlistlock);
-    for (cur = sess->connlist; cur; cur = cur->next) {
-      if ((FD_ISSET(cur->fd, &fds)) || 
-	  ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
-	   FD_ISSET(cur->fd, &wfds))) {
-	*status = 2;
-	faim_mutex_unlock(&sess->connlistlock);
-	return cur; /* XXX race condition here -- shouldnt unlock connlist */
-      }
-    }
-    *status = 0; /* shouldn't happen */
-  } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
-    *status = 0;
-  else
-    *status = i; /* can be 0 or -1 */
-
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return NULL;  /* no waiting or error, return */
-}
+	/* 
+	 * If we have data waiting to be sent, return
+	 *
+	 * We have to not do this if theres at least one
+	 * connection thats still connecting, since that connection
+	 * may have queued data and this return would prevent
+	 * the connection from ever completing!  This is a major
+	 * inadequacy of the libfaim way of doing things.  It means
+	 * that nothing can transmit as long as there's connecting
+	 * sockets. Evil.
+	 *
+	 * But its still better than having blocking connects.
+	 *
+	 */
+	if (!haveconnecting && sess->queue_outgoing) {
+		*status = 1;
+		return NULL;
+	} 
 
-/**
- * aim_conn_isready - Test if a connection is marked ready
- * @conn: Connection to test
- *
- * Returns true if the connection is ready, false otherwise.
- * Returns -1 if the connection is invalid.
- *
- * XXX: This is deprecated.
- *
- */
-faim_export int aim_conn_isready(struct aim_conn_t *conn)
-{
-  if (conn)
-    return (conn->status & 0x0001);
-  return -1;
-}
+	if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
+		faim_mutex_lock(&sess->connlistlock);
+		for (cur = sess->connlist; cur; cur = cur->next) {
+			if ((FD_ISSET(cur->fd, &fds)) || 
+					((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
+					FD_ISSET(cur->fd, &wfds))) {
+				*status = 2;
+				faim_mutex_unlock(&sess->connlistlock);
+				return cur; /* XXX race condition here -- shouldnt unlock connlist */
+			}
+		}
+		*status = 0; /* shouldn't happen */
+	} else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
+		*status = 0;
+	else
+		*status = i; /* can be 0 or -1 */
 
-/**
- * aim_conn_setstatus - Set the status of a connection
- * @conn: Connection
- * @status: New status
- *
- * @newstatus is %XOR'd with the previous value of the connection
- * status and returned.  Returns -1 if the connection is invalid.
- *
- * This isn't real useful.
- *
- */
-faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
-{
-  int val;
+	faim_mutex_unlock(&sess->connlistlock);
 
-  if (!conn)
-    return -1;
-  
-  faim_mutex_lock(&conn->active);
-  val = conn->status ^= status;
-  faim_mutex_unlock(&conn->active);
-
-  return val;
+	return NULL;  /* no waiting or error, return */
 }
 
 /**
@@ -711,17 +671,18 @@
  * backs off like the real rate limiting does.
  *
  */
-faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
+faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval)
 {
-  if (!conn)
-    return -1;
+
+	if (!conn)
+		return -1;
 
-  faim_mutex_lock(&conn->active);
-  conn->forcedlatency = newval;
-  conn->lastactivity = 0; /* reset this just to make sure */
-  faim_mutex_unlock(&conn->active);
+	faim_mutex_lock(&conn->active);
+	conn->forcedlatency = newval;
+	conn->lastactivity = 0; /* reset this just to make sure */
+	faim_mutex_unlock(&conn->active);
 
-  return 0;
+	return 0;
 }
 
 /**
@@ -738,27 +699,31 @@
  * Set username and password to %NULL if not applicable.
  *
  */
-faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
+faim_export void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password)
 {
-  /* clear out the proxy info */
-  if (!server || !strlen(server)) {
-    memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
-    memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
-    memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
-    return;
-  }
+	/* clear out the proxy info */
+	if (!server || !strlen(server)) {
+		memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
+		memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
+		memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
+		return;
+	}
 
-  strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
-  if (username && strlen(username)) 
-    strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
-  if (password && strlen(password))
-    strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
-  return;
+	strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
+	if (username && strlen(username)) 
+		strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
+	if (password && strlen(password))
+		strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
+
+	return;
 }
 
-static void defaultdebugcb(struct aim_session_t *sess, int level, const char *format, va_list va)
+static void defaultdebugcb(aim_session_t *sess, int level, const char *format, va_list va)
 {
-  vfprintf(stderr, format, va);
+
+	vfprintf(stderr, format, va);
+
+	return;
 }
 
 /**
@@ -770,74 +735,74 @@
  * Sets up the initial values for a session.
  *
  */
-faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags, int debuglevel)
+faim_export void aim_session_init(aim_session_t *sess, fu32_t flags, int debuglevel)
 {
-  if (!sess)
-    return;
+
+	if (!sess)
+		return;
 
-  memset(sess, 0, sizeof(struct aim_session_t));
-  aim_connrst(sess);
-  sess->queue_outgoing = NULL;
-  sess->queue_incoming = NULL;
-  sess->pendingjoin = NULL;
-  sess->pendingjoinexchange = 0;
-  aim_initsnachash(sess);
-  sess->msgcookies = NULL;
-  sess->snac_nextid = 0x00000001;
+	memset(sess, 0, sizeof(aim_session_t));
+	aim_connrst(sess);
+	sess->queue_outgoing = NULL;
+	sess->queue_incoming = NULL;
+	sess->pendingjoin = NULL;
+	sess->pendingjoinexchange = 0;
+	aim_initsnachash(sess);
+	sess->msgcookies = NULL;
+	sess->snacid_next = 0x00000001;
 
-  sess->flags = 0;
-  sess->debug = debuglevel;
-  sess->debugcb = defaultdebugcb;
+	sess->flags = 0;
+	sess->debug = debuglevel;
+	sess->debugcb = defaultdebugcb;
 
-  sess->modlistv = NULL;
+	sess->modlistv = NULL;
 
-  /*
-   * Default to SNAC login unless XORLOGIN is explicitly set.
-   */
-  if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
-    sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
-  sess->flags |= flags;
+	/*
+	 * Default to SNAC login unless XORLOGIN is explicitly set.
+	 */
+	if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
+		sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+	sess->flags |= flags;
 
-  /*
-   * This must always be set.  Default to the queue-based
-   * version for back-compatibility.  
-   */
-  aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
+	/*
+	 * This must always be set.  Default to the queue-based
+	 * version for back-compatibility.  
+	 */
+	aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
 
 
-  /*
-   * Register all the modules for this session...
-   */
-  aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
-  aim__registermodule(sess, buddylist_modfirst);
-  aim__registermodule(sess, admin_modfirst);
-  aim__registermodule(sess, bos_modfirst);
-  aim__registermodule(sess, search_modfirst);
-  aim__registermodule(sess, stats_modfirst);
-  aim__registermodule(sess, auth_modfirst);
-  aim__registermodule(sess, msg_modfirst);
-  aim__registermodule(sess, chatnav_modfirst);
-  aim__registermodule(sess, chat_modfirst);
-  aim__registermodule(sess, locate_modfirst);
-  aim__registermodule(sess, general_modfirst);
+	/*
+	 * Register all the modules for this session...
+	 */
+	aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
+	aim__registermodule(sess, buddylist_modfirst);
+	aim__registermodule(sess, admin_modfirst);
+	aim__registermodule(sess, bos_modfirst);
+	aim__registermodule(sess, search_modfirst);
+	aim__registermodule(sess, stats_modfirst);
+	aim__registermodule(sess, auth_modfirst);
+	aim__registermodule(sess, msg_modfirst);
+	aim__registermodule(sess, chatnav_modfirst);
+	aim__registermodule(sess, chat_modfirst);
+	aim__registermodule(sess, locate_modfirst);
+	aim__registermodule(sess, general_modfirst);
 
-  return;
+	return;
 }
 
 /**
  * aim_session_kill - Deallocate a session
  * @sess: Session to kill
  *
- *
  */
-faim_export void aim_session_kill(struct aim_session_t *sess)
+faim_export void aim_session_kill(aim_session_t *sess)
 {
 
-  aim_logoff(sess);
+	aim_logoff(sess);
 
-  aim__shutdownmodules(sess);
+	aim__shutdownmodules(sess);
 
-  return;
+	return;
 }
 
 /**
@@ -850,15 +815,15 @@
  * the value faimdprintf was called with.
  *
  */
-faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t cb)
+faim_export int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t cb)
 {
 
-  if (!sess)
-    return -1;
+	if (!sess)
+		return -1;
 
-  sess->debugcb = cb;
+	sess->debugcb = cb;
 
-  return 0;
+	return 0;
 }
 
 /**
@@ -870,76 +835,81 @@
  * has yet to be called on it).
  *
  */
-faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
+faim_export int aim_conn_isconnecting(aim_conn_t *conn)
 {
-  if (!conn)
-    return 0;
-  return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
+
+	if (!conn)
+		return 0;
+
+	return !!(conn->status & AIM_CONN_STATUS_INPROGRESS);
 }
 
-faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
+/*
+ * XXX this is nearly as ugly as proxyconnect().
+ */
+faim_export int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
 {
-  fd_set fds, wfds;
-  struct timeval tv;
-  int res, error = ETIMEDOUT;
-  aim_rxcallback_t userfunc;
-
-  if (!conn || (conn->fd == -1))
-    return -1;
+	fd_set fds, wfds;
+	struct timeval tv;
+	int res, error = ETIMEDOUT;
+	aim_rxcallback_t userfunc;
 
-  if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
-    return -1;
+	if (!conn || (conn->fd == -1))
+		return -1;
+
+	if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
+		return -1;
 
-  FD_ZERO(&fds);
-  FD_SET(conn->fd, &fds);
-  FD_ZERO(&wfds);
-  FD_SET(conn->fd, &wfds);
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
+	FD_ZERO(&fds);
+	FD_SET(conn->fd, &fds);
+	FD_ZERO(&wfds);
+	FD_SET(conn->fd, &wfds);
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
 
-  if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
-    error = errno;
-    aim_conn_close(conn);
-    errno = error;
-    return -1;
-  } else if (res == 0) {
-    faimdprintf(sess, 0, "aim_conn_completeconnect: false alarm on %d\n", conn->fd);
-    return 0; /* hasn't really completed yet... */
-  } 
+	if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
+		error = errno;
+		aim_conn_close(conn);
+		errno = error;
+		return -1;
+	} else if (res == 0) {
+		faimdprintf(sess, 0, "aim_conn_completeconnect: false alarm on %d\n", conn->fd);
+		return 0; /* hasn't really completed yet... */
+	} 
 
-  if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
-    int len = sizeof(error);
+	if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
+		int len = sizeof(error);
 
-    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
-      error = errno;
-  }
+		if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+			error = errno;
+	}
 
-  if (error) {
-    aim_conn_close(conn);
-    errno = error;
-    return -1;
-  }
+	if (error) {
+		aim_conn_close(conn);
+		errno = error;
+		return -1;
+	}
 
-  fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
+	fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
 
-  conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
+	conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
 
-  if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
-    userfunc(sess, NULL, conn);
+	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
+		userfunc(sess, NULL, conn);
 
-  /* Flush out the queues if there was something waiting for this conn  */
-  aim_tx_flushqueue(sess);
+	/* Flush out the queues if there was something waiting for this conn  */
+	aim_tx_flushqueue(sess);
 
-  return 0;
+	return 0;
 }
 
-faim_export struct aim_session_t *aim_conn_getsess(struct aim_conn_t *conn)
+faim_export aim_session_t *aim_conn_getsess(aim_conn_t *conn)
 {
 
-  if (!conn)
-    return NULL;
+	if (!conn)
+		return NULL;
 
-  return (struct aim_session_t *)conn->sessv;
+	return (aim_session_t *)conn->sessv;
 }
 
 /*
@@ -948,12 +918,12 @@
  * Closes -ALL- open connections.
  *
  */
-faim_export int aim_logoff(struct aim_session_t *sess)
+faim_export int aim_logoff(aim_session_t *sess)
 {
 
-  aim_connrst(sess);  /* in case we want to connect again */
+	aim_connrst(sess);  /* in case we want to connect again */
 
-  return 0;
+	return 0;
 
 }
 
--- a/src/protocols/oscar/faimconfig.h	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/faimconfig.h	Sun Sep 09 10:07:14 2001 +0000
@@ -40,16 +40,6 @@
 #define FAIM_LOGIN_PORT 5190
 
 /*
- * The integer extraction/copying functions in aim_util.c have
- * both a function version and a macro version.  The macro 
- * version is suggested.  Since the function version is more
- * readable, I leave both around for reference.
- *
- * Default: defined.
- */
-#define AIMUTIL_USEMACROS
-
-/*
  * What type of synchronisation to use.
  * 
  * We don't actually use threads, but can use the POSIX mutex
@@ -83,7 +73,7 @@
 #define FAIM_SNAC_HASH_SIZE 16
 
 /*
- * If building on Win32,define WIN32_STATIC if you don't want
+ * If building on Win32, define WIN32_STATIC if you don't want
  * to compile libfaim as a DLL (and instead link it right into
  * your app).
  */
--- a/src/protocols/oscar/ft.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/ft.c	Sun Sep 09 10:07:14 2001 +0000
@@ -21,10 +21,17 @@
    o look for memory leaks.. there's going to be shitloads, i'm sure. 
 */
 
+struct aim_directim_intdata {
+	fu8_t cookie[8];
+	char sn[MAXSNLEN+1];
+	char ip[22];
+};
+
+static int listenestablish(fu16_t portnum);
 static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr);
  
 /**
- * aim_handlerendconnect - call this to accept OFT connections and set up the requisite structures
+ * aim_handlerendconnect - call this to accept OFT connections and set up the required structures
  * @sess: the session
  * @cur: the conn the incoming connection is on
  *
@@ -34,82 +41,70 @@
  * listener conn are both returned to the client in the
  * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback.
  */
-faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur)
+faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
 { 
-  int acceptfd = 0;
-  aim_rxcallback_t userfunc;
-  struct sockaddr cliaddr;
-  int clilen = sizeof(cliaddr);
-  int ret = 0;
-  struct aim_conn_t *newconn;
+	int acceptfd = 0;
+	struct sockaddr cliaddr;
+	int clilen = sizeof(cliaddr);
+	int ret = 0;
+	aim_conn_t *newconn;
 
-  if ( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
-    return -1;
-  if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
-    close(acceptfd);
-    aim_conn_close(cur);
-    return -1;
-  } 
+	if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
+		return 0; /* not an error */
 
-  /* safe? maybe cur->priv should be NULLed after this. --mid */
-
-  /* That would be bad. very bad. we want cur->priv->sn to make it up
-     to the client-level for conn management and such. even though
-     that is abusing the interface --jbm */
+	if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
+		close(acceptfd);
+		aim_conn_close(cur);
+		return -1;
+	} 
 
-  if (!(newconn = aim_cloneconn(sess, cur))) {
-    close(acceptfd);
-    aim_conn_close(cur);
-    return -1;
-  }
+	if (!(newconn = aim_cloneconn(sess, cur))) {
+		close(acceptfd);
+		aim_conn_close(cur);
+		return -1;
+	}
 
-  newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
-  newconn->fd = acceptfd;
+	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
+	newconn->fd = acceptfd;
 
-  switch(newconn->subtype) {
-  case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { 
-    struct aim_directim_priv *priv;
-
-    priv = cur->priv;
+	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { 
+		struct aim_directim_intdata *priv;
+		aim_rxcallback_t userfunc;
 
-    newconn->priv = cur->priv;
-
-    cur->priv = NULL;
+		priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal);
+		cur->internal = NULL;
 
-    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", 
-	     inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), 
-	     ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", 
+				inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), 
+				ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
 
-    if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
-      ret = userfunc(sess, NULL, newconn, cur);
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
+			ret = userfunc(sess, NULL, newconn, cur);
 
-    break;
-  }
-  case AIM_CONN_SUBTYPE_OFT_GETFILE: { 
-    struct aim_filetransfer_priv *priv;
+	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+#if 0
+		struct aim_filetransfer_priv *priv;
+		aim_rxcallback_t userfunc;
 
 
-    newconn->priv = cur->priv;
-    cur->priv = NULL;
-    priv = (struct aim_filetransfer_priv *)newconn->priv;
+		newconn->priv = cur->priv;
+		cur->priv = NULL;
+		priv = (struct aim_filetransfer_priv *)newconn->priv;
 
-    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-    if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
-      ret = userfunc(sess, NULL, newconn, cur);
+		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
 
-    break;
-  }
-  default: { 
-    faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous(??!) Closing conn.\n");
-    aim_conn_close(newconn);
-    break;
-  }
-  }
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
+			ret = userfunc(sess, NULL, newconn, cur);
+#endif
+	} else { 
+		faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous(??!) Closing conn.\n");
+		aim_conn_close(newconn);
+		ret = -1;
+	}
 
-  return ret;
+	return ret;
 }
- 
+
 /**
  * aim_send_im_direct - send IM client-to-client over established connection
  * @sess: session to conn
@@ -119,105 +114,176 @@
  * Call this just like you would aim_send_im, to send a directim. You
  * _must_ have previously established the directim connection.
  */
-faim_export int aim_send_im_direct(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg)
+faim_export int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg)
 {
-  struct command_tx_struct *newpacket;
-  struct aim_directim_priv *priv = NULL;
-  int i;
- 
-  if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv) { 
-    faimdprintf(sess, 2,"faim: directim: invalid arguments\n");
-    return -1;
-  }
+	struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	aim_frame_t *fr;
+	aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */
 
-  priv = (struct aim_directim_priv *)conn->priv;
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
+		return -EINVAL; 
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, strlen(msg)))) { 
-    faimdprintf(sess, 2,"faim: directim: tx_new failed\n");
-    return -1;
-  } 
-
-  newpacket->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, strlen(msg))))
+	       return -ENOMEM;	
 
-  /* if msg is non-null, we'resending an IM, else a "typing" notice */
-  if (msg) { 
-    if (strlen(msg) >= MAXMSGLEN)
-      return -1;
-    newpacket->hdr.oft.hdr2len = 0x54;
-    if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { 
-      newpacket->lock = 0;
-      aim_tx_destroy(newpacket);
-      return -1;
-    } 
-  } else { 
-    newpacket->hdr.oft.hdr2len = 0x44;
-    if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { 
-      newpacket->lock = 0;
-      aim_tx_destroy(newpacket);
-      return -1;
-    } 
-  } 
+	memcpy(fr->hdr.oft.magic, "ODC2", 4);
+	
+	fr->hdr.oft.hdr2len = 0x44;
+	
+	if (!(fr->hdr.oft.hdr2 = calloc(1, fr->hdr.oft.hdr2len))) { 
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+	
+	aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
 
-  memcpy(newpacket->hdr.oft.magic, "ODC2", 4);
-  newpacket->data = NULL;
+	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, strlen(msg));
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
 
-  i = 0;
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg));
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+	/* flags -- 0x000e for "typing", 0x0000 for message */
+	aimbs_put16(&hdrbs, msg ? 0x0000 : 0x000e);
+
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, sess->sn, strlen(sess->sn));
+
+	aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
 
-  /* flags -- 0x000e for "typing", 0x0000 for message */
-  if (msg)
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  else 
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000e);
-
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
-  i = 52;
+	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);
 
-  i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-
-  /* end of hdr2 */
+	/* end of hdr2 */
 
-  if (msg) { 
-    /* values grabbed from a dump */
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
-    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
-    if(!(newpacket->data = strdup(msg)))
-      return -1;
-  } 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-  return 0;
+	if (msg) {
+#if 0 /* XXX this is how you send buddy icon info... */	
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
+		i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
+#endif
+		aimbs_putraw(&fr->data, msg, strlen(msg));
+	} 
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 } 
 
 /* XXX: give the client author the responsibility of setting up a
  * listener, then we no longer have a libfaim problem with broken
  * solaris *innocent smile* -jbm */
 
+static int getlocalip(fu8_t *ip)
+{
+	struct hostent *hptr;
+	char localhost[129];
+
+	/* XXX if available, use getaddrinfo() */
+	/* XXX allow client to specify which IP to use for multihomed boxes */
+
+	if (gethostname(localhost, 128) < 0)
+		return -1;
+
+	if (!(hptr = gethostbyname(localhost)))
+		return -1;
+
+	memcpy(ip, hptr->h_addr_list[0], 4);
+
+	return 0;
+}
+
+/* XXX this should probably go in im.c */
+static int aim_request_directim(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu8_t *ip, fu16_t port, fu8_t *ckret)
+{
+	fu8_t ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	int hdrlen, i;
+	fu8_t *hdr;
+	aim_bstream_t hdrbs;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
+		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?
+	 *
+	 */
+	for (i = 0; i < 7; i++)
+	       	ck[i] = 0x30 + ((fu8_t) rand() % 10);
+	ck[7] = '\0';
+
+	if (ckret)
+		memcpy(ckret, ck, 8);
+
+	/* Cookie */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/* Channel */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/* Destination SN */
+	aimbs_put8(&fr->data, strlen(destsn));
+	aimbs_putraw(&fr->data, destsn, strlen(destsn));
+
+	aim_addtlvtochain_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, ck, 8);
+	aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE);
+
+	aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+	aim_addtlvtochain_raw(&itl, 0x0003, 4, ip);
+	aim_addtlvtochain16(&itl, 0x0005, port);
+	aim_addtlvtochain_noval(&itl, 0x000f);
+	
+	aim_writetlvchain(&hdrbs, &itl);
+
+	aim_addtlvtochain_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+	aim_writetlvchain(&fr->data, &tl);
+
+	free(hdr);
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
 /**
  * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves.
  * @sess: your session,
@@ -226,131 +292,58 @@
  * @destsn: the SN to connect to.
  *
  */
-faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess, 
-						     struct aim_conn_t *conn,
-						     struct aim_directim_priv *priv, 
-						     char *destsn)
+faim_export aim_conn_t *aim_directim_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn)
 { 
-
-  struct command_tx_struct *newpacket;
-  struct aim_conn_t *newconn;
-  struct aim_msgcookie_t *cookie;
-  int curbyte, i, listenfd;
-  short port = 4443;
-  struct hostent *hptr;
-  char localhost[129];
-  unsigned char cap[16];
-  char d[4]; /* IPv6 is a bit bigger... */
-
-  /* XXX: TLVlist-ize this */
- 
-  /* Open our socket */
-
-  if ( (listenfd = aim_listenestablish(port)) == -1)
-    return NULL;
+	aim_conn_t *newconn;
+	aim_msgcookie_t *cookie;
+	struct aim_directim_intdata *priv;
+	int listenfd;
+	fu16_t port = 4443;
+	fu8_t localip[4];
+	fu8_t ck[8];
 
-  /* get our local IP */
-  /* XXX if available, use getaddrinfo() */
-  /* XXX allow client to specify which IP to use for multihomed boxes */
-  if (gethostname(localhost, 128) < 0)
-    return NULL;
-  if ( (hptr = gethostbyname(localhost)) == NULL)
-    return NULL;
-  memcpy(&d, hptr->h_addr_list[0], 4);
+	if (getlocalip(localip) == -1)
+		return NULL;
 
-  aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
-
-  /* create the OSCAR packet */
+	if ((listenfd = listenestablish(port)) == -1)
+		return NULL;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x32)))
-    return NULL;
-  newpacket->lock = 1;
-
-  curbyte = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+	aim_request_directim(sess, conn, destsn, localip, port, ck);
 
-  /* Generate a random message cookie */
-  /* This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
-  for (i=0; i<7; i++) 
-    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20));
+	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
+	memcpy(cookie->cookie, ck, 8);
+	cookie->type = AIM_COOKIETYPE_OFTIM;
 
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /* grab all the data for cookie caching */
-  cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
-  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
-  cookie->type = AIM_COOKIETYPE_OFTIM;
-  priv = cookie->data;
+	priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata));
 
-  if (!priv)
-    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
- 
-  memcpy(priv->cookie, cookie, 8);
-  memcpy(priv->sn, destsn, sizeof(priv->sn));
-  cookie->data = priv;
-  aim_cachecookie(sess, cookie);
-
-  /* Channel ID */
-  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
+	cookie->data = priv;
+	aim_cachecookie(sess, cookie);
 
-  /* Destination SN (prepended with byte length)*/
-  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-
-  /* enTLV start */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
-
-  /* Flag data / ICBM Parameters  */
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /* Cookie  */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
-
-  /*Capability String  */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+	/* XXX switch to aim_cloneconn()? */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
+		close(listenfd);
+		return NULL;
+	}
 
-  /* 000a/0002 : 0001  */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+	/* this one is for the conn */
+	priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata));
 
-  /* 0003/0004: IP address  */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
-  for(i = 0;i < 4; i++) 
-    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
- 
-  /* 0005/0002: Port */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, port);
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
 
-  /* 000f/0000: ?? */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = priv;
+	newconn->lastactivity = time(NULL);
 
-  /* XXX switch to aim_cloneconn()? */
-  if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL)))
-    return NULL;
+	faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
 
-  newconn->fd = listenfd;
-  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-  newconn->priv = priv;
-  newconn->lastactivity = time(NULL);
-
-  faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
-
-  return newconn;
+	return newconn;
 } 
 
+#if 0
 /**
  * unsigned int aim_oft_listener_clean - close up old listeners
  * @sess: session to clean up in
@@ -381,35 +374,71 @@
   faim_mutex_unlock(&sess->connlistlock);
   return hit;
 } 
+#endif 
+
+faim_export const char *aim_directim_getsn(aim_conn_t *conn)
+{
+	struct aim_directim_intdata *intdata;
+
+	if (!conn)
+	       return NULL;
+
+	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
+			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+	       return NULL;
+
+	if (!conn->internal)
+		return NULL;
+
+	intdata = (struct aim_directim_intdata *)conn->internal;
+
+	return intdata->sn;
+}
 
 /**
  * aim_directim_connect - connect to buddy for directim
  * @sess: the session to append the conn to,
- * @conn: the BOS connection,
- * @priv: the filled-in priv data structure for the connection 
+ * @sn: the SN we're connecting to
+ * @addr: address to connect to
+ *
+ * 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.
  *
- * returns conn if connected, %NULL on error
  */
-faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv)
+faim_export aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
 { 
-  struct aim_conn_t *newconn = NULL;
+	aim_conn_t *newconn;
+	struct aim_directim_intdata *intdata;
+
+	if (!sess || !sn)
+		return NULL;
+
+	if (!(intdata = malloc(sizeof(struct aim_directim_intdata))))
+		return NULL;
+	memset(intdata, 0, sizeof(struct aim_directim_intdata));
 
-  if (!sess || !conn || !priv)
-    return NULL;
-  
-  /* XXX verify that non-blocking connects actually work */
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip);
-  if (!newconn || (newconn->fd == -1)) { 
-    faimdprintf(sess, 2, "could not connect to %s\n", priv->ip);
-    perror("aim_newconn");
-    return newconn;
-  }
+	memcpy(intdata->cookie, cookie, 8);
+	strncpy(intdata->sn, sn, sizeof(intdata->sn));
+	if (addr)
+		strncpy(intdata->ip, addr, sizeof(intdata->ip));
 
-  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-  newconn->priv = priv;
-  faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+	/* XXX verify that non-blocking connects actually work */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
+		free(intdata);
+		return NULL;
+	}
 
-  return newconn;
+	if (!newconn) {
+		free(intdata);
+		return newconn;
+	}
+
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = intdata;
+
+	return newconn;
 } 
 
 /**
@@ -420,24 +449,26 @@
  * returns conn for directim with name, %NULL if none found. 
  *
  */
-faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
+faim_export aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name)
 {
-  struct aim_conn_t *cur;
-  struct aim_directim_priv *priv;
+	aim_conn_t *cur;
 
-  if (!sess || !name)
-    return NULL;
+	if (!sess || !name || !strlen(name))
+		return NULL;
 
-  faim_mutex_lock(&sess->connlistlock);
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		struct aim_directim_intdata *intdata;
+		
+		if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+			continue;
 
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)
-      continue;
-    priv = cur->priv;
-    if (aim_sncmp(priv->sn, name) == 0)
-      break;
-  } faim_mutex_unlock(&sess->connlistlock);
-  return cur;
+		intdata = cur->internal;
+
+		if (aim_sncmp(intdata->sn, name) == 0)
+			break;
+	}
+
+	return cur;
 } 
 
 /**
@@ -454,17 +485,21 @@
  * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)  
  *
  * Returns new connection or %NULL on error.
+ *
+ * XXX this should take a struct.
  */
-faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, 
-						  struct aim_conn_t *conn, 
-						  char *sn, char *cookie, 
-						  char *ip, 
-						  unsigned short listingfiles, 
-						  unsigned short listingtotsize, 
-						  unsigned short listingsize, 
-						  unsigned int listingchecksum, 
-						  unsigned short rendid)
-{ 
+faim_export aim_conn_t *aim_accepttransfer(aim_session_t *sess, 
+						  aim_conn_t *conn, 
+						  const char *sn, const fu8_t *cookie, 
+						  const fu8_t *ip, 
+						  fu16_t listingfiles, 
+						  fu16_t listingtotsize, 
+						  fu16_t listingsize, 
+						  fu32_t listingchecksum, 
+						  fu16_t rendid)
+{
+       return NULL;	
+#if 0
   struct command_tx_struct *newpacket, *newoft;
   struct aim_conn_t *newconn;
   struct aim_fileheader_t *fh;
@@ -547,7 +582,7 @@
 
       if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
 	newoft->lock = 0;
-	aim_tx_destroy(newoft);
+	aim_frame_destroy(newoft);
 	/* XXX: conn leak */
 	perror("calloc (1)");
 	return NULL;
@@ -609,6 +644,7 @@
   aim_tx_enqueue(sess, newpacket);
 
   return newconn;
+#endif
 }
 
 /**
@@ -621,9 +657,10 @@
  * guess.
  *
  */
-
-faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *file) 
+faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file) 
 {
+	return NULL;
+#if 0
   struct aim_fileheader_t *fh;
   u_long totsize = 0, size = 0, checksum = 0xffff0000;
   short totfiles = 0;
@@ -727,6 +764,7 @@
 
   faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
   return fh;
+#endif
 }
 
 /**
@@ -736,80 +774,580 @@
  * you need to call accept() when it's connected. returns your fd 
  *
  */
-faim_export int aim_listenestablish(u_short portnum)
+static int listenestablish(fu16_t portnum)
 {
 #if defined(__linux__)
-  /* XXX what other OS's support getaddrinfo? */
-  int listenfd;
-  const int on = 1;
-  struct addrinfo hints, *res, *ressave;
-  char serv[5];
+	/* XXX what other OS's support getaddrinfo? */
+	int listenfd;
+	const int on = 1;
+	struct addrinfo hints, *res, *ressave;
+	char serv[5];
 
-  snprintf(serv, sizeof(serv), "%d", portnum);
-  memset(&hints, 0, sizeof(struct addrinfo));
-  hints.ai_flags = AI_PASSIVE;
-  hints.ai_family = AF_UNSPEC;
-  hints.ai_socktype = SOCK_STREAM;
-  if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
-    perror("getaddrinfo");
-    return -1;
-  } 
-  ressave = res;
-  do { 
-    listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);    
-    if (listenfd < 0)
-      continue;
-    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-    if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
-      break;
-    /* success */
-    close(listenfd);
-  } while ( (res = res->ai_next) );
+	snprintf(serv, sizeof(serv), "%d", portnum);
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_flags = AI_PASSIVE;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
+		perror("getaddrinfo");
+		return -1;
+	} 
+	ressave = res;
+	do { 
+		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);    
+		if (listenfd < 0)
+			continue;
+		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
+			break;
+		/* success */
+		close(listenfd);
+	} while ( (res = res->ai_next) );
+
+	if (!res)
+		return -1;
 
-  if (!res)
-    return -1;
-  
-  if (listen(listenfd, 1024)!=0) { 
-    perror("listen");
-    return -1;
-  } 
+	if (listen(listenfd, 1024)!=0) { 
+		perror("listen");
+		return -1;
+	} 
 
-  freeaddrinfo(ressave);
-  return listenfd;
+	freeaddrinfo(ressave);
+	return listenfd;
 #else
-  int listenfd;
-  const int on = 1;
-  struct sockaddr_in sockin;
+	int listenfd;
+	const int on = 1;
+	struct sockaddr_in sockin;
 
-  if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-    perror("socket(listenfd)");
-    return -1;
-  }
+	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		perror("socket(listenfd)");
+		return -1;
+	}
 
-  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) {
-    perror("setsockopt(listenfd)");
-    close(listenfd);
-    return -1;
-  } 
-  
-  memset(&sockin, 0, sizeof(struct sockaddr_in));
-  sockin.sin_family = AF_INET;
-  sockin.sin_port = htons(portnum);
+	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) {
+		perror("setsockopt(listenfd)");
+		close(listenfd);
+		return -1;
+	} 
+
+	memset(&sockin, 0, sizeof(struct sockaddr_in));
+	sockin.sin_family = AF_INET;
+	sockin.sin_port = htons(portnum);
 
-  if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
-    perror("bind(listenfd)");
-    close(listenfd);
-    return -1;
-  }
-  if (listen(listenfd, 4) != 0) {
-    perror("listen(listenfd)");
-    close(listenfd);
-    return -1;
-  }
-  return listenfd;
+	if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
+		perror("bind(listenfd)");
+		close(listenfd);
+		return -1;
+	}
+	if (listen(listenfd, 4) != 0) {
+		perror("listen(listenfd)");
+		close(listenfd);
+		return -1;
+	}
+	return listenfd;
 #endif
 } 
 
+static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	aim_rxcallback_t userfunc;
+
+	ft = conn->priv;
+	if (ft->state == 2) {
+		/* waiting on listing data */
+		int ret = 0;
+		char *listing;
+		struct command_tx_struct *newoft;
+
+		if (!(listing = malloc(ft->fh.size)))
+			return -1;
+
+		ft->state = 0;
+		if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)	
+			faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
+
+		if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
+			faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
+			faim_mutex_unlock(&conn->active);
+			free(listing);
+			aim_conn_close(conn);
+			return -1;
+		}
+
+		memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+		newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+		/* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */
+
+		ft->fh.nrecvd = ft->fh.size;
+		ft->fh.recvcsum = ft->fh.checksum;
+		ft->fh.flags = 0;
+
+		if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+			aim_frame_destroy(newoft);
+			free(listing);
+			return -1;
+		}
+
+		if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
+			faimdprintf(sess, 2, "eek! bh fail listing\n");
+
+		/* send the 120b */
+		aim_tx_enqueue(sess, newoft);
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
+			ret = userfunc(sess, NULL, conn, ft, listing);
+
+		free(listing);
+		return ret;
+	}
+
+	if (ft->state == 3) { 
+		/* waiting on file data */
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) 
+			return userfunc(sess, NULL, conn, ft);
+		return 0;
+	}
+
+	if (ft->state == 4) {
+		if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) )
+			return userfunc(sess, NULL, conn);
+		aim_conn_close(conn);
+		return 0;
+	}	
+
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_msgcookie_t *cook;
+	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+	cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	free(conn->internal);
+
+	return;
+}
+
+static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_msgcookie_t *cook;
+	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+	cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	free(conn->internal);
+
+	return;
+}
+
+static void connclose_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	aim_msgcookie_t *cook;
+
+	cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	free(conn->internal);
+
+	return;
+}
+
+faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+		return;
+
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+		connclose_sendfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		connclose_getfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+		connclose_directim(sess, conn);
+
+	return;
+}
+
+faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+		return;
+
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+		connkill_sendfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		connkill_getfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+		connkill_directim(sess, conn);
+
+	return;
+}
+
+static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+	aim_frame_t fr;
+	aim_rxcallback_t userfunc;
+	fu32_t payloadlength;
+	fu16_t flags;
+	char *snptr = NULL;
+
+	fr.conn = conn;
+
+	payloadlength = aimutil_get32(hdr+22);
+	flags = aimutil_get16(hdr+32);
+	snptr = (char *)hdr+38;
+
+	faimdprintf(sess, 2, "faim: OFT frame: handlehdr_directim: %04x / %04x / %s\n", payloadlength, flags, snptr);
+
+	if (flags == 0x000e) { 
+		int ret = 0;
+
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr);
+
+		return ret;
+
+	} else if ((flags == 0x0000) && payloadlength) { 
+		char *msg;
+		int ret = 0;
+
+		if (!(msg = calloc(1, payloadlength+1)))
+			return -1;
+
+		if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
+			free(msg);
+			return -1;
+		}
+
+		msg[payloadlength] = '\0';
+
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
+			ret = userfunc(sess, &fr, snptr, msg);
+
+		free(msg);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_fileheader_t *fh;
+	struct aim_msgcookie_t *cook;
+	struct command_tx_struct *newoft;
+	aim_rxcallback_t userfunc;
+
+	faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
+	fh = aim_oft_getfh(hdr);
+
+	faim_mutex_unlock(&conn->active);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+
+	/* we're waaaaiiiting.. for listing.txt */
+	ft->state = 2;
+
+	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+	free(fh);
+
+	if(aim_cachecookie(sess, cook) == -1) {
+		faimdprintf(sess, 1, "error caching cookie\n");
+		return -1;
+	}     
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
+		aim_conn_close(conn);
+		return -1;
+	}
+
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+		newoft->lock = 0;
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+		newoft->lock = 0;
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+#endif
+	return -1;
+}
+
+static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_fileheader_t *fh;
+	struct aim_msgcookie_t *cook;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
+		faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n",  AIM_COOKIETYPE_OFTGET, fh->bcookie);
+
+	ft = cook->data;
+
+	if (ft->fh.size != fh->size)
+		faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size);
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
+		ret = userfunc(sess, NULL, conn, fh);
+
+	faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
+
+	free(fh);
+
+	return ret;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct aim_fileheader_t *fh;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		free(fh);
+		return -1;
+	}
+
+	free(fh);
+
+	ft = cook->data;
+
+	if (aim_cachecookie(sess, cook) == -1)
+		return -1;
+
+	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
+		return userfunc(sess, NULL, conn);
+#endif
+	return -1;
+}
+
+static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct aim_fileheader_t *fh;
+	struct command_tx_struct *newoft;
+	int i = 0;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+	free(fh);
+
+	aim_cachecookie(sess, cook);
+
+	faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
+		i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
+
+	if (i < 0)
+		return i;
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
+		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+		return -1;
+	}
+
+	newoft->lock = 1;
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	} 
+
+	/* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
+	ft->fh.nrecvd = 0;
+	ft->fh.recvcsum = 0;
+	ft->fh.flags = 0x20;
+
+	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+
+	faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
+
+	return i;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct command_tx_struct *newoft;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		free(fh);
+		return -1;
+	}
+
+	free(fh);
+
+	ft = cook->data;
+
+	ft->state = 3;
+
+	if (aim_cachecookie(sess, cook) == -1)
+		return -1;
+
+	faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+		return -1;
+	}
+
+	newoft->lock = 1;
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+
+	faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
+		return 1;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	int ret = 1;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+
+	faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
+		ret = userfunc(sess, NULL, conn, fh);
+
+	free(fh);
+
+	return ret;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
+		userfunc(sess, NULL, conn, fh);
+
+	free(fh);
+#endif
+
+	return -1;
+}
+
 /**
  * aim_get_command_rendezvous - OFT equivalent of aim_get_command
  * @sess: session to work on
@@ -818,576 +1356,76 @@
  * this reads and handles data from conn->fd. currently a little rough
  * around the edges
  */
-faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_internal int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn)
 {
-  unsigned char hdrbuf1[6];
-  unsigned char *hdr = NULL;
-  int hdrlen, hdrtype;
-  int flags = 0;
-  aim_rxcallback_t userfunc = NULL;
-  
-  if (!sess || !conn || !conn->priv)
-    return -1;
-
-  memset(hdrbuf1, 0, sizeof(hdrbuf1));
-  faim_mutex_lock(&conn->active);
-  
- /* gets locked down for the entirety */
-
-  if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE ) { 
-    struct aim_filetransfer_priv *ft;
-    ft = conn->priv;
-    if (ft->state == 2) {
-      /* waiting on listing data */
-      int ret = 0;
-      char *listing;
-      struct command_tx_struct *newoft;
-      if (!(listing = malloc(ft->fh.size))) {
-	faim_mutex_unlock(&conn->active);
-	return -1;
-      }
-
-     ft->state = 0;
-     if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)	
-       faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
-
-     if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
-       faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
-       faim_mutex_unlock(&conn->active);
-       free(listing);
-       aim_conn_close(conn);
-       return -1;
-     }
-
-     newoft->lock = 1;
-
-     memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-     newoft->hdr.oft.hdr2len = 0x100 - 8;
-     
-     /* Protocol BS - set nrecvd to size of listing, recvcsum to
-	listing checksum, flags to 0 */
-
-     ft->fh.nrecvd = ft->fh.size;
-     ft->fh.recvcsum = ft->fh.checksum;
-     ft->fh.flags = 0;
-     
-     if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-       newoft->lock = 0;
-       aim_tx_destroy(newoft);
-       free(listing);
-       faim_mutex_unlock(&conn->active);
-       return -1;
-     }
-     
-     if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
-       faimdprintf(sess, 2, "eek! bh fail listing\n");
+	fu8_t hdrbuf1[6];
+	fu8_t *hdr = NULL;
+	int hdrlen, hdrtype;
+	int ret = -1;
 
-     /* send the 120b	*/
-     newoft->lock = 0;
-     aim_tx_enqueue(sess, newoft);
-     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
-       ret = userfunc(sess, NULL, conn, ft, listing);
-     
-     faim_mutex_unlock(&conn->active);
-     free(listing);
-     return ret;
-   }
-   if (ft->state == 3) { 
-     /* waiting on file data */
-     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) )  {
-       faim_mutex_unlock(&conn->active);
-       return userfunc(sess, NULL, conn, ft);
-     }
-     faim_mutex_unlock(&conn->active);
-     return 0;
-   }
-   if(ft->state == 4) {
-     if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) ) {
-       faim_mutex_unlock(&conn->active);
-       return userfunc(sess, NULL, conn);
-     }
-     faim_mutex_unlock(&conn->active);
-     aim_conn_close(conn);
-     return 0;
-   }	
- }
- 
-  if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) {
-    faimdprintf(sess, 2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", 
-	       conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[2],hdrbuf1[3],hdrbuf1[4],hdrbuf1[5],hdrlen);
-   faim_mutex_unlock(&conn->active);
-   if (hdrlen < 0)
-     perror("read");
-   else { /* disconnected */
-     char *screenname = NULL;
-     int ret;
-     struct aim_msgcookie_t *cook;
+	if (!sess || !conn)
+		return -1;
 
-     switch(conn->subtype) { 
-     case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { 
-       struct aim_directim_priv *priv = NULL;
-       if (!(priv = (struct aim_directim_priv *)conn->priv) )
-	 return -1;
-
-       screenname = strdup(priv->sn);
-
-       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM);
-       aim_cookie_free(sess, cook);
-       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) {
-	 aim_conn_close(conn);
-	 ret = userfunc(sess, NULL, conn, screenname);
-	 free(screenname);
-	 return ret;
-       }
-       break;
-     } 
-     case AIM_CONN_SUBTYPE_OFT_GETFILE: {
-       struct aim_filetransfer_priv *priv;
-       if (!(priv = (struct aim_filetransfer_priv *)conn->priv))
-	 return -1;
-       screenname = strdup(priv->sn);
-
-       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
-
-       aim_cookie_free(sess, cook);
+	memset(hdrbuf1, 0, sizeof(hdrbuf1));
 
-       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) {
-	 aim_conn_close(conn);
-	 ret =  userfunc(sess, NULL, conn, screenname);
-	 free(screenname);
-	 return ret;
-       }
-       break;
-     }
-     case AIM_CONN_SUBTYPE_OFT_SENDFILE: {
-       struct aim_filetransfer_priv *priv;
-       if (!(priv = (struct aim_filetransfer_priv *)conn->priv))
-	 return -1;
-
-       screenname = strdup(priv->sn);
-
-       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
-       aim_cookie_free(sess, cook);
-       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) { 
-	 aim_conn_close(conn);
-	 ret = userfunc(sess, NULL, conn, screenname);
-	 free(screenname);
-	 return ret;
-       }
-       break;
-     } 
-     }
-
-     aim_conn_close(conn);
-     return -1;
-   }
- }
-
- hdrlen = aimutil_get16(hdrbuf1+4);
- hdrlen -= 6;
+	/* I guess? I didn't understand any of that mess... */
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		return getcommand_getfile(sess, conn);
 
- if (!(hdr = malloc(hdrlen))) { 
-   faim_mutex_unlock(&conn->active);
-   return -1;
- }
-
- if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
-   perror("read");
-   faimdprintf(sess, 2,"faim: rend: read2 error on %d (%d)\n", conn->fd, hdrlen);
-   free(hdr);
-   faim_mutex_unlock(&conn->active);
-   aim_conn_close(conn);
-   return -1;
- }
- hdrtype = aimutil_get16(hdr);
+	/* XXX fix all the error cases here */
+	if (aim_recv(conn->fd, hdrbuf1, 6) < 6) {
 
- switch (hdrtype) {
- case 0x0001: {    /* directim */
-   int payloadlength = 0;
-   char *snptr = NULL;
-   struct aim_directim_priv *priv;
-   int i;
-
-   if (!(priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)))) {
-     faim_mutex_unlock(&conn->active);
-     free(hdr);
-     return -1;
-   }
-   
-   payloadlength = aimutil_get32(hdr+22);
-   flags = aimutil_get16(hdr+32);
-   snptr = (char *)hdr+38;
-   strncpy(priv->sn, snptr, MAXSNLEN);
-
-   faimdprintf(sess, 2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, priv->sn);
-
-   free(hdr);
-   hdr = NULL;
+		faimdprintf(sess, 2, "faim: rend: read error (fd: %i)\n", conn->fd);
 
-   if (flags == 0x000e) { 
-     faim_mutex_unlock(&conn->active);
-     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) )
-       return userfunc(sess, NULL, conn);
-   } else {
-
-     if ((flags == 0x0000) && payloadlength) { 
-       unsigned char *msg;
-
-       if (!(msg = calloc(1, payloadlength+1))) {
-	 faim_mutex_unlock(&conn->active);
-	 return -1;
-       }
+		aim_conn_close(conn);
 
-       if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
-	 perror("read");
-	 faimdprintf(sess, 2,"faim: rend: read3 error\n");
-	 free(msg);
-	 faim_mutex_unlock(&conn->active);
-	 aim_conn_close(conn);
-	 return -1;
-       }
-
-       faim_mutex_unlock(&conn->active);
-       msg[payloadlength] = 0x00;
-       faimdprintf(sess, 2, "faim: directim: %s/%04x/%04x/%s\n", priv->sn, payloadlength, flags, msg);
-
-       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
-	 i = userfunc(sess, NULL, conn, msg);
-       else {
-	  faimdprintf(sess, 0, "directim: %s/%04x/%04x/%s\n", priv->sn, payloadlength, flags, msg);
-	 i = 1;
-       }
-
-       free(msg);
+		return -1;
+	}
 
-       return i;
-     }
-   }
-   break;
- }
- case 0x1108: { /* getfile listing.txt incoming tx->rx */
-   struct aim_filetransfer_priv *ft;
-   struct aim_fileheader_t *fh;
-   struct aim_msgcookie_t *cook;
-   struct command_tx_struct *newoft;
-
-   faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
-   fh = aim_oft_getfh(hdr);
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
+	hdrlen = aimutil_get16(hdrbuf1+4);
+	hdrlen -= 6;
 
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-     faim_mutex_unlock(&conn->active);
-     free(fh);
-     return -1;
-   }
-
-   ft = cook->data;
-
-   /* we're waaaaiiiting.. for listing.txt */
-   ft->state = 2;
-
-   memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-   free(fh);
-
-   if(aim_cachecookie(sess, cook) == -1) {
-     faimdprintf(sess, 1, "error caching cookie\n");
-     return -1;
-   }     
-
-   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
-     aim_conn_close(conn);
-     return -1;
-   }
+	hdr = malloc(hdrlen);
 
-   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-   newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-   if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-     newoft->lock = 0;
-     aim_tx_destroy(newoft);
-     return -1;
-   }
-
-   if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
-     newoft->lock = 0;
-     aim_tx_destroy(newoft);
-     return -1;
-   }
-
-   newoft->lock = 0;
-   aim_tx_enqueue(sess, newoft);
-   break;
-   
- } 
- case 0x1209: { /* get file listing ack rx->tx */
-   struct aim_filetransfer_priv *ft;
-   struct aim_fileheader_t *fh;
-   struct aim_msgcookie_t *cook;
-   int ret = 0;
-
-   if(!(fh = aim_oft_getfh(hdr))) {
-     perror("getfh");
-     free(hdr);
-     return -1;
-   }   
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
-
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
-     faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", 
-		 AIM_COOKIETYPE_OFTGET, fh->bcookie);
-
-   ft = cook->data;   
-
-   if (ft->fh.size != fh->size)
-     faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", 
-		 ft->fh.size, fh->size);
-   
-   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
-     ret = userfunc(sess, NULL, conn, fh);
-
-   faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
-
-   free(fh);
-
-   return ret;
-
-   break;
- }
- case 0x120b: {    /* getfile listing.txt rx confirm */
-   struct aim_filetransfer_priv *ft;
-   struct aim_msgcookie_t *cook;
-   struct aim_fileheader_t *fh;
-
-   fh = aim_oft_getfh(hdr);
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
+	if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
+		faimdprintf(sess, 2, "faim: rend: read2 error on %d (%d)\n", conn->fd, hdrlen);
+		free(hdr);
+		aim_conn_close(conn);
+		return -1;
+	}
 
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))     {
-     free(fh);
-     return -1;
-   }
-
-   free(fh);
-
-   ft = cook->data;
-
-   if (aim_cachecookie(sess, cook) == -1) {
-     return -1;
-   }
-
-   if((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
-     return userfunc(sess, NULL, conn);
-
-   break;
- }
- case 0x120c: { /* getfile file request */
-   struct aim_filetransfer_priv *ft;
-   struct aim_msgcookie_t *cook;
-   struct aim_fileheader_t *fh;
-   struct command_tx_struct *newoft;
-   int i = 0;
-
-   fh = aim_oft_getfh(hdr);
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
-
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-     faimdprintf(sess, 2, "no cookie in 120c\n");
-     return -1;
-   }
-
-   ft = cook->data;
-   memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-   free(fh);
-
-   aim_cachecookie(sess, cook);
-
-   faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
-
-   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
-     i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
-
-   if (i < 0)
-     return i;
-
-   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
-     faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
-     return -1;
-   }
-
-   newoft->lock = 1;
-   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-   newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-   if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
-     newoft->lock = 0;
-     aim_tx_destroy(newoft);
-     return -1;
-   } 
-
-   /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
-   ft->fh.nrecvd = 0;
-   ft->fh.recvcsum = 0;
-   ft->fh.flags = 0x20;
-
-   aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+	hdrtype = aimutil_get16(hdr);
 
-   newoft->lock = 0;
-   aim_tx_enqueue(sess, newoft);
-
-   faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
-
-   return i;
-
-   break;
- }
- case 0x0101: {    /* getfile: sending data  */
-   struct aim_fileheader_t *fh;
-   struct aim_filetransfer_priv *ft;
-   struct aim_msgcookie_t *cook;
-   struct command_tx_struct *newoft;
-
-   fh = aim_oft_getfh(hdr);
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
-
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-     free(fh);
-     return -1;
-   }
-   free(fh);
-
-   ft = cook->data;
-
-   ft->state = 3;
-
-   if (aim_cachecookie(sess, cook) == -1) {
-     perror("aim_cachecookie");
-     return -1;
-   }
-
-   faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
-
-   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
-     aim_conn_close(conn);
-     faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
-     return -1;
-   }
-
-   newoft->lock = 1;
-   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-
-   newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-   if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
-     newoft->lock = 0;
-     aim_tx_destroy(newoft);
-     return -1;
-   }
-
-   aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
-
-   newoft->lock = 0;
-   aim_tx_enqueue(sess, newoft);
-
-   faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
-
-   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
-     return 1;
+	if (hdrtype == 0x0001)
+		ret = handlehdr_directim(sess, conn, hdr);
+	else if (hdrtype == 0x1108) /* getfile listing.txt incoming tx->rx */
+		ret = handlehdr_getfile_listing(sess, conn, hdr);
+	else if (hdrtype == 0x1209) /* get file listing ack rx->tx */
+		ret = handlehdr_getfile_listing2(sess, conn, hdr);
+	else if (hdrtype == 0x120b) /* get file listing rx confirm */
+		ret = handlehdr_getfile_listing3(sess, conn, hdr);
+	else if (hdrtype == 0x120c) /* getfile request */
+		ret = handlehdr_getfile_request(sess, conn, hdr);
+	else if (hdrtype == 0x0101) /* getfile sending data */
+		ret = handlehdr_getfile_sending(sess, conn, hdr);
+	else if (hdrtype == 0x0202) /* getfile recv data */
+		ret = handlehdr_getfile_recv(sess, conn, hdr);
+	else if (hdrtype == 0x0204) /* getfile finished */
+		ret = handlehdr_getfile_finish(sess, conn, hdr);
+	else {
+		faimdprintf(sess, 2,"faim: OFT frame: uknown type %04x\n", hdrtype);
+		ret = -1;
+	}
+	
+	free(hdr);
 
-   break;
- }
- case 0x0202: {    /* get file: ready to receive data */
-   struct aim_fileheader_t *fh;
-   struct aim_filetransfer_priv *ft;
-   struct aim_msgcookie_t *cook;
-   int ret = 1;
-
-   fh = aim_oft_getfh(hdr);
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
-
-   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-     free(fh);
-     return -1;
-   }
-   
-   ft = cook->data;
-
-   faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
-
-   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
-     ret = userfunc(sess, NULL, conn, fh);
-
-   free(fh);
-
-   return ret;
-   break;
- }
- case 0x0204: {    /* get file: finished. close it up */
-   int i;
-   struct aim_fileheader_t *fh;
+	if (ret == -1)
+		aim_conn_close(conn);
 
-   if(!(fh = aim_oft_getfh(hdr)))
-     return -1;
-
-   free(hdr);
-   hdr = NULL;
-
-   faim_mutex_unlock(&conn->active);
-
-   faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
-
-   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
-     i = userfunc(sess, NULL, conn, fh);
-   else 
-     i = 1;
-
-   if (conn)
-     aim_conn_close(conn);
-
-   free(fh);
+	return ret;
+}
 
-   return i;
-   break;
- }
- default: {
-   free(hdr);
-   hdr = NULL;
-   faimdprintf(sess, 2,"faim: OFT frame: uknown type %04x\n", hdrtype);
-   faim_mutex_unlock(&conn->active);
-   break;
- } 
- } /* switch */
-
- if (hdr) {
-    faimdprintf(sess, 0, "hdr wasn't freed by a rendezvous switch case (hdrtype: %0x04x)!\n", hdrtype);
-   free(hdr);
-   hdr = NULL;
- }
- return 0;
-}
- 
+#if 0
 /**
  * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
  * @hdr: buffer to extract header from  
@@ -1458,6 +1496,7 @@
   i += 64;
   return fh;
 } 
+#endif
 
 /**
  * aim_oft_checksum - calculate oft checksum of buffer
@@ -1477,10 +1516,13 @@
  * Also, it's been said that this is incorrect as currently
  * written. You were warned.
  */
-faim_export int aim_oft_checksum(struct aim_session_t *sess, char *buffer, int bufsize, int *checksum)
+faim_export fu32_t aim_oft_checksum(aim_session_t *sess, const char *buffer, int bufsize, fu32_t *checksum)
 {
-  short check0, check1;
+	return 0xdeadbeef;
+#if 0
+  fu16_t check0, check1;
   int i;
+
   check0 = ((*checksum & 0xFF000000) >> 16);
   check1 = ((*checksum & 0x00ff0000) >> 16);
   for(i = 0; i < bufsize; i++) {
@@ -1524,8 +1566,10 @@
 
   *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
   return *checksum;
+#endif
 } 
 
+#if 0
 /**
  * aim_oft_buildheader - fills a buffer with network-order fh data
  * @dest: buffer to fill -- pre-alloced
@@ -1535,7 +1579,7 @@
  * DOES NOT DO BOUNDS CHECKING!
  *
  */
-faim_internal int aim_oft_buildheader(unsigned char *dest,struct aim_fileheader_t *fh) 
+static int oft_buildheader(unsigned char *dest, struct aim_fileheader_t *fh) 
 { 
   int i, curbyte;
   if (!dest || !fh)
@@ -1577,26 +1621,7 @@
   curbyte += 64;
   return curbyte;
 }
-
-
-/**
- * aim_tx_destroy - free's tx_command_t's
- * @command: the command to free  
- *
- * if command is locked, doesn't free.
- * returns -1 on error (locked struct); 0 on success.  
- *
- */
-faim_internal int aim_tx_destroy(struct command_tx_struct *command){
-  if (command->lock)
-    return -1;
-  if (command->data)
-    free(command->data);
-  if (command->hdrtype == AIM_FRAMETYPE_OFT && command->hdr.oft.hdr2)
-    free(command->hdr.oft.hdr2);
-  free(command);
-  return 0;
-} 
+#endif
 
 /**
  * aim_getfile_intitiate - Request an OFT getfile session
@@ -1606,8 +1631,10 @@
  * 
  * returns a new &aim_conn_t on success, %NULL on error
  */
-faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn)
+faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn)
 { 
+	return NULL;
+#if 0
   struct command_tx_struct *newpacket;
   struct aim_conn_t *newconn;
   struct aim_filetransfer_priv *priv;
@@ -1745,6 +1772,7 @@
   faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
 
   return newconn;
+#endif
 }
  
 /**
@@ -1757,8 +1785,10 @@
  *
  * returns -1 on error, 0 on successful enqueuing
  */
-faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size)
+faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size)
 {
+	return -EINVAL;
+#if 0
   struct command_tx_struct *newoft;
   struct aim_filetransfer_priv *ft;
   if (!sess || !conn || !conn->priv || !name)
@@ -1787,13 +1817,13 @@
 
   if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
     newoft->lock = 0;
-    aim_tx_destroy(newoft);
+    aim_frame_destroy(newoft);
     return -1;
   }
 
   if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
     newoft->lock = 0;
-    aim_tx_destroy(newoft);
+    aim_frame_destroy(newoft);
     return -1;
   }
 
@@ -1801,6 +1831,7 @@
 
   aim_tx_enqueue(sess, newoft);
   return 0;
+#endif
 }
  
 /**
@@ -1812,8 +1843,10 @@
  * filetransfer. Returns -1 on error, 0 on apparent success
  *
  */
-faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn) 
+faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn) 
 {
+	return -EINVAL;
+#if 0
   struct command_tx_struct *newoft;
   struct aim_filetransfer_priv *ft;
 
@@ -1832,7 +1865,7 @@
 
  if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
    newoft->lock = 0;
-   aim_tx_destroy(newoft);
+   aim_frame_destroy(newoft);
    return -1;
  }
 
@@ -1840,13 +1873,14 @@
 
  if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
    newoft->lock = 0;
-   aim_tx_destroy(newoft);
+   aim_frame_destroy(newoft);
    return -1;
  }
 
  newoft->lock = 0;
  aim_tx_enqueue(sess, newoft);
  return 0;
+#endif
 }
  
 /**
@@ -1857,8 +1891,10 @@
  * call this before you close the getfile connection if you're on the
  * receiving/requesting end.
  */
-faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn)
 {
+	return -EINVAL;
+#if 0
   struct command_tx_struct *newoft;
   struct aim_filetransfer_priv *ft;
   
@@ -1877,7 +1913,7 @@
   
   if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
     newoft->lock = 0;
-    aim_tx_destroy(newoft);
+    aim_frame_destroy(newoft);
     return -1;
   }
   
@@ -1889,7 +1925,7 @@
   
   if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
     newoft->lock = 0;
-    aim_tx_destroy(newoft);
+    aim_frame_destroy(newoft);
     return -1;
   }
   
@@ -1897,4 +1933,6 @@
   aim_tx_enqueue(sess, newoft);
   
   return 0;
+#endif /* 0 */
 }
+
--- a/src/protocols/oscar/im.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/im.c	Sun Sep 09 10:07:14 2001 +0000
@@ -38,29 +38,33 @@
  *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
  *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
  *                                      TOC client
+ *
+ * Note that in this function, only the feature bytes are tested, since
+ * the rest will always be the same.
+ *
  */
-faim_export unsigned short aim_fingerprintclient(unsigned char *msghdr, int len)
+faim_export fu16_t aim_fingerprintclient(fu8_t *msghdr, int len)
 {
 	static const struct {
-		unsigned short clientid;
+		fu16_t clientid;
 		int len;
-		unsigned char data[10];
+		fu8_t data[10];
 	} fingerprints[] = {
 		/* AOL Mobile Communicator, WinAIM 1.0.414 */
 		{ AIM_CLIENTTYPE_MC, 
-		  9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01}},
+		  3, {0x01, 0x01, 0x01}},
 
 		/* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
 		{ AIM_CLIENTTYPE_WINAIM, 
-		  9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01}},
+		  3, {0x01, 0x01, 0x02}},
 
 		/* WinAIM 4.1.2010, libfaim */
 		{ AIM_CLIENTTYPE_WINAIM41,
-		 10, {0x05, 0x01, 0x00, 0x04, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01}},
+		  4, {0x01, 0x01, 0x01, 0x02}},
 
 		/* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
 		{ AIM_CLIENTTYPE_AOL_TOC,
-		  7, {0x05, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01}},
+		  1, {0x01}},
 
 		{ 0, 0}
 	};
@@ -80,9 +84,9 @@
 }
 
 /* This should be endian-safe now... but who knows... */
-faim_export unsigned short aim_iconsum(const unsigned char *buf, int buflen)
+faim_export fu32_t aim_iconsum(const fu8_t *buf, int buflen)
 {
-	unsigned long sum;
+	fu32_t sum;
 	int i;
 
 	for (i = 0, sum = 0; i < buflen; i += 2)
@@ -90,7 +94,7 @@
 
 	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
 
-	return sum & 0xffff;
+	return sum;
 }
 
 /*
@@ -128,14 +132,26 @@
  * 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 support multipart
+ *
  */
-faim_export int aim_send_im_ext(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_sendimext_args *args)
+faim_export int aim_send_im_ext(aim_session_t *sess, aim_conn_t *conn, struct aim_sendimext_args *args)
 {
-	int curbyte,i;
-	struct command_tx_struct *newpacket;
+	static const fu8_t deffeatures[] = {
+		0x01, 0x01, 0x01, 0x02, 0x42,
+	};
+	int i, msgtlvlen;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
 	if (!sess || !conn || !args)
-	       	return -EINVAL;
+		return -EINVAL;
 
 	if (!args->msg || (args->msglen <= 0))
 		return -EINVAL;
@@ -143,14 +159,18 @@
 	if (args->msglen >= MAXMSGLEN)
 		return -E2BIG;
 
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, args->msglen+512)))
+	msgtlvlen = 12 + args->msglen;
+	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
+		msgtlvlen += args->featureslen;
+	else
+		msgtlvlen += sizeof(deffeatures);
+		
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, args->msglen+512)))
 		return -ENOMEM;
 
-	newpacket->lock = 1; /* lock struct */
-
-	curbyte  = 0;
-	curbyte += aim_putsnac(newpacket->data+curbyte, 
-				0x0004, 0x0006, 0x0000, sess->snac_nextid);
+	/* 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 a random message cookie 
@@ -161,113 +181,103 @@
 	 * SNAC ID.
 	 *
 	 */
-	for (i = 0; i < 8; i++) {
-		curbyte += aimutil_put8(newpacket->data+curbyte, 
-					(unsigned char) rand());
-	}
+	for (i = 0; i < 8; i++)
+		aimbs_put8(&fr->data, (fu8_t) rand());
 
 	/*
 	 * Channel ID
 	 */
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+	aimbs_put16(&fr->data, 0x0001);
 
 	/*
 	 * Destination SN (prepended with byte length)
 	 */
-	curbyte += aimutil_put8(newpacket->data+curbyte, strlen(args->destsn));
-	curbyte += aimutil_putstr(newpacket->data+curbyte, 
-					args->destsn, strlen(args->destsn));
+	aimbs_put8(&fr->data, strlen(args->destsn));
+	aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
 
 	/*
 	 * metaTLV start.
 	 */
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-	curbyte += aimutil_put16(newpacket->data+curbyte, args->msglen + 0x10);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, msgtlvlen);
 
 	/*
-	 * Flag data / ICBM Parameters?
-	 *
-	 * I don't know what these are...
+	 * Features 
 	 *
 	 */
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x05);
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
+	aimbs_put8(&fr->data, 0x05);
+	aimbs_put8(&fr->data, 0x01);
 
-	/* number of bytes to follow */
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-	curbyte += aimutil_put8(newpacket->data+curbyte, 0x02);
+	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));
+	}
 
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101);
+	aimbs_put16(&fr->data, 0x0101);
 
 	/* 
 	 * Message block length.
 	 */
-	curbyte += aimutil_put16(newpacket->data+curbyte, args->msglen + 0x04);
+	aimbs_put16(&fr->data, args->msglen + 0x04);
 
 	/*
 	 * Character set.
 	 */
 	if (args->flags & AIM_IMFLAGS_UNICODE)
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+		aimbs_put16(&fr->data, 0x0002);
 	else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+		aimbs_put16(&fr->data, 0x0003);
 	else
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+		aimbs_put16(&fr->data, 0x0000);
 
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
 
 	/*
 	 * Message.  Not terminated.
 	 */
-	curbyte += aimutil_putstr(newpacket->data+curbyte, 
-					args->msg, args->msglen);
+	aimbs_putraw(&fr->data, args->msg, args->msglen);
 
 	/*
 	 * Set the Request Acknowledge flag.  
 	 */
 	if (args->flags & AIM_IMFLAGS_ACK) {
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0003);
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
+		aimbs_put16(&fr->data, 0x0003);
+		aimbs_put16(&fr->data, 0x0000);
 	}
-  
+
 	/*
 	 * Set the Autoresponse flag.
 	 */
 	if (args->flags & AIM_IMFLAGS_AWAY) {
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0004);
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
+		aimbs_put16(&fr->data, 0x0004);
+		aimbs_put16(&fr->data, 0x0000);
 	}
 
 	/*
 	 * Set the Buddy Icon Requested flag.
 	 */
 	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0009);
-		curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
+		aimbs_put16(&fr->data, 0x0009);
+		aimbs_put16(&fr->data, 0x0000);
 	}
 
 	/*
-	 * Set the I HAVE A REALLY PURTY ICON flag (with timestamp).
+	 * Set the I HAVE A REALLY PURTY ICON flag.
 	 */
 	if (args->flags & AIM_IMFLAGS_HASICON) {
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0008);
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
-		curbyte += aimutil_put32(newpacket->data+curbyte, args->iconlen);
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-		curbyte += aimutil_put16(newpacket->data+curbyte, args->iconsum);
-		curbyte += aimutil_put32(newpacket->data+curbyte, args->iconstamp);
+		aimbs_put16(&fr->data, 0x0008);
+		aimbs_put16(&fr->data, 0x000c);
+		aimbs_put32(&fr->data, args->iconlen);
+		aimbs_put32(&fr->data, args->iconsum);
+		aimbs_put32(&fr->data, args->iconstamp);
 	}
 
-	newpacket->commandlen = curbyte;
-	newpacket->lock = 0;
-
-	aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
 #if 1 /* XXX do this with autoconf or something... */
-	aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
 	aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
 #endif
 
@@ -284,7 +294,7 @@
  * that requires an explicit message length.  Use aim_send_im_ext().
  *
  */
-faim_export int aim_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, const char *destsn, unsigned short flags, const char *msg)
+faim_export int aim_send_im(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu16_t flags, const char *msg)
 {
 	struct aim_sendimext_args args;
 
@@ -293,158 +303,164 @@
 	args.msg = msg;
 	args.msglen = strlen(msg);
 
+	/* Make these don't get set by accident -- they need aim_send_im_ext */
+	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON);
+
 	return aim_send_im_ext(sess, conn, &args);
 }
 
-faim_export int aim_send_icon(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn, const unsigned char *icon, int iconlen, time_t stamp, unsigned short iconsum)
+/*
+ * This is also performance sensative. (If you can believe it...)
+ *
+ */
+faim_export int aim_send_icon(aim_session_t *sess, aim_conn_t *conn, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu32_t iconsum)
 {
-	struct command_tx_struct *np;
-	int i, curbyte = 0;
-	unsigned char ck[8];
+	int i;
+	fu8_t ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-	if (!sess || !conn || !sn || !icon || 
-			(iconlen <= 0) || (iconlen >= MAXICONLEN))
-	return -EINVAL;
+	if (!sess || !conn || !sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
+		return -EINVAL;
 
 	if (conn->type != AIM_CONN_TYPE_BOS)
 		return -EINVAL;
 
-	for (i = 0, curbyte = 0; i < 8; i++)
-		curbyte += aimutil_put8(ck+curbyte, (u_char)rand());
+	for (i = 0; i < 8; i++)
+		aimutil_put8(ck+i, (fu8_t) rand());
 
-	if (!(np = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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)))
+	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;
 
-	np->lock = 1;
-
-	curbyte = aim_putsnac(np->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
 	/*
 	 * Cookie
 	 */
-	memcpy(np->data+curbyte, ck, 8);
-	curbyte += 8;
+	aimbs_putraw(&fr->data, ck, 8);
 
 	/*
 	 * Channel (2)
 	 */
-	curbyte += aimutil_put16(np->data+curbyte, 0x0002);
+	aimbs_put16(&fr->data, 0x0002);
 
 	/*
 	 * Dest sn
 	 */
-	curbyte += aimutil_put8(np->data+curbyte, strlen(sn));
-	curbyte += aimutil_putstr(np->data+curbyte, sn, strlen(sn));
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
 	/*
 	 * TLV t(0005)
+	 *
+	 * Encompasses everything below.
 	 */
-	curbyte += aimutil_put16(np->data+curbyte, 0x0005);
-	curbyte += aimutil_put16(np->data+curbyte, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
 
-	curbyte += aimutil_put16(np->data+curbyte, 0x0000);
-
-	memcpy(np->data+curbyte, ck, 8);
-	curbyte += 8;
-
-	curbyte += aim_putcap(np->data+curbyte, 16, AIM_CAPS_BUDDYICON);
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, ck, 8);
+	aim_putcap(&fr->data, AIM_CAPS_BUDDYICON);
 
 	/* TLV t(000a) */
-	curbyte += aimutil_put16(np->data+curbyte, 0x000a);
-	curbyte += aimutil_put16(np->data+curbyte, 0x0002);
-	curbyte += aimutil_put16(np->data+curbyte, 0x0001);
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
 
 	/* TLV t(000f) */
-	curbyte += aimutil_put16(np->data+curbyte, 0x000f);
-	curbyte += aimutil_put16(np->data+curbyte, 0x0000);
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
 
 	/* TLV t(2711) */
-	curbyte += aimutil_put16(np->data+curbyte, 0x2711);
-	curbyte += aimutil_put16(np->data+curbyte, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
-	curbyte += aimutil_put16(np->data+curbyte, 0x0000);
-	curbyte += aimutil_put16(np->data+curbyte, iconsum);
-	curbyte += aimutil_put32(np->data+curbyte, iconlen);
-	curbyte += aimutil_put32(np->data+curbyte, stamp);
-	memcpy(np->data+curbyte, icon, iconlen);
-	curbyte += iconlen;
-	memcpy(np->data+curbyte, AIM_ICONIDENT, strlen(AIM_ICONIDENT));
-	curbyte += strlen(AIM_ICONIDENT);
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
+	aimbs_put32(&fr->data, iconsum);
+	aimbs_put32(&fr->data, iconlen);
+	aimbs_put32(&fr->data, stamp);
+	aimbs_putraw(&fr->data, icon, iconlen);
+	aimbs_putraw(&fr->data, AIM_ICONIDENT, strlen(AIM_ICONIDENT));
 
 	/* TLV t(0003) */
-	curbyte += aimutil_put16(np->data+curbyte, 0x0003);
-	curbyte += aimutil_put16(np->data+curbyte, 0x0000);
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
 
-	np->commandlen = curbyte;
-	np->lock = 0;
-	aim_tx_enqueue(sess, np);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
 
-static int outgoingim(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-	unsigned int i, ret = 0;
+	int i, ret = 0;
 	aim_rxcallback_t userfunc;
-	unsigned char cookie[8];
-	int channel;
-	struct aim_tlvlist_t *tlvlist;
-	char sn[MAXSNLEN];
-	unsigned short icbmflags = 0;
-	unsigned char flag1 = 0, flag2 = 0;
-	unsigned char *msgblock = NULL, *msg = NULL;
+	fu8_t cookie[8];
+	fu16_t channel;
+	aim_tlvlist_t *tlvlist;
+	char *sn;
+	int snlen;
+	fu16_t icbmflags = 0;
+	fu8_t flag1 = 0, flag2 = 0;
+	fu8_t *msg = NULL;
+	aim_tlv_t *msgblock;
 
 	/* ICBM Cookie. */
 	for (i = 0; i < 8; i++)
-		cookie[i] = aimutil_get8(data+i);
+		cookie[i] = aimbs_get8(bs);
 
 	/* Channel ID */
-	channel = aimutil_get16(data+i);
-	i += 2;
+	channel = aimbs_get16(bs);
 
 	if (channel != 0x01) {
 		faimdprintf(sess, 0, "icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
-		return 1;
+		return 0;
 	}
 
-	strncpy(sn, (char *) data+i+1, (int) *(data+i));
-	i += 1 + (int) *(data+i);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
 
-	tlvlist = aim_readtlvchain(data+i, datalen-i);
+	tlvlist = aim_readtlvchain(bs);
 
 	if (aim_gettlv(tlvlist, 0x0003, 1))
 		icbmflags |= AIM_IMFLAGS_ACK;
 	if (aim_gettlv(tlvlist, 0x0004, 1))
 		icbmflags |= AIM_IMFLAGS_AWAY;
 
-	if ((msgblock = (unsigned char *)aim_gettlv_str(tlvlist, 0x0002, 1))) {
-		int j = 0;
+	if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) {
+		aim_bstream_t mbs;
+		int featurelen, msglen;
 
-		/* no, this really is correct.  I'm not high or anything either. */
-		j += 2;
-		j += 2 + aimutil_get16(msgblock+j);
-		j += 2;
+		aim_bstream_init(&mbs, msgblock->value, msgblock->length);
 
-		j += 2; /* final block length */
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
+			aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
 
-		flag1 = aimutil_get16(msgblock);
-		j += 2;
-		flag2 = aimutil_get16(msgblock);
-		j += 2;
+		msglen = aimbs_get16(&mbs) - 4; /* final block length */
 
-		msg = msgblock+j;
+		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(msgblock);
+	free(sn);
 	aim_freetlvchain(&tlvlist);
 
 	return ret;
 }
 
 /*
- * A multipart IM:
+ *
+ * This should use tlvlists, but doesn't for performance reasons.
+ *
+ * XXX support multipart IMs:
  *
  * 0004 0007 0000 8f08 d295 
  *      0031 6520 3b7b f9fd
@@ -464,12 +480,13 @@
  * 		0101 000b 0000 0000 3c2f 4854 4d4c 3e   another ASCII part
  *
  */
-static int incomingim_ch1(struct aim_session_t *sess, aim_module_t *mod,  struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned short channel, struct aim_userinfo_s *userinfo, unsigned char *data, int datalen, unsigned char *cookie)
+static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, struct aim_userinfo_s *userinfo, aim_bstream_t *bs, fu8_t *cookie)
 {
-	unsigned short type, length;
+	fu16_t type, length;
 	aim_rxcallback_t userfunc;
-	int i, ret = 0;
+	int ret = 0;
 	struct aim_incomingim_ch1_args args;
+	int endpos;
 
 	memset(&args, 0, sizeof(args));
 
@@ -478,54 +495,41 @@
 	 * I've changed it to process the TLVs in-place.  This avoids lots
 	 * of per-IM memory allocations.
 	 */
-	for (i = 0; i < datalen; ) {
+	while (aim_bstream_empty(bs)) {
 
-		type = aimutil_get16(data+i);
-		i += 2;
-		      
-		length = aimutil_get16(data+i);
-		i += 2;
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		endpos = aim_bstream_curpos(bs) + length;
 
 		if (type == 0x0002) { /* Message Block */
-			unsigned short wastebits;
-			unsigned char *msgblock;
-			int j = 0, y = 0, z = 0;
 
-			msgblock = data+i;
-			      
 			/*
-			 * Extracting the message from the unknown cruft.
-			 * 
-			 * This is a bit messy, and I'm not really qualified,
-			 * even as the author, to comment on it.  At least
-			 * its not as bad as a while loop shooting into 
-			 * infinity.
-			 *
-			 * "Do you believe in magic?"
+			 * This TLV consists of the following:
+			 *   - 0501 -- Unknown
+			 *   - Features: Don't know how to interpret these
+			 *   - 0101 -- Unknown
+			 *   - Message
 			 *
 			 */
 
-			wastebits = aimutil_get8(msgblock+j++);
-			wastebits = aimutil_get8(msgblock+j++);
-			      
-			y = aimutil_get16(msgblock+j);
-			j += 2;
-			for (z = 0; z < y; z++)
-				wastebits = aimutil_get8(msgblock+j++);
-			wastebits = aimutil_get8(msgblock+j++);
-			wastebits = aimutil_get8(msgblock+j++);
+			aimbs_get8(bs); /* 05 */
+			aimbs_get8(bs); /* 01 */
 
-			args.finlen = j;
-			if (args.finlen > sizeof(args.fingerprint))
-				args.finlen = sizeof(args.fingerprint);
-			memcpy(args.fingerprint, msgblock, args.finlen);
+			args.featureslen = aimbs_get16(bs);
+			/* XXX XXX this is all evil! */
+			args.features = bs->data + bs->offset;
+			aim_bstream_advance(bs, args.featureslen);
+			args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
+
+			aimbs_get8(bs); /* 01 */
+			aimbs_get8(bs); /* 01 */
 
 			/* Message string length, including flag words. */
-			args.msglen = aimutil_get16(msgblock+j);
-			j += 2;
+			args.msglen = aimbs_get16(bs);
 
 			/* Flag words. */
-			args.flag1 = aimutil_get16(msgblock+j);
+			args.flag1 = aimbs_get16(bs);
 			if (args.flag1 == 0x0000)
 				; /* ASCII */
 			else if (args.flag1 == 0x0002)
@@ -534,17 +538,16 @@
 				args.icbmflags |= AIM_IMFLAGS_ISO_8859_1;
 			else if (args.flag1 == 0xffff)
 				; /* no encoding (yeep!) */
-			j += 2;
 
-			args.flag2 = aimutil_get16(msgblock+j);
+			args.flag2 = aimbs_get16(bs);
 			if (args.flag2 == 0x0000)
 				; /* standard subencoding? */
 			else if (args.flag2 == 0x000b)
 				args.icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
 			else if (args.flag2 == 0xffff)
 				; /* no subencoding */
-			j += 2;
-			
+
+			/* XXX this isn't really necesary... */	
 			if (	((args.flag1 != 0x0000) &&
 				 (args.flag1 != 0x0002) &&
 				 (args.flag1 != 0x0003) &&
@@ -555,18 +558,22 @@
 				faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
 			}
 
-			/* Message string. */
+			/* Message. */
 			args.msglen -= 4;
 			if (args.icbmflags & AIM_IMFLAGS_UNICODE) {
+				fu8_t *umsg;
+
+				/* Can't use getstr because of wide null */
+				umsg = aimbs_getraw(bs, args.msglen);
 				args.msg = malloc(args.msglen+2);
-				memcpy(args.msg, msgblock+j, args.msglen);
+				memcpy(args.msg, umsg, args.msglen);
 				args.msg[args.msglen] = '\0'; /* wide NULL */
 				args.msg[args.msglen+1] = '\0';
-			} else {
-				args.msg = malloc(args.msglen+1);
-				memcpy(args.msg, msgblock+j, args.msglen);
-				args.msg[args.msglen] = '\0';
-			}
+
+				free(umsg);
+
+			} else
+				args.msg = aimbs_getstr(bs, args.msglen);
 
 		} else if (type == 0x0003) { /* Server Ack Requested */
 
@@ -576,11 +583,11 @@
 
 			args.icbmflags |= AIM_IMFLAGS_AWAY;
 
-		} else if ((type == 0x0008) && (length == 0x000c)) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
+		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
 
-			args.iconchecksum = aimutil_get32(data+i);
-			args.iconlength = aimutil_get32(data+i+4);
-			args.iconstamp = aimutil_get32(data+i+8);
+			args.iconsum = aimbs_get32(bs);
+			args.iconlen = aimbs_get32(bs);
+			args.iconstamp = aimbs_get32(bs);
 			args.icbmflags |= AIM_IMFLAGS_HASICON;
 
 		} else if (type == 0x0009) {
@@ -590,31 +597,351 @@
 		} else if (type == 0x0017) {
 
 			args.extdatalen = length;
-			args.extdata = data+i;
+			args.extdata = aimbs_getraw(bs, args.extdatalen);
 
 		} else {
 			faimdprintf(sess, 0, "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
 		}
 
-		i += length;
+		/*
+		 * This is here to protect ourselves from ourselves.  That
+		 * is, if something above doesn't completly 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);
 
+	free(args.extdata);
 	free(args.msg);
 
 	return ret;
 }
 
-static int incomingim_ch2(struct aim_session_t *sess, aim_module_t *mod,  struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned short channel, struct aim_userinfo_s *userinfo, struct aim_tlvlist_t *tlvlist, unsigned char *cookie)
+static int incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	aim_tlv_t *tse;
+	aim_bstream_t tbs;
+
+	if (args->status != 0x0000)
+		return 1; /* ignore it -- not sure what it means */
+
+	tse = aim_gettlv(list2, 0x2711, 1);
+	aim_bstream_init(&tbs, tse->value, tse->length);
+
+	/*
+	 * 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 (aim_bstream_empty(&tbs)) {
+		fu16_t gnlen, numb;
+		int i;
+		char *gn;
+
+		gnlen = aimbs_get16(&tbs);
+		gn = aimbs_getstr(&tbs, gnlen);
+		numb = aimbs_get16(&tbs);
+
+		for (i = 0; i < numb; i++) {
+			fu16_t bnlen;
+			char *bn;
+
+			bnlen = aimbs_get16(&tbs);
+			bn = aimbs_getstr(&tbs, bnlen);
+
+			faimdprintf(sess, 0, "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
+
+			free(bn);
+		}
+
+		free(gn);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+	return ret;
+}
+
+static int incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
 {
 	aim_rxcallback_t userfunc;
-	struct aim_tlv_t *block1;
-	struct aim_tlvlist_t *list2;
+	int ret = 0;
+	aim_tlv_t *miscinfo;
+	aim_bstream_t tbs;
+
+	miscinfo = aim_gettlv(list2, 0x2711, 1);
+	aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
+
+	args->info.icon.checksum = aimbs_get32(&tbs);
+	args->info.icon.length = aimbs_get32(&tbs);
+	args->info.icon.timestamp = aimbs_get32(&tbs);
+	args->info.icon.icon = aimbs_getraw(&tbs, args->info.icon.length);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+	free(args->info.icon.icon);
+
+	return ret;
+}
+
+static int incomingim_ch2_voice(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+	aim_msgcookie_t *cachedcook;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	faimdprintf(sess, 1, "rend: voice!\n");
+
+	if (!(cachedcook = (aim_msgcookie_t*)calloc(1, sizeof(aim_msgcookie_t))))
+		return 0;
+
+	memcpy(cachedcook->cookie, args->cookie, 8);
+	cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
+	cachedcook->data = NULL;
+
+	if (aim_cachecookie(sess, cachedcook) == -1)
+		faimdprintf(sess, 0, "ERROR caching message cookie\n");
+
+	/* XXX: implement all this */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) 
+		ret = userfunc(sess, rx, 0x0002, userinfo, &args);
+
+	return ret;
+}
+
+static int incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+	aim_tlv_t *miscinfo;
+	aim_bstream_t tbs;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+
+	miscinfo = aim_gettlv(list2, 0x2711, 1);
+	aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
+
+	aim_chat_readroominfo(&tbs, &args->info.chat.roominfo);
+		  
+	if (aim_gettlv(list2, 0x000c, 1))
+		args->info.chat.msg = aim_gettlv_str(list2, 0x000c, 1);
+	
+	if (aim_gettlv(list2, 0x000d, 1))
+		args->info.chat.encoding = aim_gettlv_str(list2, 0x000d, 1);
+	
+	if (aim_gettlv(list2, 0x000e, 1))
+		args->info.chat.lang = aim_gettlv_str(list2, 0x000e, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, &args);
+
+	/* XXX free_roominfo */
+	free(args->info.chat.roominfo.name);
+	free(args->info.chat.msg);
+	free(args->info.chat.encoding);
+	free(args->info.chat.lang);
+
+	return ret;
+}
+
+static int incomingim_ch2_getfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+	char ip[30];
+	aim_msgcookie_t *cachedcook;
+	aim_tlv_t *miscinfo;
+	aim_tlv_t *iptlv, *porttlv;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	memset(ip, 0, 30);
+
+	if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) {
+		aim_freetlvchain(&list2);
+		return 0;
+	}
+
+	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
+		!(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
+		!(porttlv = aim_gettlv(list2, 0x0005, 1))) {
+		
+		faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
+		aim_cookie_free(sess, cachedcook);
+		aim_freetlvchain(&list2);
+		
+		return 0;
+	}
+
+	snprintf(ip, 30, "%d.%d.%d.%d:%d",
+		aimutil_get8(iptlv->value+0),
+		aimutil_get8(iptlv->value+1),
+		aimutil_get8(iptlv->value+2),
+		aimutil_get8(iptlv->value+3),
+		aimutil_get16(porttlv->value));
+
+	faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo->sn, ip);
+
+	args->info.getfile.ip = ip;
+	memcpy(args->info.getfile.cookie, args->cookie, 8);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, &args);
+
+	return ret;
+}
+
+static int incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+#if 0
+	char ip[30];
+	aim_msgcookie_t *cachedcook;
+	aim_tlv_t *miscinfo;
+	aim_tlv_t *iptlv, *porttlv;
+	int ret =0;
+	aim_rxcallback_t userfunc;
+	char *desc = NULL;
+
+	memset(ip, 0, 30);
+
+	if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) 
+		return 0;
+
+	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
+		!(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
+		!(porttlv = aim_gettlv(list2, 0x0005, 1))) {
+	
+		faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
+		aim_cookie_free(sess, cachedcook);
+
+		return 0;
+	}
+
+	snprintf(ip, 30, "%d.%d.%d.%d:%d",
+		aimutil_get8(iptlv->value+0),
+		aimutil_get8(iptlv->value+1),
+		aimutil_get8(iptlv->value+2),
+		aimutil_get8(iptlv->value+3),
+		aimutil_get16(porttlv->value));
+
+	if (aim_gettlv(list2, 0x000c, 1))
+		desc = aim_gettlv_str(list2, 0x000c, 1);
+
+	faimdprintf(sess, 0, "rend: file transfer request from %s: %s (%s)\n",
+		userinfo->sn, desc, ip);
+
+	memcpy(cachedcook->cookie, args->cookie, 8);
+
+	ft = malloc(sizeof(struct aim_filetransfer_priv)); /* XXX */
+	strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
+	strncpy(ft->ip, ip, sizeof(ft->ip));
+	strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
+	cachedcook->type = AIM_COOKIETYPE_OFTSEND;
+	cachedcook->data = ft;
+
+	if (aim_cachecookie(sess, cachedcook) == -1)
+		faimdprintf(sess, 0, "ERROR caching message cookie\n");
+
+	aim_accepttransfer(sess, rx->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
+
+	if (desc)
+		free(desc);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, &args);
+#endif
+	return 0;
+}
+
+static int incomingim_ch2_imimage(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+
+	/* Primary IP address */
+	if (aim_gettlv(list2, 0x0003, 1)) {
+		aim_tlv_t *tlv;
+
+		tlv = aim_gettlv(list2, 0x0003, 1);
+
+		snprintf(args->info.imimage.ip, sizeof(args->info.imimage.ip),
+					"%d.%d.%d.%d:4443",
+					tlv->value[0],
+					tlv->value[1],
+					tlv->value[2],
+					tlv->value[3]);
+	}
+
+	/* 
+	 * Alternate IP address
+	 *
+	 * Sort of.  The peer doesn't send this -- the OSCAR
+	 * server does.  So it will be the IP address that the
+	 * peer is directly connected to the internet with, which 
+	 * may not be the same as the IP above.  If these two
+	 * values differ, it's rather unlikely that this
+	 * rendezvous is going to happen...
+	 *
+	 */
+	if (aim_gettlv(list2, 0x0004, 1))
+		;
+		
+	/* Port number (not correct -- ignore) */
+	if (aim_gettlv(list2, 0x0005, 1)) 
+		;
+
+	/* Unknown -- two bytes = 0x0001 */
+	if (aim_gettlv(list2, 0x000a, 1))
+		;
+
+	/* Unknown -- no value */
+	if (aim_gettlv(list2, 0x000f, 1))
+		;
+
+	faimdprintf(sess, 1, "rend: directIM request from %s (%s)\n", userinfo->sn, args->info.imimage.ip);
+
+	/* 
+	 * XXX: there are a couple of different request packets for
+	 *          different things 
+	 */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+	return ret;
+}
+
+/* XXX Ugh.  I think its obvious. */
+static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, struct aim_userinfo_s *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
+{
+	aim_tlv_t *block1;
+	aim_tlvlist_t *list2;
 	int ret = 0;
 	struct aim_incomingim_ch2_args args;
+	aim_bstream_t bbs;
+	fu8_t *cookie2;
 
 	memset(&args, 0, sizeof(args));
 
@@ -626,18 +953,23 @@
 		return 0;
 	}
 
+	aim_bstream_init(&bbs, block1->value, block1->length);
+
 	/*
 	 * First two bytes represent the status of the connection.
 	 *
 	 * 0 is a request, 2 is an accept
 	 */ 
-	args.status = aimutil_get16(block1->value+0);
+	args.status = aimbs_get16(&bbs);
 
 	/*
 	 * Next comes the cookie.  Should match the ICBM cookie.
 	 */
-	if (memcmp(block1->value+2, cookie, 8) != 0) 
+	cookie2 = aimbs_getraw(&bbs, 8);
+	if (memcmp(cookie, cookie2, 8) != 0) 
 		faimdprintf(sess, 0, "rend: warning cookies don't match!\n");
+	memcpy(args.cookie, cookie2, 8);
+	free(cookie2);
 
 	/*
 	 * The next 16bytes are a capability block so we can
@@ -651,7 +983,7 @@
 	 * Read off one capability string and we should have it ID'd.
 	 * 
 	 */
-	if ((args.reqclass = aim_getcap(sess, block1->value+2+8, 0x10)) == 0x0000) {
+	if ((args.reqclass = aim_getcap(sess, &bbs, 0x10)) == 0x0000) {
 		faimdprintf(sess, 0, "rend: no ID block\n");
 		return 0;
 	}
@@ -662,18 +994,19 @@
 	 *
 	 * Ack packets for instance have nothing more to them.
 	 */
-	list2 = aim_readtlvchain(block1->value+2+8+16, block1->length-2-8-16);
+	list2 = aim_readtlvchain(&bbs);
 
+#if 0 /* this should be in the per-type blocks */
 	if (!list2 || ((args.reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) {
-		struct aim_msgcookie_t *cook;
+		aim_msgcookie_t *cook;
 		int type;
 
 		type = aim_msgcookie_gettype(args.reqclass); /* XXX: fix this shitty code */
 
 		if ((cook = aim_checkcookie(sess, cookie, type)) == NULL) {
 			faimdprintf(sess, 0, "non-data rendezvous thats not in cache (type %d)\n", type);
-		aim_freetlvchain(&list2);
-		return 1;
+			aim_freetlvchain(&list2);
+			return 1;
 		}
 
 		if (cook->type == AIM_COOKIETYPE_OFTGET) {
@@ -686,14 +1019,14 @@
 
 				if (args.status != 0x0002) {
 
-				  if (aim_gettlv(list2, 0x000b, 1))
-					    errorcode = aim_gettlv16(list2, 0x000b, 1);
+					if (aim_gettlv(list2, 0x000b, 1))
+						errorcode = aim_gettlv16(list2, 0x000b, 1);
 
-				  /* XXX this should make it up to the client, you know.. */
-				  if (errorcode)
-					faimdprintf(sess, 0, "transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode);
+					/* XXX this should make it up to the client, you know.. */
+					if (errorcode)
+						faimdprintf(sess, 0, "transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode);
 				} /* args.status != 0x0002 */
-				
+
 			} else {
 				faimdprintf(sess, 0, "no data attached to file transfer\n");
 			} /* !cook->data */
@@ -711,210 +1044,26 @@
 
 		return 1;
 	}
+#endif
 
 	/*
 	 * The rest of the handling depends on what type it is.
 	 */
-	if (args.reqclass & AIM_CAPS_BUDDYICON) {
-		struct aim_tlv_t *miscinfo;
-		int curpos = 0;
-
-		miscinfo = aim_gettlv(list2, 0x2711, 1);
-
-		args.info.icon.checksum = aimutil_get32(miscinfo->value+curpos);
-		curpos += 4;
-		args.info.icon.length = aimutil_get32(miscinfo->value+curpos);
-		curpos += 4;
-		args.info.icon.timestamp = aimutil_get32(miscinfo->value+curpos);
-		curpos += 4;
-		args.info.icon.icon = malloc(args.info.icon.length);
-		memcpy(args.info.icon.icon, miscinfo->value+curpos, args.info.icon.length);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-		free(args.info.icon.icon);
-
-	} else if (args.reqclass & AIM_CAPS_VOICE) {
-		struct aim_msgcookie_t *cachedcook;
-
-		faimdprintf(sess, 1, "rend: voice!\n");
-
-		if(!(cachedcook = (struct aim_msgcookie_t*)calloc(1, sizeof(struct aim_msgcookie_t)))) {
-			aim_freetlvchain(&list2);
-			return 0;
-		}
-
-		memcpy(cachedcook->cookie, cookie, 8);
-		cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
-		cachedcook->data = NULL;
-
-		if (aim_cachecookie(sess, cachedcook) == -1)
-			faimdprintf(sess, 0, "ERROR caching message cookie\n");
-
-		/* XXX: implement all this */
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) 
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-	} else if (args.reqclass & AIM_CAPS_IMIMAGE) {
-		char ip[30];
-		struct aim_directim_priv *priv;
-
-		memset(ip, 0, sizeof(ip));
-
-		if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
-			struct aim_tlv_t *iptlv, *porttlv;
-			  
-			iptlv = aim_gettlv(list2, 0x0003, 1);
-			porttlv = aim_gettlv(list2, 0x0005, 1);
-
-			snprintf(ip, 30, "%d.%d.%d.%d:%d", 
-				aimutil_get8(iptlv->value+0),
-				aimutil_get8(iptlv->value+1),
-				aimutil_get8(iptlv->value+2),
-				aimutil_get8(iptlv->value+3),
-				4443 /*aimutil_get16(porttlv->value)*/);
-		}
-
-		faimdprintf(sess, 1, "rend: directIM request from %s (%s)\n",
-				userinfo->sn, ip);
-
-		/* 
-		 * XXX: there are a couple of different request packets for
-		 *          different things 
-		 */
-
-		args.info.directim = priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); /* XXX error */
-		memcpy(priv->ip, ip, sizeof(priv->ip));
-		memcpy(priv->sn, userinfo->sn, sizeof(priv->sn));
-		memcpy(priv->cookie, cookie, sizeof(priv->cookie));
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-	} else if (args.reqclass & AIM_CAPS_CHAT) {
-		struct aim_tlv_t *miscinfo;
-
-		miscinfo = aim_gettlv(list2, 0x2711, 1);
-		aim_chat_readroominfo(miscinfo->value, &args.info.chat.roominfo);
-			  
-		if (aim_gettlv(list2, 0x000c, 1))
-			args.info.chat.msg = aim_gettlv_str(list2, 0x000c, 1);
-		
-		if (aim_gettlv(list2, 0x000d, 1))
-			args.info.chat.encoding = aim_gettlv_str(list2, 0x000d, 1);
-		
-		if (aim_gettlv(list2, 0x000e, 1))
-			args.info.chat.lang = aim_gettlv_str(list2, 0x000e, 1);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-		free(args.info.chat.roominfo.name);
-		free(args.info.chat.msg);
-		free(args.info.chat.encoding);
-		free(args.info.chat.lang);
-
-	} else if (args.reqclass & AIM_CAPS_GETFILE) {
-		char ip[30];
-		struct aim_msgcookie_t *cachedcook;
-		struct aim_tlv_t *miscinfo;
-		struct aim_tlv_t *iptlv, *porttlv;
-
-		memset(ip, 0, 30);
-
-		if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t)))) {
-			aim_freetlvchain(&list2);
-			return 0;
-		}
-
-		if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
-			!(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
-			!(porttlv = aim_gettlv(list2, 0x0005, 1))) {
-			
-			faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
-			aim_cookie_free(sess, cachedcook);
-			aim_freetlvchain(&list2);
-			
-			return 0;
-		}
-
-		snprintf(ip, 30, "%d.%d.%d.%d:%d",
-			aimutil_get8(iptlv->value+0),
-			aimutil_get8(iptlv->value+1),
-			aimutil_get8(iptlv->value+2),
-			aimutil_get8(iptlv->value+3),
-			aimutil_get16(porttlv->value));
-
-		faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo->sn, ip);
-
-		args.info.getfile.ip = ip;
-		args.info.getfile.cookie = cookie;
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-	} else if (args.reqclass & AIM_CAPS_SENDFILE) {
-#if 0 
-		char ip[30];
-		struct aim_msgcookie_t *cachedcook;
-		struct aim_tlv_t *miscinfo;
-		struct aim_tlv_t *iptlv, *porttlv;
-
-		memset(ip, 0, 30);
-
-		if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t)))) {
-			aim_freetlvchain(&list2);
-			return 0;
-		}
-
-		if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
-			!(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
-			!(porttlv = aim_gettlv(list2, 0x0005, 1))) {
-		
-			faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
-			aim_cookie_free(sess, cachedcook);
-			aim_freetlvchain(&list2);
-
-			return 0;
-		}
-
-		snprintf(ip, 30, "%d.%d.%d.%d:%d",
-			aimutil_get8(iptlv->value+0),
-			aimutil_get8(iptlv->value+1),
-			aimutil_get8(iptlv->value+2),
-			aimutil_get8(iptlv->value+3),
-			aimutil_get16(porttlv->value));
-
-		if (aim_gettlv(list2, 0x000c, 1))
-			desc = aim_gettlv_str(list2, 0x000c, 1);
-
-		faimdprintf(sess, 0, "rend: file transfer request from %s: %s (%s)\n",
-			userinfo->sn, desc, ip);
-
-		memcpy(cachedcook->cookie, cookie, 8);
-
-		ft = malloc(sizeof(struct aim_filetransfer_priv)); /* XXX */
-		strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
-		strncpy(ft->ip, ip, sizeof(ft->ip));
-		strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
-		cachedcook->type = AIM_COOKIETYPE_OFTSEND;
-		cachedcook->data = ft;
-
-		if (aim_cachecookie(sess, cachedcook) == -1)
-			faimdprintf(sess, 0, "ERROR caching message cookie\n");
-
-		aim_accepttransfer(sess, rx->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
-
-		if (desc)
-			free(desc);
-
-		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-			ret = userfunc(sess, rx, channel, userinfo, &args);
-
-#endif	
-	} else
+	if (args.reqclass & AIM_CAPS_BUDDYICON)
+		ret = incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST)
+		ret = incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_VOICE)
+		ret = incomingim_ch2_voice(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_IMIMAGE)
+		ret = incomingim_ch2_imimage(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_CHAT)
+		ret = incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_GETFILE)
+		ret = incomingim_ch2_getfile(sess, mod, rx, snac, userinfo, &args, list2);
+	else if (args.reqclass & AIM_CAPS_SENDFILE)
+		ret = incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, list2);
+	else
 		faimdprintf(sess, 0, "rend: unknown rendezvous 0x%04x\n", args.reqclass);
 
 	aim_freetlvchain(&list2);
@@ -933,11 +1082,11 @@
  * I have access to.  Its not fast, its not clean.  But it works.
  *
  */
-static int incomingim(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int i, ret = 0;
-	unsigned char cookie[8];
-	int channel;
+	fu8_t cookie[8];
+	fu16_t channel;
 	struct aim_userinfo_s userinfo;
 
 	memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
@@ -946,7 +1095,7 @@
 	 * Read ICBM Cookie.  And throw away.
 	 */
 	for (i = 0; i < 8; i++)
-		cookie[i] = aimutil_get8(data+i);
+		cookie[i] = aimbs_get8(bs);
 
 	/*
 	 * Channel ID.
@@ -961,15 +1110,14 @@
 	 * connection negotiations come from.
 	 * 
 	 */
-	channel = aimutil_get16(data+i);
-	i += 2;
+	channel = aimbs_get16(bs);
 
 	/*
 	 * Technically Channel 3 in chat could be done here too.
 	 */
 	if ((channel != 0x01) && (channel != 0x02)) {
 		faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
-		return 1;
+		return 0;
 	}
 
 	/*
@@ -988,7 +1136,7 @@
 	 * never be two TLVs of the same type in one block.
 	 * 
 	 */
-	i += aim_extractuserinfo(sess, data+i, &userinfo);
+	aim_extractuserinfo(sess, bs, &userinfo);
 
 	/*
 	 * From here on, its depends on what channel we're on.
@@ -999,16 +1147,16 @@
 	 */
 	if (channel == 1) {
 
-		ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, data+i, datalen-i, cookie);
+		ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
 
 	} else if (channel == 0x0002) {
-		struct aim_tlvlist_t *tlvlist;
+		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_readtlvchain(data+i, datalen-i);
+		tlvlist = aim_readtlvchain(bs);
 
 		ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
 
@@ -1028,30 +1176,29 @@
  *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
  * 
  */
-faim_export int aim_denytransfer(struct aim_session_t *sess,
-					struct aim_conn_t *conn, 
-					const char *sender,
-					const char *cookie, 
-					unsigned short code)
+faim_export int aim_denytransfer(aim_session_t *sess, aim_conn_t *conn, const char *sender, const char *cookie, fu16_t code)
 {
-	struct command_tx_struct *newpacket;
-	int curbyte, i;
-
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sender)+6)))
+	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+8+2+1+strlen(sender)+6)))
 		return -ENOMEM;
 
-	newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+	
+	aimbs_putraw(&fr->data, cookie, 8);
 
-	curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid++);
-	for (i = 0; i < 8; i++)
-		curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-	curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender));
-	curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender));
-	curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code);
+	aimbs_put16(&fr->data, 0x0002); /* channel */
+	aimbs_put8(&fr->data, strlen(sender));
+	aimbs_putraw(&fr->data, sender, strlen(sender));
 
-	newpacket->lock = 0;
-	aim_tx_enqueue(sess, newpacket);
+	aim_addtlvtochain16(&tl, 0x0003, code);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
@@ -1062,96 +1209,77 @@
  * Request ICBM parameter information.
  *
  */
-faim_export unsigned long aim_reqicbmparams(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_reqicbmparams(aim_session_t *sess, aim_conn_t *conn)
 {
 	return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
 }
 
 /*
  *
+ * I definitly recommend sending this.  If you don't, you'll be stuck
+ * with the rather unreasonable defaults.  You don't want those.  Send this.
+ * 
  */
-faim_export unsigned long aim_seticbmparam(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_icbmparameters *params)
+faim_export int aim_seticbmparam(aim_session_t *sess, aim_conn_t *conn, struct aim_icbmparameters *params)
 {
-	struct command_tx_struct *newpacket;
-	int curbyte;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
 	if (!sess || !conn || !params)
 		return -EINVAL;
 
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+16)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
 		return -ENOMEM;
 
-	newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
 
-	curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid++);
-
-	/* This is read-only (in Parameter Reply). Must be set to zero here. */
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+	/* This is read-only (see Parameter Reply). Must be set to zero here. */
+	aimbs_put16(&fr->data, 0x0000);
 
 	/* These are all read-write */
-	curbyte += aimutil_put32(newpacket->data+curbyte, params->flags); 
-	curbyte += aimutil_put16(newpacket->data+curbyte, params->maxmsglen);
-	curbyte += aimutil_put16(newpacket->data+curbyte, params->maxsenderwarn); 
-	curbyte += aimutil_put16(newpacket->data+curbyte, params->maxrecverwarn); 
-	curbyte += aimutil_put32(newpacket->data+curbyte, params->minmsginterval);
+	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);
 
-	newpacket->lock = 0;
-	aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
 
-static int paraminfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	struct aim_icbmparameters params;
 	aim_rxcallback_t userfunc;
-	int i = 0;
 
-	params.maxchan = aimutil_get16(data+i);
-	i += 2;
-
-	params.flags = aimutil_get32(data+i);
-	i += 4;
-
-	params.maxmsglen = aimutil_get16(data+i);
-	i += 2;
-
-	params.maxsenderwarn = aimutil_get16(data+i);
-	i += 2;
-
-	params.maxrecverwarn = aimutil_get16(data+i);
-	i += 2;
-
-	params.minmsginterval = aimutil_get32(data+i);
-	i += 4;
-
+	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;
 }
 
-static int missedcall(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-	int i, ret = 0;
+	int ret = 0;
 	aim_rxcallback_t userfunc;
-	unsigned short channel, nummissed, reason;
+	fu16_t channel, nummissed, reason;
 	struct aim_userinfo_s userinfo;
-	
-	for (i = 0; i < datalen; ) {
 
-		/* Channel ID. */
-		channel = aimutil_get16(data+i);
-		i += 2;
+	while (aim_bstream_empty(bs)) {	
 
-		/* Extract the standard user info block. */
-		i += aim_extractuserinfo(sess, data+i, &userinfo);
-
-		nummissed = aimutil_get16(data+i);
-		i += 2;
-
-		reason = aimutil_get16(data+i);
-		i += 2;
+		channel = aimbs_get16(bs);
+		aim_extractuserinfo(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);
@@ -1160,51 +1288,67 @@
 	return ret;
 }
 
-static int msgack(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int clienterr(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;
+	fu8_t *ck, snlen;
+
+	ck = aimbs_getraw(bs, 8);
+	channel = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+	reason = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		 ret = userfunc(sess, rx, channel, sn, reason);
+
+	return ret;
+}
+
+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;
-	char sn[MAXSNLEN];
-	unsigned char ck[8];
-	unsigned short type;
-	int i = 0;
-	unsigned char snlen;
-
-	memcpy(ck, data, 8);
-	i += 8;
+	fu16_t type;
+	fu8_t snlen, *ck;
+	char *sn;
 
-	type = aimutil_get16(data+i);
-	i += 2;
-
-	snlen = aimutil_get8(data+i);
-	i++;
-
-	memset(sn, 0, sizeof(sn));
-	strncpy(sn, (char *)data+i, snlen);
+	ck = aimbs_getraw(bs, 8);
+	type = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 		return userfunc(sess, rx, type, sn);
 
-	return 0;
-}
-
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
-{
-
-	if (snac->subtype == 0x0005)
-		return paraminfo(sess, mod, rx, snac, data, datalen);
-	else if (snac->subtype == 0x0006)
-		return outgoingim(sess, mod, rx, snac, data, datalen);
-	else if (snac->subtype == 0x0007)
-		return incomingim(sess, mod, rx, snac, data, datalen);
-	else if (snac->subtype == 0x000a)
-		return missedcall(sess, mod, rx, snac, data, datalen);
-	else if (snac->subtype == 0x000c)
-		return msgack(sess, mod, rx, snac, data, datalen);
+	free(sn);
+	free(ck);
 
 	return 0;
 }
 
-faim_internal int msg_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+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 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 clienterr(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000c)
+		return msgack(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
 	mod->family = 0x0004;
--- a/src/protocols/oscar/info.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/info.c	Sun Sep 09 10:07:14 2001 +0000
@@ -2,7 +2,7 @@
  * aim_info.c
  *
  * The functions here are responsible for requesting and parsing information-
- * gathering SNACs.  
+ * gathering SNACs.  Or something like that. 
  *
  */
 
@@ -10,533 +10,434 @@
 #include <aim.h>
 
 struct aim_priv_inforeq {
-  char sn[MAXSNLEN+1];
-  unsigned short infotype;
+	char sn[MAXSNLEN+1];
+	fu16_t infotype;
 };
 
-faim_export int aim_getinfo(struct aim_session_t *sess,
-			    struct aim_conn_t *conn, 
-			    const char *sn,
-			    unsigned short infotype)
+faim_export int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu16_t infotype)
 {
-  struct command_tx_struct *newpacket;
-  struct aim_priv_inforeq privdata;
-  int i = 0;
+	struct aim_priv_inforeq privdata;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!sess || !conn || !sn)
-    return -1;
+	if (!sess || !conn || !sn)
+		return -EINVAL;
 
-  if ((infotype != AIM_GETINFO_GENERALINFO) &&
-      (infotype != AIM_GETINFO_AWAYMESSAGE))
-    return -1;
+	if ((infotype != AIM_GETINFO_GENERALINFO) && (infotype != AIM_GETINFO_AWAYMESSAGE))
+		return -EINVAL;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 12+1+strlen(sn))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
+		return -ENOMEM;
 
-  i += aimutil_put16(newpacket->data+i, infotype);
-  i += aimutil_put8(newpacket->data+i, strlen(sn));
-  i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
+	strncpy(privdata.sn, sn, sizeof(privdata.sn));
+	privdata.infotype = infotype;
+	snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
+	
+	aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
+	aimbs_put16(&fr->data, infotype);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  strncpy(privdata.sn, sn, sizeof(privdata.sn));
-  privdata.infotype = infotype;
-  aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
-
-  return 0;
+	return 0;
 }
 
 /*
  * Capability blocks.  
  */
 static const struct {
-  unsigned short flag;
-  unsigned char data[16];
+	unsigned short flag;
+	unsigned char data[16];
 } aim_caps[] = {
-  
-  {AIM_CAPS_BUDDYICON,
-   {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 
-    0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-  
-  {AIM_CAPS_VOICE,
-   {0x09, 0x46, 0x13, 0x41, 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_VOICE,
+	 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
-  {AIM_CAPS_IMIMAGE,
-   {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
-    0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-  
-  {AIM_CAPS_CHAT,
-   {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 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_IMIMAGE,
+	 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_CHAT,
+	 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 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_SENDFILE,
-   {0x09, 0x46, 0x13, 0x43, 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_SAVESTOCKS,
-   {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
-    0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+	{AIM_CAPS_SAVESTOCKS,
+	 {0x09, 0x46, 0x13, 0x47, 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}},
+	/*
+	 * 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}},
+	{AIM_CAPS_SENDBUDDYLIST,
+	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
-  {AIM_CAPS_LAST}
+	{AIM_CAPS_LAST}
 };
 
-faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen)
+/*
+ * This still takes a length parameter even with a bstream because capabilities
+ * are not naturally bounded.
+ * 
+ */
+faim_internal fu16_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len)
 {
-  unsigned short flags;
-  int i;
-  int offset = 0;
-  int identified;
+	fu16_t flags = 0;
+	int offset;
 
-  for (offset = 0, flags = 0; offset < buflen; offset += 0x0010) {
+	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
+		fu8_t *cap;
+		int i, identified;
 
-    for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
+		cap = aimbs_getraw(bs, 0x10);
 
-      if (memcmp(&aim_caps[i].data, capblock+offset, 0x10) == 0) {
-	flags |= aim_caps[i].flag;
-	identified++;
-	break; /* should only match once... */
-      }
+		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)
-      faimdprintf(sess, 0, "unknown capability!\n");
+		if (!identified)
+			faimdprintf(sess, 0, "unknown capability!\n");
 
-  }
+		free(cap);
+	}
 
-  return flags;
+	return flags;
 }
 
-faim_internal int aim_putcap(unsigned char *capblock, int buflen, unsigned short caps)
+faim_internal int aim_putcap(aim_bstream_t *bs, fu16_t caps)
 {
-  int offset, i;
+	int i;
 
-  if (!capblock)
-    return 0;
+	if (!bs)
+		return -EINVAL;
+
+	for (i = 0; aim_bstream_empty(bs); i++) {
 
-  for (i = 0, offset = 0; 
-       !(aim_caps[i].flag & AIM_CAPS_LAST) && (offset < buflen); i++) {
+		if (aim_caps[i].flag == AIM_CAPS_LAST)
+			break;
 
-    if (caps & aim_caps[i].flag) {
-      memcpy(capblock+offset, aim_caps[i].data, 16);
-      offset += 16;
-    }
+		if (caps & aim_caps[i].flag)
+			aimbs_putraw(bs, aim_caps[i].data, 0x10);
 
-  }
+	}
 
-  return offset;
+	return 0;
 }
 
 /*
- * AIM is fairly regular about providing user info.  This
- * is a generic routine to extract it in its standard form.
+ * AIM is fairly regular about providing user info.  This is a generic 
+ * routine to extract it in its standard form.
  */
-faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *buf, struct aim_userinfo_s *outinfo)
+faim_internal int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, struct aim_userinfo_s *outinfo)
 {
-  int i = 0;
-  int tlvcnt = 0;
-  int curtlv = 0;
-  int tlv1 = 0;
-  u_short curtype;
-  int lastvalid;
+	int curtlv, tlvcnt;
+	fu8_t snlen;
 
+	if (!bs || !outinfo)
+		return -EINVAL;
 
-  if (!buf || !outinfo)
-    return -1;
+	/* Clear out old data first */
+	memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));
 
-  /* Clear out old data first */
-  memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));
+	/*
+	 * Screen name.  Stored as an unterminated string prepended with a 
+	 * byte containing its length.
+	 */
+	snlen = aimbs_get8(bs);
+	aimbs_getrawbuf(bs, outinfo->sn, snlen);
 
-  /*
-   * Screen name.    Stored as an unterminated string prepended
-   *                 with an unsigned byte containing its length.
-   */
-  if (buf[i] < MAXSNLEN) {
-    memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
-    outinfo->sn[(int)buf[i]] = '\0';
-  } else {
-    memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1);
-    outinfo->sn[MAXSNLEN] = '\0';
-  }
-  i = 1 + (int)buf[i];
+	/*
+	 * 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);
 
-  /*
-   * Warning Level.  Stored as an unsigned short.
-   */
-  outinfo->warnlevel = aimutil_get16(&buf[i]);
-  i += 2;
+	/* 
+	 * Parse out the Type-Length-Value triples as they're found.
+	 */
+	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
+		int endpos;
+		fu16_t type, length;
 
-  /*
-   * TLV Count.      Unsigned short representing the number of 
-   *                 Type-Length-Value triples that follow.
-   */
-  tlvcnt = aimutil_get16(&buf[i]);
-  i += 2;
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		endpos = aim_bstream_curpos(bs) + length;
 
-  /* 
-   * Parse out the Type-Length-Value triples as they're found.
-   */
-  while (curtlv < tlvcnt) {
-    lastvalid = 1;
-    curtype = aimutil_get16(&buf[i]);
-    switch (curtype) {
-      /*
-       * Type = 0x0000: Invalid
-       *
-       * AOL has been trying to throw these in just to break us.
-       * They're real nice guys over there at AOL.  
-       *
-       * Just skip the two zero bytes and continue on. (This doesn't
-       * count towards tlvcnt!)
-       */
-    case 0x0000:
-      lastvalid = 0;
-      i += 2;
-      break;
+		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
+			 *
+			 */
+			outinfo->flags = aimbs_get16(bs);
+
+		} else if (type == 0x0002) {
+			/*
+			 * Type = 0x0002: Member-Since date. 
+			 *
+			 * The time/date that the user originally registered for
+			 * the service, stored in time_t format.
+			 */
+			outinfo->membersince = aimbs_get32(bs);
+
+		} 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);
 
-      /*
-       * Type = 0x0001: User flags
-       * 
-       * Specified as any of the following bitwise 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
-       *
-       * In some odd cases, we can end up with more
-       * than one of these.  We only want the first,
-       * as the others may not be something we want.
-       *
-       */
-    case 0x0001:
-      if (tlv1) /* use only the first */
-	break;
-      outinfo->flags = aimutil_get16(&buf[i+4]);
-      tlv1++;
-      break;
-      
-      /*
-       * Type = 0x0002: Member-Since date. 
-       *
-       * The time/date that the user originally
-       * registered for the service, stored in 
-       * time_t format
-       */
-    case 0x0002: 
-      outinfo->membersince = aimutil_get32(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0003: On-Since date.
-       *
-       * The time/date that the user started 
-       * their current session, stored in time_t
-       * format.
-       */
-    case 0x0003:
-      outinfo->onlinesince = aimutil_get32(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0004: Idle time.
-       *
-       * Number of seconds since the user
-       * actively used the service.
-       */
-    case 0x0004:
-      outinfo->idletime = aimutil_get16(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0006: ICQ Online Status
-       *
-       * ICQ's Away/DND/etc "enriched" status
-       * Some decoding of values done by Scott <darkagl@pcnet.com>
-       */
-    case 0x0006:
-      outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2);
-      break;
+		} else if (type == 0x0004) {
+			/*
+			 * Type = 0x0004: Idle time.
+			 *
+			 * Number of seconds 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);
 
+		} 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);
 
-      /*
-       * Type = 0x000a
-       *
-       * ICQ User IP Address.
-       * Ahh, the joy of ICQ security.
-       */
-    case 0x000a:
-      outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]);
-      break;
+		} else if (type == 0x000a) {
+			/*
+			 * Type = 0x000a
+			 *
+			 * ICQ User IP Address.
+			 * Ahh, the joy of ICQ security.
+			 */
+			outinfo->icqinfo.ipaddr = aimbs_get32(bs);
 
-      /* Type = 0x000c
-       *
-       * random crap containing the IP address,
-       * apparently a port number, and some Other Stuff.
-       *
-       */
-    case 0x000c:
-      memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25);
-      break;
+		} else if (type == 0x000c) {
+			/* 
+			 * Type = 0x000c
+			 *
+			 * random crap containing the IP address,
+			 * apparently a port number, and some Other Stuff.
+			 *
+			 */
+			aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
 
-      /*
-       * Type = 0x000d
-       *
-       * Capability information.  Not real sure of
-       * actual decoding.  See comment on aim_bos_setprofile()
-       * in aim_misc.c about the capability block, its the same.
-       *
-       */
-    case 0x000d:
-      {
-	int len;
-	len = aimutil_get16(buf+i+2);
-	if (!len)
-	  break;
-	
-	outinfo->capabilities = aim_getcap(sess, buf+i+4, len);
-      }
-      break;
-      
-      /*
-       * Type = 0x000e
-       *
-       * Unknown.  Always of zero length, and always only
-       * on AOL users.
-       *
-       * Ignore.
-       *
-       */
-    case 0x000e:
-      break;
-      
-      /*
-       * 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).
-       *
-       */
-    case 0x000f:
-    case 0x0010:
-      outinfo->sessionlen = aimutil_get32(&buf[i+4]);
-      break;
-      
-      /*
-       * 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.
-       *
-       */
-    default:
-      {
-	int len,z = 0, y = 0, x = 0;
-	char tmpstr[160];
+		} else if (type == 0x000d) {
+			/*
+			 * Type = 0x000d
+			 *
+			 * Capability information.
+			 *
+			 */
+			outinfo->capabilities = aim_getcap(sess, bs, length);
+
+		} else if (type == 0x000e) {
+			/*
+			 * Type = 0x000e
+			 *
+			 * Unknown.  Always of zero length, and always only
+			 * on AOL users.
+			 *
+			 * Ignore.
+			 *
+			 */
 
-        faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
-	faimdprintf(sess, 0, "userinfo:   sn    =%s\n", outinfo->sn);
-	faimdprintf(sess, 0, "userinfo:   curtlv=0x%04x\n", curtlv);
-	faimdprintf(sess, 0, "userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
-	faimdprintf(sess, 0, "userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
-	faimdprintf(sess, 0, "userinfo:   data: \n");
-	while (z<len)
-	  {
-	    x = snprintf(tmpstr, sizeof(tmpstr), "userinfo:      ");
-	    for (y = 0; y < 8; y++)
-	      {
-		if (z<len)
-		  {
-		    snprintf(tmpstr+x, sizeof(tmpstr)-x, "%02x ", buf[i+4+z]);
-		    z++;
-		    x += 3;
-		  }
-		else
-		  break;
-	      }
-	    faimdprintf(sess, 0, "%s\n", tmpstr);
-	  }
-      }
-      break;
-    }  
-    /*
-     * No matter what, TLV triplets should always look like this:
-     *
-     *   u_short type;
-     *   u_short length;
-     *   u_char  data[length];
-     *
-     */
-    if (lastvalid) {
-      i += (2 + 2 + aimutil_get16(&buf[i+2])); 	   
-      curtlv++;
-    }
-  }
-  
-  return i;
+		} 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);
+
+		} 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.
+			 *
+			 */
+			faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
+			faimdprintf(sess, 0, "userinfo:   sn    =%s\n", outinfo->sn);
+			faimdprintf(sess, 0, "userinfo:   type  =0x%04x\n",type);
+			faimdprintf(sess, 0, "userinfo:   length=0x%04x\n", length);
+
+		}
+
+		/* Save ourselves. */
+		aim_bstream_setpos(bs, endpos);
+	}
+
+	return 0;
 }
 
 /*
  * Inverse of aim_extractuserinfo()
  */
-faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
+faim_internal int aim_putuserinfo(aim_bstream_t *bs, struct aim_userinfo_s *info)
 {
-  int i = 0, numtlv = 0;
-  struct aim_tlvlist_t *tlvlist = NULL;
+	aim_tlvlist_t *tlvlist = NULL;
 
-  if (!buf || !info)
-    return 0;
+	if (!bs || !info)
+		return -EINVAL;
 
-  i += aimutil_put8(buf+i, strlen(info->sn));
-  i += aimutil_putstr(buf+i, info->sn, strlen(info->sn));
+	aimbs_put8(bs, strlen(info->sn));
+	aimbs_putraw(bs, info->sn, strlen(info->sn));
 
-  i += aimutil_put16(buf+i, info->warnlevel);
+	aimbs_put16(bs, info->warnlevel);
 
 
-  aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
-  numtlv++;
-
-  aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
-  numtlv++;
-
-  aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
-  numtlv++;
-
-  aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
-  numtlv++;
+	aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
+	aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
+	aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
+	aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
 
 #if ICQ_OSCAR_SUPPORT
-  if(atoi(info->sn) != 0) {
-    aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
-    aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
-  }
+	if (atoi(info->sn) != 0) {
+		aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
+		aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
+	}
 #endif
 
-  aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
-  numtlv++;
+	aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
 
-  aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen);
-  numtlv++;
+	aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
 
-  i += aimutil_put16(buf+i, numtlv); /* tlvcount */
-  i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */
-  aim_freetlvchain(&tlvlist);
+	aimbs_put16(bs, aim_counttlvchain(&tlvlist));
+	aim_writetlvchain(bs, &tlvlist);
+	aim_freetlvchain(&tlvlist);
 
-  return i;
+	return 0;
 }
 
-faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info)
+faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *info)
 {
-  struct command_tx_struct *tx;
-  int i = 0;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!sess || !conn || !info)
-    return 0;
-
-  if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
-    return -1;
-
-  tx->lock = 1;
+	if (!sess || !conn || !info)
+		return -EINVAL;
 
-  i += aimutil_put16(tx->data+i, 0x0003);
-  i += aimutil_put16(tx->data+i, 0x000b);
-  i += aimutil_put16(tx->data+i, 0x0000);
-  i += aimutil_put16(tx->data+i, 0x0000);
-  i += aimutil_put16(tx->data+i, 0x0000);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
 
-  i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info);
+	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
+	aim_putuserinfo(&fr->data, info);
 
-  tx->commandlen = i;
-  tx->lock = 0;
-  aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
-  return 0;
+	return 0;
 }
 
-faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn)
+faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
-  struct command_tx_struct *tx;
-  int i = 0;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!sess || !conn || !sn)
-    return 0;
-
-  if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
-    return -1;
-
-  tx->lock = 1;
+	if (!sess || !conn || !sn)
+		return -EINVAL;
 
-  i += aimutil_put16(tx->data+i, 0x0003);
-  i += aimutil_put16(tx->data+i, 0x000c);
-  i += aimutil_put16(tx->data+i, 0x0000);
-  i += aimutil_put16(tx->data+i, 0x0000);
-  i += aimutil_put16(tx->data+i, 0x0000);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
 
-  i += aimutil_put8(tx->data+i, strlen(sn));
-  i += aimutil_putstr(tx->data+i, sn, strlen(sn));
-  
-  tx->lock = 0;
-  aim_tx_enqueue(sess, tx);
+	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-  return 0;
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
-faim_export int aim_0002_000b(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn)
+/*
+ * Huh? What is this?
+ */
+faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
-  struct command_tx_struct *tx;
-  int i = 0;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!sess || !conn || !sn)
-    return 0;
-
-  if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
-    return -1;
+	if (!sess || !conn || !sn)
+		return -EINVAL;
 
-  tx->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
 
-  i = aim_putsnac(tx->data, 0x0002, 0x000b, 0x0000, sess->snac_nextid);
-  i += aimutil_put8(tx->data+i, strlen(sn));
-  i += aimutil_putstr(tx->data+i, sn, strlen(sn));
-  
-  tx->commandlen = i;
-  tx->lock = 0;
-  aim_tx_enqueue(sess, tx);
+	snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
-  return 0;
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
 /*
@@ -545,14 +446,14 @@
  *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
  *   t(0003)  - short - unknown (value = 7)
  */
-static int rights(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-	struct aim_tlvlist_t *tlvlist;
+	aim_tlvlist_t *tlvlist;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
-	unsigned short maxsiglen = 0;
+	fu16_t maxsiglen = 0;
 
-	tlvlist = aim_readtlvchain(data, datalen);
+	tlvlist = aim_readtlvchain(bs);
 
 	if (aim_gettlv(tlvlist, 0x0001, 1))
 		maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
@@ -565,87 +466,83 @@
 	return ret;
 }
 
-static int userinfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  struct aim_userinfo_s userinfo;
-  char *text_encoding = NULL;
-  char *text = NULL;
-  int i = 0;
-  aim_rxcallback_t userfunc;
-  struct aim_tlvlist_t *tlvlist;
-  struct aim_snac_t *origsnac = NULL;
-  struct aim_priv_inforeq *inforeq;
-  int ret = 0;
+	struct aim_userinfo_s userinfo;
+	char *text_encoding = NULL, *text = NULL;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	aim_snac_t *origsnac = NULL;
+	struct aim_priv_inforeq *inforeq;
+	int ret = 0;
+
+	origsnac = aim_remsnac(sess, snac->id);
 
-  origsnac = aim_remsnac(sess, snac->id);
+	if (!origsnac || !origsnac->data) {
+		faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
+		return 0;
+	}
 
-  if (!origsnac || !origsnac->data) {
-    faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
-    return 0;
-  }
+	inforeq = (struct aim_priv_inforeq *)origsnac->data;
 
-  inforeq = (struct aim_priv_inforeq *)origsnac->data;
+	if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
+			(inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) {
+		faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
+		return 0;
+	}
 
-  if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
-      (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE)) {
-    faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
-    return 0;
-  }
+	aim_extractuserinfo(sess, bs, &userinfo);
 
-  i = aim_extractuserinfo(sess, data, &userinfo);
-  
-  tlvlist = aim_readtlvchain(data+i, datalen-i);
+	tlvlist = aim_readtlvchain(bs);
 
-  /* 
-   * Depending on what informational text was requested, different
-   * TLVs will appear here.
-   *
-   * Profile will be 1 and 2, away message will be 3 and 4.
-   */
-  if (aim_gettlv(tlvlist, 0x0001, 1)) {
-    text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
-    text = aim_gettlv_str(tlvlist, 0x0002, 1);
-  } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
-    text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
-    text = aim_gettlv_str(tlvlist, 0x0004, 1);
-  }
+	/* 
+	 * Depending on what informational text was requested, different
+	 * TLVs will appear here.
+	 *
+	 * Profile will be 1 and 2, away message will be 3 and 4.
+	 */
+	if (aim_gettlv(tlvlist, 0x0001, 1)) {
+		text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
+		text = aim_gettlv_str(tlvlist, 0x0002, 1);
+	} else if (aim_gettlv(tlvlist, 0x0003, 1)) {
+		text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
+		text = aim_gettlv_str(tlvlist, 0x0004, 1);
+	}
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo, text_encoding, text, inforeq->infotype);
 
-  free(text_encoding);
-  free(text);
-
-  aim_freetlvchain(&tlvlist);
+	free(text_encoding);
+	free(text);
 
-  if (origsnac) {
-    if (origsnac->data)
-      free(origsnac->data);
-    free(origsnac);
-  }
+	aim_freetlvchain(&tlvlist);
 
-  return ret;
+	if (origsnac)
+		free(origsnac->data);
+	free(origsnac);
+
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if (snac->subtype == 0x0006)
-    return userinfo(sess, mod, rx, snac, data, datalen);
+	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;
+	return 0;
 }
 
-faim_internal int locate_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x0002;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "locate", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x0002;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "locate", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
--- a/src/protocols/oscar/login.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/login.c	Sun Sep 09 10:07:14 2001 +0000
@@ -12,109 +12,152 @@
 
 static int aim_encode_password(const char *password, unsigned char *encoded);
 
-faim_export int aim_sendflapver(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn)
 {
-	int curbyte=0;
+	aim_frame_t *fr;
 
-	struct command_tx_struct *newpacket;
-
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 4)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4)))
 		return -ENOMEM;
 
-	newpacket->lock = 1;
+	aimbs_put32(&fr->data, 0x00000001);
 
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+	aim_tx_enqueue(sess, fr);
 
-	newpacket->lock = 0;
-
-	return aim_tx_enqueue(sess, newpacket);
+	return 0;
 }
 
 /*
- * 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).  
+ * 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()...
+ *
  */
-faim_export int aim_request_login(struct aim_session_t *sess, struct aim_conn_t *conn, const char *sn)
+static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
-	int curbyte;
-	struct command_tx_struct *newpacket;
+	aim_frame_t fr;
+	aim_rxcallback_t userfunc;
+	
+	sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
+	sess->flags |= AIM_SESS_FLAGS_XORLOGIN;
+
+	fr.conn = conn;
+	
+	if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007)))
+		userfunc(sess, &fr, "");
+
+	return 0;
+}
 
+/*
+ * 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). 
+ *
+ * XXX make ICQ logins work again. 
+ */
+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;
 
-	/*
-	 * For ICQ, we enable the ancient horrible login and stuff
-	 * a key packet into the queue to make it look like we got
-	 * a reply back. This is so the client doesn't know we're
-	 * really not doing MD5 login.
-	 *
-	 * This may sound stupid, but I'm not in the best of moods and 
-	 * I don't plan to keep support for this crap around much longer.
-	 * Its all AOL's fault anyway, really. I hate AOL.  Really.  They
-	 * always seem to be able to piss me off by doing the dumbest little
-	 * things.  Like disabling MD5 logins for ICQ UINs, or adding
-	 * purposefully wrong TLV lengths, or adding superfluous information 
-	 * to host strings, or... I'll stop.
-	 *
-	 */
-	if ((sn[0] >= '0') && (sn[0] <= '9')) {
-		struct command_rx_struct *newrx;
-		int i;
-
-		/* XXX Uhm why doesn't this use aim_tx_new? */
-		if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct))))
-			return -ENOMEM;
-
-		memset(newrx, 0x00, sizeof(struct command_rx_struct));
-		newrx->lock = 1; 
-		newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
-		newrx->hdr.oscar.type = 0x02;
-		newrx->hdr.oscar.seqnum = 0;
-		newrx->commandlen = 10+2+1;
-		newrx->nofree = 0; 
-
-		if (!(newrx->data = malloc(newrx->commandlen))) {
-			free(newrx);
-			return -ENOMEM;
-		}
-
-		i = aim_putsnac(newrx->data, 0x0017, 0x0007, 0x0000, 0x0000);
-		i += aimutil_put16(newrx->data+i, 0x01);
-		i += aimutil_putstr(newrx->data+i, "0", 1);
-
-		newrx->conn = conn;
-
-		newrx->next = sess->queue_incoming;
-		sess->queue_incoming = newrx;
-
-		newrx->lock = 0;
-
-		sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
-
-		return 0;
-	} 
+	if ((sn[0] >= '0') || (sn[0] <= '9'))
+		return goddamnicq(sess, conn, sn);
 
 	sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
 
 	aim_sendflapver(sess, conn);
 
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(sn))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn))))
 		return -ENOMEM;
 
-	newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid);
+
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
 
-	curbyte  = aim_putsnac(newpacket->data, 0x0017, 0x0006, 0x0000, 0x00010000);
-	curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+/*
+ * Part two of the ICQ hack.  Note the ignoring of the key and clientinfo.
+ */
+static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password)
+{
+	static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85"};
+	static const char lang[] = {"en"};
+	static const char country[] = {"us"};
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	char *password_encoded;
+
+	if (!(password_encoded = (char *) malloc(strlen(password))))
+		return -ENOMEM;
 
-	newpacket->commandlen = curbyte;
-	newpacket->lock = 0;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) {
+		free(password_encoded);
+		return -ENOMEM;
+	}
+
+	aim_encode_password(password, password_encoded);
 
-	return aim_tx_enqueue(sess, newpacket);
+	aimbs_put32(&fr->data, 0x00000001);
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn);
+	aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded);
+	aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), clientstr);
+	aim_addtlvtochain16(&tl, 0x0016, 0x010a);
+	aim_addtlvtochain16(&tl, 0x0017, 0x0004);
+	aim_addtlvtochain16(&tl, 0x0018, 0x0041);
+	aim_addtlvtochain16(&tl, 0x0019, 0x0001);
+	aim_addtlvtochain16(&tl, 0x001a, 0x0cd1);
+	aim_addtlvtochain32(&tl, 0x0014, 0x00000055);
+	aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), lang);
+	aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), country);
+
+	aim_writetlvchain(&fr->data, &tl);
+
+	free(password_encoded);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
 /*
@@ -176,79 +219,62 @@
  *   serverstore = 0x01
  *
  */
-faim_export int aim_send_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *password, struct client_info_s *clientinfo, char *key)
+faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *clientinfo, const char *key)
 {
-	int curbyte=0;
-	struct command_tx_struct *newpacket;
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	fu8_t digest[16];
+	aim_snacid_t snacid;
 
 	if (!clientinfo || !sn || !password)
 		return -EINVAL;
 
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+	if (sess->flags & AIM_SESS_FLAGS_XORLOGIN)
+		return goddamnicq2(sess, conn, sn, password);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
 		return -ENOMEM;
 
-	newpacket->lock = 1;
-
-	newpacket->hdr.oscar.type = (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)?0x02:0x01;
-
-	if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
-		curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0002, 0x0000, 0x00010000);
-	else {
-		curbyte  = aimutil_put16(newpacket->data, 0x0000);
-		curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-	}
-
-	curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+	if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) {
+		fr->hdr.flap.type = 0x01;
 
-	if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
-		unsigned char digest[16];
-
-		aim_encode_password_md5(password, key, digest);
-		curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0025, 16, (char *)digest);
-	} else { 
-		char *password_encoded;
-
-		password_encoded = (char *) malloc(strlen(password));
-		aim_encode_password(password, password_encoded);
-		curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded);
-		free(password_encoded);
+		/* Use very specific version numbers to further indicate hack */
+		clientinfo->major2 = 0x010a;
+		clientinfo->major = 0x0004;
+		clientinfo->minor = 0x003c;
+		clientinfo->minor2 = 0x0001;
+		clientinfo->build = 0x0cce;
+		clientinfo->unknown = 0x00000055;
 	}
 
-	curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring);
-
-	if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
+	snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
 
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, (unsigned short)clientinfo->major2);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, (unsigned short)clientinfo->major);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, (unsigned short)clientinfo->minor);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, (unsigned short)clientinfo->minor2);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, (unsigned short)clientinfo->build);
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn);
+
+	aim_encode_password_md5(password, key, digest);
+	aim_addtlvtochain_raw(&tl, 0x0025, 16, digest);
 
-	} else {
-		/* Use very specific version numbers, to further indicate the hack. */
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0004);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x003c);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0cce);
-		curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055);
-	}
+	aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring);
+	aim_addtlvtochain16(&tl, 0x0016, (fu16_t)clientinfo->major2);
+	aim_addtlvtochain16(&tl, 0x0017, (fu16_t)clientinfo->major);
+	aim_addtlvtochain16(&tl, 0x0018, (fu16_t)clientinfo->minor);
+	aim_addtlvtochain16(&tl, 0x0019, (fu16_t)clientinfo->minor2);
+	aim_addtlvtochain16(&tl, 0x001a, (fu16_t)clientinfo->build);
+	aim_addtlvtochain_raw(&tl, 0x000e, strlen(clientinfo->country), clientinfo->country);
+	aim_addtlvtochain_raw(&tl, 0x000f, strlen(clientinfo->lang), clientinfo->lang);
+	aim_addtlvtochain16(&tl, 0x0009, 0x0015);
 
-	curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, strlen(clientinfo->country), clientinfo->country);
-	curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, strlen(clientinfo->lang), clientinfo->lang);
+	aim_writetlvchain(&fr->data, &tl);
 
-	if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
-		curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, clientinfo->unknown);
-		curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
-	}
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, fr);
 
-	newpacket->commandlen = curbyte;
-	newpacket->lock = 0;
-
-	return aim_tx_enqueue(sess, newpacket);
+	return 0;
 }
 
-faim_export int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest)
+faim_export int aim_encode_password_md5(const char *password, const char *key, fu8_t *digest)
 {
 	md5_state_t state;
 
@@ -278,9 +304,9 @@
  * This is only used for the XOR method, not the better MD5 method.
  *
  */
-static int aim_encode_password(const char *password, unsigned char *encoded)
+static int aim_encode_password(const char *password, fu8_t *encoded)
 {
-	unsigned char encoding_table[] = {
+	fu8_t encoding_table[] = {
 #if 0 /* old v1 table */
 		0xf3, 0xb3, 0x6c, 0x99,
 		0x95, 0x3f, 0xac, 0xb6,
@@ -304,55 +330,51 @@
 /*
  * Generate an authorization response.  
  *
- * You probably don't want this unless you're writing an AIM server.
+ * You probably don't want this unless you're writing an AIM server. Which
+ * I hope you're not doing.  Because it's far more difficult than it looks.
  *
  */
-faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, 
-					   struct aim_conn_t *conn, 
-					   char *sn, int errorcode,
-					   char *errorurl, char *bosip, 
-					   char *cookie, char *email, 
-					   int regstatus)
+faim_export int aim_sendauthresp(aim_session_t *sess, aim_conn_t *conn, const char *sn, int errorcode, const char *errorurl, const char *bosip, const char *cookie, const char *email, int regstatus)
 {	
-	struct command_tx_struct *tx;
-	struct aim_tlvlist_t *tlvlist = NULL;
+	aim_tlvlist_t *tlvlist = NULL;
+	aim_frame_t *fr;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0004, 1152)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x04, 1152)))
 		return -ENOMEM;
 
-	tx->lock = 1;
-
 	if (sn)
-		aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn));
+		aim_addtlvtochain_raw(&tlvlist, 0x0001, strlen(sn), sn);
 	else
-		aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn));
+		aim_addtlvtochain_raw(&tlvlist, 0x0001, strlen(sess->sn), sess->sn);
 
 	if (errorcode) {
 		aim_addtlvtochain16(&tlvlist, 0x0008, errorcode);
-		aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl));
+		aim_addtlvtochain_raw(&tlvlist, 0x0004, strlen(errorurl), errorurl);
 	} else {
-		aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip));
-		aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
-		aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email));
-		aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus);
+		aim_addtlvtochain_raw(&tlvlist, 0x0005, strlen(bosip), bosip);
+		aim_addtlvtochain_raw(&tlvlist, 0x0006, AIM_COOKIELEN, cookie);
+		aim_addtlvtochain_raw(&tlvlist, 0x0011, strlen(email), email);
+		aim_addtlvtochain16(&tlvlist, 0x0013, (fu16_t)regstatus);
 	}
 
-	tx->commandlen = aim_writetlvchain(tx->data, tx->commandlen, &tlvlist);
-	tx->lock = 0;
+	aim_writetlvchain(&fr->data, &tlvlist);
+	aim_freetlvchain(&tlvlist);
 
-	return aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
 /*
  * Generate a random cookie.  (Non-client use only)
  */
-faim_export int aim_gencookie(unsigned char *buf)
+faim_export int aim_gencookie(fu8_t *buf)
 {
 	int i;
 
 	srand(time(NULL));
 
-	for (i=0; i < AIM_COOKIELEN; i++)
+	for (i = 0; i < AIM_COOKIELEN; i++)
 		buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0));
 
 	return i;
@@ -361,102 +383,97 @@
 /*
  * Send Server Ready.  (Non-client)
  */
-faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_sendserverready(aim_session_t *sess, aim_conn_t *conn)
 {
-	struct command_tx_struct *tx;
-	int i;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+0x22)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+0x22)))
 		return -ENOMEM;
 
-	tx->lock = 1;
-
-	i = aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0003, 0x0000, NULL, 0);
 
-	i += aimutil_put16(tx->data+i, 0x0001);  
-	i += aimutil_put16(tx->data+i, 0x0002);
-	i += aimutil_put16(tx->data+i, 0x0003);
-	i += aimutil_put16(tx->data+i, 0x0004);
-	i += aimutil_put16(tx->data+i, 0x0006);
-	i += aimutil_put16(tx->data+i, 0x0008);
-	i += aimutil_put16(tx->data+i, 0x0009);
-	i += aimutil_put16(tx->data+i, 0x000a);
-	i += aimutil_put16(tx->data+i, 0x000b);
-	i += aimutil_put16(tx->data+i, 0x000c);
-	i += aimutil_put16(tx->data+i, 0x0013);
-	i += aimutil_put16(tx->data+i, 0x0015);
+	aim_putsnac(&fr->data, 0x0001, 0x0003, 0x0000, snacid);
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0006);
+	aimbs_put16(&fr->data, 0x0008);
+	aimbs_put16(&fr->data, 0x0009);
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x000b);
+	aimbs_put16(&fr->data, 0x000c);
+	aimbs_put16(&fr->data, 0x0013);
+	aimbs_put16(&fr->data, 0x0015);
 
-	tx->commandlen = i;
-	tx->lock = 0;
+	aim_tx_enqueue(sess, fr);
 
-	return aim_tx_enqueue(sess, tx);
+	return 0;
 }
 
 
 /* 
  * Send service redirect.  (Non-Client)
  */
-faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie)
+faim_export int aim_sendredirect(aim_session_t *sess, aim_conn_t *conn, fu16_t servid, const char *ip, const char *cookie)
 {	
-	struct command_tx_struct *tx;
-	struct aim_tlvlist_t *tlvlist = NULL;
-	int i;
+	aim_tlvlist_t *tlvlist = NULL;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
 		return -ENOMEM;
 
-	tx->lock = 1;
-
-	i = aim_putsnac(tx->data, 0x0001, 0x0005, 0x0000, 0x00000000);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0005, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0005, 0x0000, snacid);
 
 	aim_addtlvtochain16(&tlvlist, 0x000d, servid);
-	aim_addtlvtochain_str(&tlvlist, 0x0005, ip, strlen(ip));
-	aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
+	aim_addtlvtochain_raw(&tlvlist, 0x0005, strlen(ip), ip);
+	aim_addtlvtochain_raw(&tlvlist, 0x0006, AIM_COOKIELEN, cookie);
 
-	tx->commandlen = aim_writetlvchain(tx->data+i, tx->commandlen-i, &tlvlist)+i;
+	aim_writetlvchain(&fr->data, &tlvlist);
 	aim_freetlvchain(&tlvlist);
 
-	tx->lock = 0;
+	aim_tx_enqueue(sess, fr);
 
-	return aim_tx_enqueue(sess, tx);
+	return 0;
 }
 
 
-static int hostonline(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int hostonline(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;
-	unsigned short *families;
-	int famcount, i;
+	fu16_t *families;
+	int famcount;
 
-	famcount = datalen/2;
-
-	if (!(families = malloc(datalen)))
+	if (!(families = malloc(aim_bstream_empty(bs))))
 		return 0;
 
-	for (i = 0; i < famcount; i++)
-		families[i] = aimutil_get16(data+(i*2));
+	for (famcount = 0; aim_bstream_empty(bs); famcount++)
+		families[famcount] = aimbs_get16(bs);
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 		ret = userfunc(sess, rx, famcount, families);
 
 	free(families);
 
-	return ret;  
+	return ret; 
 }
 
-static int redirect(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int serviceid;
-	unsigned char *cookie;
+	fu8_t *cookie;
 	char *ip;
 	aim_rxcallback_t userfunc;
-	struct aim_tlvlist_t *tlvlist;
+	aim_tlvlist_t *tlvlist;
 	char *chathack = NULL;
 	int chathackex = 0;
 	int ret = 0;
 
-	tlvlist = aim_readtlvchain(data, datalen);
+	tlvlist = aim_readtlvchain(bs);
 
 	if (!aim_gettlv(tlvlist, 0x000d, 1) ||
 			!aim_gettlv(tlvlist, 0x0005, 1) ||
@@ -542,7 +559,7 @@
  */
 
 /* XXX parse this */
-static int rateresp(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
 
@@ -552,33 +569,22 @@
 	return 0;
 }
 
-static int ratechange(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int ratechange(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 i = 0, code;
-	unsigned long currentavg, maxavg;
-	unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
-
-	code = aimutil_get16(data+i);
-	i += 2;
-
-	rateclass = aimutil_get16(data+i);
-	i += 2;
+	fu16_t code, rateclass;
+	fu32_t currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
 
-	windowsize = aimutil_get32(data+i);
-	i += 4;
-	clear = aimutil_get32(data+i);
-	i += 4;
-	alert = aimutil_get32(data+i);
-	i += 4;
-	limit = aimutil_get32(data+i);
-	i += 4;
-	disconnect = aimutil_get32(data+i);
-	i += 4;
-	currentavg = aimutil_get32(data+i);
-	i += 4;
-	maxavg = aimutil_get32(data+i);
-	i += 4;
+	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)))
 		return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
@@ -587,7 +593,7 @@
 }
 
 /* XXX parse this */
-static int selfinfo(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
 
@@ -597,20 +603,18 @@
 	return 0;
 }
 
-static int evilnotify(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-	aim_rxcallback_t userfunc = NULL;
-	int i = 0;
-	unsigned short newevil;
+	aim_rxcallback_t userfunc;
+	fu16_t newevil;
 	struct aim_userinfo_s userinfo;
 
-	newevil = aimutil_get16(data);
-	i += 2;
+	memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
+	
+	newevil = aimbs_get16(bs);
 
-	memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
-
-	if (datalen-i)
-		i += aim_extractuserinfo(sess, data+i, &userinfo);
+	if (aim_bstream_empty(bs))
+		aim_extractuserinfo(sess, bs, &userinfo);
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 		return userfunc(sess, rx, newevil, &userinfo);
@@ -618,13 +622,13 @@
 	return 0;
 }
 
-static int motd(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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;
-	struct aim_tlvlist_t *tlvlist;
-	unsigned short id;
+	aim_tlvlist_t *tlvlist;
+	fu16_t id;
 
 	/*
 	 * Code.
@@ -636,13 +640,14 @@
 	 *   4 Nothing's wrong ("top o the world" -- normal)
 	 *
 	 */
-	id = aimutil_get16(data);
+	id = aimbs_get16(bs);
 
 	/* 
 	 * TLVs follow 
 	 */
-	if ((tlvlist = aim_readtlvchain(data+2, datalen-2)))
-		msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+	tlvlist = aim_readtlvchain(bs);
+
+	msg = aim_gettlv_str(tlvlist, 0x000b, 1);
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 		ret = userfunc(sess, rx, id, msg);
@@ -654,15 +659,19 @@
 	return ret;
 }
 
-static int hostversions(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int hostversions(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 vercount;
+	fu8_t *versions;
 
-	vercount = datalen/4;
+	vercount = aim_bstream_empty(bs)/4;
+	versions = aimbs_getraw(bs, aim_bstream_empty(bs));
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		return userfunc(sess, rx, vercount, data);
+		return userfunc(sess, rx, vercount, versions);
+
+	free(versions);
 
 	return 0;
 }
@@ -704,24 +713,18 @@
  * Anyway, neener.  We win again.
  *
  */
-static int memrequest(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
-	unsigned long offset, len;
-	int i = 0;
-	struct aim_tlvlist_t *list;
-	char *modname = NULL;
-
-	offset = aimutil_get32(data);
-	i += 4;
+	fu32_t offset, len;
+	aim_tlvlist_t *list;
+	char *modname;
 
-	len = aimutil_get32(data+4);
-	i += 4;
+	offset = aimbs_get32(bs);
+	len = aimbs_get32(bs);
+	list = aim_readtlvchain(bs);
 
-	list = aim_readtlvchain(data+i, datalen-i);
-
-	if (aim_gettlv(list, 0x0001, 1))
-		modname = aim_gettlv_str(list, 0x0001, 1);
+	modname = aim_gettlv_str(list, 0x0001, 1);
 
 	faimdprintf(sess, 1, "data at 0x%08lx (%d bytes) of requested\n", offset, len, modname ? modname : "aim.exe");
 
@@ -734,7 +737,8 @@
 	return 0;
 }
 
-static void dumpbox(struct aim_session_t *sess, unsigned char *buf, int len)
+#if 0
+static void dumpbox(aim_session_t *sess, unsigned char *buf, int len)
 {
 	int i;
 
@@ -754,39 +758,42 @@
 
 	return;
 }
+#endif
 
-faim_export int aim_sendmemblock(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long offset, unsigned long len, const unsigned char *buf, unsigned char flag)
+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 command_tx_struct *tx;
-	int i = 0;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
 	if (!sess || !conn)
 		return -EINVAL;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+16)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
 		return -ENOMEM;
 
-	tx->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
 
-	i = aim_putsnac(tx->data, 0x0001, 0x0020, 0x0000, sess->snac_nextid++);
-	i += aimutil_put16(tx->data+i, 0x0010); /* md5 is always 16 bytes */
+	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 */
 
-		memcpy(tx->data+i, buf, 0x10);
-		i += 0x10;
+		aimbs_putraw(&fr->data, buf, 0x10); 
 
 	} else if (buf && (len > 0)) { /* use input buffer */
 		md5_state_t state;
+		md5_byte_t digest[0x10];
 
 		md5_init(&state);	
 		md5_append(&state, (const md5_byte_t *)buf, len);
-		md5_finish(&state, (md5_byte_t *)(tx->data+i));
-		i += 0x10;
+		md5_finish(&state, digest);
+
+		aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10);
 
 	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
 		md5_state_t state;
-		unsigned char nil = '\0';
+		fu8_t nil = '\0';
+		md5_byte_t digest[0x10];
 
 		/*
 		 * These MD5 routines are stupid in that you have to have
@@ -795,8 +802,9 @@
 		 */
 		md5_init(&state);
 		md5_append(&state, (const md5_byte_t *)&nil, 0);
-		md5_finish(&state, (md5_byte_t *)(tx->data+i));
-		i += 0x10;
+		md5_finish(&state, digest);
+
+		aimbs_putraw(&fr->data, (fu8_t *)digest, 0x10);
 
 	} else {
 
@@ -810,62 +818,60 @@
 		if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
 
 #if 1 /* with "AnrbnrAqhfzcd" */
-			i += aimutil_put32(tx->data+i, 0x44a95d26);
-			i += aimutil_put32(tx->data+i, 0xd2490423);
-			i += aimutil_put32(tx->data+i, 0x93b8821f);
-			i += aimutil_put32(tx->data+i, 0x51c54b01);
+			aimbs_put32(&fr->data, 0x44a95d26);
+			aimbs_put32(&fr->data, 0xd2490423);
+			aimbs_put32(&fr->data, 0x93b8821f);
+			aimbs_put32(&fr->data, 0x51c54b01);
 #else /* no filename */
-			i += aimutil_put32(tx->data+i, 0x1df8cbae);
-			i += aimutil_put32(tx->data+i, 0x5523b839);
-			i += aimutil_put32(tx->data+i, 0xa0e10db3);
-			i += aimutil_put32(tx->data+i, 0xa46d3b39);
+			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)) {
 
-			i += aimutil_put32(tx->data+i, 0xd41d8cd9);
-			i += aimutil_put32(tx->data+i, 0x8f00b204);
-			i += aimutil_put32(tx->data+i, 0xe9800998);
-			i += aimutil_put32(tx->data+i, 0xecf8427e);
+			aimbs_put32(&fr->data, 0xd41d8cd9);
+			aimbs_put32(&fr->data, 0x8f00b204);
+			aimbs_put32(&fr->data, 0xe9800998);
+			aimbs_put32(&fr->data, 0xecf8427e);
 
 		} else
 			faimdprintf(sess, 0, "sendmemblock: WARNING: unknown hash request\n");
 
 	}
 
-	tx->commandlen = i;
-	tx->lock = 0;
-	aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
+		return hostonline(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0005)
-		return redirect(sess, mod, rx, snac, data, datalen);
+		return redirect(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0007)
-		return rateresp(sess, mod, rx, snac, data, datalen);
+		return rateresp(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x000a)
-		return ratechange(sess, mod, rx, snac, data, datalen);
+		return ratechange(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x000f)
-		return selfinfo(sess, mod, rx, snac, data, datalen);
+		return selfinfo(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0010)
-		return evilnotify(sess, mod, rx, snac, data, datalen);
+		return evilnotify(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0013)
-		return motd(sess, mod, rx, snac, data, datalen);
+		return motd(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0018)
-		return hostversions(sess, mod, rx, snac, data, datalen);
+		return hostversions(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x001f)
-		return memrequest(sess, mod, rx, snac, data, datalen);
+		return memrequest(sess, mod, rx, snac, bs);
 
 	return 0;
 }
 
-faim_internal int general_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int general_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
 	mod->family = 0x0001;
--- a/src/protocols/oscar/meta.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/meta.c	Sun Sep 09 10:07:14 2001 +0000
@@ -8,41 +8,42 @@
 
 faim_export char *aim_getbuilddate(void)
 {
-  return AIM_BUILDDATE;
+	return AIM_BUILDDATE;
 }
 
 faim_export char *aim_getbuildtime(void)
 {
-  return AIM_BUILDTIME;
+	return AIM_BUILDTIME;
 }
 
 faim_export int aim_getbuildstring(char *buf, int buflen)
 {
 
-  snprintf(buf, buflen, "%d.%d.%d-%s%s", 
-	   FAIM_VERSION_MAJOR,
-	   FAIM_VERSION_MINOR,
-	   FAIM_VERSION_MINORMINOR,
-	   aim_getbuilddate(),
-	   aim_getbuildtime());
+	snprintf(buf, buflen, "%d.%d.%d-%s%s", 
+			FAIM_VERSION_MAJOR,
+			FAIM_VERSION_MINOR,
+			FAIM_VERSION_MINORMINOR,
+			aim_getbuilddate(),
+			aim_getbuildtime());
 
-  return 0;
+	return 0;
 }
 
-faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...)
+faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...)
 {
-  if (!sess) {
-    fprintf(stderr, "faimdprintf: no session! boo! (%d, %s)\n", dlevel, format);
-    return;
-  }
+	if (!sess) {
+		fprintf(stderr, "faimdprintf: no session! boo! (%d, %s)\n", dlevel, format);
+		return;
+	}
 
-  if ((dlevel <= sess->debug) && sess->debugcb) {
-    va_list ap;
+	if ((dlevel <= sess->debug) && sess->debugcb) {
+		va_list ap;
 
-    va_start(ap, format);
-    sess->debugcb(sess, dlevel, format, ap);
-    va_end(ap);
-  }
+		va_start(ap, format);
+		sess->debugcb(sess, dlevel, format, ap);
+		va_end(ap);
+	}
 
-  return;
+	return;
 }
+
--- a/src/protocols/oscar/misc.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/misc.c	Sun Sep 09 10:07:14 2001 +0000
@@ -22,11 +22,9 @@
  *  time.  
  *
  */
-faim_export unsigned long aim_bos_setidle(struct aim_session_t *sess,
-					  struct aim_conn_t *conn, 
-					  u_long idletime)
+faim_export int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, fu32_t idletime)
 {
-  return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
 }
 
 
@@ -56,70 +54,58 @@
  *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
  *      the list of users to be blocked
  *
- *
+ * XXX ye gods.
  */
-faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *sess,
-						   struct aim_conn_t *conn, 
-						   int changetype, 
-						   char *denylist)
+faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist)
 {
-  struct command_tx_struct *newpacket;
-  int packlen = 0;
-  u_short subtype;
+	aim_frame_t *fr;
+	int packlen = 0;
+	fu16_t subtype;
+	char *localcpy = NULL, *tmpptr = NULL;
+	int i;
+	int listcount;
+	aim_snacid_t snacid;
 
-  char *localcpy = NULL;
-  char *tmpptr = NULL;
-  int i,j;
-  int listcount;
-
-  if (!denylist)
-    return 0;
+	if (!denylist)
+		return -EINVAL;
 
-  localcpy = (char *) malloc(strlen(denylist)+1);
-  memcpy(localcpy, denylist, strlen(denylist)+1);
-  
-  listcount = aimutil_itemcnt(localcpy, '&');
-  packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
-
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen)))
-    return -1;
-
-  newpacket->lock = 1;
+	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;
 
-  switch(changetype)
-    {
-    case AIM_VISIBILITYCHANGE_PERMITADD:    subtype = 0x05; break;
-    case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break;
-    case AIM_VISIBILITYCHANGE_DENYADD:      subtype = 0x07; break;
-    case AIM_VISIBILITYCHANGE_DENYREMOVE:   subtype = 0x08; break;
-    default:
-      free(newpacket->data);
-      free(newpacket);
-      return 0;
-    }
+	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);
 
-  /* We actually DO NOT send a SNAC ID with this one! */
-  aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0);
- 
-  j = 10;  /* the next byte */
-  
-  for (i=0; (i < (listcount - 1)) && (i < 99); i++)
-    {
-      tmpptr = aimutil_itemidx(localcpy, i, '&');
+	for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
+		tmpptr = aimutil_itemidx(localcpy, i, '&');
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr));
 
-      newpacket->data[j] = strlen(tmpptr);
-      memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
-      j += strlen(tmpptr)+1;
-      free(tmpptr);
-    }
-  free(localcpy);
+		free(tmpptr);
+	}
+	free(localcpy);
 
-  newpacket->lock = 0;
+	aim_tx_enqueue(sess, fr);
 
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid); /* dont increment */
-
+	return 0;
 }
 
 
@@ -136,63 +122,50 @@
  * XXX: I can't stress the TODO enough.
  *
  */
-faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *sess,
-					       struct aim_conn_t *conn, 
-					       char *buddy_list)
+faim_export int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
 {
-  int i, j;
-
-  struct command_tx_struct *newpacket;
-
-  int len = 0;
-
-  char *localcpy = NULL;
-  char *tmpptr = NULL;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int i, len = 0;
+	char *localcpy = NULL;
+	char *tmpptr = NULL;
 
-  len = 10; /* 10B SNAC headers */
-
-  if (!buddy_list || !(localcpy = (char *) malloc(strlen(buddy_list)+1))) 
-    return -1;
-  strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
+	if (!buddy_list || !(localcpy = strdup(buddy_list))) 
+		return -EINVAL;
 
-  i = 0;
-  tmpptr = strtok(localcpy, "&");
-  while ((tmpptr != NULL) && (i < 150)) {
-    faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
-    len += 1+strlen(tmpptr);
-    i++;
-    tmpptr = strtok(NULL, "&");
-  }
-  faimdprintf(sess, 2, "*** send buddy list len: %d (%x)\n", len, len);
+	i = 0;
+	tmpptr = strtok(localcpy, "&");
+	while ((tmpptr != NULL) && (i < 150)) {
+		faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
+		len += 1+strlen(tmpptr);
+		i++;
+		tmpptr = strtok(NULL, "&");
+	}
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, len)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-  
-  aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, 0);
-
-  j = 10;  /* the next byte */
+	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);
-  i = 0;
-  tmpptr = strtok(localcpy, "&");
-  while ((tmpptr != NULL) & (i < 150)) {
-    faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
-    newpacket->data[j] = strlen(tmpptr);
-    memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
-    j += 1+strlen(tmpptr);
-    i++;
-    tmpptr = strtok(NULL, "&");
-  }
+	strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
+	i = 0;
+	tmpptr = strtok(localcpy, "&");
+	while ((tmpptr != NULL) & (i < 150)) {
+		faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
+		
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr));
+		i++;
+		tmpptr = strtok(NULL, "&");
+	}
 
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  free(localcpy);
+	free(localcpy);
 
-  return (sess->snac_nextid);
+	return 0;
 }
 
 /* 
@@ -202,49 +175,38 @@
  *
  * 
  */
-faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     const char *profile,
-					     const char *awaymsg,
-					     unsigned short caps)
+faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu16_t caps)
 {
-  struct command_tx_struct *newpacket;
-  int i = 0, tmp, caplen;
-  static const char defencoding[] = {"text/aolrtf; charset=\"us-ascii\""};
-
-  i = 10;
-  if (profile)
-    i += 4+strlen(defencoding)+4+strlen(profile);
-  if (awaymsg)
-    i += 4+strlen(defencoding)+4+strlen(awaymsg);
-  i += 4+512; /* for capabilities */
-
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, i)))
-    return -1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x004, 0x0000, sess->snac_nextid);
+	static const char defencoding[] = {"text/aolrtf; charset=\"us-ascii\""};
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
 
-  if (profile) {
-    i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(defencoding), defencoding);
-    i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(profile), profile);
-  }
+	/* Build to packet first to get real length */
+	if (profile) {
+		aim_addtlvtochain_raw(&tl, 0x0001, strlen(defencoding), defencoding);
+		aim_addtlvtochain_raw(&tl, 0x0002, strlen(profile), profile);
+	}
+	
+	if (awaymsg) {
+		aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), defencoding);
+		aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), awaymsg);
+	}
 
-  if (awaymsg) {
-    i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(defencoding), defencoding);
-    i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(awaymsg), awaymsg);
-  }
+	aim_addtlvtochain_caps(&tl, 0x0005, caps);
 
-  /* Capability information. */
- 
-  tmp = (i += aimutil_put16(newpacket->data+i, 0x0005));
-  i += aimutil_put16(newpacket->data+i, 0x0000); /* rewritten later */
-  i += (caplen = aim_putcap(newpacket->data+i, 512, caps));
-  aimutil_put16(newpacket->data+tmp, caplen); /* rewrite TLV size */
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
+		return -ENOMEM;
 
-  newpacket->commandlen = i;
-  aim_tx_enqueue(sess, newpacket);
-  
-  return (sess->snac_nextid++);
+	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
 /*
@@ -253,85 +215,75 @@
  * Send Client Ready.  
  *
  */
-faim_export unsigned long aim_bos_clientready(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
+faim_export int aim_bos_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct aim_tool_version tools[] = {
-    {0x0001, 0x0003,    AIM_TOOL_WIN32, 0x0686},
-    {0x0002, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x0003, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0004, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0006, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x0008, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0009, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x000a, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x000b, 0x0001,    AIM_TOOL_WIN32, 0x0001}
-  };
-  int i,j;
-  struct command_tx_struct *newpacket;
-  int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
+	struct aim_tool_version tools[] = {
+		{0x0001, 0x0003,    AIM_TOOL_WIN32, 0x0686},
+		{0x0002, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+		{0x0003, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+		{0x0004, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+		{0x0006, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+		{0x0008, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+		{0x0009, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+		{0x000a, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+		{0x000b, 0x0001,    AIM_TOOL_WIN32, 0x0001}
+	};
+	int j;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
 
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
 
-  for (j = 0; j < toolcount; j++) {
-    i += aimutil_put16(newpacket->data+i, tools[j].group);
-    i += aimutil_put16(newpacket->data+i, tools[j].version);
-    i += aimutil_put16(newpacket->data+i, tools[j].tool);
-    i += aimutil_put16(newpacket->data+i, tools[j].toolversion);
-  }
+	for (j = 0; j < toolcount; j++) {
+		aimbs_put16(&fr->data, tools[j].group);
+		aimbs_put16(&fr->data, tools[j].version);
+		aimbs_put16(&fr->data, tools[j].tool);
+		aimbs_put16(&fr->data, tools[j].toolversion);
+	}
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
+	aim_tx_enqueue(sess, fr);
 
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
+	return 0;
 }
 
 /* 
  *  Request Rate Information.
  * 
  */
-faim_export unsigned long aim_bos_reqrate(struct aim_session_t *sess,
-					  struct aim_conn_t *conn)
+faim_export int aim_bos_reqrate(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
+	return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
 }
 
 /* 
  *  Rate Information Response Acknowledge.
  *
  */
-faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
+faim_export int aim_bos_ackrateresp(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-  int packlen = 20, i=0;
+	aim_frame_t *fr;	
+	aim_snacid_t snacid;
 
-  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen)))
-    return (sess->snac_nextid);
-  
-  newpacket->lock = 1;
+	if(!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+10)))
+		return -ENOMEM; 
 
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, 0);
-  i += aimutil_put16(newpacket->data+i, 0x0001); 
-  i += aimutil_put16(newpacket->data+i, 0x0002);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-  i += aimutil_put16(newpacket->data+i, 0x0004);
-  i += aimutil_put16(newpacket->data+i, 0x0005);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
+	aimbs_put16(&fr->data, 0x0001); 
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_put16(&fr->data, 0x0005);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
+	aim_tx_enqueue(sess, fr);
 
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid);
+	return 0;
 }
 
 /* 
@@ -343,81 +295,58 @@
  *  Bit 2:  Allows other AIM users to see how long you've been a member.
  *
  */
-faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *sess,
-						  struct aim_conn_t *conn, 
-						  u_long flags)
+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);
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
 }
 
 /*
  * aim_bos_reqpersonalinfo()
  *
- * Requests the current user's information. Can't go generic on this one
- * because aparently it uses SNAC flags.
- *
  */
-faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *sess,
-						  struct aim_conn_t *conn)
+faim_export int aim_bos_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
+	return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
 }
 
-faim_export unsigned long aim_setversions(struct aim_session_t *sess,
-					  struct aim_conn_t *conn)
+faim_export int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-  int i;
-
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*16))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
-
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-
-  i += aimutil_put16(newpacket->data+i, 0x0002);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x0004);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	static const struct version {
+		fu16_t group;
+		fu16_t version;
+	} versions[] = {
+		{0x0001, 0x0003},
+		{0x0002, 0x0001},
+		{0x0003, 0x0001},
+		{0x0004, 0x0001},
+		{0x0006, 0x0001},
+		{0x0008, 0x0001},
+		{0x0009, 0x0001},
+		{0x000a, 0x0001},
+		{0x000b, 0x0002},
+		{0x000c, 0x0001},
+		{0x0013, 0x0001},
+		{0x0015, 0x0001},
+	};
+	int numversions = sizeof(versions) / sizeof(struct version);
+	int i;
 
-  i += aimutil_put16(newpacket->data+i, 0x0006);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x0008);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + (4*numversions))))
+		return -ENOMEM;
 
-  i += aimutil_put16(newpacket->data+i, 0x0009);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x000a);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x000b);
-  i += aimutil_put16(newpacket->data+i, 0x0002);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
 
-  i += aimutil_put16(newpacket->data+i, 0x000c);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x0013);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
+	aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
+	for (i = 0; i < numversions; i++) {
+		aimbs_put16(&fr->data, versions[i].group);
+		aimbs_put16(&fr->data, versions[i].version);
+	}
 
-  i += aimutil_put16(newpacket->data+i, 0x0015);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
+	aim_tx_enqueue(sess, fr);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
+	return 0;
 }
 
 
@@ -427,11 +356,9 @@
  * Service request. 
  *
  */
-faim_export unsigned long aim_bos_reqservice(struct aim_session_t *sess,
-			  struct aim_conn_t *conn, 
-			  u_short serviceid)
+faim_export int aim_bos_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid)
 {
-  return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
+	return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
 }
 
 /*
@@ -441,10 +368,9 @@
  * the connection alive.  Its not real necessary.
  *
  */
-faim_export unsigned long aim_bos_nop(struct aim_session_t *sess,
-				      struct aim_conn_t *conn)
+faim_export int aim_bos_nop(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
+	return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
 }
 
 /*
@@ -453,21 +379,16 @@
  * No-op.  WinAIM 4.x sends these _every minute_ to keep
  * the connection alive.  
  */
-faim_export unsigned long aim_flap_nop(struct aim_session_t *sess,
-				       struct aim_conn_t *conn)
+faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *newpacket;
-
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0005, 0)))
-    return sess->snac_nextid;
+	aim_frame_t *fr;
 
-  newpacket->lock = 1;
-  newpacket->commandlen = 0;
-  newpacket->lock = 0;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
+		return -ENOMEM;
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return (sess->snac_nextid);
+	return 0;
 }
 
 /*
@@ -476,10 +397,9 @@
  * Request BOS rights.
  *
  */
-faim_export unsigned long aim_bos_reqrights(struct aim_session_t *sess,
-					    struct aim_conn_t *conn)
+faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
+	return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
 }
 
 /*
@@ -488,10 +408,9 @@
  * Request Buddy List rights.
  *
  */
-faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *sess,
-						 struct aim_conn_t *conn)
+faim_export int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
+	return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
 }
 
 /*
@@ -503,35 +422,27 @@
  * returns -1 on error (couldn't alloc packet), 0 on success. 
  *
  */
-faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, const char *destsn, unsigned long flags)
+faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags)
 {
-	struct command_tx_struct *newpacket;
-	int curbyte;
-	unsigned short outflags = 0x0000;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	fu16_t outflags = 0x0000;
 
-	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 
-					strlen(destsn)+13)))
-		return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13)))
+		return -ENOMEM;
 
-	newpacket->lock = 1;
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
 
-	curbyte  = 0;
-	curbyte += aim_putsnac(newpacket->data+curbyte,
-			0x0004, 0x0008, 0x0000, sess->snac_nextid);
+	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
 
 	if (flags & AIM_WARN_ANON)
 		outflags |= 0x0001;
 
-	curbyte += aimutil_put16(newpacket->data+curbyte, outflags); 
-	curbyte += aimutil_put8(newpacket->data+curbyte, strlen(destsn));
-	curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+	aimbs_put16(&fr->data, outflags); 
+	aimbs_put8(&fr->data, strlen(destsn));
+	aimbs_putraw(&fr->data, destsn, strlen(destsn));
 
-	newpacket->commandlen = curbyte;
-	newpacket->lock = 0;
-
-	aim_tx_enqueue(sess, newpacket);
-
-	aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
@@ -541,10 +452,9 @@
  *
  * For aimdebugd.  If you don't know what it is, you don't want to.
  */
-faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess,
-						    struct aim_conn_t *conn)
+faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT);
+	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT);
 }
 
 /*
@@ -559,102 +469,81 @@
  * back to the single.  I don't see any advantage to doing it either way.
  *
  */
-faim_internal unsigned long aim_genericreq_n(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype)
+faim_internal int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype)
 {
-  struct command_tx_struct *newpacket;
+	aim_frame_t *fr;
+	aim_snacid_t snacid = 0x00000000;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10)))
-    return 0;
-
-  newpacket->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+		return -ENOMEM;
 
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, 0x00000000);
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
-faim_internal unsigned long aim_genericreq_n_snacid(struct aim_session_t *sess,
-						    struct aim_conn_t *conn, 
-						    unsigned short family, 
-						    unsigned short subtype)
+faim_internal int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype)
 {
-  struct command_tx_struct *newpacket;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10)))
-    return 0;
-
-  newpacket->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+		return -ENOMEM;
 
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid++;
+	return 0;
 }
 
 /*
  *
  *
  */
-faim_internal unsigned long aim_genericreq_l(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype, 
-					     u_long *longdata)
+faim_internal int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu32_t *longdata)
 {
-  struct command_tx_struct *newpacket;
-  u_long newlong;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  /* If we don't have data, there's no reason to use this function */
-  if (!longdata)
-    return aim_genericreq_n(sess, conn, family, subtype);
+	if (!longdata)
+		return aim_genericreq_n(sess, conn, family, subtype);
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_long))))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4)))
+		return -ENOMEM; 
 
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, 0x00000000);
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
 
-  /* copy in data */
-  newlong = htonl(*longdata);
-  memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long));
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+	aimbs_put32(&fr->data, *longdata);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
-faim_internal unsigned long aim_genericreq_s(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype, 
-					     u_short *shortdata)
+faim_internal int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t subtype, fu16_t *shortdata)
 {
-  struct command_tx_struct *newpacket;
-  u_short newshort;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  /* If we don't have data, there's no reason to use this function */
-  if (!shortdata)
-    return aim_genericreq_n(sess, conn, family, subtype);
+	if (!shortdata)
+		return aim_genericreq_n(sess, conn, family, subtype);
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_short))))
-    return -1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2)))
+		return -ENOMEM; 
 
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, 0x00000000);
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
 
-  /* copy in data */
-  newshort = htons(*shortdata);
-  memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short));
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+	aimbs_put16(&fr->data, *shortdata);
 
-  aim_tx_enqueue(sess, newpacket);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
 /*
@@ -663,202 +552,169 @@
  * Request Location services rights.
  *
  */
-faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *sess,
-						  struct aim_conn_t *conn)
+faim_export int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
 {
-  return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
+	return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
 }
 
 /* 
  * Set directory profile data (not the same as aim_bos_setprofile!)
+ *
+ * privacy: 1 to allow searching, 0 to disallow.
  */
-faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy) 
+faim_export int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, 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) 
 {
-  struct command_tx_struct *newpacket;
-  int packlen = 0, i = 0;
-
-  packlen += 2+2+2;
-
-  if(first) /* TLV 0001 */
-    packlen += (strlen(first) + 4);
-  if(middle) 
-    packlen += (strlen(middle) + 4);
-  if(last)
-    packlen += (strlen(last) + 4);
-  if(maiden)
-    packlen += (strlen(maiden) + 4);
-  if(nickname)
-    packlen += (strlen(nickname) + 4);
-  if(street)
-    packlen += (strlen(street) + 4);
-  if(state)
-    packlen += (strlen(state) + 4);
-  if(city)
-    packlen += (strlen(city) + 4);
-  if(zip)
-    packlen += (strlen(zip) + 4);
-    
-  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x0009, 0x0000, 0);
-
-  /* 000a/0002: privacy: 1 to allow search/disp, 0 to disallow */
-  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy);
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
 
 
-  if (first)
-    i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(first), first);
-  if (middle)
-    i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(middle), middle);
-  if (last)
-    i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(last), last);
-  if (maiden)
-    i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(maiden), maiden);
-  if (nickname)
-    i += aim_puttlv_str(newpacket->data+i, 0x000c, strlen(nickname), nickname);
-  if (street)
-    i += aim_puttlv_str(newpacket->data+i, 0x0021, strlen(street), street);
-  if (city)
-    i += aim_puttlv_str(newpacket->data+i, 0x0008, strlen(city), city);
-  if (state)
-    i += aim_puttlv_str(newpacket->data+i, 0x0007, strlen(state), state);
-  if (zip)
-    i += aim_puttlv_str(newpacket->data+i, 0x000d, strlen(zip), zip);
+	aim_addtlvtochain16(&tl, 0x000a, privacy);
+
+	if (first)
+		aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), first);
+	if (last)
+		aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), last);
+	if (middle)
+		aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), middle);
+	if (maiden)
+		aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), maiden);
+
+	if (state)
+		aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), state);
+	if (city)
+		aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), city);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
+	if (nickname)
+		aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), nickname);
+	if (zip)
+		aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), zip);
+
+	if (street)
+		aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), street);
 
-  aim_tx_enqueue(sess, newpacket);
-   
-  return(sess->snac_nextid);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
-faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy)
+/* XXX pass these in better */
+faim_export int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy)
 {
-  struct command_tx_struct *newpacket;
-  int packlen = 0, i = 0;
-
-  packlen += 2+2+2;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
 
-  if(interest1)
-    packlen += (strlen(interest1) + 4);
-  if(interest2)
-    packlen += (strlen(interest2) + 4);
-  if(interest3)
-    packlen += (strlen(interest3) + 4);
-  if(interest4)
-    packlen += (strlen(interest4) + 4);
-  if(interest5)
-    packlen += (strlen(interest5) + 4) ;
-
-    
-  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10)))
-    return -1;
+	/* ?? privacy ?? */
+	aim_addtlvtochain16(&tl, 0x000a, privacy);
 
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x000f, 0x0000, 0);
-
-  /* 000a/0002: 0000 ?? ?privacy? */
-  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); 
+	if (interest1)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), interest1);
+	if (interest2)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), interest2);
+	if (interest3)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), interest3);
+	if (interest4)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), interest4);
+	if (interest5)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), interest5);
 
-  if(interest1) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest1);
-  if(interest2) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest2), interest2);
-  if(interest3) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest3), interest3);
-  if(interest4) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest4), interest4);
-  if(interest5) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest5);
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-    
-  aim_tx_enqueue(sess, newpacket);
-    
-  return(sess->snac_nextid);
+	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
 }
 
-faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess,
-					    struct aim_conn_t *conn, 
-					    unsigned long status)
+faim_export int aim_icq_setstatus(aim_session_t *sess, aim_conn_t *conn, fu32_t status)
 {
-  struct command_tx_struct *newpacket;
-  int i;
-  unsigned long data;
-  
-  data = 0x00030000 | status; /* yay for error checking ;^) */
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	fu32_t data;
+
+	data = 0x00030000 | status; /* yay for error checking ;^) */
 
-  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4)))
-    return -1;
-
-  newpacket->lock = 1;
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4)))
+		return -ENOMEM;
 
-  i = aim_putsnac(newpacket->data, 0x0001, 0x001e, 0x0000, 0x0000001e);
-  i += aim_puttlv_32(newpacket->data+i, 0x0006, data);
+	snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+	
+	aim_addtlvtochain32(&tl, 0x0006, data);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, fr);
 
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return(sess->snac_nextid);
+	return 0;
 }
 
 /*
  * Should be generic enough to handle the errors for all families...
  *
  */
-static int generror(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  int ret = 0;
-  int error = 0;
-  aim_rxcallback_t userfunc;
-  struct aim_snac_t *snac2;
+	int ret = 0;
+	int error = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
 
-  snac2 = aim_remsnac(sess, snac->id);
+	snac2 = aim_remsnac(sess, snac->id);
 
-  if (datalen)
-    error = aimutil_get16(data);
+	if (aim_bstream_empty(bs))
+		error = aimbs_get16(bs);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, error, snac2?snac2->data:NULL);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL);
 
-  if (snac2)
-    free(snac2->data);
-  free(snac2);
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
 
-  return ret;
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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 generror(sess, mod, rx, snac, data, datalen);
-  else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
-    aim_rxcallback_t userfunc;
+	if (snac->subtype == 0x0001)
+		return generror(sess, mod, rx, snac, bs);
+	else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
+		aim_rxcallback_t userfunc;
 
-    if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-      return userfunc(sess, rx);
-  }
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			return userfunc(sess, rx);
+	}
 
-  return 0;
+	return 0;
 }
 
-faim_internal int misc_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int misc_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0xffff;
-  mod->version = 0x0000;
-  mod->flags = AIM_MODFLAG_MULTIFAMILY;
-  strncpy(mod->name, "misc", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0xffff;
+	mod->version = 0x0000;
+	mod->flags = AIM_MODFLAG_MULTIFAMILY;
+	strncpy(mod->name, "misc", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
--- a/src/protocols/oscar/msgcookie.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/msgcookie.c	Sun Sep 09 10:07:14 2001 +0000
@@ -27,29 +27,27 @@
  * in may be free'd, so don't count on its value after calling this!
  * 
  */
-faim_internal int aim_cachecookie(struct aim_session_t *sess,
-				  struct aim_msgcookie_t *cookie)
+faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie)
 {
-  struct aim_msgcookie_t *newcook;
+	aim_msgcookie_t *newcook;
 
-  if (!sess || !cookie)
-    return -1;
+	if (!sess || !cookie)
+		return -EINVAL;
 
-  if( (newcook = aim_checkcookie(sess, cookie->cookie, cookie->type)) ) {
-    if(newcook != cookie) {
-      aim_cookie_free(sess, newcook);
-    } else {
-      newcook->addtime = time(NULL);
-      return 1;
-    }
-  }
+	newcook = aim_checkcookie(sess, cookie->cookie, cookie->type);
+	
+	if (newcook == cookie) {
+		newcook->addtime = time(NULL);
+		return 1;
+	} else if (newcook)
+		aim_cookie_free(sess, newcook);
 
-  cookie->addtime = time(NULL);  
+	cookie->addtime = time(NULL);  
 
-  cookie->next = sess->msgcookies;
-  sess->msgcookies = cookie;
+	cookie->next = sess->msgcookies;
+	sess->msgcookies = cookie;
 
-  return 0;
+	return 0;
 }
 
 /**
@@ -62,23 +60,23 @@
  *
  * if found, returns the struct; if none found (or on error), returns NULL:
  */
-faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type)
+faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type)
 {
-  struct aim_msgcookie_t *cur, **prev;
+	aim_msgcookie_t *cur, **prev;
 
-  if (!cookie || !sess->msgcookies)
-    return NULL;
+	if (!cookie || !sess->msgcookies)
+		return NULL;
 
-  for (prev = &sess->msgcookies; (cur = *prev); ) {
-    if ((cur->type == type) && 
-	(memcmp(cur->cookie, cookie, 8) == 0)) {
-      *prev = cur->next;
-      return cur;
-    }
-    prev = &cur->next;
-  }
+	for (prev = &sess->msgcookies; (cur = *prev); ) {
+		if ((cur->type == type) && 
+				(memcmp(cur->cookie, cookie, 8) == 0)) {
+			*prev = cur->next;
+			return cur;
+		}
+		prev = &cur->next;
+	}
 
-  return NULL;
+	return NULL;
 }
 
 /**
@@ -91,21 +89,21 @@
  * success.
  *
  */
-faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *c, int type, void *data) 
+faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data) 
 {
-  struct aim_msgcookie_t *cookie;
+	aim_msgcookie_t *cookie;
 
-  if (!c)
-    return NULL;
+	if (!c)
+		return NULL;
 
-  if (!(cookie = calloc(1, sizeof(struct aim_msgcookie_t))))
-    return NULL;
-  
-  cookie->data = data;
-  cookie->type = type;
-  memcpy(cookie->cookie, c, 8);
-  
-  return cookie;
+	if (!(cookie = calloc(1, sizeof(aim_msgcookie_t))))
+		return NULL;
+
+	cookie->data = data;
+	cookie->type = type;
+	memcpy(cookie->cookie, c, 8);
+
+	return cookie;
 }
 
 /**
@@ -119,28 +117,31 @@
  *
  */
 
-faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *sess, 
-						      const unsigned char *cookie, 
-						      const int type)
+faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const fu8_t *cookie, int type)
 {
-  struct aim_msgcookie_t *cur;
+	aim_msgcookie_t *cur;
 
-  for (cur = sess->msgcookies; cur; cur = cur->next) {
-    if ((cur->type == type) && 
-	(memcmp(cur->cookie, cookie, 8) == 0))
-      return cur;   
-  }
+	for (cur = sess->msgcookies; cur; cur = cur->next) {
+		if ((cur->type == type) && 
+				(memcmp(cur->cookie, cookie, 8) == 0))
+			return cur;   
+	}
 
-  return NULL;
+	return NULL;
 }
 
 #if 0 /* debugging feature */
-faim_internal int aim_dumpcookie(struct aim_msgcookie_t *cookie) 
+faim_internal int aim_dumpcookie(aim_msgcookie_t *cookie) 
 {
-  if(!cookie)
-    return -1;
-  printf("\tCookie at %p: %d/%s with %p, next %p\n", cookie, cookie->type, cookie->cookie, cookie->data, cookie->next);
-  return 0;
+
+	if (!cookie)
+		return -EINVAL;
+
+	printf("\tCookie at %p: %d/%s with %p, next %p\n", 
+			cookie, cookie->type, cookie->cookie, 
+			cookie->data, cookie->next);
+
+	return 0;
 }
 #endif
 
@@ -157,56 +158,39 @@
  * returns -1 on error, 0 on success.
  *
  */
-
-faim_internal int aim_cookie_free(struct aim_session_t *sess, 
-				  struct aim_msgcookie_t *cookie) 
+faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie) 
 {
-  struct aim_msgcookie_t *cur, **prev;
+	aim_msgcookie_t *cur, **prev;
 
-  if (!sess || !cookie)
-    return -1;
-
-  if(!cookie)
-    return 0;
+	if (!sess || !cookie)
+		return -EINVAL;
 
-  for (prev = &sess->msgcookies; (cur = *prev); ) {
-    if (cur == cookie) {
-      *prev = cur->next;
-    } else
-      prev = &cur->next;
-  }
+	for (prev = &sess->msgcookies; (cur = *prev); ) {
+		if (cur == cookie)
+			*prev = cur->next;
+		else
+			prev = &cur->next;
+	}
 
-  if(cookie->data)
-    free(cookie->data);
+	free(cookie->data);
+	free(cookie);
 
-  free(cookie);
-
-  return 0;
+	return 0;
 } 
 
-faim_internal int aim_msgcookie_gettype(int reqclass) {
-  /* XXX: hokey-assed. needs fixed. */
-  switch(reqclass) {
-  case AIM_CAPS_BUDDYICON:
-    return AIM_COOKIETYPE_OFTICON;
-    break;
-  case AIM_CAPS_VOICE:
-    return AIM_COOKIETYPE_OFTVOICE;
-    break;
-  case AIM_CAPS_IMIMAGE:
-    return AIM_COOKIETYPE_OFTIMAGE;
-    break;
-  case AIM_CAPS_CHAT:
-    return AIM_COOKIETYPE_CHAT;
-    break;
-  case AIM_CAPS_GETFILE:
-    return AIM_COOKIETYPE_OFTGET;
-    break;
-  case AIM_CAPS_SENDFILE:
-    return AIM_COOKIETYPE_OFTSEND;
-    break;
-  default:
-    return AIM_COOKIETYPE_UNKNOWN;
-    break;
-  }           
+/* XXX I hate switch */
+faim_internal int aim_msgcookie_gettype(int reqclass) 
+{
+	/* XXX: hokey-assed. needs fixed. */
+	switch(reqclass) {
+	case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON;
+	case AIM_CAPS_VOICE: return AIM_COOKIETYPE_OFTVOICE;
+	case AIM_CAPS_IMIMAGE: return AIM_COOKIETYPE_OFTIMAGE;
+	case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT;
+	case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET;
+	case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND;
+	default: return AIM_COOKIETYPE_UNKNOWN;
+	}           
 }
+
+
--- a/src/protocols/oscar/oscar.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/oscar.c	Sun Sep 09 10:07:14 2001 +0000
@@ -60,8 +60,8 @@
 		       AIM_CAPS_IMIMAGE;
 
 struct oscar_data {
-	struct aim_session_t *sess;
-	struct aim_conn_t *conn;
+	aim_session_t *sess;
+	aim_conn_t *conn;
 
 	guint cnpa;
 	guint paspa;
@@ -85,9 +85,9 @@
 struct chat_connection {
 	char *name;
 	char *show; /* AOL did something funny to us */
-	int exchange;
+	fu16_t exchange; /* XXX should have instance here too */
 	int fd; /* this is redundant since we have the conn below */
-	struct aim_conn_t *conn;
+	aim_conn_t *conn;
 	int inpa;
 	int id;
 	struct gaim_connection *gc; /* i hate this. */
@@ -100,13 +100,14 @@
 	struct gaim_connection *gc;
 	char name[80];
 	int watcher;
-	struct aim_conn_t *conn;
+	aim_conn_t *conn;
 };
 
 struct ask_direct {
 	struct gaim_connection *gc;
 	char *sn;
-	struct aim_directim_priv *priv;
+	char ip[64];
+	fu8_t cookie[8];
 };
 
 struct icon_req {
@@ -117,7 +118,7 @@
 	gboolean request;
 };
 
-static struct direct_im *find_direct_im(struct oscar_data *od, char *who) {
+static struct direct_im *find_direct_im(struct oscar_data *od, const char *who) {
 	GSList *d = od->direct_ims;
 	char *n = g_strdup(normalize(who));
 	struct direct_im *m = NULL;
@@ -176,7 +177,7 @@
 }
 
 static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
-							struct aim_conn_t *conn) {
+							aim_conn_t *conn) {
 	GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
 	struct chat_connection *c = NULL;
 
@@ -191,42 +192,41 @@
 	return c;
 }
 
-static int gaim_parse_auth_resp  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_login      (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_server_ready     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_handle_redirect  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_info_change      (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_account_confirm  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_oncoming   (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_offgoing   (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_misses     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_user_info  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_motd       (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_chatnav_info     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_chat_join        (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_chat_leave       (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_chat_info_update (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_chat_incoming_msg(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_msgack     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_ratechange (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_evilnotify (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_bosrights        (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_rateresp         (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_reportinterval   (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_msgerr     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_buddyrights(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_locerr     (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_icbm_param_info  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_parse_genericerr (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_memrequest       (struct aim_session_t *, struct command_rx_struct *, ...);
-
-static int gaim_directim_initiate  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_directim_incoming  (struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_directim_disconnect(struct aim_session_t *, struct command_rx_struct *, ...);
-static int gaim_directim_typing    (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
+static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_info_change      (aim_session_t *, aim_frame_t *, ...);
+static int gaim_account_confirm  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_user_info  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_msgack     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_evilnotify (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_searcherror(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_searchreply(aim_session_t *, aim_frame_t *, ...);
+static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
+static int rateresp_bos     (aim_session_t *, aim_frame_t *, ...);
+static int rateresp_auth    (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
+static int gaim_memrequest       (aim_session_t *,  aim_frame_t*, ...);
+static int server_ready_bos      (aim_session_t *,  aim_frame_t*, ...);
+
+static int gaim_directim_initiate  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_directim_incoming  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_directim_typing    (aim_session_t *, aim_frame_t *, ...);
 
 static char *msgerrreason[] = {
 	"Invalid error",
@@ -257,10 +257,36 @@
 };
 static int msgerrreasonlen = 25;
 
+static void gaim_directim_disconnect(aim_session_t *sess, aim_conn_t *conn) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	struct conversation *cnv;
+	struct direct_im *dim;
+	char *sn;
+	char buf[256];
+
+	sn = g_strdup(aim_directim_getsn(conn));
+
+	debug_printf("%s disconnected Direct IM.\n", sn);
+
+	dim = find_direct_im(od, sn);
+	od->direct_ims = g_slist_remove(od->direct_ims, dim);
+	gaim_input_remove(dim->watcher);
+
+	g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn);
+	if ((cnv = find_conversation(sn)))
+		write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
+
+	g_free(dim); /* I guess? I don't see it anywhere else... -- mid */
+	g_free(sn);
+
+	return;
+}
+
 static void oscar_callback(gpointer data, gint source,
 				GaimInputCondition condition) {
-	struct aim_conn_t *conn = (struct aim_conn_t *)data;
-	struct aim_session_t *sess = aim_conn_getsess(conn);
+	aim_conn_t *conn = (aim_conn_t *)data;
+	aim_session_t *sess = aim_conn_getsess(conn);
 	struct gaim_connection *gc = sess ? sess->aux_data : NULL;
 	struct oscar_data *odata;
 
@@ -284,6 +310,7 @@
 			debug_printf("got information on rendezvous\n");
 			if (aim_handlerendconnect(odata->sess, conn) < 0) {
 				debug_printf(_("connection error (rend)\n"));
+				aim_conn_kill(odata->sess, &conn);
 			}
 		} else {
 			if (aim_get_command(odata->sess, conn) >= 0) {
@@ -328,8 +355,12 @@
 					debug_printf("removing authconn input watcher\n");
 					aim_conn_kill(odata->sess, &conn);
 				} else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
-					debug_printf("No handler for rendezvous disconnect (%d).\n",
-							source);
+					if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+						gaim_directim_disconnect(odata->sess, conn);
+					else {
+						debug_printf("No handler for rendezvous disconnect (%d).\n",
+								source);
+					}
 					aim_conn_kill(odata->sess, &conn);
 				} else {
 					debug_printf("holy crap! generic connection error! %d\n",
@@ -341,7 +372,7 @@
 	}
 }
 
-static void oscar_debug(struct aim_session_t *sess, int level, const char *format, va_list va) {
+static void oscar_debug(aim_session_t *sess, int level, const char *format, va_list va) {
 	char *s = g_strdup_vprintf(format, va);
 	char buf[256];
 	char *t;
@@ -360,8 +391,8 @@
 {
 	struct gaim_connection *gc = data;
 	struct oscar_data *odata;
-	struct aim_session_t *sess;
-	struct aim_conn_t *conn;
+	aim_session_t *sess;
+	aim_conn_t *conn;
 
 	if (!g_slist_find(connections, gc)) {
 		close(source);
@@ -385,8 +416,8 @@
 }
 
 static void oscar_login(struct aim_user *user) {
-	struct aim_session_t *sess;
-	struct aim_conn_t *conn;
+	aim_session_t *sess;
+	aim_conn_t *conn;
 	char buf[256];
 	struct gaim_connection *gc = new_gaim_conn(user);
 	struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
@@ -394,7 +425,7 @@
 
 	debug_printf(_("Logging in %s\n"), user->username);
 
-	sess = g_new0(struct aim_session_t, 1);
+	sess = g_new0(aim_session_t, 1);
 
 	aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
 	aim_setdebuggingcb(sess, oscar_debug);
@@ -476,8 +507,8 @@
 static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
 	struct gaim_connection *gc = data;
 	struct oscar_data *odata;
-	struct aim_session_t *sess;
-	struct aim_conn_t *bosconn;
+	aim_session_t *sess;
+	aim_conn_t *bosconn;
 
 	if (!g_slist_find(connections, gc)) {
 		close(source);
@@ -500,12 +531,11 @@
 	set_login_progress(gc, 4, _("Connection established, cookie sent"));
 }
 
-int gaim_parse_auth_resp(struct aim_session_t *sess,
-			 struct command_rx_struct *command, ...) {
+static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	struct aim_conn_t *bosconn = NULL;
+	aim_conn_t *bosconn = NULL;
 	char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
-	unsigned char *cookie = NULL;
+	fu8_t *cookie = NULL;
 	int errorcode = 0, regstatus = 0;
 	int latestbuild = 0, latestbetabuild = 0;
 	char *latestrelease = NULL, *latestbeta = NULL;
@@ -520,7 +550,7 @@
 	port = user->proto_opt[USEROPT_AUTHPORT][0] ?
 		atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT,
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	sn = va_arg(ap, char *);
 	errorcode = va_arg(ap, int);
 	errurl = va_arg(ap, char *);
@@ -589,7 +619,7 @@
 		debug_printf("Latest WinAIM released version %s, build %d, at %s (%s)\n",
 				latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo);
 	debug_printf("Closing auth connection...\n");
-	aim_conn_kill(sess, &command->conn);
+	aim_conn_kill(sess, &fr->conn);
 
 	bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
 	if (bosconn == NULL) {
@@ -599,12 +629,11 @@
 	}
 
 	aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0);
-	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, gaim_rateresp, 0); /* rate info */
+	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, rateresp_bos, 0); /* rate info */
 	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_SERVERREADY, gaim_server_ready, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, server_ready_bos, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, 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_STS, AIM_CB_STS_SETREPORTINTERVAL, gaim_reportinterval, 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);
@@ -652,7 +681,7 @@
 	unsigned long len;
 	char *modname;
 	int fd;
-	struct aim_conn_t *conn;
+	aim_conn_t *conn;
 	unsigned int inpa;
 };
 
@@ -719,24 +748,23 @@
 /* size of icbmui.ocm, the largest module in AIM 3.5 */
 #define AIM_MAX_FILE_SIZE 98304
 
-int gaim_memrequest(struct aim_session_t *sess,
-		    struct command_rx_struct *command, ...) {
+int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct pieceofcrap *pos;
-	unsigned long offset, len;
+	fu32_t offset, len;
 	char *modname;
 	int fd;
 
-	va_start(ap, command);
-	offset = va_arg(ap, unsigned long);
-	len = va_arg(ap, unsigned long);
+	va_start(ap, fr);
+	offset = (fu32_t)va_arg(ap, unsigned long);
+	len = (fu32_t)va_arg(ap, unsigned long);
 	modname = va_arg(ap, char *);
 	va_end(ap);
 
 	debug_printf("offset: %d, len: %d, file: %s\n", offset, len, modname ? modname : "aim.exe");
 	if (len == 0) {
 		debug_printf("len is 0, hashing NULL\n");
-		aim_sendmemblock(sess, command->conn, offset, len, NULL,
+		aim_sendmemblock(sess, fr->conn, offset, len, NULL,
 				AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
 		return 1;
 	}
@@ -769,7 +797,7 @@
 
 	pos = g_new0(struct pieceofcrap, 1);
 	pos->gc = sess->aux_data;
-	pos->conn = command->conn;
+	pos->conn = fr->conn;
 
 	pos->offset = offset;
 	pos->len = len;
@@ -788,8 +816,7 @@
 	return 1;
 }
 
-int gaim_parse_login(struct aim_session_t *sess,
-		     struct command_rx_struct *command, ...) {
+static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
 #if 0
 	struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
 #else
@@ -799,84 +826,87 @@
 	va_list ap;
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	key = va_arg(ap, char *);
 	va_end(ap);
 
-	aim_send_login(sess, command->conn, gc->username, gc->password, &info, key);
+	aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
+
 	return 1;
 }
 
-int gaim_server_ready(struct aim_session_t *sess,
-		      struct command_rx_struct *command, ...) {
-	static int id = 1;
+static int server_ready_auth(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = gc->proto_data;
-	struct chat_connection *chatcon;
-	switch (command->conn->type) {
-	case AIM_CONN_TYPE_AUTH:
-		aim_auth_setversions(sess, command->conn);
-		aim_bos_reqrate(sess, command->conn);
-		debug_printf("done with AUTH ServerReady\n");
-		if (od->chpass) {
-			debug_printf("changing password\n");
-			aim_auth_changepasswd(sess, command->conn, od->newp, od->oldp);
-			g_free(od->oldp);
-			g_free(od->newp);
-			od->chpass = FALSE;
-		}
-		if (od->conf) {
-			debug_printf("confirming account\n");
-			aim_auth_reqconfirm(sess, command->conn);
-			od->conf = FALSE;
-		}
-		if (od->reqemail) {
-			debug_printf("requesting email\n");
-			aim_auth_getinfo(sess, command->conn, 0x0011);
-			od->reqemail = FALSE;
-		}
-		break;
-	case AIM_CONN_TYPE_BOS:
-		aim_setversions(sess, command->conn);
-		aim_bos_reqrate(sess, command->conn); /* request rate info */
-		debug_printf("done with BOS ServerReady\n");
-		break;
-	case AIM_CONN_TYPE_CHATNAV:
-		debug_printf("chatnav: got server ready\n");
-		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
-		aim_bos_reqrate(sess, command->conn);
-		aim_bos_ackrateresp(sess, command->conn);
-		aim_chatnav_clientready(sess, command->conn);
-		aim_chatnav_reqrights(sess, command->conn);
-		break;
-	case AIM_CONN_TYPE_CHAT:
-		debug_printf("chat: got server ready\n");
-		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
-		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
-		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
-		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
-		aim_bos_reqrate(sess, command->conn);
-		aim_bos_ackrateresp(sess, command->conn);
-		aim_chat_clientready(sess, command->conn);
-		chatcon = find_oscar_chat_by_conn(gc, command->conn);
-		chatcon->id = id;
-		chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
-		break;
-	case AIM_CONN_TYPE_RENDEZVOUS:
-		break;
-	default: /* huh? */
-		debug_printf("server ready: got unexpected connection type %04x\n", command->conn->type);
-		break;
+
+	aim_auth_setversions(sess, fr->conn);
+	aim_bos_reqrate(sess, fr->conn);
+	debug_printf("done with AUTH ServerReady\n");
+	if (od->chpass) {
+		debug_printf("changing password\n");
+		aim_auth_changepasswd(sess, fr->conn, od->newp, od->oldp);
+		g_free(od->oldp);
+		g_free(od->newp);
+		od->chpass = FALSE;
 	}
+	if (od->conf) {
+		debug_printf("confirming account\n");
+		aim_auth_reqconfirm(sess, fr->conn);
+		od->conf = FALSE;
+	}
+	if (od->reqemail) {
+		debug_printf("requesting email\n");
+		aim_auth_getinfo(sess, fr->conn, 0x0011);
+		od->reqemail = FALSE;
+	}
+
+	return 1;
+}
+
+static int server_ready_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
+	aim_setversions(sess, fr->conn);
+	aim_bos_reqrate(sess, fr->conn); /* request rate info */
+	debug_printf("done with BOS ServerReady\n");
+
 	return 1;
 }
 
-static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond)
-{
+static int server_ready_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
+	debug_printf("chatnav: got server ready\n");
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
+	aim_bos_reqrate(sess, fr->conn);
+	aim_bos_ackrateresp(sess, fr->conn);
+	aim_chatnav_clientready(sess, fr->conn);
+	aim_chatnav_reqrights(sess, fr->conn);
+
+	return 1;
+}
+
+static int server_ready_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct chat_connection *chatcon;
+	static int id = 1;
+
+	debug_printf("chat: got server ready\n");
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
+	aim_bos_reqrate(sess, fr->conn);
+	aim_bos_ackrateresp(sess, fr->conn);
+	aim_chat_clientready(sess, fr->conn);
+	chatcon = find_oscar_chat_by_conn(gc, fr->conn);
+	chatcon->id = id;
+	chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
+
+	return 1;
+}
+
+static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
 	struct gaim_connection *gc = data;
 	struct oscar_data *odata;
-	struct aim_session_t *sess;
-	struct aim_conn_t *tstconn;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
 
 	if (!g_slist_find(connections, gc)) {
 		close(source);
@@ -895,7 +925,7 @@
 
 	aim_conn_completeconnect(sess, tstconn);
 	odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
-				oscar_callback, tstconn);
+					oscar_callback, tstconn);
 	debug_printf("chatnav: connected\n");
 }
 
@@ -903,8 +933,8 @@
 {
 	struct gaim_connection *gc = data;
 	struct oscar_data *odata;
-	struct aim_session_t *sess;
-	struct aim_conn_t *tstconn;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
 
 	if (!g_slist_find(connections, gc)) {
 		close(source);
@@ -932,8 +962,8 @@
 	struct chat_connection *ccon = data;
 	struct gaim_connection *gc = ccon->gc;
 	struct oscar_data *odata;
-	struct aim_session_t *sess;
-	struct aim_conn_t *tstconn;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
 
 	if (!g_slist_find(connections, gc)) {
 		close(source);
@@ -963,15 +993,15 @@
 	aim_chat_attachname(tstconn, ccon->name);
 }
 
-int gaim_handle_redirect(struct aim_session_t *sess,
-			 struct command_rx_struct *command, ...) {
+/* Hrmph. I don't know how to make this look better. --mid */
+static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	int serviceid;
+	fu16_t serviceid;
 	char *ip;
-	unsigned char *cookie;
+	fu8_t *cookie;
 	struct gaim_connection *gc = sess->aux_data;
 	struct aim_user *user = gc->user;
-	struct aim_conn_t *tstconn;
+	aim_conn_t *tstconn;
 	int i;
 	char *host;
 	int port;
@@ -979,10 +1009,10 @@
 	port = user->proto_opt[USEROPT_AUTHPORT][0] ?
 		atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT,
 
-	va_start(ap, command);
-	serviceid = va_arg(ap, int);
-	ip        = va_arg(ap, char *);
-	cookie    = va_arg(ap, unsigned char *);
+	va_start(ap, fr);
+	serviceid = (fu16_t)va_arg(ap, unsigned int);
+	ip = va_arg(ap, char *);
+	cookie = (fu8_t *)va_arg(ap, unsigned char *);
 
 	for (i = 0; i < (int)strlen(ip); i++) {
 		if (ip[i] == ':') {
@@ -1001,8 +1031,8 @@
 			g_free(host);
 			return 1;
 		}
-		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
-		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, gaim_rateresp, 0);
+		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, server_ready_auth, 0);
+		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, rateresp_auth, 0);
 		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
 		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
 		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
@@ -1024,7 +1054,7 @@
 			g_free(host);
 			return 1;
 		}
-		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
+		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, server_ready_chatnav, 0);
 
 		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
 		tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
@@ -1038,9 +1068,13 @@
 		break;
 	case 0xe: /* Chat */
 		{
-		char *roomname = va_arg(ap, char *);
-		int exchange = va_arg(ap, int);
+		char *roomname;
+		fu16_t exchange;
 		struct chat_connection *ccon;
+
+		roomname = va_arg(ap, char *);
+		exchange = (fu16_t)va_arg(ap, unsigned int);
+
 		tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
 		if (tstconn == NULL) {
 			debug_printf("unable to connect to chat server\n");
@@ -1048,7 +1082,7 @@
 			return 1;
 		}
 
-		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
+		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, server_ready_chat, 0);
 		ccon = g_new0(struct chat_connection, 1);
 		ccon->conn = tstconn;
 		ccon->gc = gc;
@@ -1083,15 +1117,14 @@
 	return 1;
 }
 
-int gaim_parse_oncoming(struct aim_session_t *sess,
-			struct command_rx_struct *command, ...) {
+static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct aim_userinfo_s *info;
 	time_t time_idle;
 	int type = 0;
 	struct gaim_connection *gc = sess->aux_data;
 
 	va_list ap;
-	va_start(ap, command);
+	va_start(ap, fr);
 	info = va_arg(ap, struct aim_userinfo_s *);
 	va_end(ap);
 
@@ -1118,13 +1151,12 @@
 	return 1;
 }
 
-int gaim_parse_offgoing(struct aim_session_t *sess,
-			struct command_rx_struct *command, ...) {
+static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct aim_userinfo_s *info;
 	va_list ap;
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	info = va_arg(ap, struct aim_userinfo_s *);
 	va_end(ap);
 
@@ -1157,8 +1189,12 @@
 		return;
 	}
 
+	if (dim->conn->fd != source)
+		dim->conn->fd = source;
+
 	aim_conn_completeconnect(od->sess, dim->conn);
-	if (!(cnv = find_conversation(dim->name))) cnv = new_conversation(dim->name);
+	if (!(cnv = find_conversation(dim->name))) 
+		cnv = new_conversation(dim->name);
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
 	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
 
@@ -1186,27 +1222,25 @@
 	dim->gc = d->gc;
 	g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
 
-	if ((dim->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL)) == NULL) {
+	dim->conn = aim_directim_connect(od->sess, d->sn, NULL, d->cookie);
+	if (!dim->conn) {
 		g_free(dim);
 		cancel_direct_im(w, d);
 		return TRUE;
 	}
-	dim->conn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-	dim->conn->priv = d->priv;
+
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
 				gaim_directim_incoming, 0);
-	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT,
-				gaim_directim_disconnect, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
 				gaim_directim_typing, 0);
 
-	for (i = 0; i < (int)strlen(d->priv->ip); i++) {
-		if (d->priv->ip[i] == ':') {
-			port = atoi(&(d->priv->ip[i+1]));
+	for (i = 0; i < (int)strlen(d->ip); i++) {
+		if (d->ip[i] == ':') {
+			port = atoi(&(d->ip[i+1]));
 			break;
 		}
 	}
-	host = g_strndup(d->priv->ip, i);
+	host = g_strndup(d->ip, i);
 	dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
 	dim->conn->fd = proxy_connect(host, port, oscar_directim_callback, dim);
 	g_free(host);
@@ -1222,151 +1256,169 @@
 	return TRUE;
 }
 
-int gaim_parse_incoming_im(struct aim_session_t *sess,
-			   struct command_rx_struct *command, ...) {
-	int channel;
+static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch1_args *args) {
+	char *tmp = g_malloc(BUF_LONG);
+	struct gaim_connection *gc = sess->aux_data;
+
+	if (args->icbmflags & AIM_IMFLAGS_HASICON) {
+		struct oscar_data *od = gc->proto_data;
+		struct icon_req *ir = NULL;
+		GSList *h = od->hasicons;
+		char *who = normalize(userinfo->sn);
+		debug_printf("%s has an icon\n", userinfo->sn);
+		while (h) {
+			ir = h->data;
+			if (!strcmp(ir->user, who))
+				break;
+			h = h->next;
+		}
+		if (!h) {
+			ir = g_new0(struct icon_req, 1);
+			ir->user = g_strdup(who);
+			od->hasicons = g_slist_append(od->hasicons, ir);
+		}
+		if ((args->iconlen != ir->length) ||
+		    (args->iconsum != ir->checksum) ||
+		    (args->iconstamp != ir->timestamp))
+			ir->request = TRUE;
+		ir->length = args->iconlen;
+		ir->checksum = args->iconsum;
+		ir->timestamp = args->iconstamp;
+	}
+
+	if (gc->iconfile && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)) {
+		FILE *file;
+		struct stat st;
+
+		if (!stat(gc->iconfile, &st)) {
+			char *buf = g_malloc(st.st_size);
+			file = fopen(gc->iconfile, "r");
+			if (file) {
+				fread(buf, 1, st.st_size, file);
+				aim_send_icon(sess, conn, userinfo->sn, buf, st.st_size,
+					      st.st_mtime, aim_iconsum(buf, st.st_size));
+				fclose(file);
+			}
+			g_free(buf);
+		}
+	}
+
+	/*
+	 * Quickly convert it to eight bit format, replacing 
+	 * non-ASCII UNICODE characters with their equivelent 
+	 * HTML entity.
+	 */
+	if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
+		int i;
+		
+		for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
+			unsigned short uni;
+			
+			uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
+
+			if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
+				
+				g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
+				
+			} else { /* something else, do UNICODE entity */
+				g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
+			}
+		}
+	} else
+		g_snprintf(tmp, BUF_LONG, "%s", args->msg);
+
+	serv_got_im(gc, userinfo->sn, tmp, args->icbmflags & AIM_IMFLAGS_AWAY, time(NULL));
+	g_free(tmp);
+
+	return 1;
+}
+
+static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, struct aim_userinfo_s *userinfo, struct aim_incomingim_ch2_args *args) {
+	struct gaim_connection *gc = sess->aux_data;
+
+	if (args->reqclass & AIM_CAPS_CHAT) {
+		char *name = extract_name(args->info.chat.roominfo.name);
+		int *exch = g_new0(int, 1);
+		GList *m = NULL;
+		m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
+		*exch = args->info.chat.roominfo.exchange;
+		m = g_list_append(m, exch);
+		serv_got_chat_invite(gc,
+				     name ? name : args->info.chat.roominfo.name,
+				     userinfo->sn,
+				     args->info.chat.msg,
+				     m);
+		if (name)
+			g_free(name);
+	} else if (args->reqclass & AIM_CAPS_SENDFILE) {
+	} else if (args->reqclass & AIM_CAPS_GETFILE) {
+	} else if (args->reqclass & AIM_CAPS_VOICE) {
+	} else if (args->reqclass & AIM_CAPS_BUDDYICON) {
+		set_icon_data(gc, normalize(userinfo->sn), args->info.icon.icon,
+				args->info.icon.length);
+	} else if (args->reqclass & AIM_CAPS_IMIMAGE) {
+		struct ask_direct *d = g_new0(struct ask_direct, 1);
+		char buf[256];
+
+		debug_printf("%s received direct im request from %s (%s)\n",
+				gc->username, userinfo->sn, args->info.imimage.ip);
+
+		d->gc = gc;
+		d->sn = g_strdup(userinfo->sn);
+		strncpy(d->ip, args->info.imimage.ip, sizeof(d->ip));
+		memcpy(d->cookie, args->cookie, 8);
+		g_snprintf(buf, sizeof buf, "%s has just asked to directly connect to %s.",
+				userinfo->sn, gc->username);
+		do_ask_dialog(buf, d, accept_direct_im, cancel_direct_im);
+	} else {
+		debug_printf("Unknown reqclass %d\n", args->reqclass);
+	}
+
+	return 1;
+}
+
+
+static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
+	int channel, ret = 0;
 	struct aim_userinfo_s *userinfo;
 	va_list ap;
-	struct gaim_connection *gc = sess->aux_data;
-
-	va_start(ap, command);
+
+	va_start(ap, fr);
 	channel = va_arg(ap, int);
 	userinfo = va_arg(ap, struct aim_userinfo_s *);
 
 	/* channel 1: standard message */
 	if (channel == 1) {
-		char *tmp = g_malloc(BUF_LONG);
 		struct aim_incomingim_ch1_args *args;
 
 		args = va_arg(ap, struct aim_incomingim_ch1_args *);
-		va_end(ap);
-
-		if (args->icbmflags & AIM_IMFLAGS_HASICON) {
-			struct oscar_data *od = gc->proto_data;
-			struct icon_req *ir = NULL;
-			GSList *h = od->hasicons;
-			char *who = normalize(userinfo->sn);
-			debug_printf("%s has an icon\n", userinfo->sn);
-			while (h) {
-				ir = h->data;
-				if (!strcmp(ir->user, who))
-					break;
-				h = h->next;
-			}
-			if (!h) {
-				ir = g_new0(struct icon_req, 1);
-				ir->user = g_strdup(who);
-				od->hasicons = g_slist_append(od->hasicons, ir);
-			}
-			if ((args->iconlength != ir->length) ||
-			    (args->iconchecksum != ir->checksum) ||
-			    (args->iconstamp != ir->timestamp))
-				ir->request = TRUE;
-			ir->length = args->iconlength;
-			ir->checksum = args->iconchecksum;
-			ir->timestamp = args->iconstamp;
-		}
-
-		if (gc->iconfile && (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)) {
-			FILE *file;
-			struct stat st;
-
-			if (!stat(gc->iconfile, &st)) {
-				char *buf = g_malloc(st.st_size);
-				file = fopen(gc->iconfile, "r");
-				if (file) {
-					fread(buf, 1, st.st_size, file);
-					aim_send_icon(sess, command->conn, userinfo->sn, buf, st.st_size,
-						      st.st_mtime, aim_iconsum(buf, st.st_size));
-					fclose(file);
-				}
-				g_free(buf);
-			}
-		}
-
-		/*
-		 * Quickly convert it to eight bit format, replacing 
-		 * non-ASCII UNICODE characters with their equivelent 
-		 * HTML entity.
-		 */
-		if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
-			int i;
-			
-			for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
-				unsigned short uni;
-				
-				uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
-
-				if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
-					
-					g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
-					
-				} else { /* something else, do UNICODE entity */
-					g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
-				}
-			}
-		} else
-			g_snprintf(tmp, BUF_LONG, "%s", args->msg);
-
-		serv_got_im(gc, userinfo->sn, tmp, args->icbmflags & AIM_IMFLAGS_AWAY, time(NULL));
-		g_free(tmp);
+
+		ret = incomingim_chan1(sess, fr->conn, userinfo, args);
+
 	} else if (channel == 2) {
 		struct aim_incomingim_ch2_args *args;
+
 		args = va_arg(ap, struct aim_incomingim_ch2_args *);
-		va_end(ap);
-		if (args->reqclass & AIM_CAPS_CHAT) {
-			char *name = extract_name(args->info.chat.roominfo.name);
-			int *exch = g_new0(int, 1);
-			GList *m = NULL;
-			m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
-			*exch = args->info.chat.roominfo.exchange;
-			m = g_list_append(m, exch);
-			serv_got_chat_invite(gc,
-					     name ? name : args->info.chat.roominfo.name,
-					     userinfo->sn,
-					     args->info.chat.msg,
-					     m);
-			if (name)
-				g_free(name);
-		} else if (args->reqclass & AIM_CAPS_SENDFILE) {
-		} else if (args->reqclass & AIM_CAPS_GETFILE) {
-		} else if (args->reqclass & AIM_CAPS_VOICE) {
-		} else if (args->reqclass & AIM_CAPS_BUDDYICON) {
-			set_icon_data(gc, normalize(userinfo->sn), args->info.icon.icon,
-					args->info.icon.length);
-		} else if (args->reqclass & AIM_CAPS_IMIMAGE) {
-			struct ask_direct *d = g_new0(struct ask_direct, 1);
-			char buf[256];
-
-			debug_printf("%s received direct im request from %s (%s)\n",
-					gc->username, userinfo->sn, args->info.directim->ip);
-
-			d->gc = gc;
-			d->sn = g_strdup(userinfo->sn);
-			d->priv = args->info.directim;
-			g_snprintf(buf, sizeof buf, "%s has just asked to directly connect to %s.",
-					userinfo->sn, gc->username);
-			do_ask_dialog(buf, d, accept_direct_im, cancel_direct_im);
-		} else {
-			debug_printf("Unknown reqclass %d\n", args->reqclass);
-		}
+
+		ret = incomingim_chan2(sess, fr->conn, userinfo, args);
 	}
 
-	return 1;
+	va_end(ap);
+
+	return ret;
 }
 
-int gaim_parse_misses(struct aim_session_t *sess,
-		      struct command_rx_struct *command, ...) {
+static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	unsigned short chan, nummissed, reason;
+	fu16_t chan, nummissed, reason;
 	struct aim_userinfo_s *userinfo;
 	char buf[1024];
 
-	va_start(ap, command);
-	chan = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	chan = (fu16_t)va_arg(ap, unsigned int);
 	userinfo = va_arg(ap, struct aim_userinfo_s *);
-	nummissed = (unsigned short)va_arg(ap, unsigned int);
-	reason = (unsigned short)va_arg(ap, unsigned int);
+	nummissed = (fu16_t)va_arg(ap, unsigned int);
+	reason = (fu16_t)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	switch(reason) {
@@ -1431,12 +1483,12 @@
 	return 1;
 }
 
-int gaim_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	unsigned short reason;
-
-	va_start(ap, command);
-	reason = va_arg(ap, int);
+	fu16_t reason;
+
+	va_start(ap, fr);
+	reason = (fu16_t)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	debug_printf("snac threw error (reason 0x%04x: %s\n", reason,
@@ -1445,15 +1497,14 @@
 	return 1;
 }
 
-int gaim_parse_msgerr(struct aim_session_t *sess,
-		      struct command_rx_struct *command, ...) {
+static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *destn;
-	unsigned short reason;
+	fu16_t reason;
 	char buf[1024];
 
-	va_start(ap, command);
-	reason = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	reason = (fu16_t)va_arg(ap, unsigned int);
 	destn = va_arg(ap, char *);
 	va_end(ap);
 
@@ -1464,15 +1515,14 @@
 	return 1;
 }
 
-int gaim_parse_locerr(struct aim_session_t *sess,
-		      struct command_rx_struct *command, ...) {
+static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *destn;
-	unsigned short reason;
+	fu16_t reason;
 	char buf[1024];
 
-	va_start(ap, command);
-	reason = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	reason = (fu16_t)va_arg(ap, unsigned int);
 	destn = va_arg(ap, char *);
 	va_end(ap);
 
@@ -1493,22 +1543,21 @@
 	return buf;
 }
 
-int gaim_parse_user_info(struct aim_session_t *sess,
-			 struct command_rx_struct *command, ...) {
+static int gaim_parse_user_info(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct aim_userinfo_s *info;
 	char *prof_enc = NULL, *prof = NULL;
-	unsigned short infotype;
+	fu16_t infotype;
 	char buf[BUF_LONG];
 	char legend[BUF_LONG];
 	struct gaim_connection *gc = sess->aux_data;
 	va_list ap;
 	char *asc;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	info = va_arg(ap, struct aim_userinfo_s *);
 	prof_enc = va_arg(ap, char *);
 	prof = va_arg(ap, char *);
-	infotype = (unsigned short)va_arg(ap, unsigned int);
+	infotype = (fu16_t)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	if (info->membersince)
@@ -1549,22 +1598,21 @@
 	return 1;
 }
 
-int gaim_parse_motd(struct aim_session_t *sess,
-		    struct command_rx_struct *command, ...) {
+static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
 	char *msg;
-	unsigned short id;
+	fu16_t id;
 	va_list ap;
 	char buildbuf[150];
 
-	va_start(ap, command);
-	id  = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	id  = (fu16_t)va_arg(ap, unsigned int);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
 	aim_getbuildstring(buildbuf, sizeof(buildbuf));
 
 	debug_printf("MOTD: %s (%d)\n", msg ? msg : "Unknown", id);
-	debug_printf("Gaim %s / Libfaim %s\n", VERSION, buildbuf);
+	debug_printf("Gaim %s / libfaim %s\n", VERSION, buildbuf);
 	if (id < 4)
 		do_error_dialog(_("Your connection may be lost."),
 				_("AOL error"));
@@ -1572,23 +1620,22 @@
 	return 1;
 }
 
-int gaim_chatnav_info(struct aim_session_t *sess,
-		      struct command_rx_struct *command, ...) {
+static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	unsigned short type;
+	fu16_t type;
 	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
 
-	va_start(ap, command);
-	type = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	type = (fu16_t)va_arg(ap, unsigned int);
 
 	switch(type) {
 		case 0x0002: {
-			int maxrooms;
+			fu8_t maxrooms;
 			struct aim_chat_exchangeinfo *exchanges;
-			int exchangecount, i = 0;
-
-			maxrooms = (unsigned char)va_arg(ap, unsigned int);
+			int exchangecount, i;
+
+			maxrooms = (fu8_t)va_arg(ap, unsigned int);
 			exchangecount = va_arg(ap, int);
 			exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
 			va_end(ap);
@@ -1596,11 +1643,11 @@
 			debug_printf("chat info: Chat Rights:\n");
 			debug_printf("chat info: \tMax Concurrent Rooms: %d\n", maxrooms);
 			debug_printf("chat info: \tExchange List: (%d total)\n", exchangecount);
-			while (i < exchangecount)
-				debug_printf("chat info: \t\t%d\n", exchanges[i++].number);
+			for (i = 0; i < exchangecount; i++)
+				debug_printf("chat info: \t\t%d\n", exchanges[i].number);
 			if (odata->create_exchange) {
 				debug_printf("creating room %s\n", odata->create_name);
-				aim_chatnav_createroom(sess, command->conn, odata->create_name,
+				aim_chatnav_createroom(sess, fr->conn, odata->create_name,
 						odata->create_exchange);
 				odata->create_exchange = 0;
 				g_free(odata->create_name);
@@ -1610,19 +1657,19 @@
 			break;
 		case 0x0008: {
 			char *fqcn, *name, *ck;
-			unsigned short instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
-			unsigned char createperms;
-			unsigned long createtime;
+			fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
+			fu8_t createperms;
+			fu32_t createtime;
 
 			fqcn = va_arg(ap, char *);
-			instance = (unsigned short)va_arg(ap, unsigned int);
-			exchange = (unsigned short)va_arg(ap, unsigned int);
-			flags = (unsigned short)va_arg(ap, unsigned int);
-			createtime = va_arg(ap, unsigned long);
-			maxmsglen = (unsigned short)va_arg(ap, unsigned int);
-			maxoccupancy = (unsigned short)va_arg(ap, unsigned int);
-			createperms = (unsigned char)va_arg(ap, int);
-			unknown = (unsigned short)va_arg(ap, unsigned int);
+			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, int);
+			unknown = (fu16_t)va_arg(ap, unsigned int);
 			name = va_arg(ap, char *);
 			ck = va_arg(ap, char *);
 			va_end(ap);
@@ -1633,7 +1680,7 @@
 					createtime,
 					maxmsglen, maxoccupancy, createperms, unknown,
 					name, ck);
-			aim_chat_join(odata->sess, odata->conn, exchange, ck);
+			aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
 			}
 			break;
 		default:
@@ -1644,79 +1691,76 @@
 	return 1;
 }
 
-int gaim_chat_join(struct aim_session_t *sess,
-		   struct command_rx_struct *command, ...) {
+static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	int count, i = 0;
+	int count, i;
 	struct aim_userinfo_s *info;
 	struct gaim_connection *g = sess->aux_data;
 
 	struct chat_connection *c = NULL;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	count = va_arg(ap, int);
 	info  = va_arg(ap, struct aim_userinfo_s *);
 	va_end(ap);
 
-	c = find_oscar_chat_by_conn(g, command->conn);
+	c = find_oscar_chat_by_conn(g, fr->conn);
 	if (!c)
 		return 1;
 
-	while (i < count)
-		add_chat_buddy(c->cnv, info[i++].sn);
+	for (i = 0; i < count; i++)
+		add_chat_buddy(c->cnv, info[i].sn);
 
 	return 1;
 }
 
-int gaim_chat_leave(struct aim_session_t *sess,
-		    struct command_rx_struct *command, ...) {
+static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	int count, i = 0;
+	int count, i;
 	struct aim_userinfo_s *info;
 	struct gaim_connection *g = sess->aux_data;
 
 	struct chat_connection *c = NULL;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	count = va_arg(ap, int);
 	info  = va_arg(ap, struct aim_userinfo_s *);
 	va_end(ap);
 
-	c = find_oscar_chat_by_conn(g, command->conn);
+	c = find_oscar_chat_by_conn(g, fr->conn);
 	if (!c)
 		return 1;
 
-	while (i < count)
-		remove_chat_buddy(c->cnv, info[i++].sn);
+	for (i = 0; i < count; i++)
+		remove_chat_buddy(c->cnv, info[i].sn);
 
 	return 1;
 }
 
-int gaim_chat_info_update(struct aim_session_t *sess,
-			  struct command_rx_struct *command, ...) {
+static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct aim_userinfo_s *userinfo;
 	struct aim_chat_roominfo *roominfo;
 	char *roomname;
 	int usercount;
 	char *roomdesc;
-	unsigned short unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
-	unsigned long creationtime;
+	fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
+	fu32_t creationtime;
 	struct gaim_connection *gc = sess->aux_data;
-	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, command->conn);
-
-	va_start(ap, command);
+	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
+
+	va_start(ap, fr);
 	roominfo = va_arg(ap, struct aim_chat_roominfo *);
 	roomname = va_arg(ap, char *);
 	usercount= va_arg(ap, int);
 	userinfo = va_arg(ap, struct aim_userinfo_s *);
 	roomdesc = va_arg(ap, char *);
-	unknown_c9 = va_arg(ap, int);
-	creationtime = va_arg(ap, unsigned long);
-	maxmsglen = va_arg(ap, int);
-	unknown_d2 = va_arg(ap, int);
-	unknown_d5 = va_arg(ap, int);
-	maxvisiblemsglen = va_arg(ap, int);
+	unknown_c9 = (fu16_t)va_arg(ap, int);
+	creationtime = (fu32_t)va_arg(ap, unsigned long);
+	maxmsglen = (fu16_t)va_arg(ap, int);
+	unknown_d2 = (fu16_t)va_arg(ap, int);
+	unknown_d5 = (fu16_t)va_arg(ap, int);
+	maxvisiblemsglen = (fu16_t)va_arg(ap, int);
 	va_end(ap);
 
 	debug_printf("inside chat_info_update (maxmsglen = %d, maxvislen = %d)\n",
@@ -1728,16 +1772,15 @@
 	return 1;
 }
 
-int gaim_chat_incoming_msg(struct aim_session_t *sess,
-			   struct command_rx_struct *command, ...) {
+static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct aim_userinfo_s *info;
 	char *msg;
 	struct gaim_connection *gc = sess->aux_data;
-	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, command->conn);
+	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
 	char *tmp;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	info = va_arg(ap, struct aim_userinfo_s *);
 	msg  = va_arg(ap, char *);
 
@@ -1749,16 +1792,16 @@
 	return 1;
 }
 
- /*
-  * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option.
-  */
-int gaim_parse_msgack(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+/*
+ * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option.
+ */
+static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	unsigned short type;
-	char *sn = NULL;
-
-	va_start(ap, command);
-	type = (unsigned short)va_arg(ap, unsigned int);
+	fu16_t type;
+	char *sn;
+
+	va_start(ap, fr);
+	type = (fu16_t)va_arg(ap, unsigned int);
 	sn = va_arg(ap, char *);
 	va_end(ap);
 
@@ -1767,27 +1810,28 @@
 	return 1;
 }
 
-int gaim_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	static char *codes[5] = {"invalid",
-				 "change",
-				 "warning",
-				 "limit",
-				 "limit cleared"};
+static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
+	static const char *codes[5] = {
+		"invalid",
+		 "change",
+		 "warning",
+		 "limit",
+		 "limit cleared",
+	};
 	va_list ap;
-	int code;
-	unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
-	unsigned long currentavg, maxavg;
-
-	va_start(ap, command); 
-	code = va_arg(ap, int);
-	rateclass= va_arg(ap, int);
-	windowsize = va_arg(ap, unsigned long);
-	clear = va_arg(ap, unsigned long);
-	alert = va_arg(ap, unsigned long);
-	limit = va_arg(ap, unsigned long);
-	disconnect = va_arg(ap, unsigned long);
-	currentavg = va_arg(ap, unsigned long);
-	maxavg = va_arg(ap, unsigned long);
+	fu16_t code, rateclass;
+	fu32_t 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 = (fu32_t)va_arg(ap, unsigned long);
+	clear = (fu32_t)va_arg(ap, unsigned long);
+	alert = (fu32_t)va_arg(ap, unsigned long);
+	limit = (fu32_t)va_arg(ap, unsigned long);
+	disconnect = (fu32_t)va_arg(ap, unsigned long);
+	currentavg = (fu32_t)va_arg(ap, unsigned long);
+	maxavg = (fu32_t)va_arg(ap, unsigned long);
 	va_end(ap);
 
 	debug_printf("rate %s (paramid 0x%04lx): curavg = %ld, maxavg = %ld, alert at %ld, "
@@ -1799,28 +1843,29 @@
 		     limit, disconnect,
 		     windowsize);
 
+	/* XXX fix these values */
 	if (code == AIM_RATE_CODE_CHANGE) {
 		if (currentavg >= clear)
-			aim_conn_setlatency(command->conn, 0);
+			aim_conn_setlatency(fr->conn, 0);
 	} else if (code == AIM_RATE_CODE_WARNING) {
-		aim_conn_setlatency(command->conn, windowsize/4);
+		aim_conn_setlatency(fr->conn, windowsize/4);
 	} else if (code == AIM_RATE_CODE_LIMIT) {
-		aim_conn_setlatency(command->conn, windowsize/2);
+		aim_conn_setlatency(fr->conn, windowsize/2);
 	} else if (code == AIM_RATE_CODE_CLEARLIMIT) {
-		aim_conn_setlatency(command->conn, 0);
+		aim_conn_setlatency(fr->conn, 0);
 	}
 
 	return 1;
 }
 
-int gaim_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	int newevil;
+	fu16_t newevil;
 	struct aim_userinfo_s *userinfo;
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
-	newevil = va_arg(ap, int);
+	va_start(ap, fr);
+	newevil = (fu16_t)va_arg(ap, unsigned int);
 	userinfo = va_arg(ap, struct aim_userinfo_s *);
 	va_end(ap);
 
@@ -1829,51 +1874,46 @@
 	return 1;
 }
 
-int gaim_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int rateresp_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
-	switch (command->conn->type) {
-	case AIM_CONN_TYPE_BOS:
-		aim_bos_ackrateresp(sess, command->conn);
-		aim_bos_reqpersonalinfo(sess, command->conn);
-		aim_bos_reqlocaterights(sess, command->conn);
-		aim_bos_setprofile(sess, command->conn, gc->user->user_info, NULL, gaim_caps);
-		aim_bos_reqbuddyrights(sess, command->conn);
-
-		account_online(gc);
-		serv_finish_login(gc);
-
-		if (bud_list_cache_exists(gc))
-			do_import(NULL, gc);
-
-		debug_printf("buddy list loaded\n");
-
-		aim_reqicbmparams(sess, command->conn);
-
-		aim_bos_reqrights(sess, command->conn);
-		aim_bos_setgroupperm(sess, command->conn, AIM_FLAG_ALLUSERS);
-		aim_bos_setprivacyflags(sess, command->conn, AIM_PRIVFLAGS_ALLOWIDLE |
-							     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
-
-		break;
-	case AIM_CONN_TYPE_AUTH:
-		aim_bos_ackrateresp(sess, command->conn);
-		aim_auth_clientready(sess, command->conn);
-		debug_printf("connected to auth (admin)\n");
-		break;
-	default:
-		debug_printf("got rate response for unhandled connection type %04x\n",
-				command->conn->type);
-		break;
-	}
+
+	aim_bos_ackrateresp(sess, fr->conn);
+	aim_bos_reqpersonalinfo(sess, fr->conn);
+	aim_bos_reqlocaterights(sess, fr->conn);
+	aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
+	aim_bos_reqbuddyrights(sess, fr->conn);
+
+	account_online(gc);
+	serv_finish_login(gc);
+
+	if (bud_list_cache_exists(gc))
+		do_import(NULL, gc);
+
+	debug_printf("buddy list loaded\n");
+
+	aim_reqicbmparams(sess, fr->conn);
+
+	aim_bos_reqrights(sess, fr->conn);
+	aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
+	aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
+						     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
 
 	return 1;
 }
 
-int gaim_icbm_param_info(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int rateresp_auth(aim_session_t *sess, aim_frame_t *fr, ...) {
+	aim_bos_ackrateresp(sess, fr->conn);
+	aim_auth_clientready(sess, fr->conn);
+	debug_printf("connected to auth (admin)\n");
+
+	return 1;
+}
+
+static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct aim_icbmparameters *params;
 	va_list ap;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	params = va_arg(ap, struct aim_icbmparameters *);
 	va_end(ap);
 
@@ -1883,36 +1923,23 @@
 			((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
 			params->minmsginterval);
 
+	/* Maybe senderwarn and recverwarn should be user preferences... */
 	params->maxmsglen = 8000;
 	params->minmsginterval = 0;
 
-	aim_seticbmparam(sess, command->conn, params);
+	aim_seticbmparam(sess, fr->conn, params);
 
 	return 1;
 }
 
-int gaim_reportinterval(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+/* XXX this is frivelous... do you really want to know this info? */
+static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	unsigned short interval = 0;
-
-	va_start(ap, command);
-	interval = va_arg(ap, int);
-	va_end(ap);
-
-	debug_printf("minimum report interval: %d (seconds?)\n", interval);
-
-	aim_reqicbmparams(sess, command->conn);
-
-	return 1;
-}
-
-int gaim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	va_list ap;
-	unsigned short maxbuddies, maxwatchers;
-
-	va_start(ap, command);
-	maxbuddies = (unsigned short)va_arg(ap, unsigned int);
-	maxwatchers = (unsigned short)va_arg(ap, unsigned int);
+	fu16_t maxbuddies, maxwatchers;
+
+	va_start(ap, fr);
+	maxbuddies = (fu16_t)va_arg(ap, unsigned int);
+	maxwatchers = (fu16_t)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	debug_printf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
@@ -1920,32 +1947,32 @@
 	return 1;
 }
 
-int gaim_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	unsigned short maxpermits, maxdenies;
+static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
+	fu16_t maxpermits, maxdenies;
 	va_list ap;
 
-	va_start(ap, command);
-	maxpermits = (unsigned short)va_arg(ap, unsigned int);
-	maxdenies = (unsigned short)va_arg(ap, unsigned int);
+	va_start(ap, fr);
+	maxpermits = (fu16_t)va_arg(ap, unsigned int);
+	maxdenies = (fu16_t)va_arg(ap, unsigned int);
 	va_end(ap);
 
 	debug_printf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
 
-	aim_bos_clientready(sess, command->conn);
-
-	aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_CHATNAV);
+	aim_bos_clientready(sess, fr->conn);
+
+	aim_bos_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
 
 	return 1;
 }
 
-int gaim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *address, *SNs;
 	int i, num;
 	char *buf;
 	int at = 0, len;
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	address = va_arg(ap, char *);
 	num = va_arg(ap, int);
 	SNs = va_arg(ap, char *);
@@ -1962,12 +1989,12 @@
 	return 1;
 }
 
-int gaim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *address;
 	char buf[BUF_LONG];
 
-	va_start(ap, command);
+	va_start(ap, fr);
 	address = va_arg(ap, char *);
 	va_end(ap);
 
@@ -1977,14 +2004,14 @@
 	return 1;
 }
 
-int gaim_account_confirm(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	int status;
+static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) {
+	fu16_t status;
 	va_list ap;
 	char msg[256];
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
-	status = va_arg(ap, int); /* status code of confirmation request */
+	va_start(ap, fr);
+	status = (fu16_t)va_arg(ap, unsigned int); /* status code of confirmation request */
 	va_end(ap);
 
 	debug_printf("account confirmation returned status 0x%04x (%s)\n", status,
@@ -1998,25 +2025,23 @@
 	return 1;
 }
 
-int gaim_info_change(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	unsigned short change = 0;
-	int perms, type, length, str;
+static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) {
+	int change, str;
+	fu16_t perms, type, length;
 	char *val;
 	va_list ap;
 	char buf[BUF_LONG];
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
-	perms = va_arg(ap, int);
-	type = va_arg(ap, int);
-	length = va_arg(ap, int);
+	va_start(ap, fr);
+	change = va_arg(ap, int);
+	perms = (fu16_t)va_arg(ap, unsigned int);
+	type = (fu16_t)va_arg(ap, unsigned int);
+	length = (fu16_t)va_arg(ap, unsigned int);
 	val = va_arg(ap, char *);
 	str = va_arg(ap, int);
 	va_end(ap);
 
-	if (aimutil_get16(command->data+2) == 0x0005)
-		change = 1;
-
 	debug_printf("info%s: perms = %d, type = %x, length = %d, val = %s\n",
 			change ? " change" : "", perms, type, length, str ? val : "(not string)");
 
@@ -2047,15 +2072,13 @@
 			return aim_send_im(odata->sess, odata->conn, name, AIM_IMFLAGS_AWAY, message);
 		else {
 			struct aim_sendimext_args args;
-
-			int flags = AIM_IMFLAGS_ACK;
-
 			GSList *h = odata->hasicons;
 			struct icon_req *ir = NULL;
 			char *who = normalize(name);
-
 			struct stat st;
 
+			args.flags = AIM_IMFLAGS_ACK;
+
 			while (h) {
 				ir = h->data;
 				if (ir->request && !strcmp(who, ir->user))
@@ -2064,7 +2087,7 @@
 			}
 			if (h) {
 				ir->request = FALSE;
-				flags |= AIM_IMFLAGS_BUDDYREQ;
+				args.flags |= AIM_IMFLAGS_BUDDYREQ;
 				debug_printf("sending buddy icon request with message\n");
 			}
 
@@ -2078,7 +2101,7 @@
 					args.iconsum   = aim_iconsum(buf, st.st_size);
 					args.iconstamp = st.st_mtime;
 
-					flags |= AIM_IMFLAGS_HASICON;
+					args.flags |= AIM_IMFLAGS_HASICON;
 
 					fclose(file);
 					g_free(buf);
@@ -2088,7 +2111,6 @@
 			args.destsn = name;
 			args.msg    = message;
 			args.msglen = strlen(message);
-			args.flags  = flags;
 
 			return aim_send_im_ext(odata->sess, odata->conn, &args);
 		}
@@ -2205,7 +2227,7 @@
 
 static void oscar_join_chat(struct gaim_connection *g, GList *data) {
 	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	struct aim_conn_t *cur = NULL;
+	aim_conn_t *cur;
 	char *name;
 	int *exchange;
 
@@ -2347,112 +2369,74 @@
 	return NULL;
 }
 
-static int gaim_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_directim_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
-	struct aim_directim_priv *priv;
-	struct aim_conn_t *newconn;
+	aim_conn_t *newconn, *listenerconn;
 	struct conversation *cnv;
 	struct direct_im *dim;
 	char buf[256];
-
-	va_start(ap, command);
-	newconn = va_arg(ap, struct aim_conn_t *);
+	char *sn;
+
+	va_start(ap, fr);
+	newconn = va_arg(ap, aim_conn_t *);
+	listenerconn = va_arg(ap, aim_conn_t *);
 	va_end(ap);
 
-	priv = (struct aim_directim_priv *)newconn->priv;
-
-	debug_printf("DirectIM: initiate success to %s\n", priv->sn);
-	dim = find_direct_im(od, priv->sn);
-
-	if (!(cnv = find_conversation(priv->sn)))
-		cnv = new_conversation(priv->sn);
+	aim_conn_close(listenerconn);
+	aim_conn_kill(sess, &listenerconn);
+
+	sn = g_strdup(aim_directim_getsn(newconn));
+
+	debug_printf("DirectIM: initiate success to %s\n", sn);
+	dim = find_direct_im(od, sn);
+
+	if (!(cnv = find_conversation(sn)))
+		cnv = new_conversation(sn);
 	gaim_input_remove(dim->watcher);
 	dim->conn = newconn;
 	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
 					oscar_callback, dim->conn);
-	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), priv->sn);
+	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
+	g_free(sn);
 	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
 
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
 				gaim_directim_incoming, 0);
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT,
-				gaim_directim_disconnect, 0);
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
 				gaim_directim_typing, 0);
 
 	return 1;
 }
 
-static int gaim_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_directim_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	char *msg = NULL;
-	struct aim_conn_t *conn;
-	struct aim_directim_priv *priv;
+	char *msg, *sn;
 	struct gaim_connection *gc = sess->aux_data;
 
-	va_start(ap, command);
-	conn = va_arg(ap, struct aim_conn_t *);
+	va_start(ap, fr);
+	sn = va_arg(ap, char *);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
-	if (!(priv = conn->priv)) {
-		return -1;
-	}
-
-	debug_printf("Got DirectIM message from %s\n", priv->sn);
-
-	serv_got_im(gc, priv->sn, msg, 0, time((time_t)NULL));
+	debug_printf("Got DirectIM message from %s\n", sn);
+
+	serv_got_im(gc, sn, msg, 0, time((time_t)NULL));
 
 	return 1;
 }
 
-static int gaim_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+static int gaim_directim_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	struct aim_conn_t *conn;
 	char *sn;
-	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
-	struct conversation *cnv;
-	struct direct_im *dim;
-	char buf[256];
-
-	va_start(ap, command);
-	conn = va_arg(ap, struct aim_conn_t *);
+
+	va_start(ap, fr);
 	sn = va_arg(ap, char *);
 	va_end(ap);
 
-	debug_printf("%s disconnected Direct IM.\n", sn);
-
-	dim = find_direct_im(od, sn);
-	od->direct_ims = g_slist_remove(od->direct_ims, dim);
-	gaim_input_remove(dim->watcher);
-
-	g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn);
-	if ((cnv = find_conversation(sn)) != NULL)
-		write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
-
-	aim_conn_kill(sess, &conn);
-
-	return 1;
-}
-
-static int gaim_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	va_list ap;
-	struct aim_conn_t *conn;
-	struct aim_directim_priv *priv;
-
-	va_start(ap, command);
-	conn = va_arg(ap, struct aim_conn_t *);
-	va_end(ap);
-
-	if (!(priv = conn->priv)) {
-		return -1;
-	}
-
 	/* I had to leave this. It's just too funny. It reminds me of my sister. */
-	debug_printf("ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", priv->sn);
+	debug_printf("ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn);
 
 	return 1;
 }
@@ -2480,7 +2464,7 @@
 	dim->gc = gc;
 	g_snprintf(dim->name, sizeof dim->name, "%s", data->who);
 
-	dim->conn = aim_directim_initiate(od->sess, od->conn, NULL, data->who);
+	dim->conn = aim_directim_initiate(od->sess, od->conn, data->who);
 	if (dim->conn != NULL) {
 		od->direct_ims = g_slist_append(od->direct_ims, dim);
 		dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
@@ -2617,7 +2601,7 @@
 static void oscar_do_action(struct gaim_connection *gc, char *act)
 {
 	struct oscar_data *od = gc->proto_data;
-	struct aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
+	aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
 
 	if (!strcmp(act, "Set User Info")) {
 		show_set_info(gc);
@@ -2673,6 +2657,20 @@
 	}
 }
 
+static void oscar_convo_closed(struct gaim_connection *gc, char *who)
+{
+	struct oscar_data *od = gc->proto_data;
+	struct direct_im *dim = find_direct_im(od, who);
+
+	if (!dim)
+		return;
+
+	od->direct_ims = g_slist_remove(od->direct_ims, dim);
+	gaim_input_remove(dim->watcher);
+	aim_conn_kill(od->sess, &dim->conn);
+	g_free(dim);
+}
+
 static struct prpl *my_protocol = NULL;
 
 void oscar_init(struct prpl *ret) {
@@ -2713,6 +2711,7 @@
 	ret->chat_whisper = NULL;
 	ret->chat_send = oscar_chat_send;
 	ret->keepalive = oscar_keepalive;
+	ret->convo_closed = oscar_convo_closed;
 
 	my_protocol = ret;
 }
--- a/src/protocols/oscar/rxhandlers.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/rxhandlers.c	Sun Sep 09 10:07:14 2001 +0000
@@ -10,561 +10,564 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
-static aim_module_t *findmodule(struct aim_session_t *sess, const char *name)
-{
-  aim_module_t *cur;
+struct aim_rxcblist_s {
+	fu16_t family;
+	fu16_t type;
+	aim_rxcallback_t handler;
+	u_short flags;
+	struct aim_rxcblist_s *next;
+};
 
-  for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
-    if (strcmp(name, cur->name) == 0)
-      return cur;
-  }
+static aim_module_t *findmodule(aim_session_t *sess, const char *name)
+{
+	aim_module_t *cur;
 
-  return NULL;
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+		if (strcmp(name, cur->name) == 0)
+			return cur;
+	}
+
+	return NULL;
 }
 
-faim_internal int aim__registermodule(struct aim_session_t *sess, int (*modfirst)(struct aim_session_t *, aim_module_t *))
+faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *))
 {
-  aim_module_t *mod;
+	aim_module_t *mod;
+
+	if (!sess || !modfirst)
+		return -1;
+
+	if (!(mod = malloc(sizeof(aim_module_t))))
+		return -1;
+	memset(mod, 0, sizeof(aim_module_t));
 
-  if (!sess || !modfirst)
-    return -1;
+	if (modfirst(sess, mod) == -1) {
+		free(mod);
+		return -1;
+	}
 
-  if (!(mod = malloc(sizeof(aim_module_t))))
-    return -1;
-  memset(mod, 0, sizeof(aim_module_t));
+	if (findmodule(sess, mod->name)) {
+		if (mod->shutdown)
+			mod->shutdown(sess, mod);
+		free(mod);
+		return -1;
+	}
+
+	mod->next = (aim_module_t *)sess->modlistv;
+	(aim_module_t *)sess->modlistv = mod;
 
-  if (modfirst(sess, mod) == -1) {
-    free(mod);
-    return -1;
-  }
+	faimdprintf(sess, 1, "registered module %s (family 0x%04x)\n", mod->name, mod->family);
+
+	return 0;
+}
+
+faim_internal void aim__shutdownmodules(aim_session_t *sess)
+{
+	aim_module_t *cur;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; ) {
+		aim_module_t *tmp;
 
-  if (findmodule(sess, mod->name)) {
-    if (mod->shutdown)
-      mod->shutdown(sess, mod);
-    free(mod);
-    return -1;
-  }
+		tmp = cur->next;
+
+		if (cur->shutdown)
+			cur->shutdown(sess, cur);
+
+		free(cur);
 
-  mod->next = (aim_module_t *)sess->modlistv;
-  (aim_module_t *)sess->modlistv = mod;
+		cur = tmp;
+	}
 
-  faimdprintf(sess, 1, "registered module %s (family 0x%04x)\n", mod->name, mod->family);
+	sess->modlistv = NULL;
 
-  return 0;
+	return;
 }
 
-faim_internal void aim__shutdownmodules(struct aim_session_t *sess)
+static int consumesnac(aim_session_t *sess, aim_frame_t *rx)
 {
-  aim_module_t *cur;
+	aim_module_t *cur;
+	aim_modsnac_t snac;
 
-  for (cur = (aim_module_t *)sess->modlistv; cur; ) {
-    aim_module_t *tmp;
+	if (aim_bstream_empty(&rx->data) < 10)
+		return 0;
 
-    tmp = cur->next;
+	snac.family = aimbs_get16(&rx->data);
+	snac.subtype = aimbs_get16(&rx->data);
+	snac.flags = aimbs_get16(&rx->data);
+	snac.id = aimbs_get32(&rx->data);
 
-    if (cur->shutdown)
-      cur->shutdown(sess, cur);
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
 
-    free(cur);
+		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+				(cur->family != snac.family))
+			continue;
 
-    cur = tmp;
-  }
+		if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+			return 1;
 
-  sess->modlistv = NULL;
+	}
 
-  return;
+	return 0;
 }
 
-static int consumesnac(struct aim_session_t *sess, struct command_rx_struct *rx)
+static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, fu16_t family, fu16_t subtype)
 {
-  aim_module_t *cur;
-  aim_modsnac_t snac;
+	aim_module_t *cur;
+	aim_modsnac_t snac;
 
-  snac.family = aimutil_get16(rx->data+0);
-  snac.subtype = aimutil_get16(rx->data+2);
-  snac.flags = aimutil_get16(rx->data+4);
-  snac.id = aimutil_get32(rx->data+6);
+	snac.family = family;
+	snac.subtype = subtype;
+	snac.flags = snac.id = 0;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
 
-  for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
-
-    if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
-	(cur->family != snac.family))
-      continue;
+		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+				(cur->family != snac.family))
+			continue;
 
-    if (cur->snachandler(sess, cur, rx, &snac, rx->data+10, rx->commandlen-10))
-      return 1;
+		if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+			return 1;
 
-  }
+	}
 
-  return 0;
+	return 0;
 }
 
-static int consumenonsnac(struct aim_session_t *sess, struct command_rx_struct *rx, unsigned short family, unsigned short subtype)
+static int negchan_middle(aim_session_t *sess, aim_frame_t *fr)
 {
-  aim_module_t *cur;
-  aim_modsnac_t snac;
+	aim_tlvlist_t *tlvlist;
+	char *msg = NULL;
+	fu16_t code = 0;
+	aim_rxcallback_t userfunc;
+	int ret = 1;
 
-  snac.family = family;
-  snac.subtype = subtype;
-  snac.flags = snac.id = 0;
-
-  for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+	if (aim_bstream_empty(&fr->data) == 0) {
+		/* XXX should do something with this */
+		return 1;
+	}
 
-    if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
-	(cur->family != snac.family))
-      continue;
+	/* Used only by the older login protocol */
+	/* XXX remove this special case? */
+	if (fr->conn->type == AIM_CONN_TYPE_AUTH)
+		return consumenonsnac(sess, fr, 0x0017, 0x0003);
+
+	tlvlist = aim_readtlvchain(&fr->data);
+
+	if (aim_gettlv(tlvlist, 0x0009, 1))
+		code = aim_gettlv16(tlvlist, 0x0009, 1);
 
-    if (cur->snachandler(sess, cur, rx, &snac, rx->data, rx->commandlen))
-      return 1;
+	if (aim_gettlv(tlvlist, 0x000b, 1))
+		msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+	if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
+		ret = userfunc(sess, fr, code, msg);
 
-  }
+	aim_freetlvchain(&tlvlist);
 
-  return 0;
+	free(msg);
+
+	return ret;
 }
 
 /*
  * Bleck functions get called when there's no non-bleck functions
  * around to cleanup the mess...
  */
-faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
+faim_internal int bleck(aim_session_t *sess, aim_frame_t *frame, ...)
 {
-  u_short family;
-  u_short subtype;
-
-  u_short maxf;
-  u_short maxs;
+	fu16_t family, subtype;
+	fu16_t maxf, maxs;
 
-  /* XXX: this is ugly. and big just for debugging. */
-  char *literals[14][25] = {
-    {"Invalid", 
-     NULL
-    },
-    {"General", 
-     "Invalid",
-     "Error",
-     "Client Ready",
-     "Server Ready",
-     "Service Request",
-     "Redirect",
-     "Rate Information Request",
-     "Rate Information",
-     "Rate Information Ack",
-     NULL,
-     "Rate Information Change",
-     "Server Pause",
-     NULL,
-     "Server Resume",
-     "Request Personal User Information",
-     "Personal User Information",
-     "Evil Notification",
-     NULL,
-     "Migration notice",
-     "Message of the Day",
-     "Set Privacy Flags",
-     "Well Known URL",
-     "NOP"
-    },
-    {"Location", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Information", 
-      "Set user information", 
-      "Request User Information", 
-      "User Information", 
-      "Watcher Sub Request",
-      "Watcher Notification"
-    },
-    {"Buddy List Management", 
-      "Invalid", 
-      "Error", 
-      "Request Rights",
-      "Rights Information",
-      "Add Buddy", 
-      "Remove Buddy", 
-      "Watcher List Query", 
-      "Watcher List Response", 
-      "Watcher SubRequest", 
-      "Watcher Notification", 
-      "Reject Notification", 
-      "Oncoming Buddy", 
-      "Offgoing Buddy"
-    },
-    {"Messeging", 
-      "Invalid",
-      "Error", 
-      "Add ICBM Parameter",
-      "Remove ICBM Parameter", 
-      "Request Parameter Information",
-      "Parameter Information",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request",
-      "Evil Reply", 
-      "Missed Calls",
-      "Message Error", 
-      "Host Ack"
-    },
-    {"Advertisements", 
-      "Invalid", 
-      "Error", 
-      "Request Ad",
-      "Ad Data (GIFs)"
-    },
-    {"Invitation / Client-to-Client", 
-     "Invalid",
-     "Error",
-     "Invite a Friend",
-     "Invitation Ack"
-    },
-    {"Administrative", 
-      "Invalid",
-      "Error",
-      "Information Request",
-      "Information Reply",
-      "Information Change Request",
-      "Information Chat Reply",
-      "Account Confirm Request",
-      "Account Confirm Reply",
-      "Account Delete Request",
-      "Account Delete Reply"
-    },
-    {"Popups", 
-      "Invalid",
-      "Error",
-      "Display Popup"
-    },
-    {"BOS", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Response",
-      "Set group permission mask",
-      "Add permission list entries",
-      "Delete permission list entries",
-      "Add deny list entries",
-      "Delete deny list entries",
-      "Server Error"
-    },
-    {"User Lookup", 
-      "Invalid",
-      "Error",
-      "Search Request",
-      "Search Response"
-    },
-    {"Stats", 
-      "Invalid",
-      "Error",
-      "Set minimum report interval",
-      "Report Events"
-    },
-    {"Translate", 
-      "Invalid",
-      "Error",
-      "Translate Request",
-      "Translate Reply",
-    },
-    {"Chat Navigation", 
-      "Invalid",
-      "Error",
-      "Request rights",
-      "Request Exchange Information",
-      "Request Room Information",
-      "Request Occupant List",
-      "Search for Room",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request", 
-      "Evil Reply", 
-      "Chat Error",
-    }
-  };
+	static const char *channels[6] = {
+		"Invalid (0)",
+		"FLAP Version",
+		"SNAC",
+		"Invalid (3)",
+		"Negotiation",
+		"FLAP NOP"
+	};
+	static const int maxchannels = 5;
+	
+	/* XXX: this is ugly. and big just for debugging. */
+	static const char *literals[14][25] = {
+		{"Invalid", 
+		 NULL
+		},
+		{"General", 
+		 "Invalid",
+		 "Error",
+		 "Client Ready",
+		 "Server Ready",
+		 "Service Request",
+		 "Redirect",
+		 "Rate Information Request",
+		 "Rate Information",
+		 "Rate Information Ack",
+		 NULL,
+		 "Rate Information Change",
+		 "Server Pause",
+		 NULL,
+		 "Server Resume",
+		 "Request Personal User Information",
+		 "Personal User Information",
+		 "Evil Notification",
+		 NULL,
+		 "Migration notice",
+		 "Message of the Day",
+		 "Set Privacy Flags",
+		 "Well Known URL",
+		 "NOP"
+		},
+		{"Location", 
+		 "Invalid",
+		 "Error",
+		 "Request Rights",
+		 "Rights Information", 
+		 "Set user information", 
+		 "Request User Information", 
+		 "User Information", 
+		 "Watcher Sub Request",
+		 "Watcher Notification"
+		},
+		{"Buddy List Management", 
+		 "Invalid", 
+		 "Error", 
+		 "Request Rights",
+		 "Rights Information",
+		 "Add Buddy", 
+		 "Remove Buddy", 
+		 "Watcher List Query", 
+		 "Watcher List Response", 
+		 "Watcher SubRequest", 
+		 "Watcher Notification", 
+		 "Reject Notification", 
+		 "Oncoming Buddy", 
+		 "Offgoing Buddy"
+		},
+		{"Messeging", 
+		 "Invalid",
+		 "Error", 
+		 "Add ICBM Parameter",
+		 "Remove ICBM Parameter", 
+		 "Request Parameter Information",
+		 "Parameter Information",
+		 "Outgoing Message", 
+		 "Incoming Message",
+		 "Evil Request",
+		 "Evil Reply", 
+		 "Missed Calls",
+		 "Message Error", 
+		 "Host Ack"
+		},
+		{"Advertisements", 
+		 "Invalid", 
+		 "Error", 
+		 "Request Ad",
+		 "Ad Data (GIFs)"
+		},
+		{"Invitation / Client-to-Client", 
+		 "Invalid",
+		 "Error",
+		 "Invite a Friend",
+		 "Invitation Ack"
+		},
+		{"Administrative", 
+		 "Invalid",
+		 "Error",
+		 "Information Request",
+		 "Information Reply",
+		 "Information Change Request",
+		 "Information Chat Reply",
+		 "Account Confirm Request",
+		 "Account Confirm Reply",
+		 "Account Delete Request",
+		 "Account Delete Reply"
+		},
+		{"Popups", 
+		 "Invalid",
+		 "Error",
+		 "Display Popup"
+		},
+		{"BOS", 
+		 "Invalid",
+		 "Error",
+		 "Request Rights",
+		 "Rights Response",
+		 "Set group permission mask",
+		 "Add permission list entries",
+		 "Delete permission list entries",
+		 "Add deny list entries",
+		 "Delete deny list entries",
+		 "Server Error"
+		},
+		{"User Lookup", 
+		 "Invalid",
+		 "Error",
+		 "Search Request",
+		 "Search Response"
+		},
+		{"Stats", 
+		 "Invalid",
+		 "Error",
+		 "Set minimum report interval",
+		 "Report Events"
+		},
+		{"Translate", 
+		 "Invalid",
+		 "Error",
+		 "Translate Request",
+		 "Translate Reply",
+		},
+		{"Chat Navigation", 
+		 "Invalid",
+		 "Error",
+		 "Request rights",
+		 "Request Exchange Information",
+		 "Request Room Information",
+		 "Request Occupant List",
+		 "Search for Room",
+		 "Outgoing Message", 
+		 "Incoming Message",
+		 "Evil Request", 
+		 "Evil Reply", 
+		 "Chat Error",
+		}
+	};
 
-  maxf = sizeof(literals) / sizeof(literals[0]);
-  maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
+	maxf = sizeof(literals) / sizeof(literals[0]);
+	maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
 
-  family = aimutil_get16(workingPtr->data+0);
-  subtype= aimutil_get16(workingPtr->data+2);
+	if (frame->hdr.flap.type == 0x02) {
 
-  if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
-    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
-  else
-    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
+		family = aimbs_get16(&frame->data);
+		subtype = aimbs_get16(&frame->data);
+		
+		if ((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
+			faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (%s)\n", channels[frame->hdr.flap.type], family, subtype, literals[family][subtype+1]);
+		else
+			faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (no literal)\n", channels[frame->hdr.flap.type], family, subtype);
+	} else {
 
-  return 1;
+		if (frame->hdr.flap.type <= maxchannels)
+			faimdprintf(sess, 0, "bleck: channel %s (0x%02x)\n", channels[frame->hdr.flap.type], frame->hdr.flap.type);
+		else
+			faimdprintf(sess, 0, "bleck: unknown channel 0x%02x\n", frame->hdr.flap.type);
+
+	}
+		
+	return 1;
 }
 
-faim_export int aim_conn_addhandler(struct aim_session_t *sess,
-				    struct aim_conn_t *conn,
-				    u_short family,
-				    u_short type,
-				    aim_rxcallback_t newhandler,
-				    u_short flags)
+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)
 {
-  struct aim_rxcblist_t *newcb;
+	struct aim_rxcblist_s *newcb;
+
+	if (!conn)
+		return -1;
 
-  if (!conn)
-    return -1;
+	faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
 
-  faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
+	if (!(newcb = (struct aim_rxcblist_s *)calloc(1, sizeof(struct aim_rxcblist_s))))
+		return -1;
 
-  if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
-    return -1;
-  newcb->family = family;
-  newcb->type = type;
-  newcb->flags = flags;
-  if (!newhandler)
-    newcb->handler = &bleck;
-  else
-    newcb->handler = newhandler;
-  newcb->next = NULL;
-  
-  if (!conn->handlerlist)
-    conn->handlerlist = newcb;
-  else {
-    struct aim_rxcblist_t *cur;
+	newcb->family = family;
+	newcb->type = type;
+	newcb->flags = flags;
+	newcb->handler = newhandler ? newhandler : bleck;
+	newcb->next = NULL;
 
-    cur = conn->handlerlist;
+	if (!conn->handlerlist)
+		conn->handlerlist = (void *)newcb;
+	else {
+		struct aim_rxcblist_s *cur;
 
-    while (cur->next)
-      cur = cur->next;
-    cur->next = newcb;
-  }
+		for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next)
+			;
+		cur->next = newcb;
+	}
 
-  return 0;
+	return 0;
 }
 
-faim_export int aim_clearhandlers(struct aim_conn_t *conn)
+faim_export int aim_clearhandlers(aim_conn_t *conn)
 {
- struct aim_rxcblist_t *cur;
+	struct aim_rxcblist_s *cur;
 
- if (!conn)
-   return -1;
+	if (!conn)
+		return -1;
 
- for (cur = conn->handlerlist; cur; ) {
-   struct aim_rxcblist_t *tmp;
+	for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) {
+		struct aim_rxcblist_s *tmp;
 
-   tmp = cur->next;
-   free(cur);
-   cur = tmp;
- }
- conn->handlerlist = NULL;
+		tmp = cur->next;
+		free(cur);
+		cur = tmp;
+	}
+	conn->handlerlist = NULL;
 
- return 0;
+	return 0;
 }
 
-faim_internal aim_rxcallback_t aim_callhandler(struct aim_session_t *sess,
-					       struct aim_conn_t *conn,
-					       unsigned short family,
-					       unsigned short type)
+faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type)
 {
-  struct aim_rxcblist_t *cur;
+	struct aim_rxcblist_s *cur;
 
-  if (!conn)
-    return NULL;
+	if (!conn)
+		return NULL;
+
+	faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
 
-  faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
-  
-  for (cur = conn->handlerlist; cur; cur = cur->next) {
-    if ((cur->family == family) && (cur->type == type))
-      return cur->handler;
-  }
+	for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) {
+		if ((cur->family == family) && (cur->type == type))
+			return cur->handler;
+	}
 
-  if (type == AIM_CB_SPECIAL_DEFAULT) {
-    faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
-    return NULL; /* prevent infinite recursion */
-  }
+	if (type == AIM_CB_SPECIAL_DEFAULT) {
+		faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
+		return NULL; /* prevent infinite recursion */
+	}
 
-  faimdprintf(sess, 1, "aim_callhandler: no handler for  0x%04x/0x%04x\n", family, type);
+	faimdprintf(sess, 1, "aim_callhandler: no handler for  0x%04x/0x%04x\n", family, type);
 
-  return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+	return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
 }
 
-faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
-					  struct aim_conn_t *conn,
-					  u_short family,
-					  u_short type,
-					  struct command_rx_struct *ptr)
+faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src)
 {
-  aim_rxcallback_t userfunc = NULL;
-  userfunc = aim_callhandler(sess, conn, family, type);
-  if (userfunc)
-    return userfunc(sess, ptr);
-  return 1; /* XXX */
+	struct aim_rxcblist_s *cur;
+
+	for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) {
+		aim_conn_addhandler(sess, dest, cur->family, cur->type, 
+						cur->handler, cur->flags);
+	}
+
+	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)
+{
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, conn, family, type)))
+		return userfunc(sess, ptr);
+
+	return 1; /* XXX */
 }
 
 /*
-  aim_rxdispatch()
-
-  Basically, heres what this should do:
-    1) Determine correct packet handler for this packet
-    2) Mark the packet handled (so it can be dequeued in purge_queue())
-    3) Send the packet to the packet handler
-    4) Go to next packet in the queue and start over
-    5) When done, run purge_queue() to purge handled commands
-
-  Note that any unhandlable packets should probably be left in the
-  queue.  This is the best way to prevent data loss.  This means
-  that a single packet may get looked at by this function multiple
-  times.  This is more good than bad!  This behavior may change.
-
-  Aren't queue's fun? 
-
-  TODO: Get rid of all the ugly if's.
-  TODO: Clean up.
-  TODO: More support for mid-level handlers.
-  TODO: Allow for NULL handlers.
-  
+ * aim_rxdispatch()
+ *
+ * Basically, heres what this should do:
+ *   1) Determine correct packet handler for this packet
+ *   2) Mark the packet handled (so it can be dequeued in purge_queue())
+ *   3) Send the packet to the packet handler
+ *   4) Go to next packet in the queue and start over
+ *   5) When done, run purge_queue() to purge handled commands
+ *
+ * TODO: Clean up.
+ * TODO: More support for mid-level handlers.
+ * TODO: Allow for NULL handlers.
+ *
  */
-faim_export int aim_rxdispatch(struct aim_session_t *sess)
+faim_export void aim_rxdispatch(aim_session_t *sess)
 {
-  int i = 0;
-  struct command_rx_struct *workingPtr = NULL;
-  static int critical = 0;
-  
-  if (critical)
-    return 0; /* don't call recursively! */
+	int i;
+	aim_frame_t *cur;
 
-  critical = 1;
+	for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) {
+
+		/*
+		 * XXX: This is still fairly ugly.
+		 */
 
-  if (sess->queue_incoming == NULL) {
-    faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
-  } else {
-    workingPtr = sess->queue_incoming;
-    for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
-      unsigned short family,subtype;
+		if (cur->handled)
+			continue;
 
-      /*
-       * XXX: This is still fairly ugly.
-       */
-      if (workingPtr->handled)
-	continue;
+		/*
+		 * This is a debugging/sanity check only and probably 
+		 * could/should be removed for stable code.
+		 */
+		if (((cur->hdrtype == AIM_FRAMETYPE_OFT) && 
+		   (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
+		  ((cur->hdrtype == AIM_FRAMETYPE_FLAP) && 
+		   (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
+			faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", cur->hdrtype, cur->conn->type);
+			cur->handled = 1;
+			continue;
+		}
 
-      /*
-       * This is a debugging/sanity check only and probably could/should be removed
-       * for stable code.
-       */
-      if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && 
-	   (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
-	  ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && 
-	   (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
-	faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
-	workingPtr->handled = 1;
-	continue;
-      }
+		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
+			if (cur->hdrtype != AIM_FRAMETYPE_OFT) {
+				faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
+				cur->handled = 1; /* get rid of it */
+			} else {
+				/* XXX: implement this */
+				faimdprintf(sess, 0, "faim: OFT frame!\n");
+				cur->handled = 1; /* get rid of it */
+			}
+			continue;
+		}
 
-      if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
-	/* make sure that we only get OFT frames on these connections */
-	if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
-	  faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
-	  workingPtr->handled = 1; /* get rid of it */
-	} else {
-	  /* XXX: implement this */
-	  faimdprintf(sess, 0, "faim: OFT frame!\n");
-	  workingPtr->handled = 1; /* get rid of it */
-	}
-	continue;
-      }
+		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+			/* not possible */
+			faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
+			cur->handled = 1;
+			continue;
+		}
 
-      if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
-	/* not possible */
-	faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
-	workingPtr->handled = 1;
-	continue;
-      }
+		if (cur->hdr.flap.type == 0x01) {
+			
+			cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
+			
+			continue;
+			
+		} else if (cur->hdr.flap.type == 0x02) {
 
-      if ((workingPtr->commandlen == 4) && 
-	  (aimutil_get32(workingPtr->data) == 0x00000001)) {
-	workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-	continue;
-      } 
+			if ((cur->handled = consumesnac(sess, cur)))
+				continue;
 
-      if (workingPtr->hdr.oscar.type == 0x04) {
-	workingPtr->handled = aim_negchan_middle(sess, workingPtr);
-	continue;
-      }
+		} else if (cur->hdr.flap.type == 0x04) {
 
-      if ((workingPtr->handled = consumesnac(sess, workingPtr)))
-	continue;
-	  
-      if (!workingPtr->handled) {
-	family = aimutil_get16(workingPtr->data);
-	subtype = aimutil_get16(workingPtr->data+2);
+			cur->handled = negchan_middle(sess, cur);
+			continue;
 
-	faimdprintf(sess, 1, "warning: unhandled packet %04x/%04x\n", family, subtype);
-	consumenonsnac(sess, workingPtr, 0xffff, 0xffff); /* last chance! */
-	workingPtr->handled = 1;
-      }
-    }
-  }
+		} else if (cur->hdr.flap.type == 0x05)
+			;
+		
+		if (!cur->handled) {
+			consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */
+			cur->handled = 1;
+		}
+	}
 
-  /* 
-   * This doesn't have to be called here.  It could easily be done
-   * by a seperate thread or something. It's an administrative operation,
-   * and can take a while. Though the less you call it the less memory
-   * you'll have :)
-   */
-  aim_purge_rxqueue(sess);
+	/* 
+	 * This doesn't have to be called here.  It could easily be done
+	 * by a seperate thread or something. It's an administrative operation,
+	 * and can take a while. Though the less you call it the less memory
+	 * you'll have :)
+	 */
+	aim_purge_rxqueue(sess);
 
-  critical = 0;
-  
-  return 0;
+	return;
 }
 
-faim_internal int aim_parse_unknown(struct aim_session_t *sess,
-					  struct command_rx_struct *command, ...)
+faim_internal int aim_parse_unknown(aim_session_t *sess, aim_frame_t *frame, ...)
 {
-  u_int i = 0;
+	int i;
 
-  if (!sess || !command)
-    return 1;
-
-  faimdprintf(sess, 1, "\nRecieved unknown packet:");
+	faimdprintf(sess, 1, "\nRecieved unknown packet:");
 
-  for (i = 0; i < command->commandlen; i++)
-    {
-      if ((i % 8) == 0)
-	faimdprintf(sess, 1, "\n\t");
+	for (i = 0; aim_bstream_empty(&frame->data); i++) {
+		if ((i % 8) == 0)
+			faimdprintf(sess, 1, "\n\t");
 
-      faimdprintf(sess, 1, "0x%2x ", command->data[i]);
-    }
-  
-  faimdprintf(sess, 1, "\n\n");
+		faimdprintf(sess, 1, "0x%2x ", aimbs_get8(&frame->data));
+	}
 
-  return 1;
+	faimdprintf(sess, 1, "\n\n");
+
+	return 1;
 }
 
 
-faim_internal int aim_negchan_middle(struct aim_session_t *sess,
-				     struct command_rx_struct *command)
-{
-  struct aim_tlvlist_t *tlvlist;
-  char *msg = NULL;
-  unsigned short code = 0;
-  aim_rxcallback_t userfunc = NULL;
-  int ret = 1;
 
-  /* Used only by the older login protocol */
-  /* XXX remove this special case? */
-  if (command->conn->type == AIM_CONN_TYPE_AUTH)
-    return consumenonsnac(sess, command, 0x0017, 0x0003);
-
-  tlvlist = aim_readtlvchain(command->data, command->commandlen);
-
-  if (aim_gettlv(tlvlist, 0x0009, 1))
-    code = aim_gettlv16(tlvlist, 0x0009, 1);
-
-  if (aim_gettlv(tlvlist, 0x000b, 1))
-    msg = aim_gettlv_str(tlvlist, 0x000b, 1);
-
-  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
-    ret = userfunc(sess, command, code, msg);
-
-  aim_freetlvchain(&tlvlist);
-
-  if (msg)
-    free(msg);
-
-  return ret;
-}
-
--- a/src/protocols/oscar/rxqueue.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/rxqueue.c	Sun Sep 09 10:07:14 2001 +0000
@@ -14,161 +14,389 @@
 #endif
 
 /*
- * Since not all implementations support MSG_WAITALL, define
- * an alternate guarenteed read function...
- *
- * We keep recv() for systems that can do it because it means
- * a single system call for the entire packet, where read may
- * take more for a badly fragmented packet.
  *
  */
 faim_internal int aim_recv(int fd, void *buf, size_t count)
 {
-#ifdef MSG_WAITALL
-  return recv(fd, buf, count, MSG_WAITALL);
-#else
-  int left, ret, cur = 0; 
+	int left, cur; 
+
+	for (cur = 0, left = count; left; ) {
+		int ret;
+		
+		ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
+		if (ret == -1)
+			return -1;
+		else if (ret == 0)
+			return cur;
+
+		cur += ret;
+		left -= ret;
+	}
+
+	return cur;
+}
+
+/*
+ * Read into a byte stream.  Will not read more than count, but may read
+ * less if there is not enough room in the stream buffer.
+ */
+faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
+{
+	int red = 0;
+
+	if (!bs || (fd < 0) || (count < 0))
+		return -1;
+	
+	if (count > (bs->len - bs->offset))
+		count = bs->len - bs->offset; /* truncate to remaining space */
+
+	if (count) {
+
+		red = aim_recv(fd, bs->data + bs->offset, count);
+
+		if (red <= 0)
+			return -1;
+	}
+
+	bs->offset += red;
+
+	return red;
+}
+
+faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len)
+{
+	
+	if (!bs)
+		return -1;
+
+	bs->data = data;
+	bs->len = len;
+	bs->offset = 0;
+
+	return 0;
+}
 
-  left = count;
+faim_internal int aim_bstream_empty(aim_bstream_t *bs)
+{
+	return bs->len - bs->offset;
+}
+
+faim_internal int aim_bstream_curpos(aim_bstream_t *bs)
+{
+	return bs->offset;
+}
+
+faim_internal int aim_bstream_setpos(aim_bstream_t *bs, int off)
+{
+
+	if (off > bs->len)
+		return -1;
+
+	bs->offset = off;
+
+	return off;
+}
+
+faim_internal void aim_bstream_rewind(aim_bstream_t *bs)
+{
+
+	aim_bstream_setpos(bs, 0);
+
+	return;
+}
+
+faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n)
+{
+
+	if (aim_bstream_empty(bs) < n)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += n;
+
+	return n;
+}
+
+faim_internal fu8_t aimbs_get8(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset++;
+	
+	return aimutil_get8(bs->data + bs->offset - 1);
+}
+
+faim_internal fu16_t aimbs_get16(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 2;
+	
+	return aimutil_get16(bs->data + bs->offset - 2);
+}
 
-  while (left) {
-    ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
-    if (ret == -1)
-      return -1;
-    if (ret == 0)
-      return cur;
-    
-    cur += ret;
-    left -= ret;
-  }
+faim_internal fu32_t aimbs_get32(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 4;
+	
+	return aimutil_get32(bs->data + bs->offset - 4);
+}
+
+faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v)
+{
+
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put8(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v)
+{
+
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put16(bs->data + bs->offset, v);
+
+	return 2;
+}
+
+faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v)
+{
+
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put32(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len)
+{
+
+	if (aim_bstream_empty(bs) < len)
+		return 0;
+
+	memcpy(buf, bs->data + bs->offset, len);
+	bs->offset += len;
+
+	return len;
+}
+
+faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len)
+{
+	fu8_t *ob;
+
+	if (!(ob = malloc(len)))
+		return NULL;
+
+	if (aimbs_getrawbuf(bs, ob, len) < len) {
+		free(ob);
+		return NULL;
+	}
 
-  return cur;
-#endif
+	return ob;
+}
+
+faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len)
+{
+	char *ob;
+
+	if (!(ob = malloc(len+1)))
+		return NULL;
+
+	if (aimbs_getrawbuf(bs, ob, len) < len) {
+		free(ob);
+		return NULL;
+	}
+	
+	ob[len] = '\0';
+
+	return ob;
+}
+
+faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len)
+{
+
+	if (aim_bstream_empty(bs) < len)
+		return 0; /* XXX throw an exception */
+
+	memcpy(bs->data + bs->offset, v, len);
+	bs->offset += len;
+
+	return len;
 }
 
+faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len)
+{
+
+	if (aim_bstream_empty(srcbs) < len)
+		return 0; /* XXX throw exception (underrun) */
+
+	if (aim_bstream_empty(bs) < len)
+		return 0; /* XXX throw exception (overflow) */
+
+	memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
+	bs->offset += len;
+	srcbs->offset += len;
+
+	return len;
+}
+
+/**
+ * aim_frame_destroy - free aim_frame_t 
+ * @frame: the frame to free  
+ *
+ * returns -1 on error; 0 on success.  
+ *
+ */
+faim_internal void aim_frame_destroy(aim_frame_t *frame)
+{
+
+	free(frame->data.data); /* XXX aim_bstream_free */
+
+	if (frame->hdrtype == AIM_FRAMETYPE_OFT)
+		free(frame->hdr.oft.hdr2);
+	free(frame);
+	
+	return;
+} 
+
+
 /*
  * Grab a single command sequence off the socket, and enqueue
  * it in the incoming event queue in a seperate struct.
  */
-faim_export int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
 {
-  unsigned char generic[6]; 
-  struct command_rx_struct *newrx = NULL;
+	fu8_t flaphdr_raw[6];
+	aim_bstream_t flaphdr;
+	aim_frame_t *newrx;
+	fu16_t payloadlen;
+	
+	if (!sess || !conn)
+		return 0;
 
-  if (!sess || !conn)
-    return 0;
+	if (conn->fd == -1)
+		return -1; /* its a aim_conn_close()'d connection */
 
-  if (conn->fd == -1)
-    return -1; /* its a aim_conn_close()'d connection */
+	if (conn->fd < 3)  /* can happen when people abuse the interface */
+		return 0;
+
+	if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+		return aim_conn_completeconnect(sess, conn);
 
-  if (conn->fd < 3)  /* can happen when people abuse the interface */
-    return 0;
-
-  if (conn->status & AIM_CONN_STATUS_INPROGRESS)
-    return aim_conn_completeconnect(sess, conn);
+	/*
+	 * Rendezvous (client-client) connections do not speak
+	 * FLAP, so this function will break on them.
+	 */
+	if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) 
+		return aim_get_command_rendezvous(sess, conn);
+	else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+		faimdprintf(sess, 0, "AIM_CONN_TYPE_RENDEZVOUS_OUT on fd %d\n", conn->fd);
+		return 0; 
+	}
 
-  /*
-   * Rendezvous (client-client) connections do not speak
-   * FLAP, so this function will break on them.
-   */
-  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) 
-    return aim_get_command_rendezvous(sess, conn);
-  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
-    faimdprintf(sess, 0, "out on fd %d\n", conn->fd);
-    return 0; 
-  }
+	aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw));
 
-  /*
-   * Read FLAP header.  Six bytes:
-   *    
-   *   0 char  -- Always 0x2a
-   *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
-   *   2 short -- Sequence number 
-   *   4 short -- Number of data bytes that follow.
-   */
-  faim_mutex_lock(&conn->active);
-  if (aim_recv(conn->fd, generic, 6) < 6){
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
+	/*
+	 * Read FLAP header.  Six bytes:
+	 *    
+	 *   0 char  -- Always 0x2a
+	 *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
+	 *   2 short -- Sequence number 
+	 *   4 short -- Number of data bytes that follow.
+	 */
+	faim_mutex_lock(&conn->active);
+	if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
+		aim_conn_close(conn);
+		faim_mutex_unlock(&conn->active);
+		return -1;
+	}
 
-  /*
-   * This shouldn't happen unless the socket breaks, the server breaks,
-   * or we break.  We must handle it just in case.
-   */
-  if (generic[0] != 0x2a) {
-    faimdprintf(sess, 1, "Bad incoming data!");
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }	
+	aim_bstream_rewind(&flaphdr);
+
+	/*
+	 * This shouldn't happen unless the socket breaks, the server breaks,
+	 * or we break.  We must handle it just in case.
+	 */
+	if (aimbs_get8(&flaphdr) != 0x2a) {
+		faimdprintf(sess, 0, "FLAP framing disrupted");
+		aim_conn_close(conn);
+		faim_mutex_unlock(&conn->active);
+		return -1;
+	}	
 
-  /* allocate a new struct */
-  if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) {
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-  memset(newrx, 0x00, sizeof(struct command_rx_struct));
-
-  newrx->lock = 1;  /* lock the struct */
-
-  /* we're doing OSCAR if we're here */
-  newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
-
-  /* store channel -- byte 2 */
-  newrx->hdr.oscar.type = (char) generic[1];
+	/* allocate a new struct */
+	if (!(newrx = (aim_frame_t *)malloc(sizeof(aim_frame_t)))) {
+		faim_mutex_unlock(&conn->active);
+		return -1;
+	}
+	memset(newrx, 0, sizeof(aim_frame_t));
 
-  /* store seqnum -- bytes 3 and 4 */
-  newrx->hdr.oscar.seqnum = aimutil_get16(generic+2);
+	/* we're doing FLAP if we're here */
+	newrx->hdrtype = AIM_FRAMETYPE_FLAP;
+	
+	newrx->hdr.flap.type = aimbs_get8(&flaphdr);
+	newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr);
+	payloadlen = aimbs_get16(&flaphdr);
 
-  /* store commandlen -- bytes 5 and 6 */
-  newrx->commandlen = aimutil_get16(generic+4);
+	newrx->nofree = 0; /* free by default */
 
-  newrx->nofree = 0; /* free by default */
+	if (payloadlen) {
+		fu8_t *payload = NULL;
 
-  /* malloc for data portion */
-  if (!(newrx->data = (u_char *) malloc(newrx->commandlen))) {
-    free(newrx);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
+		if (!(payload = (fu8_t *) malloc(payloadlen))) {
+			aim_frame_destroy(newrx);
+			faim_mutex_unlock(&conn->active);
+			return -1;
+		}
+
+		aim_bstream_init(&newrx->data, payload, payloadlen);
 
-  /* read the data portion of the packet */
-  if (aim_recv(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){
-    free(newrx->data);
-    free(newrx);
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-  faim_mutex_unlock(&conn->active);
+		/* read the payload */
+		if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
+			free(payload);
+			aim_frame_destroy(newrx);
+			aim_conn_close(conn);
+			faim_mutex_unlock(&conn->active);
+			return -1;
+		}
+	} else
+		aim_bstream_init(&newrx->data, NULL, 0);
 
-  newrx->conn = conn;
+	faim_mutex_unlock(&conn->active);
 
-  newrx->next = NULL;  /* this will always be at the bottom */
-  newrx->lock = 0; /* unlock */
+	aim_bstream_rewind(&newrx->data);
 
-  /* enqueue this packet */
-  if (sess->queue_incoming == NULL) {
-    sess->queue_incoming = newrx;
-  } else {
-    struct command_rx_struct *cur;
+	newrx->conn = conn;
+
+	newrx->next = NULL;  /* this will always be at the bottom */
+
+	if (!sess->queue_incoming)
+		sess->queue_incoming = newrx;
+	else {
+		aim_frame_t *cur;
 
-    /*
-     * This append operation takes a while.  It might be faster
-     * if we maintain a pointer to the last entry in the queue
-     * and just update that.  Need to determine if the overhead
-     * to maintain that is lower than the overhead for this loop.
-     */
-    for (cur = sess->queue_incoming; cur->next; cur = cur->next)
-      ;
-    cur->next = newrx;
-  }
-  
-  newrx->conn->lastactivity = time(NULL);
+		for (cur = sess->queue_incoming; cur->next; cur = cur->next)
+			;
+		cur->next = newrx;
+	}
 
-  return 0;  
+	newrx->conn->lastactivity = time(NULL);
+
+	return 0;  
 }
 
 /*
@@ -182,55 +410,23 @@
  * does not keep a pointer, it's lost forever.
  *
  */
-faim_export void aim_purge_rxqueue(struct aim_session_t *sess)
+faim_export void aim_purge_rxqueue(aim_session_t *sess)
 {
-  struct command_rx_struct *cur = NULL;
-  struct command_rx_struct *tmp;
+	aim_frame_t *cur, **prev;
 
-  if (sess->queue_incoming == NULL)
-    return;
-  
-  if (sess->queue_incoming->next == NULL) {
-    if (sess->queue_incoming->handled) {
-      tmp = sess->queue_incoming;
-      sess->queue_incoming = NULL;
-
-      if (!tmp->nofree) {
-	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	  free(tmp->hdr.oft.hdr2);
-	free(tmp->data);
-	free(tmp);
-      } else
-	tmp->next = NULL;
-    }
-    return;
-  }
+	for (prev = &sess->queue_incoming; (cur = *prev); ) {
+		if (cur->handled) {
 
-  for(cur = sess->queue_incoming; cur->next != NULL; ) {
-    if (cur->next->handled) {
-      tmp = cur->next;
-      cur->next = tmp->next;
-      if (!tmp->nofree) {
-	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	  free(tmp->hdr.oft.hdr2);
-	free(tmp->data);
-	free(tmp);
-      } else
-	tmp->next = NULL;
-    }	
-    cur = cur->next;
+			*prev = cur->next;
+			
+			if (!cur->nofree)
+				aim_frame_destroy(cur);
 
-    /* 
-     * Be careful here.  Because of the way we just
-     * manipulated the pointer, cur may be NULL and 
-     * the for() will segfault doing the check unless
-     * we find this case first.
-     */
-    if (cur == NULL)	
-      break;
-  }
+		} else
+			prev = &cur->next;
+	}
 
-  return;
+	return;
 }
 
 /*
@@ -240,13 +436,14 @@
  * XXX: this is something that was handled better in the old connection
  * handling method, but eh.
  */
-faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_rx_struct *currx;
+	aim_frame_t *currx;
 
-  for (currx = sess->queue_incoming; currx; currx = currx->next) {
-    if ((!currx->handled) && (currx->conn == conn))
-      currx->handled = 1;
-  }	
-  return;
+	for (currx = sess->queue_incoming; currx; currx = currx->next) {
+		if ((!currx->handled) && (currx->conn == conn))
+			currx->handled = 1;
+	}	
+	return;
 }
+
--- a/src/protocols/oscar/search.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/search.c	Sun Sep 09 10:07:14 2001 +0000
@@ -9,124 +9,112 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
-faim_export unsigned long aim_usersearch_address(struct aim_session_t *sess,
-						 struct aim_conn_t *conn, 
-						 char *address)
+faim_export int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
 {
-  struct command_tx_struct *newpacket;
-  
-  if (!address)
-    return -1;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+strlen(address))))
-    return -1;
+	if (!sess || !conn || !address)
+		return -EINVAL;
 
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid);
-
-  aimutil_putstr(newpacket->data+10, address, strlen(address));
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
+		return -ENOMEM;
 
-  aim_tx_enqueue(sess, newpacket);
+	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1);
+	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
+	
+	aimbs_putraw(&fr->data, address, strlen(address)); 
 
-  aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, address, strlen(address)+1);
+	aim_tx_enqueue(sess, fr);
 
-  return sess->snac_nextid;
+	return 0;
 }
 
 /* XXX can this be integrated with the rest of the error handling? */
-static int error(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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;
-  struct aim_snac_t *snac2;
-
-  /* XXX the modules interface should have already retrieved this for us */
-  if(!(snac2 = aim_remsnac(sess, snac->id))) {
-    faimdprintf(sess, 2, "couldn't get a snac for 0x%08lx\n", snac->id);
-    return 0;
-  }
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, snac2->data /* address */);
+	/* XXX the modules interface should have already retrieved this for us */
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		faimdprintf(sess, 2, "search error: couldn't get a snac for 0x%08lx\n", snac->id);
+		return 0;
+	}
 
-  /* XXX freesnac()? */
-  if (snac2) {
-    if(snac2->data)
-      free(snac2->data);
-    free(snac2);
-  }
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->data /* address */);
 
-  return ret;
+	/* XXX freesnac()? */
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
+
+	return ret;
 }
 
-static int reply(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  unsigned int j, m, ret = 0;
-  struct aim_tlvlist_t *tlvlist;
-  char *cur = NULL, *buf = NULL;
-  aim_rxcallback_t userfunc;
-  struct aim_snac_t *snac2;
+	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))) {
-    faimdprintf(sess, 2, "faim: couldn't get a snac for 0x%08lx\n", snac->id);
-    return 0;
-  }
+	if ((snac2 = aim_remsnac(sess, snac->id)))
+		searchaddr = (char *)snac2->data;
 
-  if (!(tlvlist = aim_readtlvchain(data, datalen)))
-    return 0;
+	tlvlist = aim_readtlvchain(bs);
+	m = aim_counttlvchain(&tlvlist);
 
-  j = 0;
-
-  m = aim_counttlvchain(&tlvlist);
+	/* XXX uhm. */
+	while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
+		buf = realloc(buf, (j+1) * (MAXSNLEN+1));
 
-  while((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
-    if(!(buf = realloc(buf, (j+1) * (MAXSNLEN+1))))
-      faimdprintf(sess, 2, "faim: couldn't realloc buf. oh well.\n");
+		strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
+		free(cur);
 
-    strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
-    free(cur);
+		j++; 
+	}
 
-    j++; 
-  }
-
-  aim_freetlvchain(&tlvlist);
+	aim_freetlvchain(&tlvlist);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    ret = userfunc(sess, rx, snac2->data /* address */, j, buf);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, searchaddr, j, buf);
 
-  /* XXX freesnac()? */
-  if(snac2) {
-    if(snac2->data)
-      free(snac2->data);
-    free(snac2);
-  }
+	/* XXX freesnac()? */
+	if (snac2)
+		free(snac2->data);
+	free(snac2);
 
-  if(buf)
-    free(buf);
+	free(buf);
 
-  return ret;
+	return ret;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
-  else if (snac->subtype == 0x0003)
-    return reply(sess, mod, rx, snac, data, datalen);
+	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;
+	return 0;
 }
 
-faim_internal int search_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int search_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x000a;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "search", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x000a;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "search", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
+
+
--- a/src/protocols/oscar/snac.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/snac.c	Sun Sep 09 10:07:14 2001 +0000
@@ -18,68 +18,62 @@
 /*
  * Called from aim_session_init() to initialize the hash.
  */
-faim_internal void aim_initsnachash(struct aim_session_t *sess)
+faim_internal void aim_initsnachash(aim_session_t *sess)
 {
-  int i;
+	int i;
 
-  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
-    sess->snac_hash[i] = NULL;
-    faim_mutex_init(&sess->snac_hash_locks[i]);
-  }
+	for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+		sess->snac_hash[i] = NULL;
+		faim_mutex_init(&sess->snac_hash_locks[i]);
+	}
 
-  return;
+	return;
 }
 
-faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess,
-					  const unsigned short family,
-					  const unsigned short type,
-					  const unsigned short flags,
-					  const void *data, const int datalen)
+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)
 {
-  struct aim_snac_t snac;
+	aim_snac_t snac;
+
+	snac.id = sess->snacid_next++;
+	snac.family = family;
+	snac.type = type;
+	snac.flags = flags;
 
-  snac.id = sess->snac_nextid++;
-  snac.family = family;
-  snac.type = type;
-  snac.flags = flags;
+	if (datalen) {
+		if (!(snac.data = malloc(datalen)))
+			return 0; /* er... */
+		memcpy(snac.data, data, datalen);
+	} else
+		snac.data = NULL;
 
-  if (datalen) {
-    if (!(snac.data = malloc(datalen)))
-      return 0; /* er... */
-    memcpy(snac.data, data, datalen);
-  } else
-    snac.data = NULL;
-
-  return aim_newsnac(sess, &snac);
+	return aim_newsnac(sess, &snac);
 }
 
 /*
  * Clones the passed snac structure and caches it in the
  * list/hash.
  */
-faim_internal unsigned long aim_newsnac(struct aim_session_t *sess,
-					struct aim_snac_t *newsnac) 
+faim_internal aim_snacid_t aim_newsnac(aim_session_t *sess, aim_snac_t *newsnac)
 {
-  struct aim_snac_t *snac = NULL;
-  int index;
+	aim_snac_t *snac;
+	int index;
 
-  if (!newsnac)
-    return 0;
+	if (!newsnac)
+		return 0;
 
-  if (!(snac = calloc(1, sizeof(struct aim_snac_t))))
-    return 0;
-  memcpy(snac, newsnac, sizeof(struct aim_snac_t));
-  snac->issuetime = time(&snac->issuetime);
-  snac->next = NULL;
+	if (!(snac = malloc(sizeof(aim_snac_t))))
+		return 0;
+	memcpy(snac, newsnac, sizeof(aim_snac_t));
+	snac->issuetime = time(NULL);
+
+	index = snac->id % FAIM_SNAC_HASH_SIZE;
 
-  index = snac->id % FAIM_SNAC_HASH_SIZE;
+	faim_mutex_lock(&sess->snac_hash_locks[index]);
+	snac->next = (aim_snac_t *)sess->snac_hash[index];
+	sess->snac_hash[index] = (void *)snac;
+	faim_mutex_unlock(&sess->snac_hash_locks[index]);
 
-  faim_mutex_lock(&sess->snac_hash_locks[index]);
-  snac->next = sess->snac_hash[index];
-  sess->snac_hash[index] = snac;
-  faim_mutex_unlock(&sess->snac_hash_locks[index]);
-
-  return(snac->id);
+	return snac->id;
 }
 
 /*
@@ -89,37 +83,24 @@
  * The returned structure must be freed by the caller.
  *
  */
-faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, 
-					     u_long id) 
+faim_internal aim_snac_t *aim_remsnac(aim_session_t *sess, aim_snacid_t id) 
 {
-  struct aim_snac_t *cur = NULL;
-  int index;
+	aim_snac_t *cur, **prev;
+	int index;
 
-  index = id % FAIM_SNAC_HASH_SIZE;
+	index = id % FAIM_SNAC_HASH_SIZE;
 
-  faim_mutex_lock(&sess->snac_hash_locks[index]);
-  if (!sess->snac_hash[index])
-    ;
-  else if (sess->snac_hash[index]->id == id) {
-    cur = sess->snac_hash[index];
-    sess->snac_hash[index] = cur->next;
-  } else {
-    cur = sess->snac_hash[index];
-    while (cur->next) {
-      if (cur->next->id == id) {
-	struct aim_snac_t *tmp;
-	
-	tmp = cur->next;
-	cur->next = cur->next->next;
-	cur = tmp;
-	break;
-      }
-      cur = cur->next;
-    }
-  }
-  faim_mutex_unlock(&sess->snac_hash_locks[index]);
+	faim_mutex_lock(&sess->snac_hash_locks[index]);
+	for (prev = (aim_snac_t **)&sess->snac_hash[index]; (cur = *prev); ) {
+		if (cur->id == id) {
+			*prev = cur->next;
+			return cur;
+		} else
+			prev = &cur->next;
+	}
+	faim_mutex_unlock(&sess->snac_hash_locks[index]);
 
-  return cur;
+	return cur;
 }
 
 /*
@@ -129,49 +110,47 @@
  * maxage is the _minimum_ age in seconds to keep SNACs.
  *
  */
-faim_internal int aim_cleansnacs(struct aim_session_t *sess,
-				 int maxage)
+faim_internal void aim_cleansnacs(aim_session_t *sess, int maxage)
 {
-  int i;
+	int i;
+
+	for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+		aim_snac_t *cur, **prev;
+		time_t curtime;
 
-  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
-    struct aim_snac_t *cur, **prev;
-    time_t curtime;
+		faim_mutex_lock(&sess->snac_hash_locks[i]);
+		if (!sess->snac_hash[i]) {
+			faim_mutex_unlock(&sess->snac_hash_locks[i]);
+			continue;
+		}
 
-    faim_mutex_lock(&sess->snac_hash_locks[i]);
-    if (!sess->snac_hash[i]) {
-      faim_mutex_unlock(&sess->snac_hash_locks[i]);
-      continue;
-    }
-
-    curtime = time(NULL); /* done here in case we waited for the lock */
+		curtime = time(NULL); /* done here in case we waited for the lock */
 
-    for (prev = &sess->snac_hash[i]; (cur = *prev); ) {
-      if ((curtime - cur->issuetime) > maxage) {
+		for (prev = (aim_snac_t **)&sess->snac_hash[i]; (cur = *prev); ) {
+			if ((curtime - cur->issuetime) > maxage) {
 
-	*prev = cur->next;
+				*prev = cur->next;
 
-	/* XXX should we have destructors here? */
-	if (cur->data)
-	  free(cur->data);
-	free(cur);
+				/* XXX should we have destructors here? */
+				free(cur->data);
+				free(cur);
 
-      } else
-	prev = &cur->next;
-    }
+			} else
+				prev = &cur->next;
+		}
+		faim_mutex_unlock(&sess->snac_hash_locks[i]);
+	}
 
-    faim_mutex_unlock(&sess->snac_hash_locks[i]);
-  }
-
-  return 0;
+	return;
 }
 
-faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
+faim_internal int aim_putsnac(aim_bstream_t *bs, fu16_t family, fu16_t subtype, fu16_t flags, aim_snacid_t snacid)
 {
-  int curbyte = 0;
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff));
-  curbyte += aimutil_put32(buf+curbyte, snacid);
-  return curbyte;
+
+	aimbs_put16(bs, family);
+	aimbs_put16(bs, subtype);
+	aimbs_put16(bs, flags);
+	aimbs_put32(bs, snacid);
+
+	return 10;
 }
--- a/src/protocols/oscar/stats.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/stats.c	Sun Sep 09 10:07:14 2001 +0000
@@ -2,36 +2,36 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
-static int reportinterval(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-  unsigned short interval;
-  aim_rxcallback_t userfunc;
+	fu16_t interval;
+	aim_rxcallback_t userfunc;
 
-  interval = aimutil_get16(data);
+	interval = aimbs_get16(bs);
 
-  if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-    return userfunc(sess, rx, interval);
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, interval);
 
-  return 0;
+	return 0;
 }
 
-static int snachandler(struct aim_session_t *sess, aim_module_t *mod, struct command_rx_struct *rx, aim_modsnac_t *snac, unsigned char *data, int datalen)
+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, data, datalen);
+	if (snac->subtype == 0x0002)
+		return reportinterval(sess, mod, rx, snac, bs);
 
-  return 0;
+	return 0;
 }
 
-faim_internal int stats_modfirst(struct aim_session_t *sess, aim_module_t *mod)
+faim_internal int stats_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
-  mod->family = 0x000b;
-  mod->version = 0x0000;
-  mod->flags = 0;
-  strncpy(mod->name, "stats", sizeof(mod->name));
-  mod->snachandler = snachandler;
+	mod->family = 0x000b;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "stats", sizeof(mod->name));
+	mod->snachandler = snachandler;
 
-  return 0;
+	return 0;
 }
--- a/src/protocols/oscar/tlv.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/tlv.c	Sun Sep 09 10:07:14 2001 +0000
@@ -2,6 +2,30 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
+static aim_tlv_t *createtlv(void)
+{
+	aim_tlv_t *newtlv;
+
+	if (!(newtlv = (aim_tlv_t *)malloc(sizeof(aim_tlv_t))))
+		return NULL;
+	memset(newtlv, 0, sizeof(aim_tlv_t));
+
+	return newtlv;
+}
+
+static void freetlv(aim_tlv_t **oldtlv)
+{
+
+	if (!oldtlv || !*oldtlv)
+		return;
+	
+	free((*oldtlv)->value);
+	free(*oldtlv);
+	*oldtlv = NULL;
+
+	return;
+}
+
 /**
  * aim_readtlvchain - Read a TLV chain from a buffer.
  * @buf: Input buffer
@@ -12,65 +36,54 @@
  * routines.  When done with a TLV chain, aim_freetlvchain() should
  * be called to free the dynamic substructures.
  *
+ * XXX There should be a flag setable here to have the tlvlist contain
+ * bstream references, so that at least the ->value portion of each 
+ * element doesn't need to be malloc/memcpy'd.  This could prove to be
+ * just as effecient as the in-place TLV parsing used in a couple places
+ * in libfaim.
+ *
  */
-faim_export struct aim_tlvlist_t *aim_readtlvchain(const unsigned char *buf, const int maxlen)
+faim_export aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs)
 {
-  int pos;
-  struct aim_tlvlist_t *list;
-  struct aim_tlvlist_t *cur;
-  
-  unsigned short type, length;
+	aim_tlvlist_t *list = NULL, *cur;
+	fu16_t type, length;
 
-  if (!buf)
-    return NULL;
+	while (aim_bstream_empty(bs)) {
 
-  list = NULL;
-  
-  pos = 0;
-
-  while (pos < maxlen)
-    {
-      type = aimutil_get16(buf+pos);
-      pos += 2;
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
 
-      if (pos < maxlen)
-	{
-	  length = aimutil_get16(buf+pos);
-	  pos += 2;
-	  
-	  if ((pos+length) <= maxlen)
-	    {
-	      /*
-	       * Okay, so now AOL has decided that any TLV of
-	       * type 0x0013 can only be two bytes, despite
-	       * what the actual given length is.  So here 
-	       * we dump any invalid TLVs of that sort.  Hopefully
-	       * theres no special cases to this special case.
-	       *   - mid (30jun2000)
-	       */
-	      if ((type == 0x0013) && (length != 0x0002))
-		length = 0x0002;
-	      else {
-		cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-		memset(cur, 0x00, sizeof(struct aim_tlvlist_t));
+#if 0 /* temporarily disabled until I know if they're still doing it or not */
+		/*
+		 * Okay, so now AOL has decided that any TLV of
+		 * type 0x0013 can only be two bytes, despite
+		 * what the actual given length is.  So here 
+		 * we dump any invalid TLVs of that sort.  Hopefully
+		 * theres no special cases to this special case.
+		 *   - mid (30jun2000)
+		 */
+		if ((type == 0x0013) && (length != 0x0002))
+			length = 0x0002;
+#else
+		if (0)
+			;
+#endif
+		else {
 
-		cur->tlv = aim_createtlv();	
-		cur->tlv->type = type;
-		cur->tlv->length = length; 
-		if (length) {
-		  cur->tlv->value = (unsigned char *)malloc(length);
-		  memcpy(cur->tlv->value, buf+pos, length);
-		} 
+			cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t));
+			memset(cur, 0, sizeof(aim_tlvlist_t));
 
-		cur->next = list;
-		list = cur;
-	      }
-	      pos += length;
-	    }
+			cur->tlv = createtlv();	
+			cur->tlv->type = type;
+			if ((cur->tlv->length = length))
+			       cur->tlv->value = aimbs_getraw(bs, length);	
+
+			cur->next = list;
+			list = cur;
+		}
 	}
-    }
 
-  return list;
+	return list;
 }
 
 /**
@@ -82,23 +95,26 @@
  * should be removed before calling this.
  *
  */
-faim_export void aim_freetlvchain(struct aim_tlvlist_t **list)
+faim_export void aim_freetlvchain(aim_tlvlist_t **list)
 {
-  struct aim_tlvlist_t *cur, *cur2;
+	aim_tlvlist_t *cur;
 
-  if (!list || !(*list))
-    return;
+	if (!list || !*list)
+		return;
 
-  cur = *list;
-  while (cur)
-    {
-      aim_freetlv(&cur->tlv);
-      cur2 = cur->next;
-      free(cur);
-      cur = cur2;
-    }
-  list = NULL;
-  return;
+	for (cur = *list; cur; ) {
+		aim_tlvlist_t *tmp;
+		
+		freetlv(&cur->tlv);
+
+		tmp = cur->next;
+		free(cur);
+		cur = tmp;
+	}
+
+	list = NULL;
+
+	return;
 }
 
 /**
@@ -108,18 +124,18 @@
  * Returns the number of TLVs stored in the passed chain.
  *
  */
-faim_export int aim_counttlvchain(struct aim_tlvlist_t **list)
+faim_export int aim_counttlvchain(aim_tlvlist_t **list)
 {
-  struct aim_tlvlist_t *cur;
-  int count = 0;
+	aim_tlvlist_t *cur;
+	int count;
 
-  if (!list || !(*list))
-    return 0;
+	if (!list || !*list)
+		return 0;
 
-  for (cur = *list; cur; cur = cur->next)
-    count++;
- 
-  return count;
+	for (cur = *list, count = 0; cur; cur = cur->next)
+		count++;
+
+	return count;
 }
 
 /**
@@ -130,18 +146,18 @@
  * write the passed TLV chain to a data buffer.
  *
  */
-faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list)
+faim_export int aim_sizetlvchain(aim_tlvlist_t **list)
 {
-  struct aim_tlvlist_t *cur;
-  int size = 0;
+	aim_tlvlist_t *cur;
+	int size;
 
-  if (!list || !(*list))
-    return 0;
+	if (!list || !*list)
+		return 0;
 
-  for (cur = *list; cur; cur = cur->next)
-    size += (4 + cur->tlv->length);
- 
-  return size;
+	for (cur = *list, size = 0; cur; cur = cur->next)
+		size += (4 + cur->tlv->length);
+
+	return size;
 }
 
 /**
@@ -155,35 +171,36 @@
  * to the TLV chain.
  *
  */
-faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, const unsigned short type, const char *str, const int len)
+faim_export int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t t, const fu16_t l, const fu8_t *v)
 {
-  struct aim_tlvlist_t *newtlv;
-  struct aim_tlvlist_t *cur;
+	aim_tlvlist_t *newtlv, *cur;
 
-  if (!list)
-    return 0;
+	if (!list)
+		return 0;
 
-  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
+	if (!(newtlv = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t))))
+		return 0;
+	memset(newtlv, 0x00, sizeof(aim_tlvlist_t));
 
-  newtlv->tlv = aim_createtlv();	
-  newtlv->tlv->type = type;
-  newtlv->tlv->length = len;
-  newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char));
-  memcpy(newtlv->tlv->value, str, newtlv->tlv->length);
-
-  newtlv->next = NULL;
+	if (!(newtlv->tlv = createtlv())) {
+		free(newtlv);
+		return 0;
+	}
+	newtlv->tlv->type = t;
+	if ((newtlv->tlv->length = l)) {
+		newtlv->tlv->value = (fu8_t *)malloc(newtlv->tlv->length);
+		memcpy(newtlv->tlv->value, v, newtlv->tlv->length);
+	}
 
-  if (*list == NULL) {
-    *list = newtlv;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtlv;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtlv;
-  }
-  return newtlv->tlv->length;
+	if (!*list)
+		*list = newtlv;
+	else {
+		for(cur = *list; cur->next; cur = cur->next)
+			;
+		cur->next = newtlv;
+	}
+
+	return newtlv->tlv->length;
 }
 
 /**
@@ -195,35 +212,13 @@
  * Adds a two-byte unsigned integer to a TLV chain.
  *
  */
-faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, const unsigned short type, const unsigned short val)
+faim_export int aim_addtlvtochain16(aim_tlvlist_t **list, const fu16_t t, const fu16_t v)
 {
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if (!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
+	fu8_t v16[2];
 
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
-  newtl->tlv->length = 2;
-  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
-  aimutil_put16(newtl->tlv->value, val);
-
-  newtl->next = NULL;
+	aimutil_put16(v16, v);
 
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return 2;
+	return aim_addtlvtochain_raw(list, t, 2, v16);
 }
 
 /**
@@ -235,35 +230,13 @@
  * Adds a four-byte unsigned integer to a TLV chain.
  *
  */
-faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, const unsigned short type, const unsigned long val)
+faim_export int aim_addtlvtochain32(aim_tlvlist_t **list, const fu16_t t, const fu32_t v)
 {
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if (!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
+	fu8_t v32[4];
 
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
-  newtl->tlv->length = 4;
-  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
-  aimutil_put32(newtl->tlv->value, val);
-
-  newtl->next = NULL;
+	aimutil_put32(v32, v);
 
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return 4;
+	return aim_addtlvtochain_raw(list, t, 4, v32);
 }
 
 /**
@@ -288,37 +261,16 @@
  *      %AIM_CAPS_SENDFILE    Supports Send File functions
  *
  */
-faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, const unsigned short type, const unsigned short caps)
+faim_export int aim_addtlvtochain_caps(aim_tlvlist_t **list, const fu16_t t, const fu16_t caps)
 {
-  unsigned char buf[128]; /* icky fixed length buffer */
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if(!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
+	fu8_t buf[16*16]; /* icky fixed length buffer */
+	aim_bstream_t bs;
 
-  newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps);
-  newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length);
-  memcpy(newtl->tlv->value, buf, newtl->tlv->length);
-
-  newtl->next = NULL;
+	aim_bstream_init(&bs, buf, sizeof(buf));
 
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return newtl->tlv->length;
+	aim_putcap(&bs, caps);
+
+	return aim_addtlvtochain_raw(list, t, aim_bstream_curpos(&bs), buf);
 }
 
 /**
@@ -329,31 +281,44 @@
  * Adds a TLV with a zero length to a TLV chain.
  *
  */
-faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, const unsigned short type)
+faim_internal int aim_addtlvtochain_noval(aim_tlvlist_t **list, const fu16_t t)
 {
-  struct aim_tlvlist_t *newtlv;
-  struct aim_tlvlist_t *cur;
-
-  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtlv->tlv = aim_createtlv();	
-  newtlv->tlv->type = type;
-  newtlv->tlv->length = 0;
-  newtlv->tlv->value = NULL;
+	return aim_addtlvtochain_raw(list, t, 0, NULL);
+}
 
-  newtlv->next = NULL;
+/*
+ * Note that the inner TLV chain will not be modifiable as a tlvchain once
+ * it is written using this.  Or rather, it can be, but updates won't be
+ * made to this.
+ *
+ * XXX should probably support sublists for real.
+ * 
+ * This is so neat.
+ *
+ */
+faim_internal int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl)
+{
+	fu8_t *buf;
+	int buflen;
+	aim_bstream_t bs;
 
-  if (*list == NULL) {
-    *list = newtlv;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtlv;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtlv;
-  }
-  return newtlv->tlv->length;
+	buflen = aim_sizetlvchain(tl);
+
+	if (buflen <= 0)
+		return 0;
+
+	if (!(buf = malloc(buflen)))
+		return 0;
+
+	aim_bstream_init(&bs, buf, buflen);
+
+	aim_writetlvchain(&bs, tl);
+
+	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+	free(buf);
+
+	return buflen;
 }
 
 /**
@@ -367,34 +332,31 @@
  * aim_freetlvchain() must still be called to free up the memory used
  * by the chain structures.
  *
+ * XXX clean this up, make better use of bstreams 
  */
-faim_export int aim_writetlvchain(unsigned char *buf, int buflen, struct aim_tlvlist_t **list)
+faim_export int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list)
 {
-  int goodbuflen = 0;
-  int i = 0;
-  struct aim_tlvlist_t *cur;
+	int goodbuflen;
+	aim_tlvlist_t *cur;
 
-  if (!list || !buf || !buflen)
-    return 0;
+	/* do an initial run to test total length */
+	for (cur = *list, goodbuflen = 0; cur; cur = cur->next) {
+		goodbuflen += 2 + 2; /* type + len */
+		goodbuflen += cur->tlv->length;
+	}
 
-  /* do an initial run to test total length */
-  for (cur = *list; cur; cur = cur->next) {
-    goodbuflen += 2 + 2; /* type + len */
-    goodbuflen += cur->tlv->length;
-  }
-
-  if (goodbuflen > buflen)
-    return 0; /* not enough buffer */
+	if (goodbuflen > aim_bstream_empty(bs))
+		return 0; /* not enough buffer */
 
-  /* do the real write-out */
-  for (cur = *list; cur; cur = cur->next) {
-    i += aimutil_put16(buf+i, cur->tlv->type);
-    i += aimutil_put16(buf+i, cur->tlv->length);
-    memcpy(buf+i, cur->tlv->value, cur->tlv->length);
-    i += cur->tlv->length;
-  }
+	/* do the real write-out */
+	for (cur = *list; cur; cur = cur->next) {
+		aimbs_put16(bs, cur->tlv->type);
+		aimbs_put16(bs, cur->tlv->length);
+		if (cur->tlv->length)
+			aimbs_putraw(bs, cur->tlv->value, cur->tlv->length);
+	}
 
-  return i;
+	return 1; /* XXX this is a nonsensical return */
 }
 
 
@@ -410,23 +372,21 @@
  * in a chain.
  *
  */
-faim_export struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, const unsigned short type, const int nth)
+faim_export aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const fu16_t t, const int n)
 {
-  int i;
-  struct aim_tlvlist_t *cur;
-  
-  i = 0;
-  for (cur = list; cur != NULL; cur = cur->next)
-    {
-      if (cur && cur->tlv)
-	{
-	  if (cur->tlv->type == type)
-	    i++;
-	  if (i >= nth)
-	    return cur->tlv;
+	aim_tlvlist_t *cur;
+	int i;
+
+	for (cur = list, i = 0; cur; cur = cur->next) {
+		if (cur && cur->tlv) {
+			if (cur->tlv->type == t)
+				i++;
+			if (i >= n)
+				return cur->tlv;
+		}
 	}
-    }
-  return NULL;
+
+	return NULL;
 }
 
 /**
@@ -440,19 +400,19 @@
  * dynamic buffer and must be freed by the caller.
  *
  */
-faim_export char *aim_gettlv_str(struct aim_tlvlist_t *list, const unsigned short type, const int nth)
+faim_export char *aim_gettlv_str(aim_tlvlist_t *list, const fu16_t t, const int n)
 {
-  struct aim_tlv_t *tlv;
-  char *newstr;
+	aim_tlv_t *tlv;
+	char *newstr;
 
-  if (!(tlv = aim_gettlv(list, type, nth)))
-    return NULL;
-  
-  newstr = (char *) malloc(tlv->length + 1);
-  memcpy(newstr, tlv->value, tlv->length);
-  *(newstr + tlv->length) = '\0';
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return NULL;
 
-  return newstr;
+	newstr = (char *) malloc(tlv->length + 1);
+	memcpy(newstr, tlv->value, tlv->length);
+	*(newstr + tlv->length) = '\0';
+
+	return newstr;
 }
 
 /**
@@ -465,13 +425,13 @@
  * 8bit integer instead of an aim_tlv_t. 
  *
  */
-faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, const unsigned short type, const int num)
+faim_internal fu8_t aim_gettlv8(aim_tlvlist_t *list, const fu16_t t, const int n)
 {
-  struct aim_tlv_t *tlv;
+	aim_tlv_t *tlv;
 
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get8(tlv->value);
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get8(tlv->value);
 }
 
 /**
@@ -484,13 +444,13 @@
  * 16bit integer instead of an aim_tlv_t. 
  *
  */
-faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, const unsigned short type, const int num)
+faim_internal fu16_t aim_gettlv16(aim_tlvlist_t *list, const fu16_t t, const int n)
 {
-  struct aim_tlv_t *tlv;
+	aim_tlv_t *tlv;
 
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get16(tlv->value);
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get16(tlv->value);
 }
 
 /**
@@ -503,144 +463,16 @@
  * 32bit integer instead of an aim_tlv_t. 
  *
  */
-faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, const unsigned short type, const int num)
+faim_internal fu32_t aim_gettlv32(aim_tlvlist_t *list, const fu16_t t, const int n)
 {
-  struct aim_tlv_t *tlv;
-
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get32(tlv->value);
-}
+	aim_tlv_t *tlv;
 
-/**
- * aim_grabtlv - Grab a single TLV from a data buffer
- * @src: Source data buffer (must be at least 4 bytes long)
- *
- * Creates a TLV structure aim_tlv_t and returns it
- * filled with values from a buffer, possibly including a 
- * dynamically allocated buffer for the value portion.
- *
- * Both the aim_tlv_t and the tlv->value pointer
- * must be freed by the caller if non-%NULL.
- *
- */
-faim_export struct aim_tlv_t *aim_grabtlv(const unsigned char *src)
-{
-  struct aim_tlv_t *dest = NULL;
-
-  dest = aim_createtlv();
-
-  dest->type = src[0] << 8;
-  dest->type += src[1];
-
-  dest->length = src[2] << 8;
-  dest->length += src[3];
-
-  dest->value = (unsigned char *) malloc(dest->length);
-  memset(dest->value, 0, dest->length);
-
-  memcpy(dest->value, &(src[4]), dest->length);
-  
-  return dest;
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get32(tlv->value);
 }
 
-/**
- * aim_grabtlvstr - Grab a single TLV from a data buffer as string
- * @src: Source data buffer (must be at least 4 bytes long)
- *
- * Creates a TLV structure aim_tlv_t and returns it
- * filled with values from a buffer, possibly including a 
- * dynamically allocated buffer for the value portion, which 
- * is %NULL-terminated as a string.
- *
- * Both the aim_tlv_t and the tlv->value pointer
- * must be freed by the caller if non-%NULL.
- *
- */
-faim_export struct aim_tlv_t *aim_grabtlvstr(const unsigned char *src)
-{
-  struct aim_tlv_t *dest = NULL;
-
-  dest = aim_createtlv();
-
-  dest->type = src[0] << 8;
-  dest->type += src[1];
-
-  dest->length = src[2] << 8;
-  dest->length += src[3];
-
-  dest->value = (unsigned char *) malloc(dest->length+1);
-  memset(dest->value, 0, dest->length+1);
-
-  memcpy(dest->value, src+4, dest->length);
-  dest->value[dest->length] = '\0';
-
-  return dest;
-}
-
-/**
- * aim_puttlv - Write a aim_tlv_t into a data buffer
- * @dest: Destination data buffer
- * @newtlv: Source TLV structure
- *
- * Writes out the passed TLV structure into the buffer. No bounds
- * checking is done on the output buffer.
- *
- * The passed aim_tlv_t is not freed. aim_freetlv() should
- * still be called by the caller to free the structure.
- *
- */
-faim_export int aim_puttlv(unsigned char *dest, struct aim_tlv_t *newtlv)
-{
-  int i=0;
-
-  dest[i++] = newtlv->type >> 8;
-  dest[i++] = newtlv->type & 0x00FF;
-  dest[i++] = newtlv->length >> 8;
-  dest[i++] = newtlv->length & 0x00FF;
-  memcpy(&(dest[i]), newtlv->value, newtlv->length);
-  i+=newtlv->length;
-  return i;
-}
-
-/**
- * aim_createtlv - Generate an aim_tlv_t structure.
- * 
- * Allocates an empty TLV structure and returns a pointer
- * to it; %NULL on error.
- *
- */
-faim_export struct aim_tlv_t *aim_createtlv(void)
-{
-  struct aim_tlv_t *newtlv;
-
-  if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t))))
-    return NULL;
-  memset(newtlv, 0, sizeof(struct aim_tlv_t));
-  return newtlv;
-}
-
-/**
- * aim_freetlv - Free a aim_tlv_t structure
- * @oldtlv: TLV to be destroyed
- *
- * Frees both the TLV structure and the value portion.
- *
- */
-faim_export int aim_freetlv(struct aim_tlv_t **oldtlv)
-{
-  if (!oldtlv)
-    return -1;
-  if (!*oldtlv)
-    return -1;
-  if ((*oldtlv)->value)
-    free((*oldtlv)->value);
-  free(*(oldtlv));
-  (*oldtlv) = NULL;
-
-  return 0;
-}
-
+#if 0
 /**
  * aim_puttlv_8 - Write a one-byte TLV.
  * @buf: Destination buffer
@@ -650,15 +482,13 @@
  * Writes a TLV with a one-byte integer value portion.
  *
  */
-faim_export int aim_puttlv_8(unsigned char *buf, const unsigned short t, const unsigned char  v)
+faim_export int aim_puttlv_8(fu8_t *buf, const fu16_t t, const fu8_t v)
 {
-  int curbyte=0;
+	fu8_t v8[1];
 
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0001);
-  curbyte += aimutil_put8(buf+curbyte, (unsigned char)(v&0xff));
+	aimutil_put8(v8, v);
 
-  return curbyte;
+	return aim_puttlv_raw(buf, t, 1, v8);
 }
 
 /**
@@ -670,15 +500,16 @@
  * Writes a TLV with a two-byte integer value portion.
  *
  */
-faim_export int aim_puttlv_16(unsigned char *buf, const unsigned short t, const unsigned short v)
+faim_export int aim_puttlv_16(fu8_t *buf, const fu16_t t, const fu16_t v)
 {
-  int curbyte=0;
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0002);
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(v&0xffff));
-  return curbyte;
+	fu8_t v16[2];
+
+	aimutil_put16(v16, v);
+
+	return aim_puttlv_raw(buf, t, 2, v16);
 }
 
+
 /**
  * aim_puttlv_32 - Write a four-byte TLV.
  * @buf: Destination buffer
@@ -688,36 +519,38 @@
  * Writes a TLV with a four-byte integer value portion.
  *
  */
-faim_export int aim_puttlv_32(unsigned char *buf, const unsigned short t, const unsigned long v)
+faim_export int aim_puttlv_32(fu8_t *buf, const fu16_t t, const fu32_t v)
 {
-  int curbyte=0;
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0004);
-  curbyte += aimutil_put32(buf+curbyte, (unsigned long)(v&0xffffffff));
-  return curbyte;
+	fu8_t v32[4];
+
+	aimutil_put32(v32, v);
+
+	return aim_puttlv_raw(buf, t, 4, v32);
 }
 
 /**
- * aim_puttlv_str - Write a string TLV.
+ * aim_puttlv_raw - Write a raw TLV.
  * @buf: Destination buffer
  * @t: TLV type
  * @l: Length of string
  * @v: String to write
  *
- * Writes a TLV with a string value portion.  (Only the first @l
- * bytes of the passed string will be written, which should not
- * include the terminating NULL.)
+ * Writes a TLV with a raw value portion.  (Only the first @l
+ * bytes of the passed buffer will be written, which should not
+ * include a terminating NULL.)
  *
  */
-faim_export int aim_puttlv_str(unsigned char *buf, const unsigned short t, const int l, const char *v)
+faim_export int aim_puttlv_raw(fu8_t *buf, const fu16_t t, const fu16_t l, const fu8_t *v)
 {
-  int curbyte;
-  
-  curbyte  = 0;
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(l&0xffff));
-  if (v)
-    memcpy(buf+curbyte, (unsigned char *)v, l);
-  curbyte += l;
-  return curbyte;
+	int i;
+
+	i = aimutil_put16(buf, t);
+	i += aimutil_put16(buf+i, l);
+	if (l)
+		memcpy(buf+i, v, l);
+	i += l;
+
+	return i;
 }
+#endif
+
--- a/src/protocols/oscar/txqueue.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/txqueue.c	Sun Sep 09 10:07:14 2001 +0000
@@ -20,60 +20,65 @@
  * Right now, that is.  If/when we implement a pool of transmit
  * frames, this will become the request-an-unused-frame part.
  *
- * framing = AIM_FRAMETYPE_OFT/OSCAR
- * chan = channel for OSCAR, hdrtype for OFT
+ * framing = AIM_FRAMETYPE_OFT/FLAP
+ * chan = channel for FLAP, hdrtype for OFT
  *
  */
-faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, 
-						   struct aim_conn_t *conn, 
-						   unsigned char framing, 
-						   int chan, 
-						   int datalen)
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu8_t chan, int datalen)
 {
-  struct command_tx_struct *newtx;
+	aim_frame_t *fr;
+
+	if (!conn) {
+		faimdprintf(sess, 0, "aim_tx_new: ERROR: no connection specified\n");
+		return NULL;
+	}
 
-  if (!conn) {
-    faimdprintf(sess, 0, "aim_tx_new: ERROR: no connection specified\n");
-    return NULL;
-  }
+	/* For sanity... */
+	if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || 
+			(conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
+		if (framing != AIM_FRAMETYPE_OFT) {
+			faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n");
+			return NULL;
+		}
+	} else {
+		if (framing != AIM_FRAMETYPE_FLAP) {
+			faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n");
+			return NULL;
+		}
+	}
+
+	if (!(fr = (aim_frame_t *)malloc(sizeof(aim_frame_t))))
+		return NULL;
+	memset(fr, 0, sizeof(aim_frame_t));
 
-  /* For sanity... */
-  if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
-    if (framing != AIM_FRAMETYPE_OFT) {
-      faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n");
-      return NULL;
-    }
-  } else {
-    if (framing != AIM_FRAMETYPE_OSCAR) {
-      faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n");
-      return NULL;
-    }
-  }
+	fr->conn = conn; 
+
+	fr->hdrtype = framing;
+
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
 
-  newtx = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
-  if (!newtx)
-    return NULL;
-  memset(newtx, 0, sizeof(struct command_tx_struct));
+		fr->hdr.flap.type = chan;
+
+	} else if (fr->hdrtype == AIM_FRAMETYPE_OFT) {
+
+		fr->hdr.oft.type = chan;
+		fr->hdr.oft.hdr2len = 0; /* this will get setup by caller */
 
-  newtx->conn = conn; 
+	} else 
+		faimdprintf(sess, 0, "tx_new: unknown framing\n");
 
-  if(datalen) {
-    newtx->data = (unsigned char *)malloc(datalen);
-    newtx->commandlen = datalen;
-  } else
-    newtx->data = NULL;
+	if (datalen > 0) {
+		fu8_t *data;
 
-  newtx->hdrtype = framing;
-  if (newtx->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    newtx->hdr.oscar.type = chan;
-  } else if (newtx->hdrtype == AIM_FRAMETYPE_OFT) {
-    newtx->hdr.oft.type = chan;
-    newtx->hdr.oft.hdr2len = 0; /* this will get setup by caller */
-  } else { 
-    faimdprintf(sess, 0, "tx_new: unknown framing\n");
-  }
+		if (!(data = (unsigned char *)malloc(datalen))) {
+			aim_frame_destroy(fr);
+			return NULL;
+		}
 
-  return newtx;
+		aim_bstream_init(&fr->data, data, datalen);
+	}
+
+	return fr;
 }
 
 /*
@@ -82,48 +87,41 @@
  * The overall purpose here is to enqueue the passed in command struct
  * into the outgoing (tx) queue.  Basically...
  *   1) Make a scope-irrelevent copy of the struct
- *   2) Lock the struct
  *   3) Mark as not-sent-yet
  *   4) Enqueue the struct into the list
- *   5) Unlock the struct once it's linked in
  *   6) Return
  *
  * Note that this is only used when doing queue-based transmitting;
  * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
  *
  */
-static int aim_tx_enqueue__queuebased(struct aim_session_t *sess, struct command_tx_struct *newpacket)
+static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr)
 {
-  struct command_tx_struct *cur;
+
+	if (!fr->conn) {
+		faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
+		fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+	}
+
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+		/* assign seqnum -- XXX should really not assign until hardxmit */
+		fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+	}
 
-  if (newpacket->conn == NULL) {
-      faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
-      newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
-  }
- 
-  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    /* assign seqnum */
-    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
-  }
-  /* set some more fields */
-  newpacket->lock = 1; /* lock */
-  newpacket->sent = 0; /* not sent yet */
-  newpacket->next = NULL; /* always last */
+	fr->handled = 0; /* not sent yet */
 
-  /* see overhead note in aim_rxqueue counterpart */
-  if (sess->queue_outgoing == NULL) {
-    sess->queue_outgoing = newpacket;
-  } else {
-    for (cur = sess->queue_outgoing;
-	 cur->next;
-	 cur = cur->next)
-      ;
-    cur->next = newpacket;
-  }
+	/* see overhead note in aim_rxqueue counterpart */
+	if (!sess->queue_outgoing)
+		sess->queue_outgoing = fr;
+	else {
+		aim_frame_t *cur;
 
-  newpacket->lock = 0; /* unlock so it can be sent */
+		for (cur = sess->queue_outgoing; cur->next; cur = cur->next)
+			;
+		cur->next = fr;
+	}
 
-  return 0;
+	return 0;
 }
 
 /*
@@ -137,63 +135,58 @@
  * right here. 
  * 
  */
-static int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_struct *newpacket)
+static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr)
 {
-  if (newpacket->conn == NULL) {
-    faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n");
-    if (newpacket->data)
-      free(newpacket->data);
-    free(newpacket);
-    return -1;
-  }
+
+	if (!fr->conn) {
+		faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n");
+		aim_frame_destroy(fr);
+		return 0;
+	}
 
-  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR)
-    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+		fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
 
-  newpacket->lock = 1; /* lock */
-  newpacket->sent = 0; /* not sent yet */
+	fr->handled = 0; /* not sent yet */
 
-  aim_tx_sendframe(sess, newpacket);
+	aim_tx_sendframe(sess, fr);
 
-  if (newpacket->data)
-    free(newpacket->data);
-  free(newpacket);
+	aim_frame_destroy(fr);
 
-  return 0;
+	return 0;
 }
 
-faim_export int aim_tx_setenqueue(struct aim_session_t *sess, 
-				  int what,  
-				  int (*func)(struct aim_session_t *, struct command_tx_struct *))
+faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
 {
-  if (!sess)
-    return -1;
+	
+	if (what == AIM_TX_QUEUED)
+		sess->tx_enqueue = &aim_tx_enqueue__queuebased;
+	else if (what == AIM_TX_IMMEDIATE) 
+		sess->tx_enqueue = &aim_tx_enqueue__immediate;
+	else if (what == AIM_TX_USER) {
+		if (!func)
+			return -EINVAL;
+		sess->tx_enqueue = func;
+	} else
+		return -EINVAL; /* unknown action */
 
-  if (what == AIM_TX_QUEUED)
-    sess->tx_enqueue = &aim_tx_enqueue__queuebased;
-  else if (what == AIM_TX_IMMEDIATE) 
-    sess->tx_enqueue = &aim_tx_enqueue__immediate;
-  else if (what == AIM_TX_USER) {
-    if (!func)
-      return -1;
-    sess->tx_enqueue = func;
-  } else
-    return -1; /* unknown action */
-
-  return 0;
+	return 0;
 }
 
-faim_internal int aim_tx_enqueue(struct aim_session_t *sess, struct command_tx_struct *command)
+faim_internal int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr)
 {
-  /*
-   * If we want to send a connection thats inprogress, we have to force
-   * them to use the queue based version. Otherwise, use whatever they
-   * want.
-   */
-  if (command && command->conn && (command->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
-    return aim_tx_enqueue__queuebased(sess, command);
-  }
-  return (*sess->tx_enqueue)(sess, command);
+	
+	/*
+	 * If we want to send a connection thats inprogress, we have to force
+	 * them to use the queue based version. Otherwise, use whatever they
+	 * want.
+	 */
+	if (fr && fr->conn && 
+			(fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
+		return aim_tx_enqueue__queuebased(sess, fr);
+	}
+
+	return (*sess->tx_enqueue)(sess, fr);
 }
 
 /* 
@@ -205,184 +198,194 @@
  *   before enqueuement (in aim_tx_enqueue()).
  *
  */
-faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn)
+faim_internal flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn)
+{
+	flap_seqnum_t ret;
+	
+	faim_mutex_lock(&conn->seqnum_lock);
+	ret = ++conn->seqnum;
+	faim_mutex_unlock(&conn->seqnum_lock);
+
+	return ret;
+}
+
+static int aim_send(int fd, const void *buf, size_t count)
 {
-  u_int ret;
-  
-  faim_mutex_lock(&conn->seqnum_lock);
-  ret = ++conn->seqnum;
-  faim_mutex_unlock(&conn->seqnum_lock);
-  return ret;
+	int left, cur;
+
+	for (cur = 0, left = count; left; ) {
+		int ret;
+
+		ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
+		if (ret == -1)
+			return -1;
+		else if (ret == 0)
+			return cur;
+
+		cur += ret;
+		left -= ret;
+	}
+
+	return cur;
+}
+
+static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
+{
+	int wrote = 0;
+
+	if (!bs || !conn || (count < 0))
+		return -EINVAL;
+
+	if (count > aim_bstream_empty(bs))
+		count = aim_bstream_empty(bs); /* truncate to remaining space */
+
+	if (count)
+		wrote = aim_send(conn->fd, bs->data + bs->offset, count);
+
+	if (((aim_session_t *)conn->sessv)->debug >= 2) {
+		int i;
+		aim_session_t *sess = (aim_session_t *)conn->sessv;
+
+		faimdprintf(sess, 2, "\nOutgoing data: (%d bytes)", wrote);
+		for (i = 0; i < wrote; i++) {
+			if (!(i % 8)) 
+				faimdprintf(sess, 2, "\n\t");
+			faimdprintf(sess, 2, "0x%02x ", *(bs->data + bs->offset + i));
+		}
+		faimdprintf(sess, 2, "\n");
+	}
+
+
+	bs->offset += wrote;
+
+	return wrote;	
 }
 
-/*
- *  aim_tx_flushqueue()
- *
- *  This the function is responsable for putting the queued commands
- *  onto the wire.  This function is critical to the operation of 
- *  the queue and therefore is the most prone to brokenness.  It
- *  seems to be working quite well at this point.
- *
- *  Procedure:
- *    1) Traverse the list, only operate on commands that are unlocked
- *       and haven't been sent yet.
- *    2) Lock the struct
- *    3) Allocate a temporary buffer to store the finished, fully
- *       processed packet in.
- *    4) Build the packet from the command_tx_struct data.
- *    5) Write the packet to the socket.
- *    6) If success, mark the packet sent, if fail report failure, do NOT
- *       mark the packet sent (so it will not get purged and therefore
- *       be attempted again on next call).
- *    7) Unlock the struct.
- *    8) Free the temp buffer
- *    9) Step to next struct in list and go back to 1.
- *
- */
-faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur)
+static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
 {
-  int buflen = 0;
-  unsigned char *curPacket;
-
-  if (!cur)
-    return -1; /* fatal */
+	aim_bstream_t obs;
+	fu8_t *obs_raw;
+	int payloadlen, err = 0, obslen;
 
-  cur->lock = 1; /* lock the struct */
-
-  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR)
-    buflen = cur->commandlen + 6;
-  else if (cur->hdrtype == AIM_FRAMETYPE_OFT)
-    buflen = cur->hdr.oft.hdr2len + 8;
-  else {
-    cur->lock = 0;
-    return -1;
-  }
+	payloadlen = aim_bstream_curpos(&fr->data);
 
-  /* allocate full-packet buffer */
-  if (!(curPacket = (unsigned char *) malloc(buflen))) {
-    cur->lock = 0;
-    return -1;
-  }
-      
-  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    /* command byte */
-    curPacket[0] = 0x2a;
-      
-    /* type/family byte */
-    curPacket[1] = cur->hdr.oscar.type;
-      
-    /* bytes 3+4: word: FLAP sequence number */
-    aimutil_put16(curPacket+2, cur->hdr.oscar.seqnum);
+	if (!(obs_raw = malloc(6 + payloadlen)))
+		return -ENOMEM;
 
-    /* bytes 5+6: word: SNAC len */
-    aimutil_put16(curPacket+4, cur->commandlen);
-      
-    /* bytes 7 and on: raw: SNAC data */  /* XXX: ye gods! get rid of this! */
-    memcpy(&(curPacket[6]), cur->data, cur->commandlen);
+	aim_bstream_init(&obs, obs_raw, 6 + payloadlen);
 
-  } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
-    int z = 0;
-
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[0]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[1]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[2]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[3]);
-
-    z += aimutil_put16(curPacket+z, cur->hdr.oft.hdr2len + 8);
-    z += aimutil_put16(curPacket+z, cur->hdr.oft.type);
-
-    memcpy(curPacket+z, cur->hdr.oft.hdr2, cur->hdr.oft.hdr2len);
-  }
+	/* FLAP header */
+	aimbs_put8(&obs, 0x2a);
+	aimbs_put8(&obs, fr->hdr.flap.type);
+	aimbs_put16(&obs, fr->hdr.flap.seqnum);
+	aimbs_put16(&obs, payloadlen);
 
-  /* 
-   * For OSCAR, a full image of the raw packet data now in curPacket.
-   * For OFT, an image of just the bloated header is in curPacket, 
-   * since OFT allows us to do the data in a different write (yay!).
-   */
-  faim_mutex_lock(&cur->conn->active);
-  if (send(cur->conn->fd, curPacket, buflen, 0) != buflen) {
-    faim_mutex_unlock(&cur->conn->active);
-    cur->sent = 1;
-    aim_conn_close(cur->conn);
-    return 0; /* bail out */
-  }
-
-  if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) {
-    int curposi;
-    for(curposi = 0; curposi < cur->commandlen; curposi++)
-      faimdprintf(sess, 0, "%02x ", cur->data[curposi]);
+	/* payload */
+	aim_bstream_rewind(&fr->data);
+	aimbs_putbs(&obs, &fr->data, payloadlen);
 
-    if (send(cur->conn->fd, cur->data, cur->commandlen, 0) != (int)cur->commandlen) {
-      /* 
-       * Theres nothing we can do about this since we've already sent the 
-       * header!  The connection is unstable.
-       */
-      faim_mutex_unlock(&cur->conn->active);
-      cur->sent = 1;
-      aim_conn_close(cur->conn);
-      return 0; /* bail out */
-    }
-
-  }
-
-  cur->sent = 1; /* mark the struct as sent */
-  cur->conn->lastactivity = time(NULL);
-
-  faim_mutex_unlock(&cur->conn->active);
+	obslen = aim_bstream_curpos(&obs);
+	aim_bstream_rewind(&obs);
 
-  if (sess->debug >= 2) {
-    int i;
+	if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
+		err = -errno;
+	
+	free(obs_raw); /* XXX aim_bstream_free */
 
-    faimdprintf(sess, 2, "\nOutgoing packet: (only valid for OSCAR)");
-    for (i = 0; i < buflen; i++) {
-      if (!(i % 8)) 
-	faimdprintf(sess, 2, "\n\t");
-      faimdprintf(sess, 2, "0x%02x ", curPacket[i]);
-    }
-    faimdprintf(sess, 2, "\n");
-  }
+	fr->handled = 1;
+	fr->conn->lastactivity = time(NULL);
 
-  cur->lock = 0; /* unlock the struct */
-
-  free(curPacket); /* free up full-packet buffer */
-
-  return 1; /* success */
+	return err;
 }
 
-faim_export int aim_tx_flushqueue(struct aim_session_t *sess)
+static int sendframe_oft(aim_session_t *sess, aim_frame_t *fr)
 {
-  struct command_tx_struct *cur;
-   
-  if (sess->queue_outgoing == NULL)
-    return 0;
+	aim_bstream_t hbs;
+	fu8_t *hbs_raw;
+	int hbslen;
+	int err = 0;
+
+	hbslen = 8 + fr->hdr.oft.hdr2len;
+
+	if (!(hbs_raw = malloc(hbslen)))
+		return -1;
+
+	aim_bstream_init(&hbs, hbs_raw, hbslen);
+
+	aimbs_putraw(&hbs, fr->hdr.oft.magic, 4);
+	aimbs_put16(&hbs, fr->hdr.oft.hdr2len + 8);
+	aimbs_put16(&hbs, fr->hdr.oft.type);
+	aimbs_putraw(&hbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
 
-  faimdprintf(sess, 2, "beginning txflush...\n");
-  for (cur = sess->queue_outgoing; cur; cur = cur->next) {
-    /* only process if its unlocked and unsent */
-    if (!cur->lock && !cur->sent) {
+	aim_bstream_rewind(&hbs);
+	
+	if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) {
+
+		err = -errno;
+		
+	} else if (aim_bstream_curpos(&fr->data)) {
+		int len;
 
-      if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
-	continue;
+		len = aim_bstream_curpos(&fr->data);
+		aim_bstream_rewind(&fr->data);
+		
+		if (aim_bstream_send(&fr->data, fr->conn, len) != len)
+			err = -errno;
+	}
+
+	free(hbs_raw); /* XXX aim_bstream_free */
+
+	fr->handled = 1;
+	fr->conn->lastactivity = time(NULL);
+
+
+	return err;
+}
 
-      /*
-       * And now for the meager attempt to force transmit
-       * latency and avoid missed messages.
-       */
-      if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
-	/* FIXME FIXME -- should be a break! we dont want to block the upper layers */
-	sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
-      }
+faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
+{
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+		return sendframe_flap(sess, fr);
+	else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
+		return sendframe_oft(sess, fr);
+	return -1;
+}
+
+faim_export int aim_tx_flushqueue(aim_session_t *sess)
+{
+	aim_frame_t *cur;
+
+	for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+
+		if (cur->handled)
+	       		continue; /* already been sent */
+
+		if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
+			continue;
 
-      /* XXX XXX XXX this should call the custom "queuing" function!! */
-      if (aim_tx_sendframe(sess, cur) == -1)
-	break;
-    }
-  }
+		/*
+		 * And now for the meager attempt to force transmit
+		 * latency and avoid missed messages.
+		 */
+		if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
+			/* 
+			 * XXX should be a break! we dont want to block the 
+			 * upper layers
+			 *
+			 * XXX or better, just do this right.
+			 *
+			 */
+			sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
+		}
 
-  /* purge sent commands from queue */
-  aim_tx_purgequeue(sess);
+		/* XXX this should call the custom "queuing" function!! */
+		aim_tx_sendframe(sess, cur);
+	}
 
-  return 0;
+	/* purge sent commands from queue */
+	aim_tx_purgequeue(sess);
+
+	return 0;
 }
 
 /*
@@ -393,47 +396,22 @@
  *  reduce memory footprint at run time!  
  *
  */
-faim_export void aim_tx_purgequeue(struct aim_session_t *sess)
+faim_export void aim_tx_purgequeue(aim_session_t *sess)
 {
-  struct command_tx_struct *cur = NULL;
-  struct command_tx_struct *tmp;
+	aim_frame_t *cur, **prev;
 
-  if (sess->queue_outgoing == NULL)
-    return;
-  
-  if (sess->queue_outgoing->next == NULL) {
-    if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) {
-      tmp = sess->queue_outgoing;
-      sess->queue_outgoing = NULL;
-      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	free(tmp->hdr.oft.hdr2);
-      free(tmp->data);
-      free(tmp);
-    }
-    return;
-  }
+	for (prev = &sess->queue_outgoing; (cur = *prev); ) {
 
-  for(cur = sess->queue_outgoing; cur->next != NULL; ) {
-    if (!cur->next->lock && cur->next->sent) {
-      tmp = cur->next;
-      cur->next = tmp->next;
-      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	free(tmp->hdr.oft.hdr2);
-      free(tmp->data);
-      free(tmp);
-    }	
-    cur = cur->next;
+		if (cur->handled) {
+			*prev = cur->next;
+
+			aim_frame_destroy(cur);
 
-    /* 
-     * Be careful here.  Because of the way we just
-     * manipulated the pointer, cur may be NULL and 
-     * the for() will segfault doing the check unless
-     * we find this case first.
-     */
-    if (cur == NULL)	
-      break;
-  }
-  return;
+		} else
+			prev = &cur->next;
+	}
+
+	return;
 }
 
 /**
@@ -444,22 +422,17 @@
  * for now this simply marks all packets as sent and lets them
  * disappear without warning.
  *
- * doesn't respect command_tx_struct locks.
  */
-
-faim_export int aim_tx_cleanqueue(struct aim_session_t *sess, struct aim_conn_t *conn)
+faim_export void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
 {
-  struct command_tx_struct *cur = NULL;
-
-  if(!sess || !conn)
-    return -1;
+	aim_frame_t *cur;
 
-  /* we don't respect locks here */
-  for(cur = sess->queue_outgoing; cur; cur = cur->next)
-    if(cur->conn == conn)
-      cur->sent = 1;
-  
-  return 0;
+	for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+		if (cur->conn == conn)
+			cur->handled = 1;
+	}
+
+	return;
 }
-    
-      
+
+
--- a/src/protocols/oscar/util.c	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/protocols/oscar/util.c	Sun Sep 09 10:07:14 2001 +0000
@@ -7,62 +7,10 @@
 #include <aim.h>
 #include <ctype.h>
 
-#ifdef AIMUTIL_USEMACROS
-/* macros in faim/aim.h */
-#else
-faim_shortfunc int aimutil_put8(u_char *buf, u_char data)
-{
-  buf[0] = (u_char)data&0xff;
-  return 1;
-}
-
-faim_shortfunc u_char aimutil_get8(u_char *buf)
-{
-  return buf[0];
-}
-
-/*
- * Endian-ness issues here?
- */
-faim_shortfunc int aimutil_put16(u_char *buf, u_short data)
-{
-  buf[0] = (u_char)(data>>8)&0xff;
-  buf[1] = (u_char)(data)&0xff;
-  return 2;
-}
-
-faim_shortfunc u_short aimutil_get16(u_char *buf)
-{
-  u_short val;
-  val = (buf[0] << 8) & 0xff00;
-  val+= (buf[1]) & 0xff;
-  return val;
-}
-
-faim_shortfunc int aimutil_put32(u_char *buf, u_long data)
-{
-  buf[0] = (u_char)(data>>24)&0xff;
-  buf[1] = (u_char)(data>>16)&0xff;
-  buf[2] = (u_char)(data>>8)&0xff;
-  buf[3] = (u_char)(data)&0xff;
-  return 4;
-}
-
-faim_shortfunc u_long aimutil_get32(u_char *buf)
-{
-  u_long val;
-  val = (buf[0] << 24) & 0xff000000;
-  val+= (buf[1] << 16) & 0x00ff0000;
-  val+= (buf[2] <<  8) & 0x0000ff00;
-  val+= (buf[3]      ) & 0x000000ff;
-  return val;
-}
-#endif /* AIMUTIL_USEMACROS */
-
 faim_export faim_shortfunc int aimutil_putstr(u_char *dest, const char *src, int len)
 {
-  memcpy(dest, src, len);
-  return len;
+	memcpy(dest, src, len);
+	return len;
 }
 
 /*
@@ -72,194 +20,184 @@
  */
 faim_export int aimutil_tokslen(char *toSearch, int index, char dl)
 {
-  int curCount = 1;
-  char *next;
-  char *last;
-  int toReturn;
+	int curCount = 1;
+	char *next;
+	char *last;
+	int toReturn;
+
+	last = toSearch;
+	next = strchr(toSearch, dl);
 
-  last = toSearch;
-  next = strchr(toSearch, dl);
-  
-  while(curCount < index && next != NULL)
-    {
-      curCount++;
-      last = next + 1;
-      next = strchr(last, dl);
-    }
-  
-  if ((curCount < index) || (next == NULL))
-    toReturn = strlen(toSearch) - (curCount - 1);
-  else
-    toReturn = next - toSearch - (curCount - 1);
+	while(curCount < index && next != NULL) {
+		curCount++;
+		last = next + 1;
+		next = strchr(last, dl);
+	}
 
-  return toReturn;
+	if ((curCount < index) || (next == NULL))
+		toReturn = strlen(toSearch) - (curCount - 1);
+	else
+		toReturn = next - toSearch - (curCount - 1);
+
+	return toReturn;
 }
 
 faim_export int aimutil_itemcnt(char *toSearch, char dl)
 {
-  int curCount;
-  char *next;
-  
-  curCount = 1;
-  
-  next = strchr(toSearch, dl);
-  
-  while(next != NULL)
-    {
-      curCount++;
-      next = strchr(next + 1, dl);
-    }
-  
-  return curCount;
+	int curCount;
+	char *next;
+
+	curCount = 1;
+
+	next = strchr(toSearch, dl);
+
+	while(next != NULL) {
+		curCount++;
+		next = strchr(next + 1, dl);
+	}
+
+	return curCount;
 }
 
 faim_export char *aimutil_itemidx(char *toSearch, int index, char dl)
 {
-  int curCount;
-  char *next;
-  char *last;
-  char *toReturn;
-  
-  curCount = 0;
-  
-  last = toSearch;
-  next = strchr(toSearch, dl);
-  
-  while(curCount < index && next != NULL)
-    {
-      curCount++;
-      last = next + 1;
-      next = strchr(last, dl);
-    }
-  
-  if (curCount < index)
-    {
-      toReturn = malloc(sizeof(char));
-      *toReturn = '\0';
-    }
-  next = strchr(last, dl);
-  
-  if (curCount < index)
-    {
-      toReturn = malloc(sizeof(char));
-      *toReturn = '\0';
-    }
-  else
-    {
-      if (next == NULL)
-	{
-	  toReturn = malloc((strlen(last) + 1) * sizeof(char));
-	  strcpy(toReturn, last);
+	int curCount;
+	char *next;
+	char *last;
+	char *toReturn;
+
+	curCount = 0;
+
+	last = toSearch;
+	next = strchr(toSearch, dl);
+
+	while (curCount < index && next != NULL) {
+		curCount++;
+		last = next + 1;
+		next = strchr(last, dl);
 	}
-      else
-	{
-	  toReturn = malloc((next - last + 1) * sizeof(char));
-	  memcpy(toReturn, last, (next - last));
-	  toReturn[next - last] = '\0';
+
+	if (curCount < index) {
+		toReturn = malloc(sizeof(char));
+		*toReturn = '\0';
 	}
-    }
-  return toReturn;
+	next = strchr(last, dl);
+
+	if (curCount < index) {
+		toReturn = malloc(sizeof(char));
+		*toReturn = '\0';
+	} else {
+		if (next == NULL) {
+			toReturn = malloc((strlen(last) + 1) * sizeof(char));
+			strcpy(toReturn, last);
+		} else {
+			toReturn = malloc((next - last + 1) * sizeof(char));
+			memcpy(toReturn, last, (next - last));
+			toReturn[next - last] = '\0';
+		}
+	}
+	return toReturn;
 }
 
 /*
- * int snlen(const char *)
- * 
- * This takes a screen name and returns its length without
- * spaces.  If there are no spaces in the SN, then the 
- * return is equal to that of strlen().
- *
- */
+* int snlen(const char *)
+* 
+* This takes a screen name and returns its length without
+* spaces.  If there are no spaces in the SN, then the 
+* return is equal to that of strlen().
+*
+*/
 faim_export int aim_snlen(const char *sn)
 {
-  int i = 0;
-  const char *curPtr = NULL;
+	int i = 0;
+	const char *curPtr = NULL;
 
-  if (!sn)
-    return 0;
+	if (!sn)
+		return 0;
 
-  curPtr = sn;
-  while ( (*curPtr) != (char) NULL) {
-      if ((*curPtr) != ' ')
-	i++;
-      curPtr++;
-    }
+	curPtr = sn;
+	while ( (*curPtr) != (char) NULL) {
+		if ((*curPtr) != ' ')
+		i++;
+		curPtr++;
+	}
 
-  return i;
+	return i;
 }
 
 /*
- * int sncmp(const char *, const char *)
- *
- * This takes two screen names and compares them using the rules
- * on screen names for AIM/AOL.  Mainly, this means case and space
- * insensitivity (all case differences and spacing differences are
- * ignored).
- *
- * Return: 0 if equal
- *     non-0 if different
- *
- */
+* int sncmp(const char *, const char *)
+*
+* This takes two screen names and compares them using the rules
+* on screen names for AIM/AOL.  Mainly, this means case and space
+* insensitivity (all case differences and spacing differences are
+* ignored).
+*
+* Return: 0 if equal
+*     non-0 if different
+*
+*/
 
 faim_export int aim_sncmp(const char *sn1, const char *sn2)
 {
-  const char *curPtr1 = NULL, *curPtr2 = NULL;
+	const char *curPtr1 = NULL, *curPtr2 = NULL;
 
-  if (aim_snlen(sn1) != aim_snlen(sn2))
-    return 1;
+	if (aim_snlen(sn1) != aim_snlen(sn2))
+		return 1;
 
-  curPtr1 = sn1;
-  curPtr2 = sn2;
-  while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) {
-    if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) {
-      if (*curPtr1 == ' ')
-	curPtr1++;
-      if (*curPtr2 == ' ')
-	curPtr2++;
-    } else {
-      if ( toupper(*curPtr1) != toupper(*curPtr2))
-	return 1;
-      curPtr1++;
-      curPtr2++;
-    }
-  }
+	curPtr1 = sn1;
+	curPtr2 = sn2;
+	while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) {
+		if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) {
+			if (*curPtr1 == ' ')
+				curPtr1++;
+			if (*curPtr2 == ' ')
+				curPtr2++;
+		} else {
+			if ( toupper(*curPtr1) != toupper(*curPtr2))
+				return 1;
+			curPtr1++;
+			curPtr2++;
+		}
+	}
 
-  return 0;
+	return 0;
 }
 
 /* strsep Copyright (C) 1992, 1993 Free Software Foundation, Inc.
-   strsep is part of the GNU C Library.
-   
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-   
-   The GNU C 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
-   Library General Public License for more details.
-   
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If
-   not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-   Cambridge, MA 02139, USA.  */
+strsep is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
 
 /* Minor changes by and1000 on 15/1/97 to make it go under Nemesis */
 
 faim_export char *aim_strsep(char **pp, const char *delim)
 {
-  char *p, *q;
-  
-  if (!(p = *pp))
-    return 0;
-  
-  if ((q = strpbrk (p, delim)))
-    {
-      *pp = q + 1;
-      *q = '\0';
-    }
-  else
-    *pp = 0;
-  
-  return p;
+	char *p, *q;
+
+	if (!(p = *pp))
+		return 0;
+
+	if ((q = strpbrk (p, delim))) {
+		*pp = q + 1;
+		*q = '\0';
+	} else
+		*pp = 0;
+
+	return p;
 }
+
+
--- a/src/prpl.h	Sun Sep 09 06:33:54 2001 +0000
+++ b/src/prpl.h	Sun Sep 09 10:07:14 2001 +0000
@@ -130,6 +130,8 @@
 	void (* keepalive)	(struct gaim_connection *);
 	void (* chat_set_topic) (struct gaim_connection *, int id, char *topic);
 
+	void (* convo_closed)   (struct gaim_connection *, char *who);
+
 	char *(* normalize)(const char *);
 };