Index: libfaim/im.c
===================================================================
--- libfaim/im.c	(revision 862371bb6a5ff5a6491550028e73824b698508bb)
+++ libfaim/im.c	(revision e374dee70435f8c596358571092675644a9ba731)
@@ -1,22 +1,28 @@
 /*
- *  Family 0x0004 - Routines for sending/receiving Instant Messages.
- *
- *  Note the term ICBM (Inter-Client Basic Message) which blankets
- *  all types of genericly routed through-server messages.  Within
- *  the ICBM types (family 4), a channel is defined.  Each channel
- *  represents a different type of message.  Channel 1 is used for
- *  what would commonly be called an "instant message".  Channel 2
- *  is used for negotiating "rendezvous".  These transactions end in
- *  something more complex happening, such as a chat invitation, or
- *  a file transfer.  Channel 3 is used for chat messages (not in 
- *  the same family as these channels).  Channel 4 is used for 
- *  various ICQ messages.  Examples are normal messages, URLs, and 
- *  old-style authorization.
- *
- *  In addition to the channel, every ICBM contains a cookie.  For
- *  standard IMs, these are only used for error messages.  However,
- *  the more complex rendezvous messages make suitably more complex
- *  use of this field.
- *
+ * Family 0x0004 - Routines for sending/receiving Instant Messages.
+ *
+ * Note the term ICBM (Inter-Client Basic Message) which blankets
+ * all types of genericly routed through-server messages.  Within
+ * the ICBM types (family 4), a channel is defined.  Each channel
+ * represents a different type of message.  Channel 1 is used for
+ * what would commonly be called an "instant message".  Channel 2
+ * is used for negotiating "rendezvous".  These transactions end in
+ * something more complex happening, such as a chat invitation, or
+ * a file transfer.  Channel 3 is used for chat messages (not in 
+ * the same family as these channels).  Channel 4 is used for 
+ * various ICQ messages.  Examples are normal messages, URLs, and 
+ * old-style authorization.
+ *
+ * In addition to the channel, every ICBM contains a cookie.  For
+ * standard IMs, these are only used for error messages.  However,
+ * the more complex rendezvous messages make suitably more complex
+ * use of this field.
+ *
+ * TODO: Split this up into an im.c file an an icbm.c file.  It 
+ *       will be beautiful, you'll see.
+ *
+ *       Need to rename all the mpmsg messages to aim_im_bleh.
+ *
+ *       Make sure aim_conn_findbygroup is used by all functions.
  */
 
@@ -27,4 +33,23 @@
 #include "win32dep.h"
 #endif
+
+/**
+ * Add a standard ICBM header to the given bstream with the given 
+ * information.
+ *
+ * @param bs The bstream to write the ICBM header to.
+ * @param c c is for cookie, and cookie is for me.
+ * @param ch The ICBM channel (1 through 4).
+ * @param sn Null-terminated scrizeen nizame.
+ * @return The number of bytes written.  It's really not useful.
+ */
+static int aim_im_puticbm(aim_bstream_t *bs, const fu8_t *c, fu16_t ch, const char *sn)
+{
+	aimbs_putraw(bs, c, 8);
+	aimbs_put16(bs, ch);
+	aimbs_put8(bs, strlen(sn));
+	aimbs_putraw(bs, sn, strlen(sn));
+	return 8+2+1+strlen(sn);
+}
 
 /*
@@ -38,12 +63,12 @@
  *
  * Heres the current collection:
- *  0501 0003 0101 0101 01       AOL Mobile Communicator, WinAIM 1.0.414
- *  0501 0003 0101 0201 01       WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
- *                                      4.3.2229, 4.4.2286
- *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
- *  0501 0003 0101 02            WinAIM 5
- *  0501 0001 01                 iChat x.x
- *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
- *                                      TOC client
+ *  0501 0003 0101 0101 01		AOL Mobile Communicator, WinAIM 1.0.414
+ *  0501 0003 0101 0201 01		WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
+ *					4.3.2229, 4.4.2286
+ *  0501 0004 0101 0102 0101		WinAIM 4.1.2010, libfaim (right here)
+ *  0501 0003 0101 02			WinAIM 5
+ *  0501 0001 01			iChat x.x, mobile buddies
+ *  0501 0001 0101 01			AOL v6.0, CompuServe 2000 v6.0, any TOC client
+ *  0501 0002 0106			WinICQ 5.45.1.3777.85
  *
  * Note that in this function, only the feature bytes are tested, since
@@ -51,5 +76,5 @@
  *
  */
-faim_export fu16_t aim_fingerprintclient(fu8_t *msghdr, int len)
+faim_export fu16_t aim_im_fingerprint(const fu8_t *msghdr, int len)
 {
 	static const struct {
@@ -91,12 +116,12 @@
 }
 
-/*
- * Subtype 0x0002
+/** 
+ * Subtype 0x0002 - Set ICBM parameters.
  *
  * 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.
+ * with the rather unreasonable defaults.
  * 
  */
-faim_export int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
+faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params)
 {
 	aim_conn_t *conn;
@@ -131,6 +156,9 @@
 }
 
-/* Subtype 0x0004 - Request ICBM parameter information. */
-faim_export int aim_reqicbmparams(aim_session_t *sess)
+/**
+ * Subtype 0x0004 - Request ICBM parameter information.
+ *
+ */
+faim_export int aim_im_reqparams(aim_session_t *sess)
 {
 	aim_conn_t *conn;
@@ -142,9 +170,12 @@
 }
 
-/* Subtype 0x0005 */
-static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
+/**
+ * Subtype 0x0005 - Receive parameter information.
+ *
+ */
+static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
 	struct aim_icbmparameters params;
-	aim_rxcallback_t userfunc;
 
 	params.maxchan = aimbs_get16(bs);
@@ -161,21 +192,5 @@
 }
 
-/* This should be endian-safe now... but who knows... */
-faim_export fu16_t aim_iconsum(const fu8_t *buf, int buflen)
-{
-	fu32_t sum;
-	int i;
-
-	for (i = 0, sum = 0; i + 1 < buflen; i += 2)
-		sum += (buf[i+1] << 8) + buf[i];
-	if (i < buflen)
-		sum += buf[i];
-
-	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
-
-	return (fu16_t)sum;
-}
-
-/*
+/**
  * Subtype 0x0006 - Send an ICBM (instant message).  
  *
@@ -220,17 +235,16 @@
  * instead of writing out the bytes manually. 
  *
- * XXX more precise verification that we never send SNACs larger than 8192
- * XXX check SNAC size for multipart
- *
- */
-faim_export int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args)
-{
-	static const fu8_t deffeatures[] = {
-		0x01, 0x01, 0x01, 0x02
-	};
+ * XXX - more precise verification that we never send SNACs larger than 8192
+ * XXX - check SNAC size for multipart
+ *
+ */
+faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args)
+{
 	aim_conn_t *conn;
-	int i, msgtlvlen;
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
+	int i, msgtlvlen;
+	static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
@@ -275,10 +289,10 @@
 		return -ENOMEM;
 
-	/* XXX should be optional */	
+	/* 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 
+	 * Generate a random message cookie
 	 *
 	 * We could cache these like we do SNAC IDs.  (In fact, it 
@@ -289,30 +303,15 @@
 	 */
 	for (i = 0; i < 8; i++)
-		aimbs_put8(&fr->data, (fu8_t) rand());
-
-	/*
-	 * Channel ID
-	 */
-	aimbs_put16(&fr->data, 0x0001);
-
-	/*
-	 * Destination SN (prepended with byte length)
-	 */
-	aimbs_put8(&fr->data, strlen(args->destsn));
-	aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
-
-	/*
-	 * Message TLV (type 2).
-	 */
+		ck[i] = (fu8_t)rand();
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0001, args->destsn);
+
+	/* Message TLV (type 0x0002) */
 	aimbs_put16(&fr->data, 0x0002);
 	aimbs_put16(&fr->data, msgtlvlen);
 
-	/*
-	 * Features 
-	 *
-	 */
-	aimbs_put8(&fr->data, 0x05);
-	aimbs_put8(&fr->data, 0x01);
-
+	/* Features TLV (type 0x0501) */
+	aimbs_put16(&fr->data, 0x0501);
 	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
 		aimbs_put16(&fr->data, args->featureslen);
@@ -326,4 +325,5 @@
 		aim_mpmsg_section_t *sec;
 
+		/* Insert each message part in a TLV (type 0x0101) */
 		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
 			aimbs_put16(&fr->data, 0x0101);
@@ -336,49 +336,25 @@
 	} else {
 
+		/* Insert message text in a TLV (type 0x0101) */
 		aimbs_put16(&fr->data, 0x0101);
 
-		/* 
-		 * Message block length.
-		 */
+		/* Message block length */
 		aimbs_put16(&fr->data, args->msglen + 0x04);
 
-		/*
-		 * Character set.
-		 */
-		if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
-
-			aimbs_put16(&fr->data, args->charset);
-			aimbs_put16(&fr->data, args->charsubset);
-
-		} else {
-			if (args->flags & AIM_IMFLAGS_UNICODE)
-				aimbs_put16(&fr->data, 0x0002);
-			else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
-				aimbs_put16(&fr->data, 0x0003);
-			else
-				aimbs_put16(&fr->data, 0x0000);
-
-			aimbs_put16(&fr->data, 0x0000);
-		}
-
-		/*
-		 * Message.  Not terminated.
-		 */
+		/* Character set */
+		aimbs_put16(&fr->data, args->charset);
+		aimbs_put16(&fr->data, args->charsubset);
+
+		/* Message.  Not terminated */
 		aimbs_putraw(&fr->data, args->msg, args->msglen);
 	}
 
-	/*
-	 * Set the Request Acknowledge flag.  
-	 */
-	if (args->flags & AIM_IMFLAGS_ACK) {
-		aimbs_put16(&fr->data, 0x0003);
-		aimbs_put16(&fr->data, 0x0000);
-	}
-
-	/*
-	 * Set the Autoresponse flag.
-	 */
+	/* Set the Autoresponse flag */
 	if (args->flags & AIM_IMFLAGS_AWAY) {
 		aimbs_put16(&fr->data, 0x0004);
+		aimbs_put16(&fr->data, 0x0000);
+	} else if (args->flags & AIM_IMFLAGS_ACK) {
+		/* Set the Request Acknowledge flag */
+		aimbs_put16(&fr->data, 0x0003);
 		aimbs_put16(&fr->data, 0x0000);
 	}
@@ -414,4 +390,5 @@
 	aim_tx_enqueue(sess, fr);
 
+	/* Move this to receive aim_flap_nop and send aim_flap_nop */
 	if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM))
 		aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */
@@ -421,41 +398,43 @@
 
 /*
- * Simple wrapper for aim_send_im_ext() 
+ * Simple wrapper for aim_im_sendch1_ext() 
  *
  * You cannot use aim_send_im if you need the HASICON flag.  You must
- * use aim_send_im_ext directly for that.
+ * use aim_im_sendch1_ext directly for that.
  *
  * aim_send_im also cannot be used if you require UNICODE messages, because
- * that requires an explicit message length.  Use aim_send_im_ext().
- *
- */
-faim_export int aim_send_im(aim_session_t *sess, const char *destsn, fu16_t flags, const char *msg)
+ * that requires an explicit message length.  Use aim_im_sendch1_ext().
+ *
+ */
+faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg)
 {
 	struct aim_sendimext_args args;
 
-	args.destsn = destsn;
+	args.destsn = sn;
 	args.flags = flags;
 	args.msg = msg;
 	args.msglen = strlen(msg);
-
-	/* Make these don't get set by accident -- they need aim_send_im_ext */
+	args.charset = 0x0000;
+	args.charsubset = 0x0000;
+
+	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
 	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
 
-	return aim_send_im_ext(sess, &args);
-}
-
-/*
- * Subtype 0x0006
+	return aim_im_sendch1_ext(sess, &args);
+}
+
+/**
+ * Subtype 0x0006 - Send your icon to a given user.
  *
  * This is also performance sensitive. (If you can believe it...)
  *
  */
-faim_export int aim_send_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
+faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
 {
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
+	int i;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
@@ -466,5 +445,5 @@
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	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)))
@@ -474,19 +453,6 @@
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	/*
@@ -531,5 +497,5 @@
 
 /*
- * Subtype 0x0006
+ * Subtype 0x0006 - Send a rich text message.
  *
  * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
@@ -545,13 +511,12 @@
  *
  */
-faim_export int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
-{
-	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
+{
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	int servdatalen;
+	fu8_t ck[8];
+	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+	int i, servdatalen;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
@@ -564,5 +529,5 @@
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
@@ -572,25 +537,8 @@
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(args->destsn));
-	aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * Encompasses everything below.
-	 */
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, args->destsn);
+
+	/* TLV t(0005) - Encompasses everything below. */
 	aimbs_put16(&fr->data, 0x0005);
 	aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
@@ -600,20 +548,14 @@
 	aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
 
-	/*
-	 * t(000a) l(0002) v(0001)
-	 */
+	/* t(000a) l(0002) v(0001) */
 	aimbs_put16(&fr->data, 0x000a);
 	aimbs_put16(&fr->data, 0x0002);
 	aimbs_put16(&fr->data, 0x0001);
 
-	/*
-	 * t(000f) l(0000) v()
-	 */
+	/* t(000f) l(0000) v() */
 	aimbs_put16(&fr->data, 0x000f);
 	aimbs_put16(&fr->data, 0x0000);
 
-	/*
-	 * Service Data TLV
-	 */
+	/* Service Data TLV */
 	aimbs_put16(&fr->data, 0x2711);
 	aimbs_put16(&fr->data, servdatalen);
@@ -648,11 +590,14 @@
 }
 
-/* Subtype 0x0006 */
-faim_internal int aim_request_directim(aim_session_t *sess, const char *destsn, fu8_t *ip, fu16_t port, fu8_t *ckret)
+/**
+ * Subtype 0x0006 - Send an "I want to directly connect to you" message
+ *
+ */
+faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port)
 {
 	aim_conn_t *conn;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
 	aim_tlvlist_t *tl = NULL, *itl = NULL;
 	int hdrlen, i;
@@ -663,5 +608,5 @@
 		return -EINVAL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn))))
 		return -ENOMEM;
 
@@ -675,5 +620,5 @@
 	 * TOC-compatible.
 	 *
-	 * XXX have I mentioned these should be generated in msgcookie.c?
+	 * XXX - have I mentioned these should be generated in msgcookie.c?
 	 *
 	 */
@@ -682,16 +627,9 @@
 	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));
+	if (cookie)
+		memcpy(cookie, ck, 8);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	aim_addtlvtochain_noval(&tl, 0x0003);
@@ -703,5 +641,5 @@
 	aimbs_put16(&hdrbs, 0x0000);
 	aimbs_putraw(&hdrbs, ck, 8);
-	aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE);
+	aim_putcap(&hdrbs, AIM_CAPS_DIRECTIM);
 
 	aim_addtlvtochain16(&itl, 0x000a, 0x0001);
@@ -725,104 +663,164 @@
 }
 
-/* Subtype 0x0006 */
-faim_internal int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, fu16_t numfiles, fu32_t totsize, fu8_t *ip, fu16_t port, fu8_t *ckret)
+/**
+ * Subtype 0x0006 - Send an "I want to send you this file" message
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, struct aim_oft_info *oft_info)
 {
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	struct aim_snac_destructor snacdest;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+	aim_tlvlist_t *tl=NULL, *subtl=NULL;
+	int i;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
 		return -EINVAL;
 
-	if (!sn || !filename)
+	/* XXX - Should be like "21CBF95" and null terminated */
+	for (i = 0; i < 7; i++)
+		oft_info->cookie[i] = 0x30 + ((fu8_t)rand() % 10);
+	oft_info->cookie[7] = '\0';
+
+	{ /* Create the subTLV chain */
+		fu8_t *buf;
+		int buflen;
+		aim_bstream_t bs;
+
+		aim_addtlvtochain16(&subtl, 0x000a, 0x0001);
+		aim_addtlvtochain_noval(&subtl, 0x000f);
+/*		aim_addtlvtochain_raw(&subtl, 0x000e, 2, "en");
+		aim_addtlvtochain_raw(&subtl, 0x000d, 8, "us-ascii");
+		aim_addtlvtochain_raw(&subtl, 0x000c, 24, "Please accept this file."); */
+		if (oft_info->clientip) {
+			fu8_t ip[4];
+			char *nexttoken;
+			int i = 0;
+			nexttoken = strtok(oft_info->clientip, ".");
+			while (nexttoken && i<4) {
+				ip[i] = atoi(nexttoken);
+				nexttoken = strtok(NULL, ".");
+				i++;
+			}
+			aim_addtlvtochain_raw(&subtl, 0x0003, 4, ip);
+		}
+		aim_addtlvtochain16(&subtl, 0x0005, oft_info->port);
+
+		/* TLV t(2711) */
+		buflen = 2+2+4+strlen(oft_info->fh.name)+1;
+		buf = malloc(buflen);
+		aim_bstream_init(&bs, buf, buflen);
+		aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
+		aimbs_put16(&bs, oft_info->fh.totfiles);
+		aimbs_put32(&bs, oft_info->fh.totsize);
+
+		/* Filename - NULL terminated, for some odd reason */
+		aimbs_putraw(&bs, oft_info->fh.name, strlen(oft_info->fh.name));
+		aimbs_put8(&bs, 0x00);
+
+		aim_addtlvtochain_raw(&subtl, 0x2711, bs.len, bs.data);
+		free(buf);
+	}
+
+	{ /* Create the main TLV chain */
+		fu8_t *buf;
+		int buflen;
+		aim_bstream_t bs;
+
+		/* TLV t(0005) - Encompasses everything from above. Gee. */
+		buflen = 2+8+16+aim_sizetlvchain(&subtl);
+		buf = malloc(buflen);
+		aim_bstream_init(&bs, buf, buflen);
+		aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE);
+		aimbs_putraw(&bs, oft_info->cookie, 8);
+		aim_putcap(&bs, AIM_CAPS_SENDFILE);
+		aim_writetlvchain(&bs, &subtl);
+		aim_freetlvchain(&subtl);
+		aim_addtlvtochain_raw(&tl, 0x0005, bs.len, bs.data);
+		free(buf);
+
+		/* TLV t(0003) - Request an ack */
+		aim_addtlvtochain_noval(&tl, 0x0003);
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, oft_info->cookie, sizeof(oft_info->cookie));
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
+	/* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send an "I will accept this file" message?
+ *
+ * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE)
+ */
+faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, struct aim_oft_info *oft_info)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
 		return -EINVAL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
 		return -ENOMEM;
 
-	for (i = 0; i < 7; i++)
-		aimutil_put8(ck+i, 0x30 + ((fu8_t) rand() % 10));
-	ck[7] = '\0';
-
-	if (ckret)
-		memcpy(ckret, ck, 8);
-
-	/* Fill in the snac destructor so we know if the request
-	 * times out.  Use the cookie in the data field, so we
-	 * know what request to cancel if there is an error.
-	 */
-	snacdest.data = malloc(8);
-	memcpy(snacdest.data, ck, 8);
-	snacdest.conn = conn;
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, &snacdest, sizeof(snacdest));
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * Encompasses everything below. Gee.
-	 */
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
 	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4);
-
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_putraw(&fr->data, ck, 8);
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT);
+	aimbs_putraw(&fr->data, oft_info->cookie, 8);
 	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
 
-	/* TLV t(000a) */
-	aimbs_put16(&fr->data, 0x000a);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, 0x0001);
-
-	/* TLV t(0003) (IP) */
-	aimbs_put16(&fr->data, 0x0003);
-	aimbs_put16(&fr->data, 0x0004);
-	aimbs_putraw(&fr->data, ip, 4);
-
-	/* TLV t(0005) (port) */
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Subtype 0x0006 - Send a "cancel this file transfer" message?
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, struct aim_oft_info *oft_info)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(oft_info->sn) + 4+2+8+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, oft_info->cookie, 0x0002, oft_info->sn);
+
 	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, port);
-
-	/* TLV t(000f) */
-	aimbs_put16(&fr->data, 0x000f);
-	aimbs_put16(&fr->data, 0x0000);
-
-	/* TLV t(2711) */
-	aimbs_put16(&fr->data, 0x2711);
-	aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4);
-
-	/* ? */
-	aimbs_put16(&fr->data, (numfiles > 1) ? 0x0002 : 0x0001);
-	aimbs_put16(&fr->data, numfiles);
-	aimbs_put32(&fr->data, totsize);
-	aimbs_putraw(&fr->data, filename, strlen(filename));
-
-	/* ? */
-	aimbs_put32(&fr->data, 0x00000000);
-
-#if 0
-	/* Newer clients seem to send this (?) -- wtm */
-	aimbs_put32(&fr->data, 0x00030000);
-#endif
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL);
+	aimbs_putraw(&fr->data, oft_info->cookie, 8);
+	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
 
 	aim_tx_enqueue(sess, fr);
@@ -840,11 +838,11 @@
  * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type)
+faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type)
 {
 	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 	int i;
 	fu8_t ck[8];
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
@@ -852,5 +850,5 @@
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
@@ -860,13 +858,6 @@
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/* Cookie */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/* Channel (2) */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/* Dest sn */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	/* TLV t(0005) - Encompasses almost everything below. */
@@ -897,8 +888,5 @@
 			aimbs_putle16(&fr->data, 0x001b); /* L */
 			aimbs_putle16(&fr->data, 0x0008); /* XXX - Protocol version */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
-			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aim_putcap(&fr->data, AIM_CAPS_EMPTY);
 			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
 			aimbs_putle16(&fr->data, 0x0003); /* Client features? */
@@ -927,6 +915,6 @@
 			aimbs_putle16(&fr->data, 0x0000); /* Status? */
 			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
-			aimbs_putle16(&fr->data, 0x0001); /* L? */
-			aimbs_putle8(&fr->data, 0x00); /* Null termination? */
+			aimbs_putle16(&fr->data, 0x0001); /* L */
+			aimbs_putle8(&fr->data, 0x00); /* String of length L */
 		} /* End TLV t(2711) */
 	} /* End TLV t(0005) */
@@ -942,5 +930,5 @@
 
 /**
- * Subtype 0x0006
+ * Subtype 0x0006 - Send an ICQ-esque ICBM.
  *
  * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."  
@@ -955,5 +943,5 @@
  * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
+faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
 {
 	aim_conn_t *conn;
@@ -961,4 +949,5 @@
 	aim_snacid_t snacid;
 	int i;
+	char ck[8];
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
@@ -974,20 +963,10 @@
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
+	/* Cookie */
 	for (i=0; i<8; i++)
-		aimbs_put8(&fr->data, (fu8_t)rand());
-
-	/*
-	 * Channel (4)
-	 */
-	aimbs_put16(&fr->data, 0x0004);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+		ck[i] = (fu8_t)rand();
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0004, sn);
 
 	/*
@@ -1022,4 +1001,7 @@
 }
 
+/*
+ * XXX - I don't see when this would ever get called...
+ */
 static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -1177,5 +1159,5 @@
 }
 
-/* XXX should provide a way of saying ISO-8859-1 specifically */
+/* XXX - should provide a way of saying ISO-8859-1 specifically */
 faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
 {
@@ -1280,5 +1262,5 @@
 		 * the specified data length, which will not include the pad.
 		 *
-		 * XXX There's an API bug here.  For sending, the UNICODE is
+		 * XXX - There's an API bug here.  For sending, the UNICODE is
 		 * given in host byte order (aim_mpmsg_addunicode), but here
 		 * the received messages are given in network byte order.
@@ -1313,5 +1295,4 @@
 			args->charset = sec->charset;
 			args->charsubset = sec->charsubset;
-			args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET;
 
 			/* Set up the simple flags */
@@ -1331,16 +1312,4 @@
 			else if (args->charsubset == 0xffff)
 				; /* no subencoding */
-#if 0
-			/* XXX this isn't really necesary... */	
-			if (	((args.flag1 != 0x0000) &&
-				 (args.flag1 != 0x0002) &&
-				 (args.flag1 != 0x0003) &&
-				 (args.flag1 != 0xffff)) ||
-				((args.flag2 != 0x0000) &&
-				 (args.flag2 != 0x000b) &&
-				 (args.flag2 != 0xffff))) {
-				faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
-			}
-#endif
 
 			args->msg = sec->data;
@@ -1419,5 +1388,5 @@
 		} else if (type == 0x0006) { /* Message was received offline. */
 
-			/* XXX not sure if this actually gets sent. */
+			/* XXX - not sure if this actually gets sent. */
 			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
 
@@ -1555,5 +1524,5 @@
 {
 
-	/* XXX aim_chat_roominfo_free() */
+	/* XXX - aim_chat_roominfo_free() */
 	free(args->info.chat.roominfo.name);
 
@@ -1616,5 +1585,5 @@
 	aim_bstream_advance(servdata, hdrlen);
 
-	/* XXX This is such a hack. */
+	/* XXX - This is such a hack. */
 	args->reqclass = AIM_CAPS_ICQRTF;
 
@@ -1634,5 +1603,7 @@
 	args->destructor = (void *)incomingim_ch2_sendfile_free;
 
-	if (servdata) {
+	/* Maybe there is a better way to tell what kind of sendfile 
+	 * this is?  Maybe TLV t(000a)? */
+	if (servdata) { /* Someone is sending us a file */
 		int flen;
 
@@ -1642,5 +1613,11 @@
 		args->info.sendfile.totsize = aimbs_get32(servdata);
 
-		/* XXX - create an aimbs_getnullstr function */
+		/*
+		 * I hope to God I'm right when I guess that there is a 
+		 * 32 char max filename length for single files.  I think 
+		 * OFT tends to do that.  Gotta love inconsistency.  I saw 
+		 * a 26 byte filename?
+		 */
+		/* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */
 		/* Use an inelegant way of getting the null-terminated filename, 
 		 * since there's no easy bstream routine. */
@@ -1651,4 +1628,5 @@
 		/* There is sometimes more after the null-terminated filename, 
 		 * but I'm unsure of its format. */
+		/* I don't believe him. */
 	}
 
@@ -1668,6 +1646,6 @@
 	int ret = 0;
 
-	char clientip1[30] = {""};
-	char clientip2[30] = {""};
+	char proxyip[30] = {""};
+	char clientip[30] = {""};
 	char verifiedip[30] = {""};
 
@@ -1711,5 +1689,7 @@
 
 	/*
-	 * IP address from the perspective of the client.
+	 * IP address to proxy the file transfer through.
+	 *
+	 * XXX - I don't like this.  Maybe just read in an int?  Or inet_ntoa...
 	 */
 	if (aim_gettlv(list2, 0x0002, 1)) {
@@ -1717,14 +1697,12 @@
 
 		iptlv = aim_gettlv(list2, 0x0002, 1);
-
-		snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d",
-				aimutil_get8(iptlv->value+0),
-				aimutil_get8(iptlv->value+1),
-				aimutil_get8(iptlv->value+2),
-				aimutil_get8(iptlv->value+3));
-	}
-
-	/*
-	 * Secondary IP address from the perspective of the client.
+		if (iptlv->length == 4)
+			snprintf(proxyip, sizeof(proxyip), "%hhd.%hhd.%hhd.%hhd",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
+	}
+
+	/*
+	 * IP address from the perspective of the client.
 	 */
 	if (aim_gettlv(list2, 0x0003, 1)) {
@@ -1732,10 +1710,8 @@
 
 		iptlv = aim_gettlv(list2, 0x0003, 1);
-
-		snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d",
-				aimutil_get8(iptlv->value+0),
-				aimutil_get8(iptlv->value+1),
-				aimutil_get8(iptlv->value+2),
-				aimutil_get8(iptlv->value+3));
+		if (iptlv->length == 4)
+			snprintf(clientip, sizeof(clientip), "%hhd.%hhd.%hhd.%hhd",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
 	}
 
@@ -1749,10 +1725,8 @@
 
 		iptlv = aim_gettlv(list2, 0x0004, 1);
-
-		snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d",
-				aimutil_get8(iptlv->value+0),
-				aimutil_get8(iptlv->value+1),
-				aimutil_get8(iptlv->value+2),
-				aimutil_get8(iptlv->value+3));
+		if (iptlv->length == 4)
+			snprintf(verifiedip, sizeof(verifiedip), "%hhd.%hhd.%hhd.%hhd",
+				iptlv->value[0], iptlv->value[1],
+				iptlv->value[2], iptlv->value[3]);
 	}
 
@@ -1762,4 +1736,12 @@
 	if (aim_gettlv(list2, 0x0005, 1))
 		args.port = aim_gettlv16(list2, 0x0005, 1);
+
+	/*
+	 * Something to do with ft -- two bytes
+	 * 0x0001 - "I want to send you this file"
+	 * 0x0002 - "I will accept this file from you"
+	 */
+	if (aim_gettlv(list2, 0x000a, 1))
+		;
 
 	/*
@@ -1787,21 +1769,29 @@
 		args.language = aim_gettlv_str(list2, 0x000e, 1);
 
-	/* Unknown -- two bytes = 0x0001 */
-	if (aim_gettlv(list2, 0x000a, 1))
-		;
-
-	/* Unknown -- no value */
+	/*
+	 * Unknown -- no value
+	 *
+	 * Maybe means we should connect directly to transfer the file?
+	 */
 	if (aim_gettlv(list2, 0x000f, 1))
 		;
 
-	if (strlen(clientip1))
-		args.clientip = (char *)clientip1;
-	if (strlen(clientip2))
-		args.clientip2 = (char *)clientip2;
+	/*
+	 * Unknown -- no value
+	 *
+	 * Maybe means we should proxy the file transfer through an AIM server?
+	 */
+	if (aim_gettlv(list2, 0x0010, 1))
+		;
+
+	if (strlen(proxyip))
+		args.proxyip = (char *)proxyip;
+	if (strlen(clientip))
+		args.clientip = (char *)clientip;
 	if (strlen(verifiedip))
 		args.verifiedip = (char *)verifiedip;
 
 	/*
-	 * This is must be present in PROPOSALs, but will probably not
+	 * This must be present in PROPOSALs, but will probably not
 	 * exist in CANCELs and ACCEPTs.
 	 *
@@ -1863,6 +1853,8 @@
 
 	args.uin = aimbs_getle32(&meat);
-	args.type = aimbs_getle16(&meat);
-	args.msg = aimbs_getraw(&meat, aimbs_getle16(&meat));
+	args.type = aimbs_getle8(&meat);
+	args.flags = aimbs_getle8(&meat);
+	args.msglen = aimbs_getle16(&meat);
+	args.msg = aimbs_getraw(&meat, args.msglen);
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
@@ -1905,12 +1897,12 @@
 	 * Channel ID.
 	 *
-	 * Channel 0x0001 is the message channel.  There are 
-	 * other channels for things called "rendevous"
-	 * which represent chat and some of the other new
-	 * features of AIM2/3/3.5. 
+	 * Channel 0x0001 is the message channel.  It is 
+	 * used to send basic ICBMs.
 	 *
 	 * Channel 0x0002 is the Rendevous channel, which
 	 * is where Chat Invitiations and various client-client
 	 * connection negotiations come from.
+	 *
+	 * Channel 0x0003 is used for chat messages.
 	 *
 	 * Channel 0x0004 is used for ICQ authorization, or 
@@ -1927,7 +1919,6 @@
 	 * userinfo block contains the number of TLVs that contain user
 	 * information, the rest are not even though there is no seperation.
-	 * aim_extractuserinfo() returns the number of bytes used by the
-	 * userinfo tlvs, so you can start reading the rest of them right
-	 * afterward.  
+	 * You can start reading the message TLVs after aim_info_extract() 
+	 * parses out the standard userinfo block.
 	 *
 	 * That also means that TLV types can be duplicated between the
@@ -1936,5 +1927,5 @@
 	 * 
 	 */
-	aim_extractuserinfo(sess, bs, &userinfo);
+	aim_info_extract(sess, bs, &userinfo);
 
 	/*
@@ -1970,9 +1961,8 @@
 
 	} else {
-
-		faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
-
-		return 0;
-	}
+		faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
+	}
+
+	aim_info_free(&userinfo);
 
 	return ret;
@@ -1980,5 +1970,5 @@
 
 /*
- * Subtype 0x0008 - Send a warning to destsn.
+ * Subtype 0x0008 - Send a warning to sn.
  * 
  * Flags:
@@ -1988,23 +1978,21 @@
  *
  */
-faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags)
+faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	fu16_t outflags = 0x0000;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13)))
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13)))
 		return -ENOMEM;
 
-	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
-
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
 	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
 
-	if (flags & AIM_WARN_ANON)
-		outflags |= 0x0001;
-
-	aimbs_put16(&fr->data, outflags); 
-	aimbs_put8(&fr->data, strlen(destsn));
-	aimbs_putraw(&fr->data, destsn, strlen(destsn));
+	aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); 
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
 	aim_tx_enqueue(sess, fr);
@@ -2024,5 +2012,5 @@
 
 		channel = aimbs_get16(bs);
-		aim_extractuserinfo(sess, bs, &userinfo);
+		aim_info_extract(sess, bs, &userinfo);
 		nummissed = aimbs_get16(bs);
 		reason = aimbs_get16(bs);
@@ -2030,4 +2018,6 @@
 		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 			 ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
+
+		aim_info_free(&userinfo);
 	}
 
@@ -2044,5 +2034,5 @@
  * 
  */
-faim_export int aim_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code)
+faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code)
 {
 	aim_conn_t *conn;
@@ -2096,4 +2086,6 @@
 
 	if (channel == 0x0002) { /* File transfer declined */
+		aimbs_get16(bs); /* Unknown */
+		aimbs_get16(bs); /* Unknown */
 		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 			ret = userfunc(sess, rx, channel, sn, reason, ck);
@@ -2159,20 +2151,26 @@
 }
 
-/* Subtype 0x000c */
+/*
+ * Subtype 0x000c - Receive an ack after sending an ICBM.
+ *
+ * You have to have send the message with the AIM_IMFLAGS_ACK flag set 
+ * (TLV t(0003)).  The ack contains the ICBM header of the message you 
+ * sent.
+ *
+ */
 static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
-	fu16_t type;
-	fu8_t snlen, *ck;
+	fu16_t ch;
+	fu8_t *ck;
 	char *sn;
 	int ret = 0;
 
 	ck = aimbs_getraw(bs, 8);
-	type = aimbs_get16(bs);
-	snlen = aimbs_get8(bs);
-	sn = aimbs_getstr(bs, snlen);
+	ch = aimbs_get16(bs);
+	sn = aimbs_getstr(bs, aimbs_get8(bs));
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, type, sn);
+		ret = userfunc(sess, rx, ch, sn);
 
 	free(sn);
@@ -2189,5 +2187,5 @@
  *
  */
-faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2)
+faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2)
 {
 	aim_conn_t *conn;
@@ -2270,5 +2268,5 @@
 
 	if (snac->subtype == 0x0005)
-		return paraminfo(sess, mod, rx, snac, bs);
+		return aim_im_paraminfo(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0006)
 		return outgoingim(sess, mod, rx, snac, bs);
@@ -2287,17 +2285,4 @@
 }
 
-static int snacdestructor(aim_session_t *sess, aim_conn_t *conn, aim_modsnac_t *snac, void *data)
-{
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-
-	if (snac->subtype == 0x0006) {
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_MSGTIMEOUT)))
-			ret = userfunc(sess, NULL, conn, data);
-	}
-	/* Note that we return 1 for success, 0 for failure. */
-	return ret;
-}
-
 faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
@@ -2306,9 +2291,8 @@
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
-	mod->toolversion = 0x047b;
+	mod->toolversion = 0x0629;
 	mod->flags = 0;
 	strncpy(mod->name, "messaging", sizeof(mod->name));
 	mod->snachandler = snachandler;
-	mod->snacdestructor = snacdestructor;
 
 	return 0;
