changeset 3952:07283934dedd

[gaim-migrate @ 4133] Ok, big commit with little functionality change. Most of it is me shuffling crap around because I'm one of them neat freaks. Lots of general code cleanup too. I'm trying to move to that whole "one-family-per-file" thing. The details... I added libfaim support for aim's new search family, 0x000f. I only tested this briefly, so if anyone uses it for anything, be aware that it could be buggy. I'll add oscar support sometime. Advantages of this family are... when you search for someone, you get the directory info for that person. So like, first name, middle name, last name, maiden name, city, state, country, zip, address, interests, nickname, and maybe some other stuff. Basically all the info that they've set in their directory info thing. Info. Oh, and I'm calling it "new search" because seach was already taken, and cookie monster ate my right brain. The reason I didn't add support to oscar.c... the new search family requires making a connection to another server. While moving stuff around I realized that I didn't really like how new connections are made. It's kind of sloppy. I'm thinking it would be nice to have an outgoing queue for each type of connection, and then let the client queue messages as much as they want. Then, if libfaim sees that there is a message for a certain type of connection, and there is no open connection of that type, it will connect, and then flush the queue when the connection is made. This seems a lot cleaner, but it also seems like a pain in the ass. I should do ssi for icq first, anyway :-) Also, I think it would be neat if there was an ICBM file that handled channels 1 through 4. Then im.c and chat.c could pass the ICBM part to the icbm stuff and it could get parsed there. im.c is really huge right now. I applied a patch from Graham Booker that paves the way for unicode in direct IMs. Thanks Graham. Now we just need Paco-Paco to git a little free time and write a patch for this. http://sourceforge.net/tracker/index.php?func=detail&aid=633589&group_id=235&atid=300235 I applied 2 patches from Will Mahan dealing with file transfer/oft/rendezous/whatever. Here's some info on them, from The Man himself: Patch 1 "Currently the Rendezvous code is rather messy; this patch attempts to bring it up to speed with the rest of the Oscar prpl. Its changes include: * Rewrite several ft.c functions to use bstreams. Apparently the code in question was written before bstreams were implemented. * Handle incoming Rendezvous packets through the rxqueue like FLAP packets, rather than handling them as a special case as soon as they are received. This takes advantage of the bstream cleanup to unify some code and simplify the aim_frame_t struct. * Change some names used to try to clarify the distinction between OFT, which refers specifically to file transfer, and Rendezvous, which encompasses OFT as well as other types of client-to-client connections." Patch 2 "* Add some comments I inadvertently left out of my last patch. * Fix a double-free that occurs when connections time out. * Correct a bug causing filenames to be truncated by 4 characters on some clients. * Preserve directory structure when sending multiple files. * Handle (throw away) resource forks sent by Mac clients." I also changed all indents to tabs in ft.c. And split all the bstream stuff from rxqueue.c and put it in bstream.c. It really is a separate thing. Especially since it can be used for outgoing connections. Also, I was going to look over the whole patch tonight to make sure it's all good, but it's like 6000 lines, so, uh, I'll do it later. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Wed, 13 Nov 2002 07:01:37 +0000
parents 32942c49dced
children 7ec84d1954fd
files src/protocols/oscar/.cvsignore src/protocols/oscar/Makefile.am src/protocols/oscar/Makefile.mingw src/protocols/oscar/admin.c src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/aim_internal.h src/protocols/oscar/bos.c src/protocols/oscar/bstream.c src/protocols/oscar/buddylist.c src/protocols/oscar/chat.c src/protocols/oscar/chatnav.c src/protocols/oscar/conn.c src/protocols/oscar/email.c src/protocols/oscar/ft.c src/protocols/oscar/icq.c src/protocols/oscar/im.c src/protocols/oscar/info.c src/protocols/oscar/invite.c src/protocols/oscar/misc.c src/protocols/oscar/msgcookie.c src/protocols/oscar/newsearch.c src/protocols/oscar/oscar.c src/protocols/oscar/popups.c src/protocols/oscar/rxhandlers.c src/protocols/oscar/rxqueue.c src/protocols/oscar/search.c src/protocols/oscar/service.c src/protocols/oscar/snac.c src/protocols/oscar/stats.c src/protocols/oscar/tlv.c src/protocols/oscar/translate.c src/protocols/oscar/txqueue.c src/protocols/oscar/util.c
diffstat 34 files changed, 2514 insertions(+), 2096 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/oscar/.cvsignore	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/.cvsignore	Wed Nov 13 07:01:37 2002 +0000
@@ -6,6 +6,7 @@
 adverts.lo
 auth.lo
 bos.lo
+bstream.lo
 buddylist.lo
 chat.lo
 chatnav.lo
@@ -20,6 +21,7 @@
 meta.lo
 misc.lo
 msgcookie.lo
+newsearch.lo
 oscar.lo
 rxhandlers.lo
 rxqueue.lo
--- a/src/protocols/oscar/Makefile.am	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/Makefile.am	Wed Nov 13 07:01:37 2002 +0000
@@ -16,6 +16,7 @@
 			adverts.c	\
 			auth.c 		\
 			bos.c 		\
+			bstream.c 	\
 			buddylist.c	\
 			chat.c		\
 			chatnav.c	\
@@ -29,6 +30,7 @@
 			meta.c		\
 			misc.c		\
 			msgcookie.c	\
+			newsearch.c	\
 			popups.c	\
 			rxhandlers.c	\
 			rxqueue.c	\
@@ -55,6 +57,7 @@
 			adverts.c	\
 			auth.c 		\
 			bos.c 		\
+			bstream.c 	\
 			buddylist.c	\
 			chat.c		\
 			chatnav.c	\
@@ -68,6 +71,7 @@
 			meta.c		\
 			misc.c		\
 			msgcookie.c	\
+			newsearch.c	\
 			popups.c	\
 			rxhandlers.c	\
 			rxqueue.c	\
--- a/src/protocols/oscar/Makefile.mingw	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/Makefile.mingw	Wed Nov 13 07:01:37 2002 +0000
@@ -71,6 +71,7 @@
 			adverts.c	\
 			auth.c 		\
 			bos.c 		\
+			bstream.c	\
 			buddylist.c	\
 			chat.c		\
 			chatnav.c	\
@@ -84,6 +85,7 @@
 			meta.c		\
 			misc.c		\
 			msgcookie.c	\
+			newsearch.c	\
 			popups.c	\
 			rxhandlers.c	\
 			rxqueue.c	\
--- a/src/protocols/oscar/admin.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/admin.c	Wed Nov 13 07:01:37 2002 +0000
@@ -20,19 +20,19 @@
  */ 
 faim_export int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, fu16_t info)
 {
-	aim_frame_t *tx;
+	aim_frame_t *fr;
 	aim_snacid_t snacid;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x0007, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&tx->data, 0x0007, 0x0002, 0x0000, snacid);
+	aim_putsnac(&fr->data, 0x0007, 0x0002, 0x0000, snacid);
 
-	aimbs_put16(&tx->data, info);
-	aimbs_put16(&tx->data, 0x0000);
+	aimbs_put16(&fr->data, info);
+	aimbs_put16(&fr->data, 0x0000);
 
-	aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
@@ -100,22 +100,22 @@
  */
 faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick)
 {
-	aim_frame_t *tx;
+	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	aim_tlvlist_t *tl = NULL;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
 	aim_addtlvtochain_raw(&tl, 0x0001, strlen(newnick), newnick);
 	
-	aim_writetlvchain(&tx->data, &tl);
+	aim_writetlvchain(&fr->data, &tl);
 	aim_freetlvchain(&tl);
 	
-	aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
 
 	return 0;
@@ -127,15 +127,15 @@
  */
 faim_export int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
 {
-	aim_frame_t *tx;
+	aim_frame_t *fr;
 	aim_tlvlist_t *tl = NULL;
 	aim_snacid_t snacid;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
 	/* new password TLV t(0002) */
 	aim_addtlvtochain_raw(&tl, 0x0002, strlen(newpw), newpw);
@@ -143,10 +143,10 @@
 	/* current password TLV t(0012) */
 	aim_addtlvtochain_raw(&tl, 0x0012, strlen(curpw), curpw);
 
-	aim_writetlvchain(&tx->data, &tl);
+	aim_writetlvchain(&fr->data, &tl);
 	aim_freetlvchain(&tl);
 
-	aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
@@ -157,22 +157,22 @@
  */
 faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
 {
-	aim_frame_t *tx;
+	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	aim_tlvlist_t *tl = NULL;
 
-	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+	aim_putsnac(&fr->data, 0x0007, 0x0004, 0x0000, snacid);
 
 	aim_addtlvtochain_raw(&tl, 0x0011, strlen(newemail), newemail);
 	
-	aim_writetlvchain(&tx->data, &tl);
+	aim_writetlvchain(&fr->data, &tl);
 	aim_freetlvchain(&tl);
 	
-	aim_tx_enqueue(sess, tx);
+	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
--- a/src/protocols/oscar/aim.h	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/aim.h	Wed Nov 13 07:01:37 2002 +0000
@@ -216,6 +216,7 @@
 #define AIM_CONN_TYPE_BOS           0x0002
 #define AIM_CONN_TYPE_CHAT          0x000e
 #define AIM_CONN_TYPE_CHATNAV       0x000d
+#define AIM_CONN_TYPE_SEARCH        0x000f
 #define AIM_CONN_TYPE_EMAIL         0x0018
 
 /* they start getting arbitrary in rendezvous stuff =) */
@@ -287,11 +288,10 @@
 			flap_seqnum_t seqnum;     
 		} flap;
 		struct {
+			fu8_t magic[4]; /* ODC2 OFT2 */
+			fu16_t hdrlen;
 			fu16_t type;
-			fu8_t magic[4]; /* ODC2 OFT2 */
-			fu16_t hdr2len;
-			fu8_t *hdr2; /* rest of bloated header */
-		} oft;
+		} rend;
 	} hdr;
 	aim_bstream_t data;	/* payload stream */
 	fu8_t handled;		/* 0 = new, !0 = been handled */
@@ -496,6 +496,7 @@
 
 /* TLV list handling. */
 faim_internal aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs);
+faim_internal aim_tlvlist_t *aim_readtlvchain_num(aim_bstream_t *bs, fu16_t num);
 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);
@@ -885,7 +886,7 @@
 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 fu16_t aim_iconsum(const fu8_t *buf, int buflen);
 faim_export int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
-faim_export int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len);
+faim_export int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len, int encoding);
 faim_export const char *aim_directim_getsn(aim_conn_t *conn);
 faim_export aim_conn_t *aim_directim_initiate(aim_session_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);
@@ -1019,13 +1020,37 @@
 faim_export int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
 faim_export int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick);
 
-/* aim_buddylist.c */
+/* buddylist.c */
 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 */
+/* search.c */
 faim_export int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
 
+/* newsearch.c */
+struct aim_usersearch {
+	char *first;
+	char *last;
+	char *middle;
+	char *maiden;
+	char *email;
+	char *country;
+	char *state;
+	char *city;
+	char *sn;
+	char *interest;
+	char *nick;
+	char *zip;
+	char *region;
+	char *address;
+	struct aim_usersearch *next;
+};
+
+faim_export int aim_usersearch_email(aim_session_t *, const char *, const char *);
+faim_export int aim_usersearch_name(aim_session_t *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *, const char *);
+faim_export int aim_usersearch_interest(aim_session_t *, const char *, const char *);
+
+
 /* These apply to exchanges as well. */
 #define AIM_CHATROOM_FLAG_EVILABLE 0x0001
 #define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
--- a/src/protocols/oscar/aim_cbtypes.h	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/aim_cbtypes.h	Wed Nov 13 07:01:37 2002 +0000
@@ -23,6 +23,7 @@
 #define AIM_CB_FAM_TRN 0x000c
 #define AIM_CB_FAM_CTN 0x000d /* ChatNav */
 #define AIM_CB_FAM_CHT 0x000e /* Chat */
+#define AIM_CB_FAM_SCH 0x000f /* "New" search */
 #define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
 #define AIM_CB_FAM_ICQ 0x0015
 #define AIM_CB_FAM_ATH 0x0017
@@ -176,6 +177,15 @@
 #define AIM_CB_CHT_DEFAULT 0xffff
 
 /*
+ * SNAC Family: "New" Search
+ *
+ * Most of these are actually special.
+ */ 
+#define AIM_CB_SCH_ERROR 0x0001
+#define AIM_CB_SCH_SEARCH 0x0002
+#define AIM_CB_SCH_RESULTS 0x0003
+
+/*
  * SNAC Family: ICQ
  *
  * Most of these are actually special.
--- a/src/protocols/oscar/aim_internal.h	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/aim_internal.h	Wed Nov 13 07:01:37 2002 +0000
@@ -55,6 +55,7 @@
 faim_internal int adverts_modfirst(aim_session_t *sess, aim_module_t *mod);
 faim_internal int icq_modfirst(aim_session_t *sess, aim_module_t *mod);
 faim_internal int email_modfirst(aim_session_t *sess, aim_module_t *mod);
+faim_internal int newsearch_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);
@@ -63,9 +64,7 @@
 
 #define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset)
 
-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_bstream_recv(aim_bstream_t *bs, int fd, size_t count);
+/* bstream.c */
 faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len);
 faim_internal int aim_bstream_empty(aim_bstream_t *bs);
 faim_internal int aim_bstream_curpos(aim_bstream_t *bs);
@@ -90,18 +89,32 @@
 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_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn);
+/* conn.c */
+faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
+
+/* ft.c */
+faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr);
 
-faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
+/* rxhandlers.c */
+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);
+faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
+faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
+
+/* rxqueue.c */
+faim_internal int aim_recv(int fd, void *buf, size_t count);
+faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count);
+faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
+faim_internal void aim_frame_destroy(aim_frame_t *);
+
+/* txqueue.c */
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen);
+faim_internal int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
 faim_internal 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, fu16_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 int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
 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);
+/* XXX - What is this?   faim_internal int aim_tx_printqueue(aim_session_t *); */
 
 /*
  * Generic SNAC structure.  Rarely if ever used.
@@ -121,6 +134,7 @@
 	void *data;
 };
 
+/* snac.c */
 faim_internal void aim_initsnachash(aim_session_t *sess);
 faim_internal aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac);
 faim_internal aim_snacid_t aim_cachesnac(aim_session_t *sess, const fu16_t family, const fu16_t type, const fu16_t flags, const void *data, const int datalen);
@@ -128,13 +142,6 @@
 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_parse_unknown(aim_session_t *, aim_frame_t *, ...);
-
 /* Stored in ->priv of the service request SNAC for chats. */
 struct chatsnacinfo {
 	fu16_t exchange;
--- a/src/protocols/oscar/bos.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/bos.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,14 +1,18 @@
+/*
+ * Family 0x0009 - Basic Oscar Service.
+ *
+ */
 
 #define FAIM_INTERNAL
 #include <aim.h>
 
-/* Request BOS rights (group 9, type 2) */
+/* Subtype 0x0002 - Request BOS rights. */
 faim_export int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
 {
 	return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
 }
 
-/* BOS Rights (group 9, type 3) */
+/* Subtype 0x0003 - BOS Rights. */
 static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -42,7 +46,7 @@
 }
 
 /* 
- * Set group permisson mask (group 9, type 4)
+ * Subtype 0x0004 - Set group permisson mask.
  *
  * Normally 0x1f (all classes).
  *
@@ -57,7 +61,7 @@
 }
 
 /*
- * Modify permit/deny lists (group 9, types 5, 6, 7, and 8)
+ * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists.
  *
  * Changes your visibility depending on changetype:
  *
@@ -158,5 +162,3 @@
 
 	return 0;
 }
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/bstream.c	Wed Nov 13 07:01:37 2002 +0000
@@ -0,0 +1,265 @@
+/*
+ * bstream.c
+ *
+ * This file contains all functions needed to use bstreams.
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+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;
+}
+
+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);
+}
+
+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 fu8_t aimbs_getle8(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset++;
+	
+	return aimutil_getle8(bs->data + bs->offset - 1);
+}
+
+faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 2;
+	
+	return aimutil_getle16(bs->data + bs->offset - 2);
+}
+
+faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 4;
+	
+	return aimutil_getle32(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_putle8(aim_bstream_t *bs, fu8_t v)
+{
+
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle8(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v)
+{
+
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle16(bs->data + bs->offset, v);
+
+	return 2;
+}
+
+faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v)
+{
+
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle32(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 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;
+}
--- a/src/protocols/oscar/buddylist.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/buddylist.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,29 +1,26 @@
+/*
+ * Family 0x0003 - Old-style Buddylist Management (non-SSI).
+ *
+ */
 
 #define FAIM_INTERNAL
 #include <aim.h>
 
 /*
- * Oncoming Buddy notifications contain a subset of the
- * user information structure.  Its close enough to run
- * through aim_extractuserinfo() however.
+ * Subtype 0x0002 - Request rights.
  *
- * Although the offgoing notification contains no information,
- * it is still in a format parsable by extractuserinfo.
+ * Request Buddy List rights.
  *
  */
-static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+faim_export int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
 {
-	aim_userinfo_t userinfo;
-	aim_rxcallback_t userfunc;
-
-	aim_extractuserinfo(sess, bs, &userinfo);
-
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		return userfunc(sess, rx, &userinfo);
-
-	return 0;
+	return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
 }
 
+/*
+ * Subtype 0x0003 - Rights.
+ *
+ */
 static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -67,37 +64,11 @@
 	return ret;  
 }
 
-static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-
-	if (snac->subtype == 0x0003)
-		return rights(sess, mod, rx, snac, bs);
-	else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
-		return buddychange(sess, mod, rx, snac, bs);
-
-	return 0;
-}
-
-faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
-{
-
-	mod->family = 0x0003;
-	mod->version = 0x0001;
-	mod->toolid = 0x0110;
-	mod->toolversion = 0x047b;
-	mod->flags = 0;
-	strncpy(mod->name, "buddylist", sizeof(mod->name));
-	mod->snachandler = snachandler;
-
-	return 0;
-}
-
 /*
- * aim_add_buddy()
+ * Subtype 0x0004 - Add buddy to list.
  *
  * Adds a single buddy to your buddy list after login.
- *
- * XXX this should just be an extension of setbuddylist()
+ * XXX This should just be an extension of setbuddylist()
  *
  */
 faim_export int aim_add_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
@@ -123,6 +94,59 @@
 }
 
 /*
+ * Subtype 0x0004 - Add multiple buddies to your buddy list.
+ *
+ * This just builds the "set buddy list" command then queues it.
+ *
+ * buddy_list = "Screen Name One&ScreenNameTwo&";
+ *
+ * XXX Clean this up.  
+ *
+ */
+faim_export int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int len = 0;
+	char *localcpy = NULL;
+	char *tmpptr = NULL;
+
+	if (!buddy_list || !(localcpy = strdup(buddy_list))) 
+		return -EINVAL;
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
+		len += 1 + strlen(tmpptr);
+		tmpptr = strtok(NULL, "&");
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+
+		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr));
+		tmpptr = strtok(NULL, "&");
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	free(localcpy);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0005 - Remove buddy from list.
+ *
  * XXX generalise to support removing multiple buddies (basically, its
  * the same as setbuddylist() but with a different snac subtype).
  *
@@ -149,3 +173,106 @@
 	return 0;
 }
 
+/* 
+ * Subtype 0x000b
+ *
+ * XXX Why would we send this?
+ *
+ */
+faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
+	aim_putuserinfo(&fr->data, info);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* 
+ * Subtype 0x000c
+ *
+ * XXX Why would we send this?
+ *
+ */
+faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtypes 0x000b and 0x000c - Change in buddy status
+ *
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure.  Its close enough to run
+ * through aim_extractuserinfo() however.
+ *
+ * Although the offgoing notification contains no information,
+ * it is still in a format parsable by extractuserinfo.
+ *
+ */
+static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t userinfo;
+	aim_rxcallback_t userfunc;
+
+	aim_extractuserinfo(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &userinfo);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+	else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
+		return buddychange(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0003;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x047b;
+	mod->flags = 0;
+	strncpy(mod->name, "buddylist", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- a/src/protocols/oscar/chat.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/chat.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,7 +1,5 @@
 /*
- * aim_chat.c
- *
- * Routines for the Chat service.
+ * Family 0x000e - Routines for the Chat service.
  *
  */
 
@@ -85,103 +83,6 @@
 	return 0;
 }
 
-/*
- * Send a Chat Message.
- *
- * Possible flags:
- *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
- *                                 should be sent to their sender.
- *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
- *                                 (Note that WinAIM does not honor this,
- *                                 and displays the message as normal.)
- *
- * XXX convert this to use tlvchains 
- */
-faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const 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;
-
-	if (!sess || !conn || !msg || (msglen <= 0))
-		return 0;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
-
-
-	/* 
-	 * 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());
-
-	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
-	cookie->data = NULL; /* XXX store something useful here */
-
-	aim_cachecookie(sess, cookie);
-
-	for (i = 0; i < sizeof(ckstr); i++)
-		aimbs_put8(&fr->data, ckstr[i]);
-
-
-	/*
-	 * Channel ID. 
-	 */
-	aimbs_put16(&fr->data, 0x0003);
-
-
-	/*
-	 * Type 1: Flag meaning this message is destined to the room.
-	 */
-	aim_addtlvtochain_noval(&otl, 0x0001);
-
-	/*
-	 * 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);
-
-	/*
-	 * 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);
-
-	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;
@@ -376,13 +277,12 @@
 }
 
 /*
- * General room information.  Lots of stuff.
+ * Subtype 0x0002 - General room information.  Lots of stuff.
  *
  * Values I know are in here but I havent attached
  * them to any of the 'Unknown's:
  *	- Language (English)
  *
- * SNAC 000e/0002
  */
 static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -546,6 +446,7 @@
 	return ret;
 }
 
+/* Subtypes 0x0003 and 0x0004 */
 static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_userinfo_t *userinfo = NULL;
@@ -567,6 +468,105 @@
 }
 
 /*
+ * Subtype 0x0005 - Send a Chat Message.
+ *
+ * Possible flags:
+ *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
+ *                                 should be sent to their sender.
+ *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
+ *                                 (Note that WinAIM does not honor this,
+ *                                 and displays the message as normal.)
+ *
+ * XXX convert this to use tlvchains 
+ */
+faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const 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;
+
+	if (!sess || !conn || !msg || (msglen <= 0))
+		return 0;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
+
+
+	/* 
+	 * 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());
+
+	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
+	cookie->data = NULL; /* XXX store something useful here */
+
+	aim_cachecookie(sess, cookie);
+
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimbs_put8(&fr->data, ckstr[i]);
+
+
+	/*
+	 * Channel ID. 
+	 */
+	aimbs_put16(&fr->data, 0x0003);
+
+
+	/*
+	 * Type 1: Flag meaning this message is destined to the room.
+	 */
+	aim_addtlvtochain_noval(&otl, 0x0001);
+
+	/*
+	 * 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);
+
+	/*
+	 * 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);
+
+	aim_writetlvchain(&fr->data, &otl);
+	
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&otl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0006
+ *
  * We could probably include this in the normal ICBM parsing 
  * code as channel 0x0003, however, since only the start
  * would be the same, we might as well do it here.
@@ -710,5 +710,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/chatnav.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/chatnav.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,9 +1,9 @@
 /*
- * Handle ChatNav.
+ * Family 0x000d - Handle ChatNav.
  *
- * [The ChatNav(igation) service does various things to keep chat
- *  alive.  It provides room information, room searching and creating, 
- *  as well as giving users the right ("permission") to use chat.]
+ * The ChatNav(igation) service does various things to keep chat
+ * alive.  It provides room information, room searching and creating,
+ * as well as giving users the right ("permission") to use chat.
  *
  */
 
@@ -11,13 +11,19 @@
 #include <aim.h>
 
 /*
+ * Subtype 0x0002
+ *
  * conn must be a chatnav connection!
+ *
  */
 faim_export int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
 {
 	return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
 }
 
+/*
+ * Subtype 0x0008
+ */
 faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange)
 {
 	static const char ck[] = {"create"};
@@ -345,6 +351,8 @@
 }
 
 /*
+ * Subtype 0x0009
+ *
  * Since multiple things can trigger this callback, we must lookup the 
  * snacid to determine the original snac subtype that was called.
  *
@@ -415,7 +423,7 @@
 {
 
 	mod->family = 0x000d;
-	mod->version = 0x0001;
+	mod->version = 0x0003;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x047c;
 	mod->flags = 0;
--- a/src/protocols/oscar/conn.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/conn.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,4 +1,3 @@
-
 /*
  * conn.c
  *
@@ -920,10 +919,11 @@
 	aim__registermodule(sess, translate_modfirst);
 	aim__registermodule(sess, chatnav_modfirst);
 	aim__registermodule(sess, chat_modfirst);
-	/* missing 0x0f - 0x12 */
+	aim__registermodule(sess, newsearch_modfirst);
+	/* missing 0x10 - 0x12 */
 	aim__registermodule(sess, ssi_modfirst);
 	/* missing 0x14 */
-	aim__registermodule(sess, icq_modfirst);
+	aim__registermodule(sess, icq_modfirst); /* XXX - Make sure this isn't sent for AIM */
 	/* missing 0x16 */
 	aim__registermodule(sess, auth_modfirst);
 	aim__registermodule(sess, email_modfirst);
@@ -1066,14 +1066,13 @@
 	aim_connrst(sess);  /* in case we want to connect again */
 
 	return 0;
-
 }
 
 /*
  * aim_flap_nop()
  *
  * No-op.  WinAIM 4.x sends these _every minute_ to keep
- * the connection alive.  
+ * the connection alive.
  */
 faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
 {
@@ -1086,5 +1085,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/email.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/email.c	Wed Nov 13 07:01:37 2002 +0000
@@ -86,10 +86,10 @@
 	for (new=sess->emailinfo; (new && strncmp(cookie16, new->cookie16, 16)); new=new->next);
 	if (new) {
 		/* Free some of the old info, if existant */
-		if (new->cookie8) free(new->cookie8);
-		if (new->cookie16) free(new->cookie16);
-		if (new->url) free(new->url);
-		if (new->domain) free(new->domain);
+		free(new->cookie8);
+		free(new->cookie16);
+		free(new->url);
+		free(new->domain);
 	} else {
 		/* We don't already have info, so create a new struct for it */
 		if (!(new = malloc(sizeof(struct aim_emailinfo))))
@@ -102,8 +102,7 @@
 	new->cookie8 = cookie8;
 	new->cookie16 = cookie16;
 
-	aimbs_get16(bs); /* Number of TLVs to follow */
-	tlvlist = aim_readtlvchain(bs);
+	tlvlist = aim_readtlvchain_num(bs, aimbs_get16(bs));
 
 	tmp = aim_gettlv16(tlvlist, 0x0080, 1);
 	if (tmp) {
@@ -177,10 +176,10 @@
 	while (sess->emailinfo) {
 		struct aim_emailinfo *tmp = sess->emailinfo;
 		sess->emailinfo = sess->emailinfo->next;
-		if (tmp->cookie16) free(tmp->cookie16);
-		if (tmp->cookie8) free(tmp->cookie8);
-		if (tmp->url) free(tmp->url);
-		if (tmp->domain) free(tmp->domain);
+		free(tmp->cookie16);
+		free(tmp->cookie8);
+		free(tmp->url);
+		free(tmp->domain);
 		free(tmp);
 	}
 
--- a/src/protocols/oscar/ft.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/ft.c	Wed Nov 13 07:01:37 2002 +0000
@@ -18,6 +18,7 @@
 #include <arpa/inet.h> /* for inet_ntoa */
 #endif
 
+/* XXX - I really don't think this should include gaim.h */
 #include "gaim.h"
 
 #ifdef _WIN32
@@ -45,8 +46,9 @@
 };
 
 static int listenestablish(fu16_t portnum);
-static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr);
-static const char *oft_basename(const char *name);
+static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs);
+static void oft_dirconvert(char *name);
+static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh);
  
 /**
  * aim_handlerendconnect - call this to accept OFT connections and set up the required structures
@@ -145,59 +147,60 @@
  */
 faim_export int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
 {
-	
-struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	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 */
+	aim_bstream_t *hdrbs;
+	fu8_t *hdr;
+	int hdrlen = 0x44;
 
-	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
-		return -EINVAL; 
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
+		return -EINVAL;
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
-	       return -ENOMEM;	
+		return -ENOMEM;
+	memcpy(fr->hdr.rend.magic, "ODC2", 4);
+	fr->hdr.rend.hdrlen = hdrlen;
 
-	memcpy(fr->hdr.oft.magic, "ODC2", 4);
-	
-	fr->hdr.oft.hdr2len = 0x44;
-	
-	if (!(fr->hdr.oft.hdr2 = calloc(1, fr->hdr.oft.hdr2len))) { 
+	if (!(hdr = calloc(1, hdrlen))) {
 		aim_frame_destroy(fr);
 		return -ENOMEM;
 	}
-	
-	aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+	hdrbs = &(fr->data);
+	aim_bstream_init(hdrbs, hdr, hdrlen);
 
-	aimbs_put16(&hdrbs, 0x0006);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_putraw(&hdrbs, intdata->cookie, 8);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put32(&hdrbs, 0x00000000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0006);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, intdata->cookie, 8);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put32(hdrbs, 0x00000000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
 
 	/* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */
-	aimbs_put16(&hdrbs, ( typing ? 0x000e : 0x0002));
+	aimbs_put16(hdrbs, ( typing ? 0x000e : 0x0002));
 
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_putraw(&hdrbs, sess->sn, strlen(sess->sn));
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));
 	
-	aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
+	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
 
-	aimbs_put8(&hdrbs, 0x00);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put8(hdrbs, 0x00);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put8(hdrbs, 0x00);
 
-	/* end of hdr2 */
+	/* end of hdr */
 
 	aim_tx_enqueue(sess, fr);
 
@@ -208,17 +211,20 @@
  * aim_send_im_direct - send IM client-to-client over established connection
  * @sess: session to conn
  * @conn: directim connection
- * @msg:  null-terminated string to send. 
- * len:   The length of the message to send, including binary data.
+ * @msg: null-terminated string to send. 
+ * @len: The length of the message to send, including binary data.
+ * @encoding: 0 for ascii, 2 for Unicode, 3 for ISO 8859-1
  * 
  * 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(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len)
+faim_export int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding)
 {
 	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 */
+	aim_bstream_t *hdrbs;
+	int hdrlen = 0x44;
+	fu8_t *hdr;
 
 	if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
 		return -EINVAL; 
@@ -226,46 +232,47 @@
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len)))
 		return -ENOMEM;	
 
-	memcpy(fr->hdr.oft.magic, "ODC2", 4);
+	memcpy(fr->hdr.rend.magic, "ODC2", 4);
+	fr->hdr.rend.hdrlen = hdrlen;
 	
-	fr->hdr.oft.hdr2len = 0x44;
-	
-	if (!(fr->hdr.oft.hdr2 = calloc(1, fr->hdr.oft.hdr2len))) { 
+	if (!(hdr = calloc(1, hdrlen + len))) {
 		aim_frame_destroy(fr);
 		return -ENOMEM;
 	}
-	
-	aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
-	
-	aimbs_put16(&hdrbs, 0x0006);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_putraw(&hdrbs, intdata->cookie, 8);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put32(&hdrbs, len);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
+
+	hdrbs = &(fr->data);
+	aim_bstream_init(hdrbs, hdr, hdrlen + len);
+
+	aimbs_put16(hdrbs, 0x0006);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, intdata->cookie, 8);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put32(hdrbs, len);
+	aimbs_put16(hdrbs, encoding);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
 	
 	/* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */
-	aimbs_put16(&hdrbs, 0x0000);
-	
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_putraw(&hdrbs, sess->sn, strlen(sess->sn));
-	
-	aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
-	
-	aimbs_put8(&hdrbs, 0x00);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
-	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));
+
+	aim_bstream_setpos(hdrbs, 52); /* bleeehh */
+
+	aimbs_put8(hdrbs, 0x00);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put8(hdrbs, 0x00);
 	
 	/* end of hdr2 */
 	
@@ -279,7 +286,7 @@
 	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
 	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
 #endif
-	aimbs_putraw(&fr->data, msg, len);
+	aimbs_putraw(hdrbs, msg, len);
 	
 	aim_tx_enqueue(sess, fr);
 	
@@ -391,8 +398,7 @@
 	if ((listenfd = listenestablish(port)) == -1)
 		return NULL;
 
-	aim_request_sendfile(sess, destsn, oft_basename(filename),
-			numfiles, totsize, localip, port, ck);
+	aim_request_sendfile(sess, destsn, filename, numfiles, totsize, localip, port, ck);
 
 	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
 	memcpy(cookie->cookie, ck, 8);
@@ -432,41 +438,41 @@
 /**
  * unsigned int aim_oft_listener_clean - close up old listeners
  * @sess: session to clean up in
- * @age: maximum age in seconds 
+ * @age: maximum age in seconds
  *
  * returns number closed, -1 on error.
  */
-faim_export unsigned int aim_oft_listener_clean(aim_session_t *sess,
-		time_t age) 
-{ 
-  aim_conn_t *cur;
-  time_t now;
-  unsigned int hit = 0;
-  
-  if (!sess)
-    return -1;
-  now = time(NULL);
-  for(cur = sess->connlist;cur; cur = cur->next)
-    if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { 
-      if (cur->lastactivity < (now - age) ) { 
-	aim_conn_close(cur);
-	hit++;
-      }
-    } 
-  return hit;
-} 
-#endif 
+faim_export unsigned int aim_oft_listener_clean(aim_session_t *sess, time_t age)
+{
+	aim_conn_t *cur;
+	time_t now;
+	unsigned int hit = 0;
+
+	if (!sess)
+		return -1;
+	now = time(NULL);
+
+	for(cur = sess->connlist;cur; cur = cur->next)
+		if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+			if (cur->lastactivity < (now - age) ) {
+				aim_conn_close(cur);
+				hit++;
+			}
+		}
+	return hit;
+}
+#endif
 
 faim_export const char *aim_directim_getsn(aim_conn_t *conn)
 {
 	struct aim_directim_intdata *intdata;
 
 	if (!conn)
-	       return NULL;
+		return NULL;
 
 	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
 			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
-	       return NULL;
+		return NULL;
 
 	if (!conn->internal)
 		return NULL;
@@ -525,7 +531,7 @@
 /**
  * aim_directim_getconn - find a directim conn for buddy name
  * @sess: your session,
- * @name: the name to get,  
+ * @name: the name to get,
  *
  * returns conn for directim with name, %NULL if none found. 
  *
@@ -560,7 +566,7 @@
  * @cookie: the cookie used
  * @ip: the ip to connect to
  * @port: the port to use
- * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)  
+ * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)
  *
  * @listingfiles: number of files to share
  * @listingtotsize: total size of shared files
@@ -571,214 +577,209 @@
  *
  * XXX this should take a struct.
  */
-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 port,
-						  fu16_t 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 port, fu16_t rendid, ...)
 {
-  aim_frame_t *newpacket;
-  aim_conn_t *newconn;
-  struct aim_filetransfer_priv *priv;
-  int i;
-  char addr[21];
+	aim_frame_t *newpacket;
+	aim_conn_t *newconn;
+	struct aim_filetransfer_priv *priv;
+	int i;
+	char addr[21];
 
-  if (!sess || !conn || !sn || !cookie || !ip) {
-    return NULL;
-  }
+	if (!sess || !conn || !sn || !cookie || !ip) {
+		return NULL;
+	}
+
+	/* OSCAR CAP accept packet */
 
-  /* OSCAR CAP accept packet */
-  
+	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
+		return NULL;
+	}
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
-	  return NULL;
-  }
-  
-  aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next);
+	aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next);
+
+	for (i = 0; i < 8; i++)
+		aimbs_put8(&newpacket->data, cookie[i]);
 
-  for (i = 0; i < 8; i++)
-    aimbs_put8(&newpacket->data, cookie[i]);
+	aimbs_put16(&newpacket->data, 0x0002);
+	aimbs_put8(&newpacket->data, strlen(sn));
+	aimbs_putraw(&newpacket->data, sn, strlen(sn));
+	aimbs_put16(&newpacket->data, 0x0005);
+	aimbs_put16(&newpacket->data, 0x001a);
+	aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_ACCEPT);
 
-  aimbs_put16(&newpacket->data, 0x0002);
-  aimbs_put8(&newpacket->data, strlen(sn));
-  aimbs_putraw(&newpacket->data, sn, strlen(sn));
-  aimbs_put16(&newpacket->data, 0x0005);
-  aimbs_put16(&newpacket->data, 0x001a);
-  aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_ACCEPT);
+	for (i = 0; i < 8; i++) /* yes, again... */
+		aimbs_put8(&newpacket->data, cookie[i]);
 
-  for (i = 0; i < 8; i++) /* yes, again... */
-    aimbs_put8(&newpacket->data, cookie[i]);
+	aim_putcap(&newpacket->data, rendid);
+	aim_tx_enqueue(sess, newpacket);
 
-  aim_putcap(&newpacket->data, rendid);
-  aim_tx_enqueue(sess, newpacket);
+	snprintf(addr, sizeof(addr), "%s:%d", ip, port);
+	newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr);
 
+	if (newconn->status & AIM_CONN_STATUS_CONNERR) {
+		return NULL;
+	}
 
-  snprintf(addr, sizeof(addr), "%s:%d", ip, port);
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr);
- 
-  if (newconn->status & AIM_CONN_STATUS_CONNERR) {
-	  return NULL;
-  }
+	if (!newconn || (newconn->fd == -1)) {
+		perror("aim_newconn");
+	faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
+		return newconn;
+	}
 
-  if (!newconn || (newconn->fd == -1)) {
-    perror("aim_newconn");
-    faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
-    return newconn;
-  }
-
-  priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
+	priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
 
-  memcpy(priv->cookie, cookie, 8);
-  priv->state = 0;
-  strncpy(priv->sn, sn, MAXSNLEN);
-  strncpy(priv->ip, ip, sizeof(priv->ip));
-  newconn->internal = (void *)priv;
+	memcpy(priv->cookie, cookie, 8);
+	priv->state = 0;
+	strncpy(priv->sn, sn, MAXSNLEN);
+	strncpy(priv->ip, ip, sizeof(priv->ip));
+	newconn->internal = (void *)priv;
 
-  faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+	faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+
+	if (rendid == AIM_CAPS_GETFILE) {
+		return NULL; /* This should never happen for now. -- wtm */
 
-  if (rendid == AIM_CAPS_GETFILE) {
-    return NULL; /* This should never happen for now. -- wtm */
 #if 0
-    struct aim_fileheader_t *fh;
-    aim_frame_t *newoft;
-    aim_msgcookie_t *cachedcook;
-    /* XXX take the following parameters	  fu16_t listingfiles, 
-						  fu16_t listingtotsize, 
-						  fu16_t listingsize, 
-						  fu32_t listingchecksum,  */
+	struct aim_fileheader_t *fh;
+	aim_frame_t *newoft;
+	aim_msgcookie_t *cachedcook;
+	/* XXX take the following parameters	fu16_t listingfiles, 
+						fu16_t listingtotsize, 
+						fu16_t listingsize, 
+						fu32_t listingchecksum, */
 
-    newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+
+	faimdprintf(sess, 2, "faim: getfile request accept\n");
 
-      faimdprintf(sess, 2, "faim: getfile request accept\n");
-
-      if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { 
-	faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-	/* XXX: conn leak here */
-	return NULL;
-      } 
+	if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { 
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+		/* XXX: conn leak here */
+		return NULL;
+	} 
 
-      memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-      newoft->hdr.oft.hdr2len = 0x100 - 8;
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
 
-      if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) {
-	/* XXX: conn leak here */
-	perror("calloc");
-	return NULL;
-      }
+	if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) {
+		/* XXX: conn leak here */
+		perror("calloc");
+		return NULL;
+	}
 
-      fh->encrypt = 0x0000;
-      fh->compress = 0x0000;
-      fh->totfiles = listingfiles;
-      fh->filesleft = listingfiles;      /* is this right -- total parts and parts left?*/
-      fh->totparts = 0x0001;
-      fh->partsleft = 0x0001;
-      fh->totsize = listingtotsize;
-      fh->size = listingsize;      /* ls -l listing.txt */
-      fh->modtime = (int)time(NULL); /* we'll go with current time for now */
-      fh->checksum = listingchecksum;
-      fh->rfcsum = 0x00000000;
-      fh->rfsize = 0x00000000;
-      fh->cretime = 0x00000000;
-      fh->rfcsum = 0x00000000;
-      fh->nrecvd = 0x00000000;
-      fh->recvcsum = 0x00000000;
-      memset(fh->idstring, 0, sizeof(fh->idstring));
-      strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
-      fh->flags = 0x02;
-      fh->lnameoffset = 0x1a;
-      fh->lsizeoffset = 0x10;
-      memset(fh->dummy, 0, sizeof(fh->dummy));
-      memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+	fh->encrypt = 0x0000;
+	fh->compress = 0x0000;
+	fh->totfiles = listingfiles;
+	fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/
+	fh->totparts = 0x0001;
+	fh->partsleft = 0x0001;
+	fh->totsize = listingtotsize;
+	fh->size = listingsize; /* ls -l listing.txt */
+	fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+	fh->checksum = listingchecksum;
+	fh->rfcsum = 0x00000000;
+	fh->rfsize = 0x00000000;
+	fh->cretime = 0x00000000;
+	fh->rfcsum = 0x00000000;
+	fh->nrecvd = 0x00000000;
+	fh->recvcsum = 0x00000000;
+	memset(fh->idstring, 0, sizeof(fh->idstring));
+	strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+	fh->flags = 0x02;
+	fh->lnameoffset = 0x1a;
+	fh->lsizeoffset = 0x10;
+	memset(fh->dummy, 0, sizeof(fh->dummy));
+	memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
 
-      /* we need to figure out these encodings for filenames */
-      fh->nencode = 0x0000;
-      fh->nlanguage = 0x0000;
-      memset(fh->name, 0, sizeof(fh->name));
-      strncpy(fh->name, "listing.txt", sizeof(fh->name));
+	/* we need to figure out these encodings for filenames */
+	fh->nencode = 0x0000;
+	fh->nlanguage = 0x0000;
+	memset(fh->name, 0, sizeof(fh->name));
+	strncpy(fh->name, "listing.txt", sizeof(fh->name));
 
-      if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
-	aim_frame_destroy(newoft);
-	/* XXX: conn leak */
-	perror("calloc (1)");
-	return NULL;
-      } 
+	if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		/* XXX: conn leak */
+		perror("calloc (1)");
+		return NULL;
+	}
 
-      memcpy(fh->bcookie, cookie, 8);
+	memcpy(fh->bcookie, cookie, 8);
 
-      if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
-	faimdprintf(sess, 1, "eek, bh fail!\n");
+	if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
+		faimdprintf(sess, 1, "eek, bh fail!\n");
+
+	aim_tx_enqueue(sess, newoft);
 
-      aim_tx_enqueue(sess, newoft);
-   
-      if (!(cachedcook = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)))) { 
-	faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
-	/* XXX: more cleanup, conn leak */
-	perror("calloc (2)");
-	return NULL;
-      }
+	if (!(cachedcook = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)))) { 
+		faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
+		/* XXX: more cleanup, conn leak */
+		perror("calloc (2)");
+		return NULL;
+	}
+
+	memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t));
+	memcpy(cachedcook->cookie, cookie, 8);
 
-      memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t));
-      memcpy(cachedcook->cookie, cookie, 8);
+	cachedcook->type = AIM_COOKIETYPE_OFTGET;
+	/* XXX doesn't priv need to be copied so we don't
+	 * double free? -- wtm
+	 */
+	cachedcook->data = (void *)priv;
 
-      cachedcook->type = AIM_COOKIETYPE_OFTGET;
-      /* XXX doesn't priv need to be copied so we don't
-       * double free? -- wtm
-       */
-      cachedcook->data = (void *)priv;
+	if (aim_cachecookie(sess, cachedcook) == -1)
+		faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
 
-      if (aim_cachecookie(sess, cachedcook) == -1)
-	faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
-
-      free(fh);     
+	free(fh);
 #endif
 
-  } else if (rendid == AIM_CAPS_SENDFILE)  {
-      newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
-      priv->fh.recvcsum = 0xffff0000;
-  } else {
-    return NULL;
-  }
+	} else if (rendid == AIM_CAPS_SENDFILE) {
+		newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+		priv->fh.recvcsum = 0xffff0000;
+	} else {
+		return NULL;
+	}
 
-  return newconn;
+	return newconn;
 }
 
 /* conn is a BOS connection over which to send the cancel msg */
 faim_export int aim_canceltransfer(aim_session_t *sess, aim_conn_t *conn,
 		const char *cookie, const char *sn, int rendid)
 {
-  aim_frame_t *newpacket;
-  int i;
+	aim_frame_t *newpacket;
+	int i;
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
-	  return 1;
-  }
-  
-  aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next);
+	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
+		return 1;
+	}
 
-  for (i = 0; i < 8; i++)
-    aimbs_put8(&newpacket->data, cookie[i]);
+	aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next);
+
+	for (i = 0; i < 8; i++)
+		aimbs_put8(&newpacket->data, cookie[i]);
 
-  aimbs_put16(&newpacket->data, 0x0002);
-  aimbs_put8(&newpacket->data, strlen(sn));
-  aimbs_putraw(&newpacket->data, sn, strlen(sn));
-  aimbs_put16(&newpacket->data, 0x0005);
-  aimbs_put16(&newpacket->data, 0x001a);
-  aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_CANCEL);
+	aimbs_put16(&newpacket->data, 0x0002);
+	aimbs_put8(&newpacket->data, strlen(sn));
+	aimbs_putraw(&newpacket->data, sn, strlen(sn));
+	aimbs_put16(&newpacket->data, 0x0005);
+	aimbs_put16(&newpacket->data, 0x001a);
+	aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_CANCEL);
 
-  for (i = 0; i < 8; i++) 
-    aimbs_put8(&newpacket->data, cookie[i]);
- 
-  aim_putcap(&newpacket->data, rendid);
-  aim_tx_enqueue(sess, newpacket);
+	for (i = 0; i < 8; i++)
+		aimbs_put8(&newpacket->data, cookie[i]);
 
-  return 0;
+	aim_putcap(&newpacket->data, rendid);
+	aim_tx_enqueue(sess, newpacket);
+
+	return 0;
 }
 
 /**
  * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE*
- *  @file is an opened listing file
+ * @file is an opened listing file
  * 
  * returns a pointer to the filled-in fileheader_t
  *
@@ -790,115 +791,115 @@
 {
 	return NULL;
 #if 0
-  struct aim_fileheader_t *fh;
-  u_long totsize = 0, size = 0, checksum = 0xffff0000;
-  short totfiles = 0;
-  char *linebuf, sizebuf[9];
-  
-  int linelength = 1024;
+	struct aim_fileheader_t *fh;
+	u_long totsize = 0, size = 0, checksum = 0xffff0000;
+	short totfiles = 0;
+	char *linebuf, sizebuf[9];
+	int linelength = 1024;
 
-  /* XXX: if we have a line longer than 1024chars, God help us. */
-  if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
-    faimdprintf(sess, 2, "linebuf calloc failed\n");
-    return NULL;
-  }  
+	/* XXX: if we have a line longer than 1024chars, God help us. */
+	if ((linebuf = (char *)calloc(1, linelength)) == NULL ) {
+		faimdprintf(sess, 2, "linebuf calloc failed\n");
+		return NULL;
+	}
 
-  if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
-    perror("getlisting END1 fseek:");
-    faimdprintf(sess, 2, "getlising fseek END1 error\n");
-  }
+	if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
+		perror("getlisting END1 fseek:");
+		faimdprintf(sess, 2, "getlising fseek END1 error\n");
+	}
 
-  if ((size = ftell(file)) == -1) {
-    perror("getlisting END1 getpos:");
-    faimdprintf(sess, 2, "getlising getpos END1 error\n");
-  }
+	if ((size = ftell(file)) == -1) {
+		perror("getlisting END1 getpos:");
+		faimdprintf(sess, 2, "getlising getpos END1 error\n");
+	}
 
-  if (fseek(file, 0, SEEK_SET) != 0) {
-    perror("getlesting fseek(SET):");
-    faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n");
-  }
+	if (fseek(file, 0, SEEK_SET) != 0) {
+		perror("getlesting fseek(SET):");
+		faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n");
+	}
+
+	memset(linebuf, 0, linelength);
 
-  memset(linebuf, 0, linelength);
-
-  size = 0;
+	size = 0;
 
-  while(fgets(linebuf,  linelength, file)) {
-    totfiles++;
-    memset(sizebuf, 0, 9);
+	while(fgets(linebuf, linelength, file)) {
+		totfiles++;
+		memset(sizebuf, 0, 9);
+
+		size += strlen(linebuf);
 
-    size += strlen(linebuf);
-    
-    if (strlen(linebuf) < 23) {
-      faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf);
-      continue;
-    }
-    if (linebuf[strlen(linebuf)-1] != '\n') {
-      faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
-    }
+		if (strlen(linebuf) < 23) {
+			faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf);
+			continue;
+		}
 
-    memcpy(sizebuf, linebuf+17, 8);
+		if (linebuf[strlen(linebuf)-1] != '\n') {
+			faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
+		}
 
-    totsize += strtol(sizebuf, NULL, 10);
-    memset(linebuf, 0, linelength);
-  }   
+		memcpy(sizebuf, linebuf+17, 8);
+
+		totsize += strtol(sizebuf, NULL, 10);
+		memset(linebuf, 0, linelength);
+	}
 
-  if (fseek(file, 0, SEEK_SET) == -1) {
-    perror("getlisting END2 fseek:");
-    faimdprintf(sess, 2, "getlising fseek END2 error\n");
-  }  
+	if (fseek(file, 0, SEEK_SET) == -1) {
+		perror("getlisting END2 fseek:");
+		faimdprintf(sess, 2, "getlising fseek END2 error\n");
+	}
 
-  free(linebuf);
+	free(linebuf);
 
-  /* we're going to ignore checksumming the data for now -- that
-   * requires walking the whole listing.txt. it should probably be
-   * done at register time and cached, but, eh. */  
+	/* we're going to ignore checksumming the data for now -- that
+	 * requires walking the whole listing.txt. it should probably be
+	 * done at register time and cached, but, eh. */
 
-  if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
-    return NULL;
+	if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
+		return NULL;
 
-  fh->encrypt     = 0x0000;
-  fh->compress    = 0x0000; 
-  fh->totfiles    = totfiles;
-  fh->filesleft   = totfiles; /* is this right ?*/
-  fh->totparts    = 0x0001;
-  fh->partsleft   = 0x0001;
-  fh->totsize     = totsize;
-  fh->size        = size; /* ls -l listing.txt */
-  fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
-  fh->checksum    = checksum; /* XXX: checksum ! */
-  fh->rfcsum      = 0x00000000;
-  fh->rfsize      = 0x00000000;
-  fh->cretime     = 0x00000000;
-  fh->rfcsum      = 0x00000000;
-  fh->nrecvd      = 0x00000000;
-  fh->recvcsum    = 0x00000000;
+	fh->encrypt = 0x0000;
+	fh->compress = 0x0000;
+	fh->totfiles = totfiles;
+	fh->filesleft = totfiles; /* is this right? */
+	fh->totparts = 0x0001;
+	fh->partsleft = 0x0001;
+	fh->totsize = totsize;
+	fh->size = size; /* ls -l listing.txt */
+	fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+	fh->checksum = checksum; /* XXX: checksum ! */
+	fh->rfcsum = 0x00000000;
+	fh->rfsize = 0x00000000;
+	fh->cretime = 0x00000000;
+	fh->rfcsum = 0x00000000;
+	fh->nrecvd = 0x00000000;
+	fh->recvcsum = 0x00000000;
 
-  /*  memset(fh->idstring, 0, sizeof(fh->idstring)); */
-  memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
-  memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
+	/* memset(fh->idstring, 0, sizeof(fh->idstring)); */
+	memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+	memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
 
-  fh->flags       = 0x02;
-  fh->lnameoffset = 0x1a;
-  fh->lsizeoffset = 0x10;
+	fh->flags = 0x02;
+	fh->lnameoffset = 0x1a;
+	fh->lsizeoffset = 0x10;
 
-  /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
-  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+	/* memset(fh->dummy, 0, sizeof(fh->dummy)); */
+	memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
 
-  fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
-  fh->nlanguage   = 0x0000;
+	fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */
+	fh->nlanguage = 0x0000;
 
-  /*  memset(fh->name, 0, sizeof(fh->name)); */
-  strncpy(fh->name, "listing.txt", sizeof(fh->name));
-  memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
+	/* memset(fh->name, 0, sizeof(fh->name)); */
+	strncpy(fh->name, "listing.txt", sizeof(fh->name));
+	memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
 
-  faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
-  return fh;
+	faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
+	return fh;
 #endif
 }
 
 /**
  * aim_listenestablish - create a listening socket on a port.
- * @portnum: the port number to bind to.  
+ * @portnum: the port number to bind to.
  *
  * you need to call accept() when it's connected. returns your fd 
  *
@@ -925,7 +926,7 @@
 	} 
 	ressave = res;
 	do { 
-		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);    
+		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 		if (listenfd < 0)
 			continue;
 		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
@@ -1146,39 +1147,47 @@
 	return;
 }
 
-static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs)
 {
 	aim_frame_t fr;
 	aim_rxcallback_t userfunc;
 	fu32_t payloadlength;
-	fu16_t flags;
+	fu16_t flags, encoding;
 	char *snptr = NULL;
 
 	fr.conn = conn;
 
-	payloadlength = aimutil_get32(hdr+22);
-	flags = aimutil_get16(hdr+32);
-	snptr = (char *)hdr+38;
+	/* XXX ugly */
+	aim_bstream_setpos(bs, 20);
+	payloadlength = aimbs_get32(bs);
+
+	aim_bstream_setpos(bs, 24);
+	encoding = aimbs_get16(bs);
+
+	aim_bstream_setpos(bs, 30);
+	flags = aimbs_get16(bs);
+
+	aim_bstream_setpos(bs, 36);
+	/* XXX -create an aimbs_getnullstr function? */
+	snptr = aimbs_getstr(bs, MAXSNLEN);
 
 	faimdprintf(sess, 2, "faim: OFT frame: handlehdr_directim: %04x / %04x / %s\n", payloadlength, flags, snptr);
 
-	if (flags == 0x000e) { 
+	if (flags & 0x0002) {
 		int ret = 0;
 
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
-			ret = userfunc(sess, &fr, snptr, 1);
+		if (flags == 0x000c) {
+			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+				ret = userfunc(sess, &fr, snptr, 1);
+			return ret;
+		}
 
-		return ret;
-	
-	} else if (flags == 0x0002) {
-		int ret = 0;
-		
 		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
 			ret = userfunc(sess, &fr, snptr, 0);
 
 		return ret;
 
-	} else if ((flags == 0x0000) && payloadlength) { 
+	} else if (((flags & 0x000f) == 0x0000) && payloadlength) {
 		char *msg, *msg2;
 		int ret = 0;
 		int recvd = 0;
@@ -1204,7 +1213,7 @@
 		}
 		
 		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
-			ret = userfunc(sess, &fr, snptr, msg, payloadlength);
+			ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding);
 
 		free(msg);
 
@@ -1244,7 +1253,7 @@
 	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);
@@ -1284,7 +1293,7 @@
 	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);
+		faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", AIM_COOKIETYPE_OFTGET, fh->bcookie);
 
 	ft = cook->data;
 
@@ -1453,14 +1462,14 @@
  * it.  We send back a similar header to confirm, then we're ready to
  * start reading the raw data.
  */
-static int handlehdr_sendfile_sending(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+static int handlehdr_sendfile_sending(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs)
 {
 	struct aim_filetransfer_priv *ft;
 	struct aim_fileheader_t *fh;
 	aim_frame_t *newoft;
 	aim_rxcallback_t userfunc;
 
-	fh = aim_oft_getfh(hdr);
+	fh = aim_oft_getfh(bs);
 
 	/* We receive a null cookie for the first file; we must fill
 	 * it in to authenticate ourselves. -- wtm
@@ -1476,22 +1485,24 @@
 		return -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);
+	if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) {
 		return -1;
 	}
-
-	if (!aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2,
-				&(ft->fh))) {
-		return -1;
-	}
+	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
+	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
 
 	aim_tx_enqueue(sess, newoft);
 
+	/*
+	 * Throw away the resource fork, in case we are receiving from a Mac.
+	 */
+	if (ft->fh.rfsize) {
+		char *buf = malloc(ft->fh.rfsize);
+		if (!buf)
+			return -1;
+		aim_recv(conn->fd, buf, ft->fh.rfsize);
+	}
+
 	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEFILEREQ)) == NULL)
 		return 1;
 
@@ -1511,60 +1522,56 @@
 /* 
  * These were originally described by Josh Myer:
  * http://www.geocrawler.com/archives/3/896/2000/9/0/4291064/
+ * XXX this doesn't actualy work yet
  * -- wtm
  */
-static int handlehdr_sendfile_resume(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) {
-  aim_frame_t *newoft;
-  aim_msgcookie_t *cook;
-  struct aim_fileheader_t *fh;
-  struct aim_filetransfer_priv *ft;
-	
-  fh = aim_oft_getfh(hdr);
-  if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
-	  free(fh);
-	  return -1;
-  }
-  ft = (struct aim_filetransfer_priv *)cook->data;
+static int handlehdr_sendfile_resume(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) {
+	aim_frame_t *newoft;
+	aim_msgcookie_t *cook;
+	struct aim_fileheader_t *fh;
+	struct aim_filetransfer_priv *ft;
+
+	fh = aim_oft_getfh(bs);
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
+		free(fh);
+		return -1;
+	}
+	ft = (struct aim_filetransfer_priv *)cook->data;
 
-  ft->fh.nrecvd = fh->nrecvd;
-  ft->fh.recvcsum = fh->recvcsum;
-  strncpy(ft->fh.name, fh->name, sizeof(ft->fh.name));
-  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0106, 0))) {
-    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-    free(fh);
-    return -1;
-  }
-  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-  newoft->hdr.oft.hdr2len = 0x100 - 8;
+	ft->fh.nrecvd = fh->nrecvd;
+	ft->fh.recvcsum = fh->recvcsum;
+	strncpy(ft->fh.name, fh->name, sizeof(ft->fh.name));
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0106, 0))) {
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+		free(fh);
+		return -1;
+	}
 
-  if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-    aim_frame_destroy(newoft);
-    return -1;
-  }
+	if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) {
+		aim_frame_destroy(newoft);
+		free(fh);
+		return -1;
+	}
+	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
+	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
 
-  if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
-    aim_frame_destroy(newoft);
-    free(fh);
-    return -1;
-  }
+	aim_tx_enqueue(sess, newoft);
+	free(fh);
 
-  aim_tx_enqueue(sess, newoft);
-  free(fh);
-
-  return 0;
+	return 0;
 }
 
 /* We are sending a file, and the buddy sent us this header indicating
  * that he or she is ready for the raw data.
  */
-static int handlehdr_sendfile_recv(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) {
+static int handlehdr_sendfile_recv(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) {
 	struct aim_fileheader_t *fh;
 	aim_msgcookie_t *cook;
 	int ret = 1;
 	struct aim_filetransfer_priv *ft;
 	aim_rxcallback_t userfunc;
 	
-	fh = aim_oft_getfh(hdr);
+	fh = aim_oft_getfh(bs);
 	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
 		free(fh);
 		return -1;
@@ -1613,14 +1620,14 @@
 /* We just sent the raw data of a file, and the buddy sent us back this
  * header indicating that the transfer is complete.
  */
-static int handlehdr_sendfile_finish(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
+static int handlehdr_sendfile_finish(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs)
 {
 	struct aim_fileheader_t *fh;
 	aim_msgcookie_t *cook;
 	aim_rxcallback_t userfunc;
 
-	fh = aim_oft_getfh(hdr);
-  
+	fh = aim_oft_getfh(bs);
+
 	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
 		free(fh);
 		return -1;
@@ -1652,107 +1659,67 @@
 	return -1;
 }
 
-/**
- * aim_get_command_rendezvous - OFT equivalent of aim_get_command
- * @sess: session to work on
- * @conn: conn to pull data from 
- *
- * this reads and handles data from conn->fd. currently a little rough
- * around the edges
- */
-faim_internal int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn)
+faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
 {
-	fu8_t hdrbuf1[6];
-	fu8_t *hdr = NULL;
-	int hdrlen, hdrtype;
+	aim_conn_t *conn = fr->conn;
+	aim_bstream_t *bs = &fr->data;
 	int ret = -1;
 
-
 	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
 		/* This should never happen. -- wtm */
 		return getcommand_getfile(sess, conn);
-	}
 
-	memset(hdrbuf1, 0, sizeof(hdrbuf1));
-
-	if (aim_recv(conn->fd, hdrbuf1, 6) < 6) {
-		faimdprintf(sess, 2, "faim: rend: read error (fd: %i)\n", conn->fd);
-		aim_conn_close(conn);
-		return -1;
-	}
-
-	hdrlen = aimutil_get16(hdrbuf1+4);
-	hdrlen -= 6;
-
-	hdr = malloc(hdrlen);
-	if (!hdr) {
-		aim_conn_close(conn);
-		return -1;
-	}
-
-	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;
-	}
-
-	hdrtype = aimutil_get16(hdr);
-
-	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
-		switch(hdrtype) {
+	} else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
+		switch(fr->hdr.rend.type) {
 			case AIM_OFT_PROTO_OFFER:
-				ret = handlehdr_sendfile_sending(sess, conn,
-						hdr);
+				ret = handlehdr_sendfile_sending(sess, conn, bs);
 				break;
 			case AIM_OFT_PROTO_RESUME:
-				ret = handlehdr_sendfile_resume(sess,
-						conn, hdr);
+				ret = handlehdr_sendfile_resume(sess, conn, bs);
 				break;
 			case AIM_OFT_PROTO_RESUMEACCEPT: /* like _ACCEPT */;
 			case AIM_OFT_PROTO_ACCEPT:
-				ret = handlehdr_sendfile_recv(sess, conn, hdr);
+				ret = handlehdr_sendfile_recv(sess, conn, bs);
 				break;
 			case AIM_OFT_PROTO_ACK:
-				ret = handlehdr_sendfile_finish(sess, conn, hdr);
+				ret = handlehdr_sendfile_finish(sess, conn, bs);
 				break;
 			default:
-				faimdprintf(sess, 2, "faim: OFT frame: uknown type %04x\n", hdrtype);
+				faimdprintf(sess, 2, "faim: OFT frame: uknown type %04x\n", fr->hdr.rend.type);
 				ret = -1;
 				break;
 		}
-		free(hdr);
-		if (ret == -1)
-			aim_conn_close(conn);
-		return ret;
-	}
 
-	if (hdrtype == 0x0001)
-		ret = handlehdr_directim(sess, conn, hdr);
-
-	/* This _really_ shouldn't happen. :) -- wtm */
+	} else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
+		if (fr->hdr.rend.type == 0x0001)
+			ret = handlehdr_directim(sess, conn, bs);
+		else
+			faimdprintf(sess, 0, "faim: DIM frame: unknown type %04x\n", fr->hdr.rend.type);
 
-	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;
+	} else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+		/* This _really_ shouldn't happen. :) -- wtm */
+		char *hdr = NULL;
+		int hdrtype = fr->hdr.rend.type;
+		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);
-
 	if (ret == -1)
 		aim_conn_close(conn);
 
@@ -1761,73 +1728,47 @@
 
 /**
  * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
- * @hdr: buffer to extract header from  
+ * @bs: bstream to extract header from
  *
- * returns pointer to new struct on success; %NULL on error.  
+ * returns pointer to new struct on success; %NULL on error.
  *
  */
-static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) 
+static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs)
 {
-  struct aim_fileheader_t *fh;
-  int i, j;
-  if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
-    return NULL;
-  
-  /* [0] and [1] are the type. we can ignore those here. */
-  i = 2;
-  for(j = 0; j < 8; j++, i++)
-    fh->bcookie[j] = hdr[i];
-  fh->encrypt = aimutil_get16(hdr+i);
-  i += 2;
-  fh->compress = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totfiles = aimutil_get16(hdr+i);
-  i += 2;
-  fh->filesleft = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totparts = aimutil_get16(hdr+i);
-  i += 2;
-  fh->partsleft = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totsize = aimutil_get32(hdr+i);
-  i += 4;
-  fh->size = aimutil_get32(hdr+i);
-  i += 4;
-  fh->modtime = aimutil_get32(hdr+i);
-  i += 4;
-  fh->checksum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfrcsum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfsize = aimutil_get32(hdr+i);
-  i += 4;
-  fh->cretime = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfcsum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->nrecvd = aimutil_get32(hdr+i);
-  i += 4;
-  fh->recvcsum = aimutil_get32(hdr+i);
-  i += 4;
-  memcpy(fh->idstring, hdr+i, 32);
-  i += 32;
-  fh->flags = aimutil_get8(hdr+i);
-  i += 1;
-  fh->lnameoffset = aimutil_get8(hdr+i);
-  i += 1;
-  fh->lsizeoffset = aimutil_get8(hdr+i);
-  i += 1;
-  memcpy(fh->dummy, hdr+i, 69);
-  i += 69;
-  memcpy(fh->macfileinfo, hdr+i, 16);
-  i += 16;
-  fh->nencode = aimutil_get16(hdr+i);
-  i += 2;
-  fh->nlanguage = aimutil_get16(hdr+i);
-  i += 2;
-  memcpy(fh->name, hdr+i, 64);
-  i += 64;
-  return fh;
+	struct aim_fileheader_t *fh;
+
+	if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+		return NULL;
+
+	/* The bstream should be positioned after the hdrtype. */
+	aimbs_getrawbuf(bs, fh->bcookie, 8);
+	fh->encrypt = aimbs_get16(bs);
+	fh->compress = aimbs_get16(bs);
+	fh->totfiles = aimbs_get16(bs);
+	fh->filesleft = aimbs_get16(bs);
+	fh->totparts = aimbs_get16(bs);
+	fh->partsleft = aimbs_get16(bs);
+	fh->totsize = aimbs_get32(bs);
+	fh->size = aimbs_get32(bs);
+	fh->modtime = aimbs_get32(bs);
+	fh->checksum = aimbs_get32(bs);
+	fh->rfrcsum = aimbs_get32(bs);
+	fh->rfsize = aimbs_get32(bs);
+	fh->cretime = aimbs_get16(bs);
+	fh->rfcsum = aimbs_get16(bs);
+	fh->nrecvd = aimbs_get16(bs);
+	fh->recvcsum = aimbs_get16(bs);
+	aimbs_getrawbuf(bs, fh->idstring, 32);
+	fh->flags = aimbs_get8(bs);
+	fh->lnameoffset = aimbs_get8(bs);
+	fh->lsizeoffset = aimbs_get8(bs);
+	aimbs_getrawbuf(bs, fh->dummy, 69);
+	aimbs_getrawbuf(bs, fh->macfileinfo, 16);
+	fh->nencode = aimbs_get16(bs);
+	fh->nlanguage = aimbs_get16(bs);
+	aimbs_getrawbuf(bs, fh->name, 64); /* XXX */
+
+	return fh;
 } 
 
 /**
@@ -1844,33 +1785,33 @@
  *
  * Thanks to Graham Booker for providing this improved checksum
  * routine, which is simpler and should be more accurate than Josh
- * Meyer's original code. -- wtm
+ * Myer's original code. -- wtm
  *
  * This algorithim works every time I have tried it.  The other fails
  * sometimes.  So, AOL who thought this up?  It has got to be the weirdest
  * checksum I have ever seen.
  */
 faim_export fu32_t aim_oft_checksum(const unsigned char *buffer, int bufferlen, int prevcheck) {
-    fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
-    int i;
-    unsigned short val;
-    
-    for(i=0;i<bufferlen;i++){
-        oldcheck = check;
-        if(i&1){
-            val = buffer[i];
-        } else {
-            val = buffer[i] << 8;
-        }
-        check -= val;
-        /*  The follownig appears to be necessary....  It happens every once in a while and the checksum doesn't fail. */
-        if(check > oldcheck) {
-            check--;
-        }
-    }
-    check = ((check & 0x0000ffff) + (check >> 16));
-    check = ((check & 0x0000ffff) + (check >> 16));
-    return check << 16;
+	fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
+	int i;
+	unsigned short val;
+
+	for (i=0; i<bufferlen; i++) {
+		oldcheck = check;
+		if (i&1) {
+			val = buffer[i];
+		} else {
+			val = buffer[i] << 8;
+		}
+		check -= val;
+		/* The follownig appears to be necessary.... It happens every once in a while and the checksum doesn't fail. */
+		if (check > oldcheck) {
+			check--;
+		}
+	}
+	check = ((check & 0x0000ffff) + (check >> 16));
+	check = ((check & 0x0000ffff) + (check >> 16));
+	return check << 16;
 }
 
 faim_export fu32_t aim_update_checksum(aim_session_t *sess, aim_conn_t *conn,
@@ -1885,54 +1826,56 @@
 
 /**
  * aim_oft_buildheader - fills a buffer with network-order fh data
- * @dest: buffer to fill -- pre-alloced
- * @fh: fh to get data from  
+ * @bs: bstream to fill -- automatically initialized
+ * @fh: fh to get data from
  *
- * returns length written; -1 on error.
- * DOES NOT DO BOUNDS CHECKING!
+ * returns -1 on error.
  *
  */
-faim_export int aim_oft_buildheader(unsigned char *dest, struct aim_fileheader_t *fh) 
+static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
 { 
-  int i, curbyte;
-  if (!dest || !fh)
-    return -1;
-  curbyte = 0;
-  for(i = 0; i < 8; i++) 
-    curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]);
-  curbyte += aimutil_put16(dest+curbyte, fh->encrypt);
-  curbyte += aimutil_put16(dest+curbyte, fh->compress);
-  curbyte += aimutil_put16(dest+curbyte, fh->totfiles);
-  curbyte += aimutil_put16(dest+curbyte, fh->filesleft);
-  curbyte += aimutil_put16(dest+curbyte, fh->totparts);
-  curbyte += aimutil_put16(dest+curbyte, fh->partsleft);
-  curbyte += aimutil_put32(dest+curbyte, fh->totsize);
-  curbyte += aimutil_put32(dest+curbyte, fh->size);
-  curbyte += aimutil_put32(dest+curbyte, fh->modtime);
-  curbyte += aimutil_put32(dest+curbyte, fh->checksum);
-  curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum);
-  curbyte += aimutil_put32(dest+curbyte, fh->rfsize);
-  curbyte += aimutil_put32(dest+curbyte, fh->cretime);
-  curbyte += aimutil_put32(dest+curbyte, fh->rfcsum);
-  curbyte += aimutil_put32(dest+curbyte, fh->nrecvd);
-  curbyte += aimutil_put32(dest+curbyte, fh->recvcsum);
-  memcpy(dest+curbyte, fh->idstring, 32);
-  curbyte += 32;
-  curbyte += aimutil_put8(dest+curbyte, fh->flags);
-  curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset);
-  curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset);
-  memcpy(dest+curbyte, fh->dummy, 69);
-  curbyte += 69;
-  memcpy(dest+curbyte, fh->macfileinfo, 16);
-  curbyte += 16;
-  curbyte += aimutil_put16(dest+curbyte, fh->nencode);
-  curbyte += aimutil_put16(dest+curbyte, fh->nlanguage);
-  memset(dest+curbyte, 0x00, 64);
-  memcpy(dest+curbyte, fh->name, 64);
+	fu8_t *hdr;
+
+	if (!bs || !fh)
+		return -1;
+
+
+
+
+	if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) {
+		return -1;
+	}
+	aim_bstream_init(bs, hdr, 0x100 - 8);
 
-  /* XXX: Filenames longer than 64B  */
-  curbyte += 64;
-  return curbyte;
+	aimbs_putraw(bs, fh->bcookie, 8);
+	aimbs_put16(bs, fh->encrypt);
+	aimbs_put16(bs, fh->compress);
+	aimbs_put16(bs, fh->totfiles);
+	aimbs_put16(bs, fh->filesleft);
+	aimbs_put16(bs, fh->totparts);
+	aimbs_put16(bs, fh->partsleft);
+	aimbs_put32(bs, fh->totsize);
+	aimbs_put32(bs, fh->size);
+	aimbs_put32(bs, fh->modtime);
+	aimbs_put32(bs, fh->checksum);
+	aimbs_put32(bs, fh->rfrcsum);
+	aimbs_put32(bs, fh->rfsize);
+	aimbs_put32(bs, fh->cretime);
+	aimbs_put32(bs, fh->rfcsum);
+	aimbs_put32(bs, fh->nrecvd);
+	aimbs_put32(bs, fh->recvcsum);
+	aimbs_putraw(bs, fh->idstring, 32);
+	aimbs_put8(bs, fh->flags);
+	aimbs_put8(bs, fh->lnameoffset);
+	aimbs_put8(bs, fh->lsizeoffset);
+	aimbs_putraw(bs, fh->dummy, 69);
+	aimbs_putraw(bs, fh->macfileinfo, 16);
+	aimbs_put16(bs, fh->nencode);
+	aimbs_put16(bs, fh->nlanguage);
+	aimbs_putraw(bs, fh->name, 64);
+
+	/* XXX: Filenames longer than 64B */
+	return 0;
 }
 
 /**
@@ -1947,143 +1890,143 @@
 { 
 	return NULL;
 #if 0
-  struct command_tx_struct *newpacket;
-  struct aim_conn_t *newconn;
-  struct aim_filetransfer_priv *priv;
-  struct aim_msgcookie_t *cookie;
-  int curbyte, i, listenfd;
-  short port = 4443;
-  struct hostent *hptr;
-  struct utsname myname;
-  char cap[16];
-  char d[4];
- 
-  /* Open our socket */
+	struct command_tx_struct *newpacket;
+	struct aim_conn_t *newconn;
+	struct aim_filetransfer_priv *priv;
+	struct aim_msgcookie_t *cookie;
+	int curbyte, i, listenfd;
+	short port = 4443;
+	struct hostent *hptr;
+	struct utsname myname;
+	char cap[16];
+	char d[4];
 
-  if ( (listenfd = aim_listenestablish(port)) == -1)
-    return NULL;
+	/* Open our socket */
+
+	if ( (listenfd = aim_listenestablish(port)) == -1)
+		return NULL;
 
-  /* get our local IP */
+	/* get our local IP */
 
-  if (uname(&myname) < 0)
-    return NULL;
-  if ( (hptr = gethostbyname(myname.nodename)) == NULL)
-    return NULL;
-  memcpy(&d, hptr->h_addr_list[0], 4);
+	if (uname(&myname) < 0)
+		return NULL;
+	if ( (hptr = gethostbyname(myname.nodename)) == NULL)
+		return NULL;
+	memcpy(&d, hptr->h_addr_list[0], 4);
 
-  aim_putcap(cap, 16, AIM_CAPS_GETFILE);
+	aim_putcap(cap, 16, AIM_CAPS_GETFILE);
 
-  /* create the OSCAR packet */
+	/* create the OSCAR packet */
 
-  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42)))
-    return NULL;
-  newpacket->lock = 1;
+	if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42)))
+		return NULL;
+	newpacket->lock = 1;
 
-  /* lock struct */
-  curbyte = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+	/* lock struct */
+	curbyte = 0;
+	curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
 
-  /* XXX: check the cookie before commiting to using it */
+	/* XXX: check the cookie before commiting to using it */
 
-  /* 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) random() % 10));
+	/* 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) random() % 10));
 
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+	curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
 
-  /* grab all the data for cookie caching. */
+	/* grab all the data for cookie caching. */
  
-  if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t))))
-    return NULL;
-  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
-  cookie->type = AIM_COOKIETYPE_OFTGET;
+	if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t))))
+		return NULL;
+	memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+	cookie->type = AIM_COOKIETYPE_OFTGET;
 
-  if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv))))
-    return NULL;
-  memcpy(priv->cookie, cookie, 8);
-  memcpy(priv->sn, destsn, sizeof(priv->sn));
-  memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
-  priv->state = 1;
+	if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv))))
+		return NULL;
+	memcpy(priv->cookie, cookie, 8);
+	memcpy(priv->sn, destsn, sizeof(priv->sn));
+	memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
+	priv->state = 1;
 
-  cookie->data = priv;
+	cookie->data = priv;
 
-  aim_cachecookie(sess, cookie);
+	aim_cachecookie(sess, cookie);
 
-  /* Channel ID */
-  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+	/* Channel ID */
+	curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
 
-  /* 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);
+	/* 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, 0x0042);
+	/* enTLV start */
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042);
 
-  /* Flag data / ICBM Parameters? */
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+	/* 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);
+	/* Cookie */
+	curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
 
-  /* Capability String */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+	/* Capability String */
+	curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
 
-  /* 000a/0002 : 0001 */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+	/* 000a/0002 : 0001 */
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
 
-  /* 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]);
+	/* 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]);
 
-  /* already in network byte order  */
+	/* already in network byte order */
  
-  /* 0005/0002: Port */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, port);
+	/* 0005/0002: Port */
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+	curbyte += aimutil_put16(newpacket->data+curbyte, port);
 
-  /* 000f/0000: ?? */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+	/* 000f/0000: ?? */
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
 
-  /* 2711/000c: ?? */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
-  curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
+	/* 2711/000c: ?? */
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+	curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
+	curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
 
-  for(i = 0; i < 0x000c - 4; i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+	for (i = 0; i < 0x000c - 4; i++)
+		curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
 
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
+	newpacket->commandlen = curbyte;
+	newpacket->lock = 0;
+	aim_tx_enqueue(sess, newpacket);
 
-  /* allocate and set up our connection */
+	/* allocate and set up our connection */
 
-  i = fcntl(listenfd, F_GETFL, 0);
-  fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
+	i = fcntl(listenfd, F_GETFL, 0);
+	fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
+	newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
 
-  if (!newconn){ 
-    perror("aim_newconn");
-    return NULL;
-  }
+	if (!newconn){ 
+		perror("aim_newconn");
+		return NULL;
+	}
 
-  newconn->fd = listenfd;
-  newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
-  newconn->internal = priv;
-  faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+	newconn->internal = priv;
+	faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
 
-  return newconn;
+	return newconn;
 #endif
 }
  
@@ -2100,41 +2043,41 @@
 #if 0
 faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size)
 {
-  aim_frame_t *newoft;
-  struct aim_filetransfer_priv *ft;
-  if (!sess || !conn || !conn->priv || !name)
-    return -1;
+	aim_frame_t *newoft;
+	struct aim_filetransfer_priv *ft;
+	if (!sess || !conn || !conn->priv || !name)
+		return -1;
 
-  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) {
-    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-    return -1;
-  }
-  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-  newoft->hdr.oft.hdr2len = 0x100 - 8;
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) {
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+		return -1;
+	}
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
 
-  ft = (struct aim_filetransfer_priv *)conn->priv;
-  ft->fh.filesleft = 1;
-  ft->fh.totfiles = 1;
-  ft->fh.totparts = 1;
-  ft->fh.partsleft = 1;
-  ft->fh.totsize = size;
-  ft->fh.size = size;
-  ft->fh.checksum = 0;
-  memcpy(ft->fh.name, name, strlen(name));
-  memset(ft->fh.name+strlen(name), 0, 1);
+	ft = (struct aim_filetransfer_priv *)conn->priv;
+	ft->fh.filesleft = 1;
+	ft->fh.totfiles = 1;
+	ft->fh.totparts = 1;
+	ft->fh.partsleft = 1;
+	ft->fh.totsize = size;
+	ft->fh.size = size;
+	ft->fh.checksum = 0;
+	memcpy(ft->fh.name, name, strlen(name));
+	memset(ft->fh.name+strlen(name), 0, 1);
 
-  if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-    aim_frame_destroy(newoft);
-    return -1;
-  }
+	if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	}
 
-  if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
-    aim_frame_destroy(newoft);
-    return -1;
-  }
+	if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	}
 
-  aim_tx_enqueue(sess, newoft);
-  return 0;
+	aim_tx_enqueue(sess, newoft);
+	return 0;
 }
 #endif
 
@@ -2143,111 +2086,104 @@
  */
 faim_export int aim_oft_sendfile_request(aim_session_t *sess, aim_conn_t *conn, const char *filename, int filesdone, int numfiles, int size, int totsize)
 {
-  aim_frame_t *newoft;
-  aim_msgcookie_t *cook;
-  struct aim_filetransfer_priv *ft = (struct aim_filetransfer_priv *)conn->internal;
-  struct aim_fileheader_t *fh;
+	aim_frame_t *newoft;
+	aim_msgcookie_t *cook;
+	struct aim_filetransfer_priv *ft = (struct aim_filetransfer_priv *)conn->internal;
+	struct aim_fileheader_t *fh;
 
-  if (!sess || !conn || !filename)
-    return -1;
+	if (!sess || !conn || !filename)
+		return -1;
 
-  if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
-    return -1;
+	if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
+		return -1;
 
 #ifdef DUMB_OFT_CHECKSUM
-  /* Yes, we are supposed to checksum the whole file before sending, and
-   * yes, it's dumb.  This is the only way to get some clients (such as
-   * Mac AIM v4.5.163) to successfully complete the transfer.  With
-   * the WinAIM clients, we seem to be able to get away with just
-   * setting the checksum to zero.
-   * -- wtm
-   */
-  {
-    int fd = open(filename, O_RDONLY);
-    if (fd >= 0) {
-       int bytes;
-       char buf[1024];
-       fh->checksum = 0xffff0000;
-       while ((bytes = read(fd, buf, 1024)) > 0) {
-         fh->checksum = aim_oft_checksum(buf, bytes, fh->checksum);
-       }
-    }
-    close(fd);
-  }
+	/* Yes, we are supposed to checksum the whole file before sending, and
+	 * yes, it's dumb.  This is the only way to get some clients (such as
+	 * Mac AIM v4.5.163) to successfully complete the transfer.  With
+	 * the WinAIM clients, we seem to be able to get away with just
+	 * setting the checksum to zero.
+	 * -- wtm
+	 */
+	{
+		int fd = open(filename, O_RDONLY);
+		if (fd >= 0) {
+			int bytes;
+			char buf[1024];
+			fh->checksum = 0xffff0000;
+			while ((bytes = aim_recv(fd, buf, 1024)) > 0) {
+				fh->checksum = aim_oft_checksum(buf, bytes, fh->checksum);
+			}
+		}
+		close(fd);
+	}
 #else
-  fh->checksum    = 0x00000000;
+	fh->checksum = 0x00000000;
 #endif
-
-  fh->encrypt     = 0x0000;
-  fh->compress    = 0x0000; 
-  fh->totfiles    = numfiles;
-  fh->filesleft   = numfiles - filesdone;
-  fh->totparts    = 0x0001;
-  fh->partsleft   = 0x0001;
-  fh->totsize     = totsize;
-  fh->size        = size;
-  fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
-  /* fh->checksum set above */
-  fh->rfcsum      = 0x00000000;
-  fh->rfsize      = 0x00000000;
-  fh->cretime     = 0x00000000;
-  fh->rfcsum      = 0x00000000;
-  fh->nrecvd      = 0x00000000; /* always zero initially */
-  fh->recvcsum    = 0x00000000; /* ditto */
-
-  strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
-  fh->flags = 0x02;
-  fh->lnameoffset = 0x1a;
-  fh->lsizeoffset = 0x10;
-  memset(fh->dummy, 0, sizeof(fh->dummy));
-  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+	fh->encrypt = 0x0000;
+	fh->compress = 0x0000;
+	fh->totfiles = numfiles;
+	fh->filesleft = numfiles - filesdone;
+	fh->totparts = 0x0001;
+	fh->partsleft = 0x0001;
+	fh->totsize = totsize; /* set to 0x0002 sending Mac resource forks */
+	fh->size = size;
+	fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+	/* fh->checksum set above */
+	fh->rfcsum = 0x00000000;
+	fh->rfsize = 0x00000000;
+	fh->cretime = 0x00000000;
+	fh->rfcsum = 0x00000000;
+	fh->nrecvd = 0x00000000; /* always zero initially */
+	fh->recvcsum= 0x00000000; /* ditto */
 
-  /* we need to figure out these encodings for filenames */
-  fh->nencode = 0x0000;
-  fh->nlanguage = 0x0000;
-
-  /* Don't "show full pathname to buddy", just because it is
-   * non-portable. -- wtm 
-   */
-  strncpy(fh->name, oft_basename(filename), sizeof(fh->name));
+	strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+	fh->flags = 0x02;
+	fh->lnameoffset = 0x1a;
+	fh->lsizeoffset = 0x10;
+	memset(fh->dummy, 0, sizeof(fh->dummy));
+	memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
 
-  /* XXX we should normally send a null cookie here, and make
-   * the receiver fill it in for authentication -- wtm
-   */
-  memcpy(fh->bcookie, ft->cookie, 8);
+	/* apparently 0 is ASCII, 2 is UCS-2 */
+	/* it is likely that 3 is ISO 8859-1 */
+	fh->nencode = 0x0000;
+	fh->nlanguage = 0x0000;
 
-  if (!(cook = aim_checkcookie(sess, ft->cookie, AIM_COOKIETYPE_OFTSEND))) {
-    return -1;
-  }
+	/* Convert the directory separator to ^A for portability. */
+	strncpy(fh->name, filename, sizeof(fh->name));
+	oft_dirconvert(fh->name);
 
-  /* Update both headers to be safe. */
-  memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-  memcpy(&(((struct aim_filetransfer_priv *)cook->data)->fh), fh,
-		  sizeof(struct aim_fileheader_t));
+	/* XXX we should normally send a null cookie here, and make
+	 * the receiver fill it in for authentication -- wtm
+	 */
+	memcpy(fh->bcookie, ft->cookie, 8);
 
-  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_OFFER, 0))) {
-    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-    free(fh);
-    return -1;
-  }
-  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-  newoft->hdr.oft.hdr2len = 0x100 - 8;
+	if (!(cook = aim_checkcookie(sess, ft->cookie, AIM_COOKIETYPE_OFTSEND))) {
+		return -1;
+	}
+
+	/* Update both headers to be safe. */
+	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+	memcpy(&(((struct aim_filetransfer_priv *)cook->data)->fh), fh, sizeof(struct aim_fileheader_t));
 
-  if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-    aim_frame_destroy(newoft);
-    free(fh);
-    return -1;
-  }
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_OFFER, 0))) {
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+		free(fh);
+		return -1;
+	}
 
-  if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, fh))) {
-    aim_frame_destroy(newoft);
-    free(fh);
-    return -1;
-  }
+	if (aim_oft_buildheader(&newoft->data, fh) == -1) {
+		aim_frame_destroy(newoft);
+		free(fh);
+		return -1;
+	}
 
-  aim_tx_enqueue(sess, newoft);
-  free(fh);
-  return 0;
+	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
+	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
+
+	aim_tx_enqueue(sess, newoft);
+	free(fh);
+	return 0;
 }
  
 /**
@@ -2263,39 +2199,39 @@
 {
 	return -EINVAL;
 #if 0
-  struct command_tx_struct *newoft;
-  struct aim_filetransfer_priv *ft;
+	struct command_tx_struct *newoft;
+	struct aim_filetransfer_priv *ft;
 
-  if (!sess || !conn || !conn->priv)
-    return -1;
+	if (!sess || !conn || !conn->priv)
+		return -1;
 
-  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
-    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-    return -1;
-  } 
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+	return -1;
+	} 
 
-  newoft->lock = 1;
+	newoft->lock = 1;
 
-  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-  newoft->hdr.oft.hdr2len = 0x100-8;
+	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 (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
+		newoft->lock = 0;
+		aim_frame_destroy(newoft);
+		return -1;
+	}
 
- ft = (struct aim_filetransfer_priv *)conn->priv;
+	ft = (struct aim_filetransfer_priv *)conn->priv;
 
- if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
-   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);
- return 0;
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+	return 0;
 #endif
 }
  
@@ -2309,43 +2245,38 @@
  */
 faim_export int aim_oft_end(aim_session_t *sess, aim_conn_t *conn)
 {
-  aim_frame_t *newoft;
-  struct aim_filetransfer_priv *ft;
-  
-  if (!sess || !conn || !conn->internal)
-    return -1;
+	aim_frame_t *newoft;
+	struct aim_filetransfer_priv *ft;
+
+	if (!sess || !conn || !conn->internal)
+		return -1;
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_ACK, 0))) {
+		faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+		return -1;
+	}
 
-  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_ACK, 0))) {
-    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-    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))) {
-    aim_frame_destroy(newoft);
-    return -1;
-  }
-  
-  ft = (struct aim_filetransfer_priv *)conn->internal;
-  ft->state = 4; /* no longer wanting data */
-  ft->fh.flags = 0x21;
-  
-  if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
-    aim_frame_destroy(newoft);
-    return -1;
-  }
-  
-  aim_tx_enqueue(sess, newoft);
-  
-  return 0;
+	ft = (struct aim_filetransfer_priv *)conn->internal;
+	ft->state = 4; /* no longer wanting data */
+	ft->fh.flags = 0x21;
+
+	if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) {
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
+	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
+
+	aim_tx_enqueue(sess, newoft);
+
+	return 0;
 }
 
-/* Portability.  Yuck. */
-static const char *oft_basename(const char *name) {
-	const char *r = strrchr(name, G_DIR_SEPARATOR);
-	r = r ? r + 1 : name;
-	return r;
+/*
+ * Convert the directory separator to ^A, which seems to be AOL's attempt at portability.
+ */
+static void oft_dirconvert(char *name) {
+	char *c = name;
+	while ((c = strchr(c, G_DIR_SEPARATOR)))
+		*c = 0x01;
 }
-
--- a/src/protocols/oscar/icq.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/icq.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- * Encapsulated ICQ.
+ * Family 0x0015 - Encapsulated ICQ.
  *
  */
 
@@ -262,5 +262,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/im.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/im.c	Wed Nov 13 07:01:37 2002 +0000
@@ -8,8 +8,10 @@
  *  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 4 is used for various ICQ messages.  
- *  Examples are normal messages, URLs, and old-style authorization.
+ *  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,
@@ -88,6 +90,76 @@
 	return AIM_CLIENTTYPE_UNKNOWN;
 }
 
+/*
+ * Subtype 0x0002
+ *
+ * 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 int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!params)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
+
+	/* This is read-only (see Parameter Reply). Must be set to zero here. */
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* These are all read-write */
+	aimbs_put32(&fr->data, params->flags); 
+	aimbs_put16(&fr->data, params->maxmsglen);
+	aimbs_put16(&fr->data, params->maxsenderwarn); 
+	aimbs_put16(&fr->data, params->maxrecverwarn); 
+	aimbs_put32(&fr->data, params->minmsginterval);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x0004 - Request ICBM parameter information. */
+faim_export int aim_reqicbmparams(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
+}
+
+/* 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)
+{
+	struct aim_icbmparameters params;
+	aim_rxcallback_t userfunc;
+
+	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;
+}
+
 /* This should be endian-safe now... but who knows... */
 faim_export fu16_t aim_iconsum(const fu8_t *buf, int buflen)
 {
@@ -105,7 +177,7 @@
 }
 
 /*
- * Send an ICBM (instant message).  
+ * Subtype 0x0006 - Send an ICBM (instant message).  
  *
  *
  * Possible flags:
@@ -373,6 +445,8 @@
 }
 
 /*
+ * Subtype 0x0006
+ *
  * This is also performance sensitive. (If you can believe it...)
  *
  */
@@ -456,6 +530,8 @@
 }
 
 /*
+ * Subtype 0x0006
+ *
  * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
  * send it to clients advertising the RTF capability.  In fact, if you send
  * it to a client that doesn't support that capability, the server will gladly
@@ -571,6 +647,7 @@
 	return 0;
 }
 
+/* Subtype 0x0006 */
 faim_internal int aim_request_directim(aim_session_t *sess, const char *destsn, fu8_t *ip, fu16_t port, fu8_t *ckret)
 {
 	aim_conn_t *conn;
@@ -647,6 +724,7 @@
 	return 0;
 }
 
+/* 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)
 {
 	aim_conn_t *conn;
@@ -753,7 +831,7 @@
 }
 
 /**
- * Request the status message of the given ICQ user.
+ * Subtype 0x0006 - Request the status message of the given ICQ user.
  *
  * @param sess The oscar session.
  * @param sn The UIN of the user of whom you wish to request info.
@@ -817,7 +895,7 @@
 		aimbs_put16(&fr->data, 0x0036);
 		{ /* V */
 			aimbs_putle16(&fr->data, 0x001b); /* L */
-			aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */
+			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 */
@@ -863,6 +941,8 @@
 }
 
 /**
+ * Subtype 0x0006
+ *
  * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."  
  * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack, 
  * mostly because it's in network byte order.  Figuring this stuff out sometimes takes a while, 
@@ -1554,14 +1634,22 @@
 	args->destructor = (void *)incomingim_ch2_sendfile_free;
 
 	if (servdata) {
+		int flen;
+
 		/* subtype is one of AIM_OFT_SUBTYPE_* */
 		args->info.sendfile.subtype = aimbs_get16(servdata);
 		args->info.sendfile.totfiles = aimbs_get16(servdata);
 		args->info.sendfile.totsize = aimbs_get32(servdata);
-		args->info.sendfile.filename = aimbs_getstr(servdata,
-				servdata->len - (2+2+4+4));
-
-		aimbs_get32(servdata); /* 0x00030000 (?) */
+
+		/* XXX - create an aimbs_getnullstr function */
+		/* Use an inelegant way of getting the null-terminated filename, 
+		 * since there's no easy bstream routine. */
+		for (flen = 0; aimbs_get8(servdata); flen++);
+		aim_bstream_advance(servdata, -flen -1);
+		args->info.sendfile.filename = aimbs_getstr(servdata, flen);
+
+		/* There is sometimes more after the null-terminated filename, 
+		 * but I'm unsure of its format. */
 	}
 
 	return;
@@ -1786,6 +1874,8 @@
 }
 
 /*
+ * Subtype 0x0007
+ *
  * It can easily be said that parsing ICBMs is THE single
  * most difficult thing to do in the in AIM protocol.  In
  * fact, I think I just did say that.
@@ -1889,6 +1979,64 @@
 }
 
 /*
+ * Subtype 0x0008 - Send a warning to destsn.
+ * 
+ * Flags:
+ *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
+ *
+ * returns -1 on error (couldn't alloc packet), 0 on success. 
+ *
+ */
+faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, 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)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+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));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Subtype 0x000a */
+static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	fu16_t channel, nummissed, reason;
+	aim_userinfo_t userinfo;
+
+	while (aim_bstream_empty(bs)) {	
+
+		channel = aimbs_get16(bs);
+		aim_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);
+	}
+
+	return ret;
+}
+
+/*
+ * Subtype 0x000b
+ *
  * Possible codes:
  *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
  *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
@@ -1927,102 +2075,10 @@
 }
 
 /*
- * aim_reqicbmparaminfo()
- *
- * Request ICBM parameter information.
- *
- */
-faim_export int aim_reqicbmparams(aim_session_t *sess)
-{
-	aim_conn_t *conn;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
-}
-
-/*
+ * Subtype 0x000b - Receive the response from an ICQ status message request.
  *
- * 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 int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
-{
-	aim_conn_t *conn;
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
-		return -EINVAL;
-
-	if (!params)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
-
-	/* This is read-only (see Parameter Reply). Must be set to zero here. */
-	aimbs_put16(&fr->data, 0x0000);
-
-	/* These are all read-write */
-	aimbs_put32(&fr->data, params->flags); 
-	aimbs_put16(&fr->data, params->maxmsglen);
-	aimbs_put16(&fr->data, params->maxsenderwarn); 
-	aimbs_put16(&fr->data, params->maxrecverwarn); 
-	aimbs_put32(&fr->data, params->minmsginterval);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-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;
-
-	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(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
-{
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	fu16_t channel, nummissed, reason;
-	aim_userinfo_t userinfo;
-
-	while (aim_bstream_empty(bs)) {	
-
-		channel = aimbs_get16(bs);
-		aim_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);
-	}
-
-	return ret;
-}
-
-/*
- * Receive the response from an ICQ status message request.  This contains the 
- * ICQ status message.  Go figure.
+ * This contains the ICQ status message.  Go figure.
+ *
  */
 static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -2038,75 +2094,71 @@
 	sn = aimbs_getstr(bs, snlen);
 	reason = aimbs_get16(bs);
 
-	if (channel == 2) {
-		/* File transfer declined. */
+	if (channel == 0x0002) { /* File transfer declined */
 		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 			ret = userfunc(sess, rx, channel, sn, reason, ck);
-
-		free(sn);
-		free(ck);
-		return ret;
+	} else if (channel == 0x0004) { /* ICQ message */
+		switch (reason) {
+			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
+				fu8_t statusmsgtype, *msg;
+				fu16_t len;
+				fu32_t state;
+
+				len = aimbs_getle16(bs); /* Should be 0x001b */
+				aim_bstream_advance(bs, len); /* Unknown */
+
+				len = aimbs_getle16(bs); /* Should be 0x000e */
+				aim_bstream_advance(bs, len); /* Unknown */
+
+				statusmsgtype = aimbs_getle8(bs);
+				switch (statusmsgtype) {
+					case 0xe8:
+						state = AIM_ICQ_STATE_AWAY;
+						break;
+					case 0xe9:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
+						break;
+					case 0xea:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
+						break;
+					case 0xeb:
+						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
+						break;
+					case 0xec:
+						state = AIM_ICQ_STATE_CHAT;
+						break;
+					default:
+						state = 0;
+						break;
+				}
+
+				aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
+				aimbs_getle16(bs); /* Unknown - 0x0000 */
+				aimbs_getle16(bs); /* Unknown - 0x0000 */
+
+				len = aimbs_getle16(bs);
+				msg = aimbs_getraw(bs, len);
+
+				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+					ret = userfunc(sess, rx, channel, sn, reason, state, msg);
+
+				free(msg);
+			} break;
+
+			default: {
+				if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+					ret = userfunc(sess, rx, channel, sn, reason);
+			} break;
+		} /* end switch */
 	}
 
-	switch (reason) {
-		case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
-			fu8_t statusmsgtype, *msg;
-			fu16_t len;
-			fu32_t state;
-
-			len = aimbs_getle16(bs); /* Should be 0x001b */
-			aim_bstream_advance(bs, len); /* Unknown */
-
-			len = aimbs_getle16(bs); /* Should be 0x000e */
-			aim_bstream_advance(bs, len); /* Unknown */
-
-			statusmsgtype = aimbs_getle8(bs);
-			switch (statusmsgtype) {
-				case 0xe8:
-					state = AIM_ICQ_STATE_AWAY;
-					break;
-				case 0xe9:
-					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
-					break;
-				case 0xea:
-					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
-					break;
-				case 0xeb:
-					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
-					break;
-				case 0xec:
-					state = AIM_ICQ_STATE_CHAT;
-					break;
-				default:
-					state = 0;
-					break;
-			}
-
-			aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
-			aimbs_getle16(bs); /* Unknown - 0x0000 */
-			aimbs_getle16(bs); /* Unknown - 0x0000 */
-
-			len = aimbs_getle16(bs);
-			msg = aimbs_getraw(bs, len);
-
-			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-				ret = userfunc(sess, rx, channel, sn, reason, state, msg);
-
-			free(msg);
-		} break;
-
-		default: {
-			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-				ret = userfunc(sess, rx, channel, sn, reason);
-		} break;
-	} /* end switch */
-
 	free(ck);
 	free(sn);
 
 	return ret;
 }
 
+/* Subtype 0x000c */
 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;
@@ -2130,7 +2182,11 @@
 }
 
 /*
- * Send a mini typing notification (mtn) packet.  This is supported by winaim 5 and up.
+ * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
+ *
+ * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, 
+ * and Gaim 0.60 and newer.
+ *
  */
 faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2)
 {
@@ -2181,7 +2237,11 @@
 }
 
 /*
- * Receive a mini typing notification (mtn) packet.  This is supported by winaim5 and up.
+ * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
+ *
+ * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, 
+ * and Gaim 0.60 and newer.
+ *
  */
 static int mtn_receive(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -2239,7 +2299,6 @@
 	return ret;
 }
 
-
 faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
--- a/src/protocols/oscar/info.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/info.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- * aim_info.c
+ * Family 0x0002 - Information.
  *
  * The functions here are responsible for requesting and parsing information-
  * gathering SNACs.  Or something like that. 
@@ -14,6 +14,72 @@
 	fu16_t infotype;
 };
 
+/*
+ * Subtype 0x0002
+ *
+ * Request Location services rights.
+ *
+ */
+faim_export int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
+{
+        return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
+}
+
+/*
+ * Subtype 0x0004
+ *
+ * Gives BOS your profile.
+ * 
+ */
+faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu32_t caps)
+{
+	static const char defencoding[] = {"text/aolrtf; charset=\"us-ascii\""};
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
+
+	/* 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);
+	}
+
+	/*
+	 * So here's how this works:
+	 *   - You are away when you have a non-zero-length type 4 TLV stored.
+	 *   - You become unaway when you clear the TLV with a zero-length
+	 *       type 4 TLV.
+	 *   - If you do not send the type 4 TLV, your status does not change
+	 *       (that is, if you were away, you'll remain away).
+	 */
+	if (awaymsg) {
+		if (strlen(awaymsg)) {
+			aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), defencoding);
+			aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), awaymsg);
+		} else
+			aim_addtlvtochain_noval(&tl, 0x0004);
+	}
+
+	aim_addtlvtochain_caps(&tl, 0x0005, caps);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	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;
+}
+
+/*
+ * Subtype 0x0005 - Request info of another AIM user.
+ *
+ */
 faim_export int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu16_t infotype)
 {
 	struct aim_priv_inforeq privdata;
@@ -199,10 +265,20 @@
 	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
+	/* from ICQ2002a
+	{AIM_CAPS_ICQUNKNOWN2,
+	 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, */
+
 	{AIM_CAPS_ICQRTF,
 	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, 
 	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
 
+	/* supposed to be ICQRTF?
+	{AIM_CAPS_TRILLUNKNOWN,
+	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, 
+	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, */
+
 	{AIM_CAPS_ICQUNKNOWN,
 	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
 	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
@@ -216,11 +292,8 @@
 	  0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
 
 	{AIM_CAPS_APINFO, 
-         {0xAA, 0x4A, 0x32, 0xB5,
-	         0xF8, 0x84,
-                0x48, 0xc6,
-	         0xA3, 0xD7,
-	         0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}},
+         {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6,
+	  0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}},
 
 	{AIM_CAPS_LAST}
 };
@@ -413,6 +486,7 @@
 			 */
 			outinfo->idletime = aimbs_get16(bs);
 			outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
+
 		} else if (type == 0x0005) {
 			/*
 			 * Type = 0x0005: Member since date. 
@@ -422,6 +496,7 @@
 			 *
 			 * This is sometimes sent instead of type 2 ("account
 			 * creation time"), particularly in the self-info.
+			 * And particularly for ICQ?
 			 */
 			outinfo->membersince = aimbs_get32(bs);
 			outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
@@ -555,6 +630,7 @@
 	if (info->present & AIM_USERINFO_PRESENT_IDLE)
 		aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
 
+/* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
 #if ICQ_OSCAR_SUPPORT
 	if (atoi(info->sn) != 0) {
 		if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
@@ -577,51 +653,8 @@
 	return 0;
 }
 
-faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !info)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
-	
-	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
-	aim_putuserinfo(&fr->data, info);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !sn)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
-	
-	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
 /*
- * Huh? What is this?
+ * Subtype 0x000b - Huh? What is this?
  */
 faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
 {
@@ -646,6 +679,8 @@
 }
 
 /*
+ * Subtype 0x0003
+ *
  * Normally contains:
  *   t(0001)  - short containing max profile length (value = 1024)
  *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
@@ -672,6 +707,7 @@
 	return ret;
 }
 
+/* Subtype 0x0006 */
 static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_userinfo_t userinfo;
@@ -743,6 +779,97 @@
 	return ret;
 }
 
+/* 
+ * Subtype 0x0009 - Set directory profile data.
+ *
+ * This is not the same as aim_bos_setprofile!
+ * privacy: 1 to allow searching, 0 to disallow.
+ *
+ */
+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) 
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	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);
+
+	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);
+
+	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;
+}
+
+/*
+ * Subtype 0x000f
+ * 
+ * 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)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	/* ?? privacy ?? */
+	aim_addtlvtochain16(&tl, 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 (!(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);
+
+	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 
--- a/src/protocols/oscar/invite.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/invite.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- * This isn't really ever used by anyone anymore.
+ * Family 0x0006 - This isn't really ever used by anyone anymore.
  *
  * Once upon a time, there used to be a menu item in AIM clients that
  * said something like "Invite a friend to use AIM..." and then it would
@@ -32,5 +32,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/misc.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/misc.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,13 +1,8 @@
-
 /*
- * aim_misc.c
+ * misc.c
  *
- * TODO: Seperate a lot of this into an aim_bos.c.
- *
- * Other things...
- *
- *   - Idle setting 
- * 
+ * Random stuff.  Basically just a few functions for sending 
+ * simple SNACs, and then the generic error handler.
  *
  */
 
@@ -15,155 +10,6 @@
 #include <aim.h> 
 
 /*
- * aim_bos_setbuddylist(buddylist)
- *
- * This just builds the "set buddy list" command then queues it.
- *
- * buddy_list = "Screen Name One&ScreenNameTwo&";
- *
- * TODO: Clean this up.  
- *
- * XXX: I can't stress the TODO enough.
- *
- */
-faim_export int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	int len = 0;
-	char *localcpy = NULL;
-	char *tmpptr = NULL;
-
-	if (!buddy_list || !(localcpy = strdup(buddy_list))) 
-		return -EINVAL;
-
-	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
-		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
-		len += 1 + strlen(tmpptr);
-		tmpptr = strtok(NULL, "&");
-	}
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
-	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
-
-	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
-
-	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
-
-		faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr));
-
-		aimbs_put8(&fr->data, strlen(tmpptr));
-		aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr));
-		tmpptr = strtok(NULL, "&");
-	}
-
-	aim_tx_enqueue(sess, fr);
-
-	free(localcpy);
-
-	return 0;
-}
-
-/* 
- * aim_bos_setprofile(profile)
- *
- * Gives BOS your profile.
- * 
- */
-faim_export int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, fu32_t caps)
-{
-	static const char defencoding[] = {"text/aolrtf; charset=\"us-ascii\""};
-	aim_frame_t *fr;
-	aim_tlvlist_t *tl = NULL;
-	aim_snacid_t snacid;
-
-	/* 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);
-	}
-
-	/*
-	 * So here's how this works:
-	 *   - You are away when you have a non-zero-length type 4 TLV stored.
-	 *   - You become unaway when you clear the TLV with a zero-length
-	 *       type 4 TLV.
-	 *   - If you do not send the type 4 TLV, your status does not change
-	 *       (that is, if you were away, you'll remain away).
-	 */
-	if (awaymsg) {
-		if (strlen(awaymsg)) {
-			aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), defencoding);
-			aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), awaymsg);
-		} else
-			aim_addtlvtochain_noval(&tl, 0x0004);
-	}
-
-	aim_addtlvtochain_caps(&tl, 0x0005, caps);
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
-		return -ENOMEM;
-
-	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;
-}
-
-/*
- * aim_bos_reqbuddyrights()
- *
- * Request Buddy List rights.
- *
- */
-faim_export int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
-}
-
-/*
- * Send a warning to destsn.
- * 
- * Flags:
- *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
- *
- * returns -1 on error (couldn't alloc packet), 0 on success. 
- *
- */
-faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, 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)))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+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));
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
  * Generic routine for sending commands.
  *
  *
@@ -249,102 +95,6 @@
 }
 
 /*
- * aim_bos_reqlocaterights()
- *
- * Request Location services rights.
- *
- */
-faim_export int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
-{
-	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 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) 
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-
-	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);
-
-	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);
-
-	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;
-}
-
-/* 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)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-	aim_tlvlist_t *tl = NULL;
-
-	/* ?? privacy ?? */
-	aim_addtlvtochain16(&tl, 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 (!(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);
-
-	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
-	aim_writetlvchain(&fr->data, &tl);
-	aim_freetlvchain(&tl);
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/*
  * Should be generic enough to handle the errors for all groups.
  *
  */
@@ -396,5 +146,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/msgcookie.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/msgcookie.c	Wed Nov 13 07:01:37 2002 +0000
@@ -192,5 +192,3 @@
 	default: return AIM_COOKIETYPE_UNKNOWN;
 	}           
 }
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/newsearch.c	Wed Nov 13 07:01:37 2002 +0000
@@ -0,0 +1,244 @@
+/*
+ * Family 0x000f - Newer Search Method
+ *
+ * Used for searching for other AIM users by email address, name, 
+ * location, commmon interests, and a few other similar things.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * Search for an AIM screen name based on their email address.
+ *
+ * @param sess The oscar session.
+ * @param region Should be "us-ascii" unless you know what you're doing.
+ * @param email The email address you want to search for.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_usersearch_email(aim_session_t *sess, const char *region, const char *email)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region || !email)
+		return -EINVAL;
+
+	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
+	aim_addtlvtochain_raw(&tl, 0x001c, strlen(region), region);
+	aim_addtlvtochain16(&tl, 0x000a, 0x0001); /* Type of search */
+	aim_addtlvtochain_raw(&tl, 0x0005, strlen(email), email);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * Search for an AIM screen name based on various info 
+ * about the person.
+ *
+ * @param sess The oscar session.
+ * @param region Should be "us-ascii" unless you know what you're doing.
+ * @param first The first name of the person you want to search for.
+ * @param middle The middle name of the person you want to search for.
+ * @param last The last name of the person you want to search for.
+ * @param maiden The maiden name of the person you want to search for.
+ * @param nick The nick name of the person you want to search for.
+ * @param city The city where the person you want to search for resides.
+ * @param state The state where the person you want to search for resides.
+ * @param country The country where the person you want to search for resides.
+ * @param zip The zip code where the person you want to search for resides.
+ * @param address The street address where the person you want to seach for resides.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_usersearch_name(aim_session_t *sess, const char *region, const char *first, const char *middle, const char *last, const char *maiden, const char *nick, const char *city, const char *state, const char *country, const char *zip, const char *address)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region)
+		return -EINVAL;
+
+	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
+	aim_addtlvtochain_raw(&tl, 0x001c, strlen(region), region);
+	aim_addtlvtochain16(&tl, 0x000a, 0x0000); /* Type of search */
+	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 (country)
+		aim_addtlvtochain_raw(&tl, 0x0006, strlen(country), country);
+	if (state)
+		aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), state);
+	if (city)
+		aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), city);
+	if (nick)
+		aim_addtlvtochain_raw(&tl, 0x000c, strlen(nick), nick);
+	if (zip)
+		aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), zip);
+	if (address)
+		aim_addtlvtochain_raw(&tl, 0x0021, strlen(address), address);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0002 - Submit a User Search Request
+ *
+ * @param sess The oscar session.
+ * @param interest1 An interest you want to search for.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_usersearch_interest(aim_session_t *sess, const char *region, const char *interest)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x000f)) || !region)
+		return -EINVAL;
+
+	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
+	aim_addtlvtochain_raw(&tl, 0x001c, strlen(region), region);
+	aim_addtlvtochain16(&tl, 0x000a, 0x0001); /* Type of search */
+	if (interest)
+		aim_addtlvtochain_raw(&tl, 0x0001, strlen(interest), interest);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+	snacid = aim_cachesnac(sess, 0x000f, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000f, 0x0002, 0x0000, snacid);
+
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
+/**
+ * Subtype 0x0003 - Receive Reply From a User Search
+ *
+ */
+static int parseresults(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	fu16_t tmp, numresults;
+	struct aim_usersearch *results = NULL;
+
+	tmp = aimbs_get16(bs); /* Unknown */
+	tmp = aimbs_get16(bs); /* Unknown */
+	aim_bstream_advance(bs, tmp);
+
+	numresults = aimbs_get16(bs); /* Number of results to follow */
+
+	/* Allocate a linked list, 1 node per result */
+	while (numresults) {
+		struct aim_usersearch *new;
+		aim_tlvlist_t *tl = aim_readtlvchain_num(bs, aimbs_get16(bs));
+		new = (struct aim_usersearch *)malloc(sizeof(struct aim_usersearch));
+		new->first = aim_gettlv_str(tl, 0x0001, 1);
+		new->last = aim_gettlv_str(tl, 0x0002, 1);
+		new->middle = aim_gettlv_str(tl, 0x0003, 1);
+		new->maiden = aim_gettlv_str(tl, 0x0004, 1);
+		new->email = aim_gettlv_str(tl, 0x0005, 1);
+		new->country = aim_gettlv_str(tl, 0x0006, 1);
+		new->state = aim_gettlv_str(tl, 0x0007, 1);
+		new->city = aim_gettlv_str(tl, 0x0008, 1);
+		new->sn = aim_gettlv_str(tl, 0x0009, 1);
+		new->interest = aim_gettlv_str(tl, 0x000b, 1);
+		new->nick = aim_gettlv_str(tl, 0x000c, 1);
+		new->zip = aim_gettlv_str(tl, 0x000d, 1);
+		new->region = aim_gettlv_str(tl, 0x001c, 1);
+		new->address = aim_gettlv_str(tl, 0x0021, 1);
+		new->next = results;
+		results = new;
+		numresults--;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, results);
+
+	/* Now free everything from above */
+	while (results) {
+		struct aim_usersearch *del = results;
+		results = results->next;
+		free(del->first);
+		free(del->last);
+		free(del->middle);
+		free(del->maiden);
+		free(del->email);
+		free(del->country);
+		free(del->state);
+		free(del->city);
+		free(del->sn);
+		free(del->interest);
+		free(del->nick);
+		free(del->zip);
+		free(del->region);
+		free(del->address);
+		free(del);
+	}
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return parseresults(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+faim_internal int newsearch_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000f;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "newsearch", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
--- a/src/protocols/oscar/oscar.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/oscar.c	Wed Nov 13 07:01:37 2002 +0000
@@ -347,10 +347,10 @@
 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 conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
 static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
-static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
 static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
-static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
 static int conninitdone_email    (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_mtn        (aim_session_t *, aim_frame_t *, ...);
@@ -411,8 +411,11 @@
 };
 static int msgerrreasonlen = 25;
 
-static void oscar_file_transfer_disconnect(aim_session_t *sess,
-		aim_conn_t *conn) {
+/*
+ * This is called to clean up whenever a file transfer is no longer in progress, 
+ * whether because it finished sucessfully, it was canceled, or there was an error.
+ */
+static void oscar_file_transfer_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 oscar_file_transfer *oft = find_oft_by_conn(gc,
@@ -1485,6 +1488,11 @@
 				      oscar_callback, dim->conn);
 }
 
+/*
+ * This is called every time we are finished sending a file and the receiving buddy 
+ * has sent back an acknowledgement; we start the next file or tear down the 
+ * connection as appropriate.
+ */
 static int oscar_sendfile_out_done(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
 	va_list ap;
@@ -1514,12 +1522,17 @@
 	int size;
 
 	transfer_get_file_info(oft->xfer, &size, &name);
+	/* AAA convert the name to UCS-2 if necessary, and pass the encoding to the call below */
 	aim_oft_sendfile_request(sess, oft->conn, name, oft->filesdone,
 			oft->totfiles, size, oft->totsize);
 
 	return 0;
 }
 
+/*
+ * This is called when sending a file and a direct connection has been set up with 
+ * the buddy; we can now transmit the appropriate headers describing the transfer.
+ */
 static int oscar_sendfile_accepted(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
@@ -1555,6 +1568,10 @@
 	return 0;
 }
 
+/*
+ * This is called when we requested to send a file to a buddy, but he or she didn't 
+ * respond; we need to clean up.
+ */
 static int oscar_sendfile_timeout(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
 	va_list ap;
@@ -1607,6 +1624,10 @@
 			oscar_callback, oft->conn);
 }
 
+/*
+ * This is called after a chunk of data has been sent out or received; it is used 
+ * to update the checksum.
+ */
 static void oscar_file_transfer_data_chunk(struct gaim_connection *gc,
 		struct file_transfer *xfer, const char *buf, int len)
 {
@@ -1617,6 +1638,7 @@
 		aim_update_checksum(sess, oft->conn, buf, len);
 }
 
+/* Called once at the beginning of an incoming transfer session. */
 static void oscar_file_transfer_in(struct gaim_connection *gc,
 		struct file_transfer *xfer, int offset) {
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
@@ -1628,6 +1650,7 @@
 			oft->port,
 			AIM_CAPS_SENDFILE);
 	if (!oft->conn) {
+		/* XXX implement reverse connections for receiving from behind a firewall */
 		char *buf = g_strdup_printf("Couldn't connect to remote host");
 		do_error_dialog(buf, NULL, GAIM_ERROR);
 		g_free(buf);
@@ -1642,8 +1665,10 @@
 			oscar_callback, oft->conn);
 }
 
-static void oscar_file_transfer_cancel(struct gaim_connection *gc,
-		struct file_transfer *xfer) {
+/*
+ * This is called when the user began a file transfer, but subsequently canceled.
+ */
+static void oscar_file_transfer_cancel(struct gaim_connection *gc, struct file_transfer *xfer) {
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
 
@@ -1912,6 +1937,7 @@
 			return 0;
 		}
 
+		/* Someone wants to send a file (or files) to us */
 		debug_printf("%s (%s) requests to send a file to %s\n",
 				userinfo->sn, args->verifiedip, gc->username);
 
@@ -2012,8 +2038,7 @@
 static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
 	struct gaim_connection *gc = sess->aux_data;
 
-	switch (args->type) 
-		{
+	switch (args->type) {
 		case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
 			gchar *uin, *message;
 			uin = g_strdup_printf("%lu", args->uin);
@@ -2267,55 +2292,54 @@
 	who = va_arg(ap, char *);
 	reason = (fu16_t)va_arg(ap, unsigned int);
 
-	if (chan == 2) {
+	if (chan == 0x0002) { /* File transfer declined */
 		char *cookie = va_arg(ap, char *);
-		va_end(ap);
-
-		return gaim_parse_clientauto_rend(sess, who, reason,
-				cookie);
-	}
-
-	switch(reason) {
-		case 0x0003: { /* Reply from an ICQ status message request */
-			int state = (int)va_arg(ap, fu32_t);
-			char *msg = va_arg(ap, char *);
-			char *status_msg = gaim_icq_status(state);
-			char *dialog_msg, **splitmsg;
-			struct oscar_data *od = gc->proto_data;
-			GSList *l = od->evilhack;
-			gboolean evilhack = FALSE;
-
-			/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
-			splitmsg = g_strsplit(msg, "\r\n", 0);
-
-			/* If who is in od->evilhack, then we're just getting the away message, otherwise this 
-			 * will just get appended to the info box (which is already showing). */
-			while (l) {
-				char *x = l->data;
-				if (!strcmp(x, normalize(who))) {
-					evilhack = TRUE;
-					g_free(x);
-					od->evilhack = g_slist_remove(od->evilhack, x);
-					break;
+		return gaim_parse_clientauto_rend(sess, who, reason, cookie);
+	} else if (chan == 0x0004) { /* ICQ message */
+		switch(reason) {
+			case 0x0003: { /* Reply from an ICQ status message request */
+				int state = (int)va_arg(ap, fu32_t);
+				char *msg = va_arg(ap, char *);
+				char *status_msg = gaim_icq_status(state);
+				char *dialog_msg, **splitmsg;
+				struct oscar_data *od = gc->proto_data;
+				GSList *l = od->evilhack;
+				gboolean evilhack = FALSE;
+
+				/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
+				splitmsg = g_strsplit(msg, "\r\n", 0);
+
+				/* If who is in od->evilhack, then we're just getting the away message, otherwise this 
+				 * will just get appended to the info box (which is already showing). */
+				while (l) {
+					char *x = l->data;
+					if (!strcmp(x, normalize(who))) {
+						evilhack = TRUE;
+						g_free(x);
+						od->evilhack = g_slist_remove(od->evilhack, x);
+						break;
+					}
+					l = l->next;
 				}
-				l = l->next;
-			}
-
-			if (evilhack)
-				dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR>%s<BR>"), who, status_msg, g_strjoinv("<BR>", splitmsg));
-			else
-				dialog_msg = g_strdup_printf(_("<B>Status:</B> %s<BR><HR><BR>%s<BR>"), status_msg, g_strjoinv("<BR>", splitmsg));
-			g_show_info_text(gc, who, 2, dialog_msg, NULL);
-
-			g_free(status_msg);
-			g_free(dialog_msg);
-			g_strfreev(splitmsg);
-		} break;
-
-		default: {
-			debug_printf("Received an unknown client auto-response from %s.  Type 0x%04x\n", who, reason);
-		} break;
-	}
+
+				if (evilhack)
+					dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR>%s<BR>"), who, status_msg, g_strjoinv("<BR>", splitmsg));
+				else
+					dialog_msg = g_strdup_printf(_("<B>Status:</B> %s<BR><HR>%s<BR>"), status_msg, g_strjoinv("<BR>", splitmsg));
+				g_show_info_text(gc, who, 2, dialog_msg, NULL);
+
+				g_free(status_msg);
+				g_free(dialog_msg);
+				g_strfreev(splitmsg);
+			} break;
+
+			default: {
+				debug_printf("Received an unknown client auto-response from %s.  Type 0x%04x\n", who, reason);
+			} break;
+		} /* end of switch */
+
+	} /* end of if */
+
 	va_end(ap);
 
 	return 1;
@@ -2530,7 +2554,7 @@
 
 	if (!od->icq) {
 		g_snprintf(legend, sizeof legend,
-				_("<br><BODY BGCOLOR=WHITE><hr><I>Legend:</I><br><br>"
+				_("<BODY BGCOLOR=WHITE><hr><I>Legend:</I><br><br>"
 				"<IMG SRC=\"free_icon.gif\"> : Normal AIM User<br>"
 				"<IMG SRC=\"aol_icon.gif\"> : AOL User <br>"
 				"<IMG SRC=\"dt_icon.gif\"> : Trial AIM User <br>"
@@ -2563,7 +2587,7 @@
 			"%s"
 			"%s"
 			"%s<BR>\n"
-			"<HR><BR>\n"),
+			"<HR>\n"),
 			info->sn, images(info->flags),
 			info->warnlevel/10,
 			onlinesince ? onlinesince : "",
@@ -2596,7 +2620,7 @@
 			g_show_info_text(gc, info->sn, 0,
 					 header,
 					 (prof && *prof) ? away_subs(prof, gc->username) : NULL,
-					 (prof && *prof) ? "<BR><HR><BR>" : NULL,
+					 (prof && *prof) ? "<BR><HR>" : NULL,
 					 NULL);
 		}
 	} else if (infotype == AIM_GETINFO_CAPABILITIES) {
@@ -3090,6 +3114,9 @@
 
 	aim_clientready(sess, fr->conn);
 
+	/* XXX - Should call aim_bos_setidle with 0x0000 */
+
+	/* XXX - Should only call reqofflinemsgs when using ICQ? */
 	aim_icq_reqofflinemsgs(sess);
 
 	aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
@@ -3198,7 +3225,7 @@
 				g_show_info_text(gc, who, 0, buf, NULL);
 			else {
 				char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
-				g_show_info_text(gc, who, 2, buf, "<B>Status:</B> ", state_msg, "<BR>\n<HR><BR><I>Remote client does not support sending status messages.</I><BR>\n", NULL);
+				g_show_info_text(gc, who, 2, buf, "<B>Status:</B> ", state_msg, "<BR>\n<HR><I>Remote client does not support sending status messages.</I><BR>\n", NULL);
 				free(state_msg);
 			}
 		} else {
@@ -3390,7 +3417,8 @@
 
 	if (dim) {
 		if (dim->connected) {  /* If we're not connected yet, send through server */
-			ret =  aim_send_im_direct(odata->sess, dim->conn, message, len == -1 ? strlen(message) : len);
+			/* AAA - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
+			ret =  aim_send_im_direct(odata->sess, dim->conn, message, len == -1 ? strlen(message) : len, 0);
 			if (ret == 0)
 				return 1;
 			else return ret;
@@ -4185,6 +4213,10 @@
 	return NULL;
 }
 
+/*
+ * This is called after the raw data for a file has been transferred (whether 
+ * we are sending or receiving), but there are other files remaining.
+ */
 void oscar_file_transfer_nextfile(struct gaim_connection *gc,
 		struct file_transfer *xfer) {
 	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
@@ -4206,6 +4238,11 @@
 		aim_oft_end(sess, conn);
 }
 
+/*
+ * This is called after the raw data for a file has been transferred (whether 
+ * we are sending or receiving), and it is the last file in the set, so we 
+ * can tear down the connection.
+ */
 void oscar_file_transfer_done(struct gaim_connection *gc,
 		struct file_transfer *xfer) {
 	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
@@ -4224,6 +4261,10 @@
 	}
 }
 
+/*
+ * This is called when there is raw data ready to be sent or received; all the 
+ * protocol details have been taken care of.
+ */
 static int oscar_file_transfer_do(aim_session_t *sess, aim_frame_t *fr, ...) {
 	struct gaim_connection *gc = sess->aux_data;
 	va_list ap;
@@ -4244,8 +4285,8 @@
 	oft->watcher = 0;
 
 	if (oft->type == OFT_SENDFILE_IN) {
-		err = transfer_in_do(oft->xfer, conn->fd,
-				fh->name, fh->size);
+		/* AAA convert fh->name from UCS-2 to UTF-8 if (fh->nencode == 0x0002) */
+		err = transfer_in_do(oft->xfer, conn->fd, fh->name, fh->size);
 	}
 	else {
 		err = transfer_out_do(oft->xfer, conn->fd, fh->nrecvd);
@@ -4340,17 +4381,19 @@
 static int gaim_directim_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *msg, *sn;
-	int len;
+	int len, encoding;
 	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, fr);
 	sn = va_arg(ap, char *);
 	msg = va_arg(ap, char *);
 	len = va_arg(ap, int);
+	encoding = va_arg(ap, int);
 	va_end(ap);
 
 	debug_printf("Got DirectIM message from %s\n", sn);
 
+	/* AAA - I imagine Paco-Paco will want to do some voodoo with the encoding here */
 	serv_got_im(gc, sn, msg, 0, time(NULL), len);
 
 	return 1;
@@ -4439,14 +4482,14 @@
 					aim_send_im_ch2_geticqmessage(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
 				else {
 					char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
-					char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR><I>Remote client does not support sending status messages.</I><BR>"), who, state_msg);
+					char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><I>Remote client does not support sending status messages.</I><BR>"), who, state_msg);
 					g_show_info_text(gc, who, 2, dialog_msg, NULL);
 					free(state_msg);
 					free(dialog_msg);
 				}
 			else {
 				char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
-				char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR><I>User has no status message.</I><BR>"), who, state_msg);
+				char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><I>User has no status message.</I><BR>"), who, state_msg);
 				g_show_info_text(gc, who, 2, dialog_msg, NULL);
 				free(state_msg);
 				free(dialog_msg);
--- a/src/protocols/oscar/popups.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/popups.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,6 @@
-
 /*
+ * Family 0x0008 - Popups.
+ *
  * Popups are just what it sounds like.  They're a way for the server to
  * open up an informative box on the client's screen.
  */
@@ -61,5 +62,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/rxhandlers.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/rxhandlers.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- * aim_rxhandlers.c
+ * rxhandlers.c
  *
  * This file contains most all of the incoming packet handlers, along
  * with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
@@ -546,9 +546,8 @@
 				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 */
+				aim_rxdispatch_rendezvous(sess, cur);
+				cur->handled = 1;
 			}
 			continue;
 		}
@@ -613,6 +612,3 @@
 
 	return 1;
 }
-
-
-
--- a/src/protocols/oscar/rxqueue.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/rxqueue.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- *  aim_rxqueue.c
+ * rxqueue.c
  *
  * This file contains the management routines for the receive
  * (incoming packet) queue.  The actual packet handlers are in
@@ -63,325 +63,38 @@
 	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;
-}
-
-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);
-}
-
-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 fu8_t aimbs_getle8(aim_bstream_t *bs)
-{
-	
-	if (aim_bstream_empty(bs) < 1)
-		return 0; /* XXX throw an exception */
-	
-	bs->offset++;
-	
-	return aimutil_getle8(bs->data + bs->offset - 1);
-}
-
-faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs)
-{
-	
-	if (aim_bstream_empty(bs) < 2)
-		return 0; /* XXX throw an exception */
-	
-	bs->offset += 2;
-	
-	return aimutil_getle16(bs->data + bs->offset - 2);
-}
-
-faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs)
-{
-	
-	if (aim_bstream_empty(bs) < 4)
-		return 0; /* XXX throw an exception */
-	
-	bs->offset += 4;
-	
-	return aimutil_getle32(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_putle8(aim_bstream_t *bs, fu8_t v)
-{
-
-	if (aim_bstream_empty(bs) < 1)
-		return 0; /* XXX throw an exception */
-
-	bs->offset += aimutil_putle8(bs->data + bs->offset, v);
-
-	return 1;
-}
-
-faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v)
-{
-
-	if (aim_bstream_empty(bs) < 2)
-		return 0; /* XXX throw an exception */
-
-	bs->offset += aimutil_putle16(bs->data + bs->offset, v);
-
-	return 2;
-}
-
-faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v)
-{
-
-	if (aim_bstream_empty(bs) < 4)
-		return 0; /* XXX throw an exception */
-
-	bs->offset += aimutil_putle32(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 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  
+ * aim_frame_destroy - free aim_frame_t
+ * @frame: the frame to free
  *
- * returns -1 on error; 0 on success.  
+ * 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 */
+	free(frame);
 
-	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.
+ * Read a FLAP header from conn into fr, and return the number of bytes in the payload.
  */
-faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
+static faim_shortfunc int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
 {
 	fu8_t flaphdr_raw[6];
 	aim_bstream_t flaphdr;
-	aim_frame_t *newrx;
 	fu16_t payloadlen;
 	
-	if (!sess || !conn)
-		return 0;
-
-	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);
-
-	/*
-	 * 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; 
-	}
-
 	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 
+	 *   2 short -- Sequence number
 	 *   4 short -- Number of data bytes that follow.
 	 */
 	if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
@@ -405,17 +118,78 @@
 		return -1;
 	}	
 
-	/* allocate a new struct */
-	if (!(newrx = (aim_frame_t *)malloc(sizeof(aim_frame_t))))
+	/* we're doing FLAP if we're here */
+	fr->hdrtype = AIM_FRAMETYPE_FLAP;
+
+	fr->hdr.flap.type = aimbs_get8(&flaphdr);
+	fr->hdr.flap.seqnum = aimbs_get16(&flaphdr);
+	payloadlen = aimbs_get16(&flaphdr); /* length of payload */
+
+	return payloadlen;
+}
+
+/*
+ * Read a rendevouz header from conn into fr, and return the number of bytes in the payload.
+ */
+static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
+{
+	fu8_t rendhdr_raw[8];
+	aim_bstream_t rendhdr;
+
+	aim_bstream_init(&rendhdr, rendhdr_raw, sizeof(rendhdr_raw));
+
+	if (aim_bstream_recv(&rendhdr, conn->fd, 8) < 8) {
+		aim_conn_close(conn);
 		return -1;
-	memset(newrx, 0, sizeof(aim_frame_t));
+	}
+
+	aim_bstream_rewind(&rendhdr);
+
+	fr->hdrtype = AIM_FRAMETYPE_OFT; /* a misnomer--rendezvous */
+
+	aimbs_getrawbuf(&rendhdr, fr->hdr.rend.magic, 4);
+	fr->hdr.rend.hdrlen = aimbs_get16(&rendhdr) - 8;
+	fr->hdr.rend.type = aimbs_get16(&rendhdr);
+
+	return fr->hdr.rend.hdrlen;
+}
 
-	/* 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);
+/*
+ * Grab a single command sequence off the socket, and enqueue it in the incoming event queue 
+ * in a separate struct.
+ */
+faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *newrx;
+	fu16_t payloadlen;
+
+	if (!sess || !conn)
+		return -1;
+
+	if (conn->fd == -1)
+		return -1; /* it's an aim_conn_close()'d connection */
+
+	if (conn->fd < 3) /* can happen when people abuse the interface */
+		return -1;
+
+	if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+		return aim_conn_completeconnect(sess, conn);
+
+	if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1)))
+		return -1;
+
+	/*
+	 * Rendezvous (client to client) connections do not speak FLAP, so this 
+	 * function will break on them.
+	 */
+	if (conn->type == AIM_CONN_TYPE_RENDEZVOUS)
+		payloadlen = aim_get_command_rendezvous(sess, conn, newrx);
+	else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+		faimdprintf(sess, 0, "AIM_CONN_TYPE_RENDEZVOUS_OUT on fd %d\n", conn->fd);
+		free(newrx);
+		return -1;
+	} else
+		payloadlen = aim_get_command_flap(sess, conn, newrx);
 
 	newrx->nofree = 0; /* free by default */
 
@@ -507,4 +281,3 @@
 	}	
 	return;
 }
-
--- a/src/protocols/oscar/search.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/search.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,6 +1,5 @@
-
 /*
- * aim_search.c
+ * Family 0x000a - User Search.
  *
  * TODO: Add aim_usersearch_name()
  *
@@ -9,28 +8,11 @@
 #define FAIM_INTERNAL
 #include <aim.h>
 
-faim_export int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
-{
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
-
-	if (!sess || !conn || !address)
-		return -EINVAL;
-
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
-		return -ENOMEM;
-
-	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1);
-	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
-	
-	aimbs_putraw(&fr->data, address, strlen(address)); 
-
-	aim_tx_enqueue(sess, fr);
-
-	return 0;
-}
-
-/* XXX can this be integrated with the rest of the error handling? */
+/*
+ * Subtype 0x0001
+ *
+ * XXX can this be integrated with the rest of the error handling?
+ */
 static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int ret = 0;
@@ -54,6 +36,35 @@
 	return ret;
 }
 
+/*
+ * Subtype 0x0002
+ *
+ */
+faim_export int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !address)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1);
+	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
+	
+	aimbs_putraw(&fr->data, address, strlen(address)); 
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Subtype 0x0003
+ *
+ */
 static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int j = 0, m, ret = 0;
@@ -118,5 +129,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/service.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/service.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- * Group 1.  This is a very special group.  All connections support
+ * Family 0x0001 - This is a very special group.  All connections support
  * this group, as it does some particularly good things (like rate limiting).
  */
 
@@ -9,7 +9,7 @@
 
 #include "md5.h"
 
-/* Client Online (group 1, subtype 2) */
+/* Subtype 0x0002 - Client Online */
 faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
 {
 	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
@@ -48,7 +48,7 @@
 }
 
 /*
- * Host Online (group 1, type 3)
+ * Subtype 0x0003 - Host Online
  * 
  * See comments in conn.c about how the group associations are supposed
  * to work, and how they really work.
@@ -91,13 +91,13 @@
 	return 1; 
 }
 
-/* Service request (group 1, type 4) */
+/* Subtype 0x0004 - Service request */
 faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, fu16_t serviceid)
 {
 	return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
 }
 
-/* Redirect (group 1, type 5) */
+/* Subtype 0x0005 - Redirect */
 static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	struct aim_redirect_data redir;
@@ -147,7 +147,7 @@
 	return ret;
 }
 
-/* Request Rate Information. (group 1, type 6) */
+/* Subtype 0x0006 - Request Rate Information. */
 faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
 {
 	return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
@@ -257,7 +257,7 @@
 	return;
 }
 
-/* Rate Parameters (group 1, type 7) */
+/* Subtype 0x0007 - Rate Parameters */
 static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
@@ -344,7 +344,7 @@
 	return 1;
 }
 
-/* Add Rate Parameter (group 1, type 8) */
+/* Subtype 0x0008 - Add Rate Parameter */
 faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
 {
 	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
@@ -366,7 +366,7 @@
 	return 0;
 }
 
-/* Delete Rate Parameter (group 1, type 9) */
+/* Subtype 0x0009 - Delete Rate Parameter */
 faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
 {
 	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
@@ -388,7 +388,7 @@
 	return 0;
 }
 
-/* Rate Change (group 1, type 0x0a) */
+/* Subtype 0x000a - Rate Change */
 static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -413,7 +413,7 @@
 }
 
 /*
- * How Migrations work.  
+ * How Migrations work.
  *
  * The server sends a Server Pause message, which the client should respond to 
  * with a Server Pause Ack, which contains the families it needs on this 
@@ -425,7 +425,7 @@
  *
  */
 
-/* Service Pause (group 1, type 0x0b) */
+/* Subtype 0x000b - Service Pause */
 static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -437,7 +437,7 @@
 }
 
 /*
- * Service Pause Acknowledgement (group 1, type 0x0c)
+ * Subtype 0x000c - Service Pause Acknowledgement
  *
  * It is rather important that aim_sendpauseack() gets called for the exact
  * same connection that the Server Pause callback was called for, since
@@ -473,7 +473,7 @@
 	return 0;
 }
 
-/* Service Resume (group 1, type 0x0d) */
+/* Subtype 0x000d - Service Resume */
 static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -484,13 +484,13 @@
 	return 0;
 }
 
-/* Request self-info (group 1, type 0x0e) */
+/* Subtype 0x000e - Request self-info */
 faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
 {
 	return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
 }
 
-/* Self User Info (group 1, type 0x0f) */
+/* Subtype 0x000f - Self User Info */
 static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -504,7 +504,7 @@
 	return 0;
 }
 
-/* Evil Notification (group 1, type 0x10) */
+/* Subtype 0x0010 - Evil Notification */
 static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -525,7 +525,7 @@
 }
 
 /*
- * Idle Notification (group 1, type 0x11)
+ * Subtype 0x0011 - Idle Notification
  *
  * Should set your current idle time in seconds.  Note that this should
  * never be called consecutively with a non-zero idle time.  That makes
@@ -539,7 +539,7 @@
 }
 
 /*
- * Service Migrate (group 1, type 0x12)
+ * Subtype 0x0012 - Service Migrate
  *
  * This is the final SNAC sent on the original connection during a migration.
  * It contains the IP and cookie used to connect to the new server, and 
@@ -591,7 +591,7 @@
 	return ret;
 }
 
-/* Message of the Day (group 1, type 0x13) */
+/* Subtype 0x0013 - Message of the Day */
 static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -631,7 +631,7 @@
 }
 
 /* 
- * Set privacy flags (group 1, type 0x14)
+ * Subtype 0x0014 - Set privacy flags
  *
  * Normally 0x03.
  *
@@ -645,10 +645,10 @@
 }
 
 /*
- * No-op (group 1, type 0x16)
+ * Subtype 0x0016 - No-op
  *
  * WinAIM sends these every 4min or so to keep the connection alive.  Its not 
- * real necessary.
+ * really necessary.
  *
  */
 faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn)
@@ -657,7 +657,7 @@
 }
 
 /* 
- * Set client versions (group 1, subtype 0x17) 
+ * Subtype 0x0017 - Set client versions
  *
  * If you've seen the clientonline/clientready SNAC you're probably 
  * wondering what the point of this one is.  And that point seems to be
@@ -704,7 +704,7 @@
 	return 0;
 }
 
-/* Host versions (group 1, subtype 0x18) */
+/* Subtype 0x0018 - Host versions */
 static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int vercount;
@@ -724,7 +724,7 @@
 }
 
 /* 
- * Set Extended Status (group 1, type 0x1e) 
+ * Subtype 0x001e - Set Extended Status
  *
  * Currently only works if using ICQ.
  *
@@ -790,7 +790,7 @@
  * Anyway, neener.  We win again.
  *
  */
-/* Client verification (group 1, subtype 0x1f) */
+/* Subtype 0x001f - Client verification */
 static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
@@ -838,7 +838,7 @@
 }
 #endif
 
-/* Client verification reply (group 1, subtype 0x20) */
+/* Subtype 0x0020 - Client verification reply */
 faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, fu32_t offset, fu32_t len, const fu8_t *buf, fu8_t flag)
 {
 	aim_frame_t *fr;
@@ -969,4 +969,3 @@
 
 	return 0;
 }
-
--- a/src/protocols/oscar/snac.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/snac.c	Wed Nov 13 07:01:37 2002 +0000
@@ -129,7 +129,6 @@
 				break;
 		}
 		free(d->data);
-		free(d);
 	}
 
 	free(snac->data);
--- a/src/protocols/oscar/stats.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/stats.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,3 +1,7 @@
+/*
+ * Family 0x000b - Statistics.
+ *
+ */
 
 #define FAIM_INTERNAL
 #include <aim.h>
--- a/src/protocols/oscar/tlv.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/tlv.c	Wed Nov 13 07:01:37 2002 +0000
@@ -28,8 +28,7 @@
 
 /**
  * aim_readtlvchain - Read a TLV chain from a buffer.
- * @buf: Input buffer
- * @maxlen: Length of input buffer
+ * @param bs Input bstream
  *
  * Reads and parses a series of TLV patterns from a data buffer; the
  * returned structure is manipulatable with the rest of the TLV
@@ -109,6 +108,74 @@
 }
 
 /**
+ * aim_readtlvchain_num - Read a TLV chain from a buffer.
+ * @param bs Input bstream
+ * @param num The max number of TLVs that will be read, or -1 if unlimited.  
+ *        There are a number of places where you want to read in a tlvchain, 
+ *        but the chain is not at the end of the SNAC, and the chain is 
+ *        preceeded by the number of TLVs.  So you can limit that.
+ *
+ * Reads and parses a series of TLV patterns from a data buffer; the
+ * returned structure is manipulatable with the rest of the TLV
+ * 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_internal aim_tlvlist_t *aim_readtlvchain_num(aim_bstream_t *bs, fu16_t num)
+{
+	aim_tlvlist_t *list = NULL, *cur;
+	
+	while ((aim_bstream_empty(bs) > 0) && (num != 0)) {
+		fu16_t type, length;
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		if (length > aim_bstream_empty(bs)) {
+			aim_freetlvchain(&list);
+			return NULL;
+		}
+
+		cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t));
+		if (!cur) {
+			aim_freetlvchain(&list);
+			return NULL;
+		}
+
+		memset(cur, 0, sizeof(aim_tlvlist_t));
+
+		cur->tlv = createtlv();
+		if (!cur->tlv) {
+			free(cur);
+			aim_freetlvchain(&list);
+			return NULL;
+		}
+		cur->tlv->type = type;
+		if ((cur->tlv->length = length)) {
+		       cur->tlv->value = aimbs_getraw(bs, length);	
+		       if (!cur->tlv->value) {
+			       freetlv(&cur->tlv);
+			       free(cur);
+			       aim_freetlvchain(&list);
+			       return NULL;
+		       }
+		}
+
+		num--;
+		cur->next = list;
+		list = cur;
+	}
+
+	return list;
+}
+
+/**
  * aim_freetlvchain - Free a TLV chain structure
  * @list: Chain to be freed
  *
@@ -246,8 +313,8 @@
 /**
  * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
  * @list: Destination chain
- * @type: TLV type to add
- * @val: Value to add
+ * @t: TLV type to add
+ * @v: Value to add
  *
  * Adds a two-byte unsigned integer to a TLV chain.
  *
@@ -608,4 +675,3 @@
 	return i;
 }
 #endif
-
--- a/src/protocols/oscar/translate.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/translate.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,4 +1,6 @@
 /*
+ * Family 0x000c - Translation.
+ *
  * I have no idea why this group was issued.  I have never seen anything
  * that uses it.  From what I remember, the last time I tried to poke at
  * the server with this group, it whined about not supporting it.
@@ -23,5 +25,3 @@
 
 	return 0;
 }
-
-
--- a/src/protocols/oscar/txqueue.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/txqueue.c	Wed Nov 13 07:01:37 2002 +0000
@@ -1,5 +1,5 @@
 /*
- *  aim_txqueue.c
+ * txqueue.c
  *
  * Herein lies all the mangement routines for the transmit (Tx) queue.
  *
@@ -63,8 +63,7 @@
 
 	} else if (fr->hdrtype == AIM_FRAMETYPE_OFT) {
 
-		fr->hdr.oft.type = chan;
-		fr->hdr.oft.hdr2len = 0; /* this will get setup by caller */
+		fr->hdr.rend.type = chan;
 
 	} else 
 		faimdprintf(sess, 0, "tx_new: unknown framing\n");
@@ -316,50 +315,37 @@
 	return err;
 }
 
-static int sendframe_oft(aim_session_t *sess, aim_frame_t *fr)
+static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr)
 {
-	aim_bstream_t hbs;
-	fu8_t *hbs_raw;
-	int hbslen;
+	aim_bstream_t bs;
+	fu8_t *bs_raw;
 	int err = 0;
-	
-	hbslen = 8 + fr->hdr.oft.hdr2len;
-	if (!(hbs_raw = malloc(hbslen)))
+	int totlen = 8 + aim_bstream_curpos(&fr->data);
+
+	if (!(bs_raw = malloc(totlen)))
 		return -1;
 
-	aim_bstream_init(&hbs, hbs_raw, hbslen);
+	aim_bstream_init(&bs, bs_raw, totlen);
+
+	aimbs_putraw(&bs, fr->hdr.rend.magic, 4);
+	aimbs_put16(&bs, 8 + fr->hdr.rend.hdrlen);
+	aimbs_put16(&bs, fr->hdr.rend.type);
 
-	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);
+	/* payload */
+	aim_bstream_rewind(&fr->data);
+	aimbs_putbs(&bs, &fr->data, totlen - 8);
 
-	aim_bstream_rewind(&hbs);
-	
-	
-	if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) {
+	aim_bstream_rewind(&bs);
 
+	if (aim_bstream_send(&bs, fr->conn, totlen) != totlen)
 		err = -errno;
-		
-	} else if (aim_bstream_curpos(&fr->data)) {
-		int len;
 
-		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 */
+	free(bs_raw); /* XXX aim_bstream_free */
 
 	fr->handled = 1;
 	fr->conn->lastactivity = time(NULL);
 
-
 	return err;
-
-
 }
 
 faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
@@ -367,7 +353,7 @@
 	if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
 		return sendframe_flap(sess, fr);
 	else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
-		return sendframe_oft(sess, fr);
+		return sendframe_rendezvous(sess, fr);
 	return -1;
 }
 
@@ -443,7 +429,7 @@
  * disappear without warning.
  *
  */
-faim_export void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
+faim_internal void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
 {
 	aim_frame_t *cur;
 
@@ -454,5 +440,3 @@
 
 	return;
 }
-
-
--- a/src/protocols/oscar/util.c	Tue Nov 12 03:40:19 2002 +0000
+++ b/src/protocols/oscar/util.c	Wed Nov 13 07:01:37 2002 +0000
@@ -138,7 +138,6 @@
 *     non-0 if different
 *
 */
-
 faim_export int aim_sncmp(const char *sn1, const char *sn2)
 {
 	const char *curPtr1 = NULL, *curPtr2 = NULL;
@@ -204,5 +203,3 @@
 
 	return p;
 }
-
-