changeset 4617:858979ab3867

[gaim-migrate @ 4908] Big Changes: -Rewrote some of the perl stuff so perl scripts can change a few of their parameters -Receiving a file with AIM over oscar works pretty well Now, the "nitty gritty": Very minor change to prefs.c: In the plugins details tab, I changed "URL" to "Web Site." I was just going to fix the tabbing, but silvestrij suggested changing it to "Web site," and I thought that sounded good. I think it fits better, too. I dunno, maybe that's just me. "Get Capabilities" has stopped working for some reason. I'm just going to blame AOL. It's really not important anyway, and some people wanted it taken off. It is now #ifdef 0'ed out. I'll remove it completely if it continues to no longer function. I took out a few plugin_event calls from oscar.c and put them in core code. "event_error" should be, uh, "evented" when there is an error signing on. Hopefully no one was using this. It's really pretty useless. The parameter is now the reason for not being able to connect rather than the archaic toc error code. I screwed around with how perl functions are called some. There was way the hell too much malloc'ing going on here. I think all in all it's an improvement, though I'm still not a big fan of how changes to parameters propagate to the actual memory. I really think it would be nice if the perl stuff was made into a C plugin. It's just so much cleaner. Especially if someone wanted to write, say, a python or tcl interpreter. That's how xchat2 does it. I just think that would be really slick. Like butter. Or ice. Very unlike Velcro. I added a "Change Password" Protocol Action for ICQ over oscar. This was really pretty easy. I'd like to thank my housemate Andrew for complaining a lot that having to use Windows ICQ to change his password was a pain. I rewrote a lot of the oscar file transfer stuff to use Christian's new xfer interface. This involved moving a few functions from ft.c to im.c, where they belong. I also removed all the #if 0'ed getfile functions. I'll be rewritting them soonish. Receiving a file should work perfectly, aside from maybe a small memleak when stuff is canceled. Sending a file is currently disabled. No ETA on when I'll have that working. I renamed pretty much all of the functions in im.c so they have kind of a scheme now. They should all be aim_im_bleh, since "im" is the family name. There comes a time when you must break the crap out of any clients that might be using libfaim in order to make stuff cleaner. Maybe. I got rid of the snac destructor stuff for now. I'll probably add it back later. I wasn't entirely comfortable with how it was done. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Wed, 26 Feb 2003 05:01:37 +0000
parents 767093a2ddaf
children 3c7d4a060d30
files src/ft.c src/gtkft.c src/multi.c src/perl.c src/prefs.c src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/aim_internal.h src/protocols/oscar/conn.c src/protocols/oscar/ft.c src/protocols/oscar/icq.c src/protocols/oscar/im.c src/protocols/oscar/oscar.c src/protocols/oscar/rxhandlers.c src/protocols/oscar/rxqueue.c src/protocols/oscar/snac.c src/protocols/oscar/tlv.c src/protocols/oscar/txqueue.c src/protocols/oscar/util.c src/protocols/toc/toc.c
diffstat 20 files changed, 2213 insertions(+), 3454 deletions(-) [+]
line wrap: on
line diff
--- a/src/ft.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/ft.c	Wed Feb 26 05:01:37 2003 +0000
@@ -437,8 +437,8 @@
 
 		r = read(xfer->fd, *buffer, s);
 
-		if ((r == 0) || ((gaim_xfer_get_size > 0) &&
-			((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer))))
+		if ((gaim_xfer_get_size > 0) &&
+			((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)))
 			gaim_xfer_set_completed(xfer, TRUE);
 	}
 
--- a/src/gtkft.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/gtkft.c	Wed Feb 26 05:01:37 2003 +0000
@@ -1116,8 +1116,9 @@
 	gaim_gtkxfer_dialog_update_xfer(xfer_dialog, xfer);
 
 	/* See if it's removed. */
-	if (xfer->ui_data == NULL)
-		gaim_xfer_destroy(xfer);
+	/* XXX - This caused some bad stuff, and I don't see a point to it */
+/*	if (xfer->ui_data == NULL)
+		gaim_xfer_destroy(xfer); */
 }
 
 static void
@@ -1126,8 +1127,9 @@
 	gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer);
 
 	/* See if it's removed. */
-	if (xfer->ui_data == NULL)
-		gaim_xfer_destroy(xfer);
+	/* XXX - This caused some looping, and I don't see a point to it */
+/*	if (xfer->ui_data == NULL)
+		gaim_xfer_destroy(xfer); */
 }
 
 struct gaim_xfer_ui_ops ops =
--- a/src/multi.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/multi.c	Wed Feb 26 05:01:37 2003 +0000
@@ -1623,10 +1623,10 @@
 				       char *title,
 				       char *prologue)
 {
-	char buf[2048];
+	gchar *buf;
 	struct kick_dlg *k = find_kick_dlg(gc->account);
 	struct signon_meter *meter = find_signon_meter(gc);
-	sprintf(buf, _("%s\n%s: %s"), full_date(), prologue, details);
+	buf = g_strdup_printf(_("%s\n%s: %s"), full_date(), prologue, details);
 	if (k)
 		gtk_widget_destroy(k->dlg);
 	k = g_new0(struct kick_dlg, 1);
@@ -1639,14 +1639,17 @@
 		meters = g_slist_remove(meters, meter);
 		g_free(meter);
 	}
+	g_free(buf);
 }
 
 void hide_login_progress(struct gaim_connection *gc, char *why)
 {
-	char buf[2048];
+	gchar *buf;
 
-	sprintf(buf, _("%s was unable to sign on"), gc->username);
+	plugin_event(event_error, gc, why);
+	buf = g_strdup_printf(_("%s was unable to sign on"), gc->username);
 	hide_login_progress_common(gc, why, _("Signon Error"), buf);
+	g_free(buf);
 }
 
 /*
@@ -1666,6 +1669,8 @@
 void hide_login_progress_error(struct gaim_connection *gc, char *why)
 {
 	char buf[2048];
+
+	plugin_event(event_error, gc, why);
 	g_snprintf(buf, sizeof(buf), _("%s has been signed off"), gc->username);
 	hide_login_progress_common(gc, why, _("Connection Error"), buf);
 }
--- a/src/perl.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/perl.c	Wed Feb 26 05:01:37 2003 +0000
@@ -132,7 +132,7 @@
 XS(XS_GAIM_add_timeout_handler); /* figure it out */
 
 /* play sound */
-XS(XS_GAIM_play_sound); /*play a sound*/
+XS(XS_GAIM_play_sound); /*play a sound */
 
 static void
 #ifdef OLD_PERL
@@ -188,76 +188,106 @@
 }
 
 /*
+  2003/02/06: execute_perl modified by Mark Doliner <mark@kingant.net>
+		Pass parameters by pushing them onto the stack rather than 
+		passing an array of strings.  This way, perl scripts can 
+		modify the parameters and we can get the changed values 
+		and then shoot ourselves.  I mean, uh, use them.
+
   2001/06/14: execute_perl replaced by Martin Persson <mep@passagen.se>
-              previous use of perl_eval leaked memory, replaced with
-              a version that uses perl_call instead
-  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
-			  args changed to char** so that we can have preparsed
-  			  arguments again, and many headaches ensued! This essentially 
-			  means we replaced one hacked method with a messier hacked 
-			  method out of perceived necessity. Formerly execute_perl 
-			  required a single char_ptr, and it would insert it into an 
-			  array of character pointers and NULL terminate the new array.
-			  Now we have to pass in pre-terminated character pointer arrays
-			  to accomodate functions that want to pass in multiple arguments.
+		previous use of perl_eval leaked memory, replaced with
+		a version that uses perl_call instead
 
-			  Previously arguments were preparsed because an argument list
-			  was constructed in the form 'arg one','arg two' and was
-			  executed via a call like &funcname(arglist) (see .59.x), so
-			  the arglist was magically pre-parsed because of the method. 
-			  With Martin Persson's change to perl_call we now need to
-			  use a null terminated list of character pointers for arguments
-			  if we wish them to be parsed. Lacking a better way to allow
-			  for both single arguments and many I created a NULL terminated
-			  array in every function that called execute_perl and passed
-			  that list into the function.  In the former version a single
-			  character pointer was passed in, and was placed into an array
-			  of character pointers with two elements, with a NULL element
-			  tacked onto the back, but this method no longer seemed prudent.
+  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
+		args changed to char** so that we can have preparsed
+  		arguments again, and many headaches ensued! This essentially 
+		means we replaced one hacked method with a messier hacked 
+		method out of perceived necessity. Formerly execute_perl 
+		required a single char_ptr, and it would insert it into an 
+		array of character pointers and NULL terminate the new array.
+		Now we have to pass in pre-terminated character pointer arrays
+		to accomodate functions that want to pass in multiple arguments.
 
-			  Enhancements in the future might be to get rid of pre-declaring
-			  the array sizes?  I am not comfortable enough with this
-			  subject to attempt it myself and hope it to stand the test
-			  of time.
+		Previously arguments were preparsed because an argument list
+		was constructed in the form 'arg one','arg two' and was
+		executed via a call like &funcname(arglist) (see .59.x), so
+		the arglist was magically pre-parsed because of the method. 
+		With Martin Persson's change to perl_call we now need to
+		use a null terminated list of character pointers for arguments
+		if we wish them to be parsed. Lacking a better way to allow
+		for both single arguments and many I created a NULL terminated
+		array in every function that called execute_perl and passed
+		that list into the function.  In the former version a single
+		character pointer was passed in, and was placed into an array
+		of character pointers with two elements, with a NULL element
+		tacked onto the back, but this method no longer seemed prudent.
+
+		Enhancements in the future might be to get rid of pre-declaring
+		the array sizes?  I am not comfortable enough with this
+		subject to attempt it myself and hope it to stand the test
+		of time.
 */
 
 static int 
 execute_perl(char *function, char **args)
 {
-	char buf[512];
-	int count, ret_value = 1;
-	SV *sv;
+	int count, i, ret_value = 1;
+	SV *sv_args[5];
+	STRLEN na;
 
+	/*
+	 * Set up the perl environment, push arguments onto the 
+	 * perl stack, then call the given function
+	 */
 	dSP;
-        ENTER;
-        SAVETMPS;
-        PUSHMARK(sp);
-        count = perl_call_argv(function, G_EVAL | G_SCALAR, args);
-        SPAGAIN;
+	ENTER;
+	SAVETMPS;
+	PUSHMARK(sp);
+	for (i=0; i<5; i++)
+		if (args[i]) {
+			sv_args[i] = sv_2mortal(newSVpv(args[i], 0));
+			XPUSHs(sv_args[i]);
+		}
+	PUTBACK;
+	count = call_pv(function, G_EVAL | G_SCALAR);
+	SPAGAIN;
 
-        sv = GvSV(gv_fetchpv("@", TRUE, SVt_PV));
-        if (SvTRUE(sv)) {
-                g_snprintf(buf, 512, "Perl error: %s\n", SvPV(sv, count));
-                debug_printf(buf);
-                POPs;
-        } else if (count != 1) {
-                g_snprintf(buf, 512, "Perl error: expected 1 value from %s, "
-                        "got: %d\n", function, count);
-                debug_printf(buf);
-        } else {
-                ret_value = POPi;
-        }
+	/* Check for "die," make sure we have 1 argument, and set our return value */
+	if (SvTRUE(ERRSV)) {
+		debug_printf("Perl function %s exited abnormally: %s\n", function, SvPV(ERRSV, na));
+		POPs;
+	} else if (count != 1) {
+		/* This should NEVER happen.  G_SCALAR ensures that we WILL have 1 parameter */
+		debug_printf("Perl error from %s: expected 1 return value, but got %d\n", function, count);
+	} else {
+		ret_value = POPi;
+	}
 
-        PUTBACK;
-        FREETMPS;
-        LEAVE;
+	/* Check for changed arguments */
+	for (i=0; i<5; i++)
+		if (args[i] && strcmp(args[i], SvPVX(sv_args[i]))) {
+			/*
+			 * Shizzel.  So the perl script changed one of the parameters, and we want 
+			 * this change to affect the original parameters.  args[i] is just a tempory 
+			 * little list of pointers.  We don't want to free args[i] here because the 
+			 * new parameter doesn't overwrite the data that args[i] points to.  That is 
+			 * done by the function that called execute_perl.  I'm not explaining this 
+			 * very well.  See, it's aggregate...  Oh, but if 2 perl scripts both modify 
+			 * the data, _that's_ a memleak.  This is really kind of hackish.  I should 
+			 * fix it.  Look how long this comment is.  Holy crap.
+			 */
+			args[i] = g_strdup(SvPV(sv_args[i], na));
+		}
 
-        return ret_value;
+	PUTBACK;
+	FREETMPS;
+	LEAVE;
 
+	return ret_value;
 }
 
 void perl_unload_file(struct gaim_plugin *plug) {
-	char *atmp[2] = { "", NULL }; /* see execute_perl --et */
+	char *atmp[1] = { "" };
 	struct perlscript *scp = NULL;
 	struct _perl_timeout_handlers *thn;
 	struct _perl_event_handlers *ehn;
@@ -305,14 +335,14 @@
 		pl = pl->next;
 	}
 
-	plug->handle=NULL;
+	plug->handle = NULL;
 	plugins = g_list_remove(plugins, plug);
 	save_prefs();
 }
 
 int perl_load_file(char *script_name)
 {
-	char *atmp[2] = { script_name, NULL }; /* see execute_perl --et */
+	char *atmp[1] = { script_name };
 	struct gaim_plugin *plug = NULL;
 	GList *p = probed_plugins;
 	GList *s;
@@ -399,13 +429,13 @@
 }
 
 static void perl_init()
-{        /*changed the name of the variable from load_file to
+{	/* changed the name of the variable from load_file to
 	   perl_definitions since now it does much more than defining
 	   the load_file sub. Moreover, deplaced the initialisation to
 	   the xs_init function. (TheHobbit)*/
-        char *perl_args[] = { "", "-e", "0", "-w" };
-        char perl_definitions[] =
-        {
+	char *perl_args[] = { "", "-e", "0", "-w" };
+	char perl_definitions[] =
+	{
 		/* We use to function one to load a file the other to
 		   execute the string obtained from the first and holding
 		   the file conents. This allows to have a realy local $/
@@ -451,7 +481,7 @@
 
 void perl_end()
 {
-	char *atmp[2] = { "", NULL }; /* see execute_perl --et */
+	char *atmp[1] = { "" };
 	struct perlscript *scp;
 	struct _perl_timeout_handlers *thn;
 	struct _perl_event_handlers *ehn;
@@ -693,7 +723,7 @@
 
 	command = SvPV(ST(0), junk);
 	if (!command) XSRETURN(0);
-	if        (!strncasecmp(command, "signon", 6)) {
+	if (!strncasecmp(command, "signon", 6)) {
 		int index = SvIV(ST(1));
 		if (g_slist_nth_data(gaim_accounts, index))
 		serv_login(g_slist_nth_data(gaim_accounts, index));
@@ -840,9 +870,8 @@
 	else
 		gaim_conversation_set_account(c, gc->account);
 
-	gaim_conversation_write(c, NULL, what, -1,
-							(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)),
-							time(NULL));
+	gaim_conversation_write(c, NULL, what, -1, 
+				(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)), time(NULL));
 	serv_send_im(gc, nick, what, -1, isauto ? IM_FLAG_AWAY : 0);
 	XSRETURN(0);
 }
@@ -884,50 +913,40 @@
 
 int perl_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
 {
-	/* changing buf to an array of char pointers so we get @_ back in	
-	 * order; basically scrapping legacy code in favor of blocky		 
-	 * unimaginative code. Additionally, assigned values one at a time 
-	 * for the sake of clarity. --Eric Timme (30 November 2002)	*/ 
-
-	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; 
-	/* 5 for max 4 arguments =\  Note, if you change the above,
-	 * change the two loops at the bottom that free up memory */
-
+	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; /* Maximum of 5 args */
+	char tmpbuf1[16], tmpbuf2[16], tmpbuf3[1];
 	GList *handler;
 	struct _perl_event_handlers *data;
 	int handler_return;
-	int i=0; /* necessary counter variable? --et */
 
+	tmpbuf1[0] = '\0';
+	tmpbuf2[0] = '\0';
+	tmpbuf3[0] = '\0';
+
+	/* Make a pretty array of char*'s with which to call perl functions */
 	switch (event) {
 	case event_signon:
 	case event_signoff:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
 		break;
 	case event_away:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", 
-				((struct gaim_connection *)arg1)->away ?
-					escape_quotes(((struct gaim_connection *)arg1)->away) : "");
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = ((struct gaim_connection *)arg1)->away ?
+				((struct gaim_connection *)arg1)->away : tmpbuf2;
 		break;
 	case event_im_recv:
-		{
-		char *tmp = *(char **)arg2 ? g_strdup(escape_quotes(*(char **)arg2)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp);
-		buf[2] = g_strdup_printf("%s",
-				*(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
-		g_free(tmp);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = *(char **)arg2 ? *(char **)arg2 : tmpbuf3;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_im_send:
-		{
-		char *tmp = arg2 ? g_strdup(escape_quotes(arg2)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp);
-		buf[2] = g_strdup_printf("%s", 
-			   *(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
-		g_free(tmp);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_buddy_signon:
 	case event_buddy_signoff:
@@ -937,103 +956,81 @@
 	case event_buddy_idle:
 	case event_buddy_unidle:
 	case event_got_typing:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", escape_quotes(arg2));
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
 		break;
 	case event_chat_invited:
-		{
-		char *tmp2, *tmp3, *tmp4;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = g_strdup(escape_quotes(arg3));
-		tmp4 = arg4 ? g_strdup(escape_quotes(arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		buf[3] = g_strdup_printf("%s", tmp4);
-		g_free(tmp2);
-		g_free(tmp3);
-		g_free(tmp4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3;
+		buf[3] = arg4;
 		break;
 	case event_chat_join:
 	case event_chat_buddy_join:
 	case event_chat_buddy_leave:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", escape_quotes(arg3));
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
 		break;
 	case event_chat_leave:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
 		break;
 	case event_chat_recv:
-		{
-		char *t3, *t4;
-		t3 = g_strdup(escape_quotes(*(char **)arg3));
-		t4 = *(char **)arg4 ? g_strdup(escape_quotes(*(char **)arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", t3);
-		buf[3] = g_strdup_printf("%s", t4);
-		g_free(t3);
-		g_free(t4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3;
+		buf[3] = *(char **)arg4 ? *(char **)arg4 : tmpbuf3;
 		break;
 	case event_chat_send_invite:
-		{
-		char *t3, *t4;
-		t3 = g_strdup(escape_quotes(arg3));
-		t4 = *(char **)arg4 ? g_strdup(escape_quotes(*(char **)arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", t3);
-		buf[3] = g_strdup_printf("%s", t4);
-		g_free(t3);
-		g_free(t4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
+		buf[3] = *(char **)arg4 ? *(char **)arg4 : tmpbuf3;
 		break;
 	case event_chat_send:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", 
-				*(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_warned:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", arg2 ? escape_quotes(arg2) : "");
-		buf[2] = g_strdup_printf("%d", (int)arg3);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg3);
+		buf[2] = tmpbuf2;
 		break;
 	case event_quit:
 	case event_blist_update:
-		buf[0] = g_malloc0(1);
+		buf[0] = tmpbuf3;
 		break;
 	case event_new_conversation:
 	case event_del_conversation:
-		buf[0] = g_strdup_printf("%s", escape_quotes(arg1));
+		buf[0] = arg1;
 		break;
 	case event_im_displayed_sent:
-		{
-		char *tmp2, *tmp3;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = *(char **)arg3 ? g_strdup(escape_quotes(*(char **)arg3)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		g_free(tmp2);
-		g_free(tmp3);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_im_displayed_rcvd:
-		{
-		char *tmp2, *tmp3;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = arg3 ? g_strdup(escape_quotes(arg3)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		g_free(tmp2);
-		g_free(tmp3);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3 ? arg3 : tmpbuf3;
 		break;
 	case event_draw_menu:
 		/* we can't handle this usefully without gtk/perl bindings */
@@ -1043,26 +1040,66 @@
 		return 0;
 	}
 
+	/* Call any applicable functions */
 	for (handler = perl_event_handlers; handler != NULL; handler = handler->next) {
 		data = handler->data;
 		if (!strcmp(event_name(event), data->event_type)) {
 			handler_return = execute_perl(data->handler_name, buf);
 			if (handler_return) {
-				/* loop to try and free up memory from the 
-				 * char pointer array? --et */
-				for (i=0;i<5;i++)
-					if (buf[i])
-						g_free(buf[i]);
 				return handler_return;
 			}
 		}
 	}
 
-	/* loop to try and free up memory from the 
-	 * char pointer array? --et */
-	for (i=0;i<5;i++)
-		if (buf[i])
-			g_free(buf[i]);
+	/* Now make changes from perl scripts affect the real data */
+	switch (event) {
+	case event_im_recv:
+		if (buf[1] != *(char **)arg2) {
+			free(*(char **)arg2);
+			*(char **)arg2 = buf[1];
+		}
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_chat_recv:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send_invite:
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_displayed_sent:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	default:
+		break;
+	}
 
 	return 0;
 }
@@ -1114,19 +1151,19 @@
 		if (!strcmp(ehn->event_type, SvPV(ST(0), junk)) &&
 			!strcmp(ehn->handler_name, SvPV(ST(1), junk)))
 		{
-	        perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
+			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
 			g_free(ehn->event_type);
 			g_free(ehn->handler_name);
 			g_free(ehn);
 		}
 
 		cur = next;
-    }
+	}
 }
 
 static int perl_timeout(gpointer data)
 {
-	char *atmp[2] = { NULL, NULL }; /* see execute_perl --et */
+	char *atmp[1] = { NULL };
 	struct _perl_timeout_handlers *handler = data;
 
 	atmp[0] = escape_quotes(handler->handler_args);
--- a/src/prefs.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/prefs.c	Wed Feb 26 05:01:37 2003 +0000
@@ -1129,7 +1129,7 @@
 #ifndef _WIN32
 		   _("<span size=\"larger\">%s %s</span>\n\n"
 		     "<span weight=\"bold\">Written by:</span>\t%s\n"
-		     "<span weight=\"bold\">URL:</span>\t%s\n"
+		     "<span weight=\"bold\">Web site:</span>\t\t%s\n"
 		     "<span weight=\"bold\">File name:</span>\t%s"),
 #else
 		   _("<span size=\"larger\">%s %s</span>\n\n"
--- a/src/protocols/oscar/aim.h	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/aim.h	Wed Feb 26 05:01:37 2003 +0000
@@ -28,6 +28,7 @@
 #ifndef _WIN32
 #include <sys/time.h>
 #include <unistd.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 #else
@@ -251,17 +252,17 @@
 /* 
  * These could be arbitrary, but its easier to use the actual AIM values 
  */
-#define AIM_CONN_TYPE_AUTH          0x0007
-#define AIM_CONN_TYPE_ADS           0x0005
-#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
+#define AIM_CONN_TYPE_BOS		0x0002
+#define AIM_CONN_TYPE_ADS		0x0005
+#define AIM_CONN_TYPE_AUTH		0x0007
+#define AIM_CONN_TYPE_CHATNAV		0x000d
+#define AIM_CONN_TYPE_CHAT		0x000e
+#define AIM_CONN_TYPE_SEARCH		0x000f
+#define AIM_CONN_TYPE_EMAIL		0x0018
 
-/* they start getting arbitrary in rendezvous stuff =) */
-#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak FLAP! */
-#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
+/* they start getting arbitrary for rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS	0xfffe /* these do not speak FLAP! */
+#define AIM_CONN_TYPE_LISTENER		0xffff /* socket waiting for accept() */
 
 /*
  * Subtypes, we need these for OFT stuff.
@@ -328,7 +329,7 @@
 			flap_seqnum_t seqnum;     
 		} flap;
 		struct {
-			fu8_t magic[4]; /* ODC2 OFT2 */
+			fu8_t magic[4]; /* ODC2 or OFT2 */
 			fu16_t hdrlen;
 			fu16_t type;
 		} rend;
@@ -679,7 +680,6 @@
 #define AIM_WARN_ANON                     0x01
 
 faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
 faim_export int aim_nop(aim_session_t *, aim_conn_t *);
 faim_export int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn);
 faim_export int aim_bos_setidle(aim_session_t *, aim_conn_t *, fu32_t);
@@ -697,14 +697,12 @@
 faim_export int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, fu16_t privacy);
 faim_export int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, fu32_t status);
 
-faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *);
-
 #define AIM_CLIENTTYPE_UNKNOWN  0x0000
 #define AIM_CLIENTTYPE_MC       0x0001
 #define AIM_CLIENTTYPE_WINAIM   0x0002
 #define AIM_CLIENTTYPE_WINAIM41 0x0003
 #define AIM_CLIENTTYPE_AOL_TOC  0x0004
-faim_export unsigned short aim_fingerprintclient(unsigned char *msghdr, int len);
+faim_export unsigned short aim_im_fingerprint(unsigned char *msghdr, int len);
 
 #define AIM_RATE_CODE_CHANGE     0x0001
 #define AIM_RATE_CODE_WARNING    0x0002
@@ -712,48 +710,39 @@
 #define AIM_RATE_CODE_CLEARLIMIT 0x0004
 faim_export int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
 
+
+
 /* im.c */
+#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
+#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
+
+/* This is what the server will give you if you don't set them yourself. */
+#define AIM_IMPARAM_DEFAULTS { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	512, /* !! Note how small this is. */ \
+	(99.9)*10, (99.9)*10, \
+	1000 /* !! And how large this is. */ \
+}
 
-struct aim_fileheader_t {
-#if 0
-	char  magic[4];		/* 0 */
-	short hdrlen; 		/* 4 */
-	short hdrtype;		/* 6 */
-#endif
-	char  bcookie[8];       /* 8 */
-	short encrypt;          /* 16 */
-	short compress;         /* 18 */
-	short totfiles;         /* 20 */
-	short filesleft;        /* 22 */
-	short totparts;         /* 24 */
-	short partsleft;        /* 26 */
-	long  totsize;          /* 28 */
-	long  size;             /* 32 */
-	long  modtime;          /* 36 */
-	long  checksum;         /* 40 */
-	long  rfrcsum;          /* 44 */
-	long  rfsize;           /* 48 */
-	long  cretime;          /* 52 */
-	long  rfcsum;           /* 56 */
-	long  nrecvd;           /* 60 */
-	long  recvcsum;         /* 64 */
-	char  idstring[32];     /* 68 */
-	char  flags;            /* 100 */
-	char  lnameoffset;      /* 101 */
-	char  lsizeoffset;      /* 102 */
-	char  dummy[69];        /* 103 */
-	char  macfileinfo[16];  /* 172 */
-	short nencode;          /* 188 */
-	short nlanguage;        /* 190 */
-	char  name[64];         /* 192 */
-				/* 256 */
+/* This is what most AIM versions use. */
+#define AIM_IMPARAM_REASONABLE { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	8000, \
+	(99.9)*10, (99.9)*10, \
+	0 \
+}
+
+struct aim_icbmparameters {
+	fu16_t maxchan;
+	fu32_t flags; /* AIM_IMPARAM_FLAG_ */
+	fu16_t maxmsglen; /* message size that you will accept */
+	fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */
+	fu16_t maxrecverwarn;
+	fu32_t minmsginterval; /* in milliseconds? */
 };
 
-#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001
-#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002
-#define AIM_OFT_SUBTYPE_GET_FILE 0x0011
-#define AIM_OPT_SUBTYPE_GET_LIST 0x0012
-
 struct aim_chat_roominfo {
 	unsigned short exchange;
 	char *name;
@@ -886,14 +875,14 @@
 #define AIM_RENDEZVOUS_ACCEPT  0x0002
 
 struct aim_incomingim_ch2_args {
+	fu16_t status;
 	fu8_t cookie[8];
 	fu16_t reqclass;
-	fu16_t status;
-	fu16_t errorcode;
 	const char *clientip;
 	const char *clientip2;
 	const char *verifiedip;
 	fu16_t port;
+	fu16_t errorcode;
 	const char *msg; /* invite message or file description */
 	const char *encoding;
 	const char *language;
@@ -935,30 +924,89 @@
 	int msglen;
 };
 
-faim_export int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
-faim_export int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args);
-faim_export int aim_send_im(aim_session_t *, const char *destsn, unsigned short flags, const char *msg);
-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, 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);
+/* SNAC sending functions */
+/* 0x0002 */ faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params);
+/* 0x0004 */ faim_export int aim_im_reqparams(aim_session_t *sess);
+/* 0x0006 */ faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args);
+/* 0x0006 */ faim_export int aim_im_sendch1(aim_session_t *, const char *destsn, unsigned short flags, const char *msg);
+/* 0x0006 */ faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum);
+/* 0x0006 */ faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
+/* 0x0006 */ faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port, const char *filename, fu16_t numfiles, fu32_t totsize);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, const fu8_t *cookie, const char *sn, fu16_t rendid);
+/* 0x0006 */ faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, const fu8_t *cookie, const char *sn, fu16_t rendid);
+/* 0x0006 */ faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type);
+/* 0x0006 */ faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message);
+/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
+/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, unsigned short code);
+/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2);
+
+
+
+/* ft.c */
+#define AIM_OFT_SUBTYPE_SEND_FILE 0x0001
+#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002
+#define AIM_OFT_SUBTYPE_GET_FILE 0x0011
+#define AIM_OPT_SUBTYPE_GET_LIST 0x0012
+
+#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
+#define AIM_TRANSFER_DENY_DECLINE 0x0001
+#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
 
-faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type);
-faim_export aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, fu16_t numfiles, fu32_t totsize, char *cookret);
-faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message);
-
-faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2);
+struct aim_fileheader_t {
+#if 0
+	char magic[4];		/* 0 */
+	fu16_t hdrlen;		/* 4 */
+	fu16_t hdrtype;		/* 6 */
+#endif
+	char bcookie[8];	/* 8 */
+	fu16_t encrypt;		/* 16 */
+	fu16_t compress;	/* 18 */
+	fu16_t totfiles;	/* 20 */
+	fu16_t filesleft;	/* 22 */
+	fu16_t totparts;	/* 24 */
+	fu16_t partsleft;	/* 26 */
+	fu32_t totsize;		/* 28 */
+	fu32_t size;		/* 32 */
+	fu32_t modtime;		/* 36 */
+	fu32_t checksum;	/* 40 */
+	fu32_t rfrcsum;		/* 44 */
+	fu32_t rfsize;		/* 48 */
+	fu32_t cretime;		/* 52 */
+	fu32_t rfcsum;		/* 56 */
+	fu32_t nrecvd;		/* 60 */
+	fu32_t recvcsum;	/* 64 */
+	fu8_t idstring[32];	/* 68 */
+	fu8_t flags;		/* 100 */
+	fu8_t lnameoffset;	/* 101 */
+	fu8_t lsizeoffset;	/* 102 */
+	char dummy[69];		/* 103 */
+	char macfileinfo[16];	/* 172 */
+	fu16_t nencode;		/* 188 */
+	fu16_t nlanguage;	/* 190 */
+	char name[64];		/* 192 */
+				/* 256 */
+};
 
-faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn);
-faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size);
-faim_export int aim_oft_sendfile_request(aim_session_t *sess, aim_conn_t *conn,
-		const char *name, int filesdone, int numfiles, int size,
-		int totsize);
-faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_oft_end(aim_session_t *sess, aim_conn_t *conn);
+struct aim_filetransfer_priv {
+	char cookie[8];
+	char ip[30];
+	int state;
+	struct aim_fileheader_t fh;
+};
+
+faim_export fu32_t aim_oft_checksum(const unsigned char *buffer, int bufferlen, fu32_t prevcheck);
+faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
+faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
+faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding);
+faim_export const char *aim_odc_getsn(aim_conn_t *conn);
+faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn);
+faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn);
+faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie);
+faim_export aim_conn_t *aim_sendfile_listen(aim_session_t *sess, const fu8_t *cookie, const fu8_t *ip, fu16_t port);
+faim_export int aim_oft_sendheader(aim_session_t *sess, aim_conn_t *conn, fu16_t type, const fu8_t *cookie, const char *filename, fu16_t filesdone, fu16_t numfiles, fu32_t size, fu32_t totsize, fu32_t modtime, fu32_t checksum, fu8_t flags);
+
+
 
 /* info.c */
 #define AIM_CAPS_BUDDYICON      0x00000001
@@ -1015,55 +1063,10 @@
 #define AIM_COOKIETYPE_OFTIMAGE 0x14
 #define AIM_COOKIETYPE_OFTICON  0x15
 
-faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
-
-#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
-#define AIM_TRANSFER_DENY_DECLINE 0x0001
-#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
-faim_export int aim_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, unsigned short code);
-faim_export aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, const fu8_t *cookie, const fu8_t *ip, fu16_t port, fu16_t rendid, ...);
-faim_export int aim_canceltransfer(aim_session_t *sess, aim_conn_t *conn,
-		                const char *cookie, const char *sn, int rendid);
-faim_export fu32_t aim_update_checksum(aim_session_t *sess, aim_conn_t *conn,
-		                const unsigned char *buffer, int bufferlen);
-
 faim_export int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, unsigned short);
 faim_export int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info);
 faim_export int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
 
-#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
-#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
-
-/* This is what the server will give you if you don't set them yourself. */
-#define AIM_IMPARAM_DEFAULTS { \
-	0, \
-	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
-	512, /* !! Note how small this is. */ \
-	(99.9)*10, (99.9)*10, \
-	1000 /* !! And how large this is. */ \
-}
-
-/* This is what most AIM versions use. */
-#define AIM_IMPARAM_REASONABLE { \
-	0, \
-	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
-	8000, \
-	(99.9)*10, (99.9)*10, \
-	0 \
-}
-
-
-struct aim_icbmparameters {
-	fu16_t maxchan;
-	fu32_t flags; /* AIM_IMPARAM_FLAG_ */
-	fu16_t maxmsglen; /* message size that you will accept */
-	fu16_t maxsenderwarn; /* this and below are *10 (999=99.9%) */
-	fu16_t maxrecverwarn;
-	fu32_t minmsginterval; /* in milliseconds? */
-};
-
-faim_export int aim_reqicbmparams(aim_session_t *sess);
-faim_export int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params);
 
 
 /* auth.c */
@@ -1075,13 +1078,19 @@
 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);
 
+
+
 /* 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 *);
 
+
+
 /* search.c */
 faim_export int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
 
+
+
 /* newsearch.c */
 struct aim_usersearch {
 	char *first;
@@ -1246,9 +1255,12 @@
 
 faim_export int aim_icq_reqofflinemsgs(aim_session_t *sess);
 faim_export int aim_icq_ackofflinemsgs(aim_session_t *sess);
+faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd);
 faim_export int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
 faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin);
 
+
+
 /* email.c */
 struct aim_emailinfo {
 	fu8_t *cookie16;
@@ -1264,6 +1276,8 @@
 faim_export int aim_email_sendcookies(aim_session_t *sess, aim_conn_t *conn);
 faim_export int aim_email_activate(aim_session_t *sess, aim_conn_t *conn);
 
+
+
 /* util.c */
 /*
  * These are really ugly.  You'd think this was LISP.  I wish it was.
@@ -1319,6 +1333,8 @@
 
 
 faim_export int aimutil_putstr(u_char *, const char *, int);
+faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen);
+faim_export int aim_util_getlocalip(fu8_t *ip);
 faim_export int aimutil_tokslen(char *toSearch, int index, char dl);
 faim_export int aimutil_itemcnt(char *toSearch, char dl);
 faim_export char *aimutil_itemidx(char *toSearch, int index, char dl);
--- a/src/protocols/oscar/aim_cbtypes.h	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/aim_cbtypes.h	Wed Feb 26 05:01:37 2003 +0000
@@ -24,6 +24,7 @@
 #define AIM_CB_FAM_CTN 0x000d /* ChatNav */
 #define AIM_CB_FAM_CHT 0x000e /* Chat */
 #define AIM_CB_FAM_SCH 0x000f /* "New" search */
+#define AIM_CB_FAM_ICO 0x0010 /* Used for uploading buddy icons */
 #define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
 #define AIM_CB_FAM_ICQ 0x0015
 #define AIM_CB_FAM_ATH 0x0017
@@ -250,11 +251,11 @@
  *
  * See non-SNAC note below.
  */
-#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP */
 #define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
 #define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
 #define AIM_CB_OFT_DIRECTIMTYPING 0x0004
-#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+#define AIM_CB_OFT_DIRECTIM_ESTABLISHED 0x0005
 
 #if 0
 #define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
@@ -262,7 +263,7 @@
 #define AIM_CB_OFT_GETFILEFILEREQ 0x0008    /* received file request */
 #define AIM_CB_OFT_GETFILEFILESEND 0x0009   /* received file request confirm -- send data */
 #define AIM_CB_OFT_GETFILECOMPLETE 0x000a   /* received file send complete*/
-#define AIM_CB_OFT_GETFILEINITIATE 0x000b   /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILE_ESTABLISHED 0x000b   /* request for file get acknowledge */
 #define AIM_CB_OFT_GETFILEDISCONNECT 0x000c   /* OFT connection disconnected.*/
 #define AIM_CB_OFT_GETFILELISTING 0x000d   /* OFT listing.txt received.*/
 #define AIM_CB_OFT_GETFILERECEIVE 0x000e   /* OFT file incoming.*/
@@ -270,10 +271,19 @@
 #define AIM_CB_OFT_GETFILESTATE4 0x0010
 #endif
 
-#define AIM_CB_OFT_SENDFILEFILEREQ 0x0011 /* started receiving file */
-#define AIM_CB_OFT_SENDFILEFILESEND 0x0012 /* buddy ready to for us to send */
-#define AIM_CB_OFT_SENDFILECOMPLETE 0x0013 /* send to buddy complete */
-#define AIM_CB_OFT_SENDFILEINITIATE 0x0014 /* connection to buddy initiated */
+#define AIM_CB_OFT_PROMPT 0x0101 /* Just connected to another buddy, receiver sends this */
+#define AIM_CB_OFT_RESUMESOMETHING 0x0106 /* I really don't know */
+#define AIM_CB_OFT_ACK 0x0202 /* Just after receiving the prompt, sender sends this */
+#define AIM_CB_OFT_DONE 0x0204 /* After a transfer is complete, sender sends this */
+#define AIM_CB_OFT_RESUME 0x0205 /* Resume transferring, send by whoever paused? */
+#define AIM_CB_OFT_RESUMEACK 0x0207 /* Not really sure */
+#define AIM_CB_OFT_ESTABLISHED 0xffff /* Internal, when a connection is established */
+
+#define AIM_CB_OFT_SENDFILE_REQUEST 0x0011 /* started receiving file */
+#define AIM_CB_OFT_SENDFILE_SEND 0x0012 /* buddy ready to for us to send */
+#define AIM_CB_OFT_SENDFILE_COMPLETE 0x0013 /* send to buddy complete */
+#define AIM_CB_OFT_SENDFILE_ESTABLISHED 0xFFFF /* connection to buddy initiated */
+#define AIM_CB_OFT_SENDFILE_RESUME 0x0015 /* somethin' */
 
 
 /*
--- a/src/protocols/oscar/aim_internal.h	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/aim_internal.h	Wed Feb 26 05:01:37 2003 +0000
@@ -24,7 +24,6 @@
 	fu16_t flags;
 	char name[AIM_MODULENAME_MAXLEN+1];
 	int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs);
-	int (*snacdestructor)(aim_session_t *sess, aim_conn_t *conn, aim_modsnac_t *snac, void *data);
 
 	void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod);
 	void *priv;
@@ -129,11 +128,6 @@
 	struct aim_snac_s *next;
 } aim_snac_t;
 
-struct aim_snac_destructor {
-	aim_conn_t *conn;
-	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);
@@ -211,11 +205,6 @@
 
 faim_internal void faimdprintf(aim_session_t *sess, int dlevel, const char *format, ...);
 
-faim_internal int aim_request_directim(aim_session_t *sess, const char *destsn, fu8_t *ip, fu16_t port, fu8_t *ckret);
-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);
-faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn);
-faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn);
-
 faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
 
 /* These are all handled internally now. */
--- a/src/protocols/oscar/conn.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/conn.c	Wed Feb 26 05:01:37 2003 +0000
@@ -172,9 +172,7 @@
 	/*
 	 * This will free ->internal if it necessary...
 	 */
-	if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
-		aim_conn_kill_rend(sess, *deadconn);
-	else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
+	if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
 		aim_conn_kill_chat(sess, *deadconn);
 
 	if ((*deadconn)->inside) {
@@ -324,8 +322,6 @@
 	deadconn->fd = -1;
 	if (deadconn->handlerlist)
 		aim_clearhandlers(deadconn);
-	if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS)
-		aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn);
 
 	return;
 }
--- a/src/protocols/oscar/ft.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/ft.c	Wed Feb 26 05:01:37 2003 +0000
@@ -1,6 +1,41 @@
 /*
- * File transfer (OFT) and DirectIM (ODC).
- * (OSCAR File Transfer, Oscar Direct Connect(ion?)
+ * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
+ * (ODC is also referred to as DirectIM and IM Image.)
+ *
+ * There are a few static helper functions at the top, then 
+ * ODC stuff, then ft stuff.
+ *
+ * I feel like this is a good place to explain OFT, so I'm going to 
+ * do just that.  Each OFT packet has a header type.  I guess this 
+ * is pretty similar to the subtype of a SNAC packet.  The type 
+ * basically tells the other client the meaning of the OFT packet.  
+ * There are two distinct types of file transfer, which I usually 
+ * call "sendfile" and "getfile."  Sendfile is when you send a file 
+ * to another AIM user.  Getfile is when you share a group of files, 
+ * and other users request that you send them the files.
+ *
+ * A typical sendfile file transfer goes like this:
+ *   1) Sender sends a channel 2 ICBM telling the other user that 
+ *      we want to send them a file.  At the same time, we open a 
+ *      listener socket (this should be done before sending the 
+ *      ICBM) on some port, and wait for them to connect to us.  
+ *      The ICBM we sent should contain our IP address and the port 
+ *      number that we're listening on.
+ *   2) The receiver connects to the sender on the given IP address 
+ *      and port.  After the connection is established, the receiver 
+ *      sends another ICBM signifying that we are ready and waiting.
+ *   3) The sender sends an OFT PROMPT message over the OFT 
+ *      connection.
+ *   4) The receiver of the file sends back an exact copy of this 
+ *      OFT packet, except the cookie is filled in with the cookie 
+ *      from the ICBM.  I think this might be an attempt to verify 
+ *      that the user that is connected is actually the guy that 
+ *      we sent the ICBM to.  Oh, I've been calling this the ACK.
+ *   5) The sender starts sending raw data across the connection 
+ *      until the entire file has been sent.
+ *   6) The receiver knows the file is finished because the sender 
+ *      sent the file size in an earlier OFT packet.  So then the 
+ *      receiver sends the DONE thingy and closes the connection.
  */
 
 #define FAIM_INTERNAL
@@ -14,7 +49,7 @@
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <sys/utsname.h> /* for aim_directim_initiate */
+#include <sys/utsname.h> /* for aim_odc_initiate */
 #include <arpa/inet.h> /* for inet_ntoa */
 #define G_DIR_SEPARATOR '/'
 #endif
@@ -23,111 +58,227 @@
 #include "win32dep.h"
 #endif
 
-#define AIM_OFT_PROTO_OFFER          0x0101
-#define AIM_OFT_PROTO_ACCEPT         0x0202
-#define AIM_OFT_PROTO_RESUME         0x0205
-#define AIM_OFT_PROTO_RESUMEACCEPT   0x0207
-#define AIM_OFT_PROTO_ACK            0x0204
-
-struct aim_filetransfer_priv {
-	char sn[MAXSNLEN+1];
-	char cookie[8];
-	char ip[30];
-	int state;
-	struct aim_fileheader_t fh;
-};
-
-struct aim_directim_intdata {
+struct aim_odc_intdata {
 	fu8_t cookie[8];
 	char sn[MAXSNLEN+1];
 	char ip[22];
 };
 
-static int listenestablish(fu16_t portnum);
-static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs);
-static void oft_dirconvert(char *name);
-static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh);
- 
+/**
+ * Convert the directory separator from / (0x2f) to ^A (0x01)
+ *
+ * @param name The filename to convert.
+ */
+static void aim_oft_dirconvert_tostupid(char *name)
+{
+	while (name[0]) {
+		if (name[0] == 0x01)
+			name[0] = G_DIR_SEPARATOR;
+		name++;
+	}
+}
+
+/**
+ * Convert the directory separator from ^A (0x01) to / (0x2f)
+ *
+ * @param name The filename to convert.
+ */
+static void aim_oft_dirconvert_fromstupid(char *name)
+{
+	while (name[0]) {
+		if (name[0] == G_DIR_SEPARATOR)
+			name[0] = 0x01;
+		name++;
+	}
+}
+
+/**
+ * Calculate oft checksum of buffer
+ *
+ * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The 
+ * checksum is kind of a rolling checksum thing, so each time you get bytes 
+ * of a file you just call this puppy and it updates the checksum.  You can 
+ * calculate the checksum of an entire file by calling this in a while or a 
+ * for loop, or something.
+ *
+ * Thanks to Graham Booker for providing this improved checksum routine, 
+ * which is simpler and should be more accurate than Josh Myer's original 
+ * code. -- wtm
+ *
+ * This 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.
+ *
+ * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
+ * @param bufsize Size of buffer.
+ * @param prevcheck Previous checksum.
+ */
+faim_export fu32_t aim_oft_checksum(const unsigned char *buffer, int bufferlen, fu32_t prevcheck)
+{
+	fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
+	int i;
+	unsigned short val;
+
+	for (i=0; i<bufferlen; i++) {
+		oldcheck = check;
+		if (i&1)
+			val = buffer[i];
+		else
+			val = buffer[i] << 8;
+		check -= val;
+		/*
+		 * The following appears to be necessary.... It happens 
+		 * every once in a while and the checksum doesn't fail.
+		 */
+		if (check > oldcheck)
+			check--;
+	}
+	check = ((check & 0x0000ffff) + (check >> 16));
+	check = ((check & 0x0000ffff) + (check >> 16));
+	return check << 16;
+}
+
 /**
- * aim_handlerendconnect - call this to accept OFT connections and set up the required structures
- * @sess: the session
- * @cur: the conn the incoming connection is on
+ * Create a listening socket on a given port.
+ *
+ * XXX - Give the client author the responsibility of setting up a 
+ *       listener, then we no longer have a libfaim problem with broken 
+ *       solaris *innocent smile* -- jbm
  *
- * call this when you get an outstanding read on a conn with subtype
- * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current
- * &aim_conn_t and tweak things as appropriate. the new conn and the
- * listener conn are both returned to the client in the
- * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback.
+ * @param portnum The port number to bind to.
+ * @return The file descriptor of the listening socket.
+ */
+static int listenestablish(fu16_t portnum)
+{
+#if HAVE_GETADDRINFO
+	int listenfd;
+	const int on = 1;
+	struct addrinfo hints, *res, *ressave;
+	char serv[5];
+
+	snprintf(serv, sizeof(serv), "%d", portnum);
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_flags = AI_PASSIVE;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if (getaddrinfo(NULL /* any IP */, serv, &hints, &res) != 0) {
+		perror("getaddrinfo");
+		return -1;
+	} 
+	ressave = res;
+	do { 
+		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+		if (listenfd < 0)
+			continue;
+		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
+			break; /* success */
+		close(listenfd);
+	} while ( (res = res->ai_next) );
+
+	if (!res)
+		return -1;
+
+	freeaddrinfo(ressave);
+#else
+	int listenfd;
+	const int on = 1;
+	struct sockaddr_in sockin;
+
+	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) {
+		perror("setsockopt");
+		close(listenfd);
+		return -1;
+	}
+
+	memset(&sockin, 0, sizeof(struct sockaddr_in));
+	sockin.sin_family = AF_INET;
+	sockin.sin_port = htons(portnum);
+
+	if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
+		perror("bind");
+		close(listenfd);
+		return -1;
+	}
+#endif
+
+	if (listen(listenfd, 4) != 0) {
+		perror("listen");
+		close(listenfd);
+		return -1;
+	}
+	fcntl(listenfd, F_SETFL, O_NONBLOCK);
+
+	return listenfd;
+}
+
+/**
+ * After establishing a listening socket, this is called to accept a connection.  It
+ * clones the conn used by the listener, and passes both of these to a signal handler.
+ * The signal handler should close the listener conn and keep track of the new conn,
+ * since this is what is used for file transfers and what not.
+ *
+ * @param sess The session.
+ * @param cur The conn the incoming connection is on.
+ * @return Return 0 if no errors, otherwise return the error number.
  */
 faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
-{ 
+{
 	int acceptfd = 0;
-	struct sockaddr cliaddr;
-	int clilen = sizeof(cliaddr);
+	struct sockaddr addr;
+	socklen_t addrlen = sizeof(addr);
 	int ret = 0;
 	aim_conn_t *newconn;
+	char ip[20];
+	int port;
 
-	if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
+debug_printf("AAA - We got a bite!  Dude connected to listener\n");
+	if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)
 		return 0; /* not an error */
 
-	if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
+	if (addr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
 		close(acceptfd);
 		aim_conn_close(cur);
 		return -1;
-	} 
+	}
+
+	strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));
+	port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
 
 	if (!(newconn = aim_cloneconn(sess, cur))) {
 		close(acceptfd);
 		aim_conn_close(cur);
-		return -1;
+		return -ENOMEM;
 	}
 
 	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
 	newconn->fd = acceptfd;
 
-	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { 
-		struct aim_directim_intdata *priv;
+	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
 		aim_rxcallback_t userfunc;
+		struct aim_odc_intdata *priv;
 
-		priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal);
+		priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);
 		cur->internal = NULL;
+		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port);
 
-		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", 
-				inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), 
-				ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))
 			ret = userfunc(sess, NULL, newconn, cur);
 
 	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
-#if 0
-		struct aim_filetransfer_priv *priv;
+	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
 		aim_rxcallback_t userfunc;
 
-		newconn->priv = cur->priv;
-		cur->priv = NULL;
-		priv = (struct aim_filetransfer_priv *)newconn->priv;
-
-		snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))
 			ret = userfunc(sess, NULL, newconn, cur);
-#endif
-	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
-		struct aim_filetransfer_priv *ft;
-		aim_rxcallback_t userfunc;
 
-		/* The new conn automatically inherits the internal value
-		 * of cur. */
-		cur->internal = NULL;
-		ft = (struct aim_filetransfer_priv *)newconn->internal;
-
-		snprintf(ft->ip, sizeof(ft->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEINITIATE)))
-			ret = userfunc(sess, NULL, newconn, cur);
-	} else { 
-		faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous Closing conn.\n");
+	} else {
+		faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous.  Closing connection.\n");
 		aim_conn_close(newconn);
 		ret = -1;
 	}
@@ -136,16 +287,16 @@
 }
 
 /**
- * aim_send_typing - send client-to-client typing notification over established connection
- * @sess: session to conn
- * @conn: directim connection
- * @typing: If true, notify user has started typing; if false, notify user has stopped.
+ * Send client-to-client typing notification over an established direct connection.
  *
- * The connection must have been previously established.
+ * @param sess The session.
+ * @param conn The already-connected ODC connection.
+ * @param typing If true, notify user has started typing; if false, notify user has stopped.
+ * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
+faim_export int aim_odc_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_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
 	aim_frame_t *fr;
 	aim_bstream_t *hdrbs;
 	fu8_t *hdr;
@@ -185,7 +336,7 @@
 	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);
@@ -203,36 +354,36 @@
 	aim_tx_enqueue(sess, fr);
 
 	return 0;
-} 
+}
 
 /**
- * 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.
- * @encoding: 0 for ascii, 2 for Unicode, 3 for ISO 8859-1
+ * Send client-to-client IM over an established direct connection.
+ * Call this just like you would aim_send_im, to send a directim.
  * 
- * Call this just like you would aim_send_im, to send a directim. You
- * _must_ have previously established the directim connection.
+ * @param sess The session.
+ * @param conn The already-connected ODC connection.
+ * @param msg Null-terminated string to send.
+ * @param len The length of the message to send, including binary data.
+ * @param encoding 0 for ascii, 2 for Unicode, 3 for ISO 8859-1.
+ * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding)
+faim_export int aim_odc_send_im(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;
+	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
 	int hdrlen = 0x44;
 	fu8_t *hdr;
 
-	if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
-		return -EINVAL; 
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
+		return -EINVAL;
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len)))
-		return -ENOMEM;	
+		return -ENOMEM;
 
 	memcpy(fr->hdr.rend.magic, "ODC2", 4);
 	fr->hdr.rend.hdrlen = hdrlen;
-	
+
 	if (!(hdr = calloc(1, hdrlen + len))) {
 		aim_frame_destroy(fr);
 		return -ENOMEM;
@@ -252,7 +403,7 @@
 	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);
 
@@ -271,419 +422,352 @@
 	aimbs_put16(hdrbs, 0x0000);
 	aimbs_put16(hdrbs, 0x0000);
 	aimbs_put8(hdrbs, 0x00);
-	
+
 	/* end of hdr2 */
-	
-#if 0 /* XXX this is how you send buddy icon info... */	
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
-	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
+
+#if 0 /* XXX - this is how you send buddy icon info... */	
+	aimbs_put16(hdrbs, 0x0008);
+	aimbs_put16(hdrbs, 0x000c);
+	aimbs_put16(hdrbs, 0x0000);
+	aimbs_put16(hdrbs, 0x1466);
+	aimbs_put16(hdrbs, 0x0001);
+	aimbs_put16(hdrbs, 0x2e0f);
+	aimbs_put16(hdrbs, 0x393e);
+	aimbs_put16(hdrbs, 0xcac8);
 #endif
 	aimbs_putraw(hdrbs, msg, len);
-	
+
 	aim_tx_enqueue(sess, fr);
-	
-	return 0;
-} 
-
-static int getlocalip(fu8_t *ip)
-{
-	struct hostent *hptr;
-	char localhost[129];
-
-	/* XXX if available, use getaddrinfo() */
-	/* XXX allow client to specify which IP to use for multihomed boxes */
-
-	if (gethostname(localhost, 128) < 0)
-		return -1;
-
-	if (!(hptr = gethostbyname(localhost)))
-		return -1;
-
-	memcpy(ip, hptr->h_addr_list[0], 4);
 
 	return 0;
 }
 
 /**
- * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves.
- * @sess: your session,
- * @conn: the BOS conn,
- * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one)
- * @destsn: the SN to connect to.
- *
+ * Get the screen name of the peer of a direct connection.
+ * 
+ * @param conn The ODC connection.
+ * @return The screen name of the dude, or NULL if there was an anomaly.
  */
-faim_export aim_conn_t *aim_directim_initiate(aim_session_t *sess, const char *destsn)
-{ 
-	aim_conn_t *newconn;
-	aim_msgcookie_t *cookie;
-	struct aim_directim_intdata *priv;
-	int listenfd;
-	fu16_t port = 4443;
-	fu8_t localip[4];
-	fu8_t ck[8];
-
-	if (getlocalip(localip) == -1)
-		return NULL;
-
-	if ((listenfd = listenestablish(port)) == -1)
-		return NULL;
-
-	aim_request_directim(sess, destsn, localip, port, ck);
-
-	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
-	memcpy(cookie->cookie, ck, 8);
-	cookie->type = AIM_COOKIETYPE_OFTIM;
-
-	/* this one is for the cookie */
-	priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata));
-
-	memcpy(priv->cookie, ck, 8);
-	strncpy(priv->sn, destsn, sizeof(priv->sn));
-	cookie->data = priv;
-	aim_cachecookie(sess, cookie);
-
-	/* XXX switch to aim_cloneconn()? */
-	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
-		close(listenfd);
-		return NULL;
-	}
-
-	/* this one is for the conn */
-	priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata));
-
-	memcpy(priv->cookie, ck, 8);
-	strncpy(priv->sn, destsn, sizeof(priv->sn));
-
-	newconn->fd = listenfd;
-	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-	newconn->internal = priv;
-	newconn->lastactivity = time(NULL);
-
-	faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
-
-	return newconn;
-}
-
-/**
- * aim_sendfile_intitiate - For those times when we want to send the file ourselves.
- * @sess: your session,
- * @conn: the BOS conn,
- * @destsn: the SN to connect to.
- * @filename: the name of the files you want to send
- *
- */
-faim_export aim_conn_t *aim_sendfile_initiate(aim_session_t *sess, const char *destsn, const char *filename, fu16_t numfiles, fu32_t totsize, char *cookret)
-{ 
-	aim_conn_t *newconn;
-	aim_msgcookie_t *cookie;
-	struct aim_filetransfer_priv *ft;
-	int listenfd;
+faim_export const char *aim_odc_getsn(aim_conn_t *conn)
+{
+	struct aim_odc_intdata *intdata;
 
-	/* XXX allow different ports */
-	fu16_t port = 4443;
-	fu8_t localip[4];
-	fu8_t ck[8];
-
-	if (getlocalip(localip) == -1)
-		return NULL;
-
-	if ((listenfd = listenestablish(port)) == -1)
-		return NULL;
-
-	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);
-	cookie->type = AIM_COOKIETYPE_OFTSEND;
-	memcpy(cookret, ck, 8);
-
-	/* this one is for the cookie */
-	ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
-
-	memcpy(ft->cookie, ck, 8);
-	strncpy(ft->sn, destsn, sizeof(ft->sn));
-	cookie->data = ft;
-	aim_cachecookie(sess, cookie);
-
-	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
-		close(listenfd);
-		return NULL;
-	}
-
-	/* this one is for the conn */
-	ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
-
-	memcpy(ft->cookie, ck, 8);
-	strncpy(ft->sn, destsn, sizeof(ft->sn));
-
-	newconn->fd = listenfd;
-	newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
-	newconn->internal = ft;
-	newconn->lastactivity = time(NULL);
-
-	faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
-
-	return newconn;
-}
-
-#if 0
-/**
- * unsigned int aim_oft_listener_clean - close up old listeners
- * @sess: session to clean up in
- * @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 const char *aim_directim_getsn(aim_conn_t *conn)
-{
-	struct aim_directim_intdata *intdata;
-
-	if (!conn)
+	if (!conn || !conn->internal)
 		return NULL;
 
 	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
 			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
 		return NULL;
 
-	if (!conn->internal)
-		return NULL;
-
-	intdata = (struct aim_directim_intdata *)conn->internal;
+	intdata = (struct aim_odc_intdata *)conn->internal;
 
 	return intdata->sn;
 }
 
 /**
- * aim_directim_connect - connect to buddy for directim
- * @sess: the session to append the conn to,
- * @sn: the SN we're connecting to
- * @addr: address to connect to
+ * Find the conn of a direct connection with the given buddy.
+ *
+ * @param sess The session.
+ * @param sn The screen name of the buddy whose direct connection you want to find.
+ * @return The conn for the direct connection with the given buddy, or NULL if no 
+ *         connection was found.
+ */
+faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
+{
+	aim_conn_t *cur;
+	struct aim_odc_intdata *intdata;
+
+	if (!sess || !sn || !strlen(sn))
+		return NULL;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+			intdata = cur->internal;
+			if (!aim_sncmp(intdata->sn, sn))
+				return cur;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * For those times when we want to open up the direct connection channel ourselves.
+ *
+ * You'll want to set up some kind of watcher on this socket.  
+ * When the state changes, call aim_handlerendconnection with 
+ * the connection returned by this.  aim_handlerendconnection 
+ * will accept the pending connection and stop listening.
+ *
+ * @param sess The session
+ * @param conn The BOS conn.
+ * @param priv A dummy priv value (we'll let it get filled in later)
+ *             (if you pass a %NULL, we alloc one).
+ * @param sn The screen name to connect to.
+ * @return The new connection.
+ */
+faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn)
+{
+	aim_conn_t *newconn;
+	aim_msgcookie_t *cookie;
+	struct aim_odc_intdata *priv;
+	int listenfd;
+	fu16_t port = 4443;
+	fu8_t localip[4];
+	fu8_t ck[8];
+
+	if (aim_util_getlocalip(localip) == -1)
+		return NULL;
+
+	if ((listenfd = listenestablish(port)) == -1)
+		return NULL;
+
+	aim_im_sendch2_odcrequest(sess, ck, sn, localip, port);
+
+	cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
+	memcpy(cookie->cookie, ck, 8);
+	cookie->type = AIM_COOKIETYPE_OFTIM;
+
+	/* this one is for the cookie */
+	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, sn, sizeof(priv->sn));
+	cookie->data = priv;
+	aim_cachecookie(sess, cookie);
+
+	/* XXX - switch to aim_cloneconn()? */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
+		close(listenfd);
+		return NULL;
+	}
+
+	/* this one is for the conn */
+	priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, sn, sizeof(priv->sn));
+
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = priv;
+	newconn->lastactivity = time(NULL);
+
+	return newconn;
+}
+
+/**
+ * Connect directly to the given buddy for directim.
  *
  * This is a wrapper for aim_newconn.
  *
  * If addr is NULL, the socket is not created, but the connection is 
  * allocated and setup to connect.
  *
+ * @param sess The Godly session.
+ * @param sn The screen name we're connecting to.  I hope it's a girl...
+ * @param addr Address to connect to.
+ * @return The new connection.
  */
-faim_export aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
-{ 
+faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
+{
 	aim_conn_t *newconn;
-	struct aim_directim_intdata *intdata;
+	struct aim_odc_intdata *intdata;
 
 	if (!sess || !sn)
 		return NULL;
 
-	if (!(intdata = malloc(sizeof(struct aim_directim_intdata))))
+	if (!(intdata = malloc(sizeof(struct aim_odc_intdata))))
 		return NULL;
-	memset(intdata, 0, sizeof(struct aim_directim_intdata));
-
+	memset(intdata, 0, sizeof(struct aim_odc_intdata));
 	memcpy(intdata->cookie, cookie, 8);
 	strncpy(intdata->sn, sn, sizeof(intdata->sn));
 	if (addr)
 		strncpy(intdata->ip, addr, sizeof(intdata->ip));
 
-	/* XXX verify that non-blocking connects actually work */
+	/* XXX - verify that non-blocking connects actually work */
 	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
 		free(intdata);
 		return NULL;
 	}
 
-	if (!newconn) {
-		free(intdata);
-		return newconn;
-	}
-
+	newconn->internal = intdata;
 	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-	newconn->internal = intdata;
 
 	return newconn;
-} 
+}
 
 /**
- * aim_directim_getconn - find a directim conn for buddy name
- * @sess: your session,
- * @name: the name to get,
+ * Creates a listener socket so the other dude can connect to us.
  *
- * returns conn for directim with name, %NULL if none found. 
+ * You'll want to set up some kind of watcher on this socket.  
+ * When the state changes, call aim_handlerendconnection with 
+ * the connection returned by this.  aim_handlerendconnection 
+ * will accept the pending connection and stop listening.
  *
+ * @param sess The session.
+ * @param cookie This better be Mrs. Fields or I'm going to be pissed.
+ * @param ip Should be 4 bytes,  each byte is 1 quartet of the IP address.
+ * @param port Ye olde port number to listen on.
+ * @return Return the new conn if everything went as planned.  Otherwise, 
+ *         return NULL.
  */
-faim_export aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name)
+faim_export aim_conn_t *aim_sendfile_listen(aim_session_t *sess, const fu8_t *cookie, const fu8_t *ip, fu16_t port)
 {
-	aim_conn_t *cur;
+	aim_conn_t *newconn;
+	int listenfd;
 
-	if (!sess || !name || !strlen(name))
+debug_printf("AAA - listening on port %d\n", port);
+	if ((listenfd = listenestablish(port)) == -1)
 		return NULL;
 
-	for (cur = sess->connlist; cur; cur = cur->next) {
-		struct aim_directim_intdata *intdata;
-		
-		if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
-			continue;
-
-		intdata = cur->internal;
-
-		if (aim_sncmp(intdata->sn, name) == 0)
-			break;
-	}
-
-	return cur;
-} 
-
-/**
- * aim_accepttransfer - accept a file transfer request
- * @sess: the session,
- * @conn: the BOS conn for the CAP reply
- * @sn: the screenname to send it to,
- * @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)
- *
- * @listingfiles: number of files to share
- * @listingtotsize: total size of shared files
- * @listingsize: length of the listing file(buffer)
- * @listingchecksum: checksum of the listing
- *
- * Returns new connection or %NULL on error.
- *
- * 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, ...)
-{
-	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;
-	}
-
-	/* 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))) {
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
+		close(listenfd);
 		return NULL;
 	}
 
-	aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next);
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+	newconn->lastactivity = time(NULL);
 
-	for (i = 0; i < 8; i++)
-		aimbs_put8(&newpacket->data, cookie[i]);
+	return newconn;
+}
 
-	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);
+/**
+ * Extract an &aim_fileheader_t from the given buffer.
+ *
+ * @param bs The should be from an incoming rendezvous packet.
+ * @return A pointer to new struct on success, or NULL on error.
+ */
+static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
+{
+	struct aim_fileheader_t *fh;
 
-	for (i = 0; i < 8; i++) /* yes, again... */
-		aimbs_put8(&newpacket->data, cookie[i]);
-
-	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 (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+		return NULL;
 
-	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;
-	}
+	/* The bstream should be positioned after the hdrtype. */
+	aimbs_getrawbuf(bs, fh->bcookie, 8);
+	fh->encrypt = aimbs_get16(bs);
+	fh->compress = aimbs_get16(bs);
+	fh->totfiles = aimbs_get16(bs);
+	fh->filesleft = aimbs_get16(bs);
+	fh->totparts = aimbs_get16(bs);
+	fh->partsleft = aimbs_get16(bs);
+	fh->totsize = aimbs_get32(bs);
+	fh->size = aimbs_get32(bs);
+	fh->modtime = aimbs_get32(bs);
+	fh->checksum = aimbs_get32(bs);
+	fh->rfrcsum = aimbs_get32(bs);
+	fh->rfsize = aimbs_get32(bs);
+	fh->cretime = aimbs_get32(bs);
+	fh->rfcsum = aimbs_get32(bs);
+	fh->nrecvd = aimbs_get32(bs);
+	fh->recvcsum = aimbs_get32(bs);
+	aimbs_getrawbuf(bs, 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 - filenames longer than 64B */
 
-	priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
+	return fh;
+} 
 
-	memcpy(priv->cookie, cookie, 8);
-	priv->state = 0;
-	strncpy(priv->sn, sn, MAXSNLEN);
-	strncpy(priv->ip, ip, sizeof(priv->ip));
-	newconn->internal = (void *)priv;
+/**
+ * Fills a buffer with network-order fh data
+ *
+ * @param bs A bstream to fill -- automatically initialized
+ * @param fh A struct aim_fileheader_t to get data from.
+ * @return Return non-zero on error.
+ */
+static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
+{ 
+	fu8_t *hdr;
 
-	faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+	if (!bs || !fh)
+		return -EINVAL;
 
-	if (rendid == AIM_CAPS_GETFILE) {
-		return NULL; /* This should never happen for now. -- wtm */
+	if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
+		return -ENOMEM;
 
-#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, */
-
-	newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+	aim_bstream_init(bs, hdr, 0x100 - 8);
+	aimbs_putraw(bs, fh->bcookie, 8);
+	aimbs_put16(bs, fh->encrypt);
+	aimbs_put16(bs, fh->compress);
+	aimbs_put16(bs, fh->totfiles);
+	aimbs_put16(bs, fh->filesleft);
+	aimbs_put16(bs, fh->totparts);
+	aimbs_put16(bs, fh->partsleft);
+	aimbs_put32(bs, fh->totsize);
+	aimbs_put32(bs, fh->size);
+	aimbs_put32(bs, fh->modtime);
+	aimbs_put32(bs, fh->checksum);
+	aimbs_put32(bs, fh->rfrcsum);
+	aimbs_put32(bs, fh->rfsize);
+	aimbs_put32(bs, fh->cretime);
+	aimbs_put32(bs, fh->rfcsum);
+	aimbs_put32(bs, fh->nrecvd);
+	aimbs_put32(bs, fh->recvcsum);
+	aimbs_putraw(bs, 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 */
 
-	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;
-	} 
-
-	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-	newoft->hdr.oft.hdr2len = 0x100 - 8;
+	return 0;
+}
 
-	if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) {
-		/* XXX: conn leak here */
-		perror("calloc");
-		return NULL;
-	}
+/**
+ * Create an OFT packet based on the given information, and send it on its merry way.
+ *
+ * @param sess The session.
+ * @param conn The already-connected OFT connection.
+ * @param cookie The cookie associated with this file transfer.
+ * @param filename The filename.
+ * @param filesdone Number of files already transferred.
+ * @param numfiles Total number of files.
+ * @param size Size in bytes of this file.
+ * @param totsize Size in bytes of all files combined.
+ * @param checksum Funky checksum of this file.
+ * @param flags Any flags you want, baby.  Send 0x21 when sending the 
+ *        "AIM_CB_OFT_DONE" message, and "0x02" for everything else.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_oft_sendheader(aim_session_t *sess, aim_conn_t *conn, fu16_t type, const fu8_t *cookie, const char *filename, fu16_t filesdone, fu16_t numfiles, fu32_t size, fu32_t totsize, fu32_t modtime, fu32_t checksum, fu8_t flags)
+{
+	aim_frame_t *newoft;
+	struct aim_fileheader_t *fh;
 
-	fh->encrypt = 0x0000;
-	fh->compress = 0x0000;
-	fh->totfiles = listingfiles;
-	fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/
-	fh->totparts = 0x0001;
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !filename)
+		return -EINVAL;
+
+	if (!(fh = (struct aim_fileheader_t *)calloc(1, sizeof(struct aim_fileheader_t))))
+		return -ENOMEM;
+
+	/*
+	 * If you are receiving a file, the cookie should be null, if you are sending a 
+	 * file, the cookie should be the same as the one used in the ICBM negotiation 
+	 * SNACs.
+	 */
+	if (cookie)
+		memcpy(fh->bcookie, cookie, 8);
+	fh->totfiles = numfiles;
+	fh->filesleft = numfiles - filesdone;
+	fh->totparts = 0x0001; /* set to 0x0002 sending Mac resource forks */
 	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));
+	fh->totsize = totsize;
+	fh->size = size;
+	fh->modtime = modtime;
+	fh->checksum = checksum;
+
 	strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
 	fh->flags = 0x02;
 	fh->lnameoffset = 0x1a;
@@ -691,461 +775,45 @@
 	memset(fh->dummy, 0, sizeof(fh->dummy));
 	memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
 
-	/* we need to figure out these encodings for filenames */
+	/* apparently 0 is ASCII, 2 is UCS-2 */
+	/* it is likely that 3 is ISO 8859-1 */
 	fh->nencode = 0x0000;
 	fh->nlanguage = 0x0000;
-	memset(fh->name, 0, sizeof(fh->name));
-	strncpy(fh->name, "listing.txt", sizeof(fh->name));
+
+	strncpy(fh->name, filename, sizeof(fh->name));
+	aim_oft_dirconvert_tostupid(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 = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, type, 0))) {
+		free(fh);
+		return -ENOMEM;
 	}
 
-	memcpy(fh->bcookie, cookie, 8);
+	if (aim_oft_buildheader(&newoft->data, fh) == -1) {
+		aim_frame_destroy(newoft);
+		free(fh);
+		return -ENOMEM;
+	}
 
-	if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
-		faimdprintf(sess, 1, "eek, bh fail!\n");
+	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
+	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
 
 	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;
-	}
-
-	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;
-
-	if (aim_cachecookie(sess, cachedcook) == -1)
-		faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
-
 	free(fh);
-#endif
-
-	} else if (rendid == AIM_CAPS_SENDFILE) {
-		newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
-		priv->fh.recvcsum = 0xffff0000;
-	} else {
-		return NULL;
-	}
-
-	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;
-
-	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);
-
-	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);
-
-	for (i = 0; i < 8; i++)
-		aimbs_put8(&newpacket->data, cookie[i]);
-
-	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
- * 
- * returns a pointer to the filled-in fileheader_t
- *
- * Currently omits checksum. we'll fix this when AOL breaks us, i
- * guess.
+ * Sometimes you just don't know with these kinds of people.
  *
- */
-faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file) 
-{
-	return NULL;
-#if 0
-	struct aim_fileheader_t *fh;
-	u_long totsize = 0, size = 0, checksum = 0xffff0000;
-	short totfiles = 0;
-	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;
-	}
-
-	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 (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);
-
-	size = 0;
-
-	while(fgets(linebuf, linelength, file)) {
-		totfiles++;
-		memset(sizebuf, 0, 9);
-
-		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");
-		}
-
-		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");
-	}
-
-	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. */
-
-	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;
-
-	/* 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;
-
-	/* 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;
-
-	/* 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;
-#endif
-}
-
-/**
- * aim_listenestablish - create a listening socket on a port.
- * @portnum: the port number to bind to.
- *
- * you need to call accept() when it's connected. returns your fd 
- *
- * XXX: give the client author the responsibility of setting up a
- * listener, then we no longer have a libfaim problem with broken
- * solaris *innocent smile* -jbm
+ * @param sess The session.
+ * @param conn The ODC connection of the incoming data.
+ * @param frr The frame allocated for the incoming data.
+ * @param bs It stands for "bologna sandwich."
+ * @return Return 0 if no errors, otherwise return the error number.
  */
-static int listenestablish(fu16_t portnum)
-{
-#if HAVE_GETADDRINFO
-	int listenfd;
-	const int on = 1;
-	struct addrinfo hints, *res, *ressave;
-	char serv[5];
-
-	snprintf(serv, sizeof(serv), "%d", portnum);
-	memset(&hints, 0, sizeof(struct addrinfo));
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-	if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
-		perror("getaddrinfo");
-		return -1;
-	} 
-	ressave = res;
-	do { 
-		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-		if (listenfd < 0)
-			continue;
-		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
-			break;
-		/* success */
-		close(listenfd);
-	} while ( (res = res->ai_next) );
-
-	if (!res)
-		return -1;
-
-	if (listen(listenfd, 1024)!=0) { 
-		perror("listen");
-		return -1;
-	} 
-
-	fcntl(listenfd, F_SETFL, O_NONBLOCK);
-
-	freeaddrinfo(ressave);
-	return listenfd;
-#else
-	int listenfd;
-	const int on = 1;
-	struct sockaddr_in sockin;
-
-	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		perror("socket(listenfd)");
-		return -1;
-	}
-
-	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) {
-		perror("setsockopt(listenfd)");
-		close(listenfd);
-		return -1;
-	} 
-
-	memset(&sockin, 0, sizeof(struct sockaddr_in));
-	sockin.sin_family = AF_INET;
-	sockin.sin_port = htons(portnum);
-
-	if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
-		perror("bind(listenfd)");
-		close(listenfd);
-		return -1;
-	}
-	if (listen(listenfd, 4) != 0) {
-		perror("listen(listenfd)");
-		close(listenfd);
-		return -1;
-	}
-	fcntl(listenfd, F_SETFL, O_NONBLOCK);
-	return listenfd;
-#endif
-} 
-
-static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn)
-{
-#if 0
-	struct aim_filetransfer_priv *ft;
-	aim_rxcallback_t userfunc;
-
-	ft = conn->priv;
-	if (ft->state == 2) {
-		/* waiting on listing data */
-		int ret = 0;
-		char *listing;
-		aim_frame_t *newoft;
-
-		if (!(listing = malloc(ft->fh.size)))
-			return -1;
-
-		ft->state = 0;
-		if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)	
-			faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
-
-		if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
-			faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
-			free(listing);
-			aim_conn_close(conn);
-			return -1;
-		}
-
-		memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-		newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-		/* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */
-
-		ft->fh.nrecvd = ft->fh.size;
-		ft->fh.recvcsum = ft->fh.checksum;
-		ft->fh.flags = 0;
-
-		if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-			aim_frame_destroy(newoft);
-			free(listing);
-			return -1;
-		}
-
-		if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
-			faimdprintf(sess, 2, "eek! bh fail listing\n");
-
-		/* send the 120b */
-		aim_tx_enqueue(sess, newoft);
-		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
-			ret = userfunc(sess, NULL, conn, ft, listing);
-
-		free(listing);
-		return ret;
-	}
-
-	if (ft->state == 3) { 
-		/* waiting on file data */
-		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) 
-			return userfunc(sess, NULL, conn, ft->fh.name,
-					ft->fh.size);
-		return 0;
-	}
-
-	if (ft->state == 4) {
-		if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) )
-			return userfunc(sess, NULL, conn);
-		aim_conn_close(conn);
-		return 0;
-	}	
-
-	return 0;
-#else
-	return -1;
-#endif
-}
-
-static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn)
-{
-	aim_msgcookie_t *cook;
-	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->internal;
-
-	cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
-	aim_cookie_free(sess, cook);
-
-	return;
-}
-
-static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn)
-{
-	free(conn->internal);
-
-	return;
-}
-
-static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn)
-{
-#if 0
-	aim_msgcookie_t *cook;
-	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
-
-	cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
-	aim_cookie_free(sess, cook);
-#endif
-	return;
-}
-
-static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn)
-{
-	
-	free(conn->internal);
-
-	return;
-}
-
-static void connclose_directim(aim_session_t *sess, aim_conn_t *conn)
-{
-	struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
-	aim_msgcookie_t *cook;
-
-	cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM);
-	aim_cookie_free(sess, cook);
-
-	return;
-}
-
-static void connkill_directim(aim_session_t *sess, aim_conn_t *conn)
-{
-	
-	free(conn->internal);
-
-	return;
-}
-
-faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn)
-{
-
-	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
-		return;
-
-	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
-		connclose_sendfile(sess, conn);
-	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
-		connclose_getfile(sess, conn);
-	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
-		connclose_directim(sess, conn);
-
-	return;
-}
-
-faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn)
-{
-
-	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
-		return;
-
-	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
-		connkill_sendfile(sess, conn);
-	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
-		connkill_getfile(sess, conn);
-	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
-		connkill_directim(sess, conn);
-
-	return;
-}
-
-static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs)
+static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
 {
 	aim_frame_t fr;
 	aim_rxcallback_t userfunc;
@@ -1155,7 +823,7 @@
 
 	fr.conn = conn;
 
-	/* XXX ugly */
+	/* AAA - ugly */
 	aim_bstream_setpos(bs, 20);
 	payloadlength = aimbs_get32(bs);
 
@@ -1166,10 +834,10 @@
 	flags = aimbs_get16(bs);
 
 	aim_bstream_setpos(bs, 36);
-	/* XXX -create an aimbs_getnullstr function? */
+	/* 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);
+	faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
 
 	if (flags & 0x0002) {
 		int ret = 0;
@@ -1206,7 +874,7 @@
 			}
 			recvd = recvd + i;
 			msg2 = msg2 + i;
-			if ((userfunc=aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
+			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
 				userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
 		}
 		
@@ -1221,1050 +889,61 @@
 	return 0;
 }
 
-static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_filetransfer_priv *ft;
-	struct aim_fileheader_t *fh;
-	struct aim_msgcookie_t *cook;
-	aim_frame_t *newoft;
-	aim_rxcallback_t userfunc;
-
-	faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
-	fh = aim_oft_getfh(hdr);
-
-	faim_mutex_unlock(&conn->active);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-		free(fh);
-		return -1;
-	}
-
-	ft = cook->data;
-
-	/* we're waaaaiiiting.. for listing.txt */
-	ft->state = 2;
-
-	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-	free(fh);
-
-	if(aim_cachecookie(sess, cook) == -1) {
-		faimdprintf(sess, 1, "error caching cookie\n");
-		return -1;
-	}
-
-	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
-		aim_conn_close(conn);
-		return -1;
-	}
-
-	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-	newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-	if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-		newoft->lock = 0;
-		aim_frame_destroy(newoft);
-		return -1;
-	}
-
-	if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
-		newoft->lock = 0;
-		aim_frame_destroy(newoft);
-		return -1;
-	}
-
-	newoft->lock = 0;
-	aim_tx_enqueue(sess, newoft);
-#endif
-	return -1;
-}
-
-static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_filetransfer_priv *ft;
-	struct aim_fileheader_t *fh;
-	struct aim_msgcookie_t *cook;
-	int ret = 0;
-	aim_rxcallback_t userfunc;
-	
-	fh = aim_oft_getfh(hdr);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
-		faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", AIM_COOKIETYPE_OFTGET, fh->bcookie);
-
-	ft = cook->data;
-
-	if (ft->fh.size != fh->size)
-		faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size);
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
-		ret = userfunc(sess, NULL, conn, fh);
-
-	faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
-
-	free(fh);
-
-	return ret;
-#else
-	return -1;
-#endif
-}
-
-static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_filetransfer_priv *ft;
-	struct aim_msgcookie_t *cook;
-	struct aim_fileheader_t *fh;
-	aim_rxcallback_t userfunc;
-
-	fh = aim_oft_getfh(hdr);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-		free(fh);
-		return -1;
-	}
-
-	free(fh);
-
-	ft = cook->data;
-
-	if (aim_cachecookie(sess, cook) == -1)
-		return -1;
-
-	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
-		return userfunc(sess, NULL, conn);
-#endif
-	return -1;
-}
-
-static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_filetransfer_priv *ft;
-	aim_msgcookie_t *cook;
-	struct aim_fileheader_t *fh;
-	aim_frame_t *newoft;
-	int i = 0;
-	aim_rxcallback_t userfunc;
-
-	fh = aim_oft_getfh(hdr);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-		free(fh);
-		return -1;
-	}
-
-	ft = cook->data;
-	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-	free(fh);
-
-	aim_cachecookie(sess, cook);
-
-	faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
-		i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
-
-	if (i < 0)
-		return i;
-
-	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
-		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
-		return -1;
-	}
-
-	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-	newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
-		aim_frame_destroy(newoft);
-		return -1;
-	} 
-
-	/* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
-	ft->fh.nrecvd = 0;
-	ft->fh.recvcsum = 0;
-	ft->fh.flags = 0x20;
-
-	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
-
-	aim_tx_enqueue(sess, newoft);
-
-	faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
-
-	return i;
-#else
-	return -1;
-#endif
-}
-
-static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_fileheader_t *fh;
-	struct aim_filetransfer_priv *ft;
-	struct aim_msgcookie_t *cook;
-	struct command_tx_struct *newoft;
-	aim_rxcallback_t userfunc;
-
-	fh = aim_oft_getfh(hdr);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-		free(fh);
-		return -1;
-	}
-
-	free(fh);
-
-	ft = cook->data;
-
-	ft->state = 3;
-
-	if (aim_cachecookie(sess, cook) == -1)
-		return -1;
-
-	faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
-
-	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
-		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
-		return -1;
-	}
-
-	newoft->lock = 1;
-	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-
-	newoft->hdr.oft.hdr2len = 0x100 - 8;
-
-	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
-		aim_frame_destroy(newoft);
-		return -1;
-	}
-
-	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
-
-	newoft->lock = 0;
-	aim_tx_enqueue(sess, newoft);
-
-	faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
-		return 1;
-#else
-	return -1;
-#endif
-}
-
-/* We are receiving a file, and the buddy sent us this header describing
- * it.  We send back a similar header to confirm, then we're ready to
- * start reading the raw data.
+/**
+ * Handle incoming data on a rendezvous connection.  This is analogous to the 
+ * consumesnac function in rxhandlers.c, and I really think this should probably 
+ * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
+ *
+ * @param sess The session.
+ * @param fr The frame allocated for the incoming data.
+ * @return Return 0 if the packet was handled correctly, otherwise return the 
+ *         error number.
  */
-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(bs);
-
-	/* We receive a null cookie for the first file; we must fill
-	 * it in to authenticate ourselves. -- wtm
-	 */
-	ft = conn->internal;
-	memcpy(&(fh->bcookie), ft->cookie, 8);
-
-	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-	free(fh);
-
-	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_ACCEPT, 0))) {
-		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
-		return -1;
-	}
-
-	if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) {
-		return -1;
-	}
-	memcpy(newoft->hdr.rend.magic, "OFT2", 4);
-	newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data);
-
-	aim_tx_enqueue(sess, newoft);
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEFILEREQ)) == NULL)
-		return 1;
-
-	{
-		char *cur;
-		/* Convert the directory separator: it is sent
-		 * as ^A (0x01).
-		 */
-		while ((cur = strchr(ft->fh.name, 0x01))) {
-			*cur = G_DIR_SEPARATOR;
-		}
-	}
-	return userfunc(sess, NULL, conn, &(ft->fh));
-}
-
-
-/* 
- * These were originally described by Josh Myer:
- * http://www.geocrawler.com/archives/3/896/2000/9/0/4291064/
- * XXX this doesn't actually work yet
- * -- wtm
- */
-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;
-	}
-
-	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);
-
-	aim_tx_enqueue(sess, newoft);
-	free(fh);
-
-	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, 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(bs);
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
-		free(fh);
-		return -1;
-	}
-	ft = (struct aim_filetransfer_priv *)cook->data;
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEFILESEND)) )
-		ret = userfunc(sess, NULL, conn, &(ft->fh));
-
-	free(fh);
-
-	return ret;
-}
-
-static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_fileheader_t *fh;
-	struct aim_msgcookie_t *cook;
-	int ret = 1;
-	aim_rxcallback_t userfunc;
-	struct aim_filetransfer_priv *ft;
-
-	fh = aim_oft_getfh(hdr);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
-		free(fh);
-		return -1;
-	}
-
-	ft = cook->data;
-
-	faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
-		ret = userfunc(sess, NULL, conn, fh);
-
-	free(fh);
-
-	return ret;
-#else
-	return -1;
-#endif
-}
-
-/* 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, aim_bstream_t *bs)
-{
-	struct aim_fileheader_t *fh;
-	aim_msgcookie_t *cook;
-	aim_rxcallback_t userfunc;
-
-	fh = aim_oft_getfh(bs);
-
-	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) {
-		free(fh);
-		return -1;
-	}
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILECOMPLETE)) )
-		userfunc(sess, NULL, conn, fh->bcookie);
-
-	free(fh);
-	return 0;
-}
-
-static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr)
-{
-#if 0
-	struct aim_fileheader_t *fh;
-	aim_rxcallback_t userfunc;
-
-	fh = aim_oft_getfh(hdr);
-
-	faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
-
-	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
-		userfunc(sess, NULL, conn, fh);
-
-	free(fh);
-#endif
-
-	return -1;
-}
-
 faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
 {
 	aim_conn_t *conn = fr->conn;
-	aim_bstream_t *bs = &fr->data;
-	int ret = -1;
+	int ret = 1;
 
-	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
-		/* This should never happen. -- wtm */
-		return getcommand_getfile(sess, conn);
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
+		if (fr->hdr.rend.type == 0x0001)
+			ret = handlehdr_odc(sess, conn, fr, &fr->data);
+		else
+			faimdprintf(sess, 0, "faim: ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type);
 
-	} 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, bs);
+	} else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+		switch (fr->hdr.rend.type) {
+			case 0x1108: /* getfile listing.txt incoming tx->rx */
+				break;
+			case 0x1209: /* get file listing ack rx->tx */
+				break;
+			case 0x120b: /* get file listing rx confirm */
 				break;
-			case AIM_OFT_PROTO_RESUME:
-				ret = handlehdr_sendfile_resume(sess, conn, bs);
+			case 0x120c: /* getfile request */
+				break;
+			case 0x0101: /* getfile sending data */
 				break;
-			case AIM_OFT_PROTO_RESUMEACCEPT: /* like _ACCEPT */;
-			case AIM_OFT_PROTO_ACCEPT:
-				ret = handlehdr_sendfile_recv(sess, conn, bs);
+			case 0x0202: /* getfile recv data */
 				break;
-			case AIM_OFT_PROTO_ACK:
-				ret = handlehdr_sendfile_finish(sess, conn, bs);
+			case 0x0204: /* getfile finished */
 				break;
 			default:
-				faimdprintf(sess, 2, "faim: OFT frame: uknown type %04x\n", fr->hdr.rend.type);
-				ret = -1;
+				faimdprintf(sess, 2, "faim: OFT getfile frame uknown, type is %04x\n", fr->hdr.rend.type);
 				break;
 		}
 
-	} 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 (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
+		aim_rxcallback_t userfunc;
+		struct aim_fileheader_t *header = aim_oft_getheader(&fr->data);
+		aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */
 
-	} 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;
-		}
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
+			ret = userfunc(sess, fr, conn, header->bcookie, header);
+
+		free(header);
 	}
-	
+
 	if (ret == -1)
 		aim_conn_close(conn);
 
 	return ret;
 }
-
-/**
- * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
- * @bs: bstream to extract header from
- *
- * returns pointer to new struct on success; %NULL on error.
- *
- */
-static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs)
-{
-	struct aim_fileheader_t *fh;
-
-	if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
-		return NULL;
-
-	/* The bstream should be positioned after the hdrtype. */
-	aimbs_getrawbuf(bs, fh->bcookie, 8);
-	fh->encrypt = aimbs_get16(bs);
-	fh->compress = aimbs_get16(bs);
-	fh->totfiles = aimbs_get16(bs);
-	fh->filesleft = aimbs_get16(bs);
-	fh->totparts = aimbs_get16(bs);
-	fh->partsleft = aimbs_get16(bs);
-	fh->totsize = aimbs_get32(bs);
-	fh->size = aimbs_get32(bs);
-	fh->modtime = aimbs_get32(bs);
-	fh->checksum = aimbs_get32(bs);
-	fh->rfrcsum = aimbs_get32(bs);
-	fh->rfsize = aimbs_get32(bs);
-	fh->cretime = aimbs_get32(bs);
-	fh->rfcsum = aimbs_get32(bs);
-	fh->nrecvd = aimbs_get32(bs);
-	fh->recvcsum = aimbs_get32(bs);
-	aimbs_getrawbuf(bs, 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;
-} 
-
-/**
- * aim_oft_checksum - calculate oft checksum of buffer
- * @buffer: buffer of data to checksum
- * @bufsize: size of buffer
- * @prevcheck: previous checksum
- *
- * Prevcheck should be 0xFFFF0000 for each new file; you can have this
- * checksum chunks of files in series if you just call it repeatedly in a
- * for(; ; ) loop and don't reset the checksum between each call. And you
- * thought we didn't care about you and your pathetic client's meomry
- * footprint ;^) 
- *
- * Thanks to Graham Booker for providing this improved checksum
- * routine, which is simpler and should be more accurate than Josh
- * Myer's original code. -- wtm
- *
- * This 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;
-}
-
-faim_export fu32_t aim_update_checksum(aim_session_t *sess, aim_conn_t *conn,
-		const unsigned char *buffer, int len) {
-	struct aim_filetransfer_priv *ft = conn->internal;
-
-	ft->fh.nrecvd += len;
-	ft->fh.recvcsum = aim_oft_checksum(buffer, len, ft->fh.recvcsum);
-
-	return 0;
-}
-
-/**
- * aim_oft_buildheader - fills a buffer with network-order fh data
- * @bs: bstream to fill -- automatically initialized
- * @fh: fh to get data from
- *
- * returns -1 on error.
- *
- */
-static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
-{ 
-	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);
-
-	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;
-}
-
-/**
- * aim_getfile_intitiate - Request an OFT getfile session
- * @sess: your session,
- * @conn: the BOS conn,
- * @destsn is the SN to connect to.
- * 
- * returns a new &aim_conn_t on success, %NULL on error
- */
-faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn)
-{ 
-	return NULL;
-#if 0
-	struct command_tx_struct *newpacket;
-	struct aim_conn_t *newconn;
-	struct aim_filetransfer_priv *priv;
-	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 */
-
-	if ( (listenfd = aim_listenestablish(port)) == -1)
-		return NULL;
-
-	/* 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);
-
-	aim_putcap(cap, 16, AIM_CAPS_GETFILE);
-
-	/* 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;
-
-	/* 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 */
-
-	/* 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);
-
-	/* 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 (!(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;
-
-	aim_cachecookie(sess, cookie);
-
-	/* 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);
-
-	/* 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);
-
-	/* Cookie */
-	curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
-
-	/* 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);
-
-	/* 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 */
- 
-	/* 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);
-
-	/* 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);
-
-	newpacket->commandlen = curbyte;
-	newpacket->lock = 0;
-	aim_tx_enqueue(sess, newpacket);
-
-	/* 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);
-
-	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);
-
-	return newconn;
-#endif
-}
- 
-/**
- * aim_oft_getfile_request - request a particular file over an established getfile connection
- * @sess: your session
- * @conn: the established OFT getfile connection
- * @name: filename to request
- * @size: size of the file 
- *
- *
- * returns -1 on error, 0 on successful enqueuing
- */
-#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;
-
-	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);
-
-	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;
-	}
-
-	aim_tx_enqueue(sess, newoft);
-	return 0;
-}
-#endif
-
-/* Identify a file that we are about to send by transmitting the
- * appropriate header.
- */
-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;
-
-	if (!sess || !conn || !filename)
-		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 = aim_recv(fd, buf, 1024)) > 0) {
-				fh->checksum = aim_oft_checksum(buf, bytes, fh->checksum);
-			}
-		}
-		close(fd);
-	}
-#else
-	fh->checksum = 0x00000000;
-#endif
-	fh->encrypt = 0x0000;
-	fh->compress = 0x0000;
-	fh->totfiles = numfiles;
-	fh->filesleft = numfiles - filesdone;
-	fh->totparts = 0x0001; /* set to 0x0002 sending Mac resource forks */
-	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));
-
-	/* apparently 0 is ASCII, 2 is UCS-2 */
-	/* it is likely that 3 is ISO 8859-1 */
-	fh->nencode = 0x0000;
-	fh->nlanguage = 0x0000;
-
-	/* Convert the directory separator to ^A for portability. */
-	strncpy(fh->name, filename, sizeof(fh->name));
-	oft_dirconvert(fh->name);
-
-	/* 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 (!(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 = 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->data, 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);
-
-	aim_tx_enqueue(sess, newoft);
-	free(fh);
-	return 0;
-}
- 
-/**
- * aim_oft_getfile_ack - acknowledge a getfile download as complete
- * @sess: your session
- * @conn: the getfile conn to send the ack over 
- *
- * Call this function after you have read all the data in a particular
- * filetransfer. Returns -1 on error, 0 on apparent success
- *
- */
-faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn) 
-{
-	return -EINVAL;
-#if 0
-	struct command_tx_struct *newoft;
-	struct aim_filetransfer_priv *ft;
-
-	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;
-	} 
-
-	newoft->lock = 1;
-
-	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-	newoft->hdr.oft.hdr2len = 0x100-8;
-
-	if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
-		newoft->lock = 0;
-		aim_frame_destroy(newoft);
-		return -1;
-	}
-
-	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;
-	}
-
-	newoft->lock = 0;
-	aim_tx_enqueue(sess, newoft);
-	return 0;
-#endif
-}
- 
-/**
- * aim_oft_end - end a getfile/sendfile.
- * @sess: your session
- * @conn: the getfile connection 
- *
- * call this before you close the getfile connection if you're on the
- * receiving/requesting end.
- */
-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;
-
-	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;
-	}
-
-	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;
-}
-
-/*
- * 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	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/icq.c	Wed Feb 26 05:01:37 2003 +0000
@@ -70,20 +70,20 @@
 	return 0;
 }
 
-faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
+faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	int bslen;
 
-	if (!xml || !strlen(xml))
+	if (!passwd)
 		return -EINVAL;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
 		return -EINVAL;
 
-	bslen = 2 + 10 + 2 + strlen(xml) + 1;
+	bslen = 2+4+2+2+2+2+strlen(passwd)+1;
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
 		return -ENOMEM;
@@ -99,9 +99,47 @@
 	aimbs_putle32(&fr->data, atoi(sess->sn));
 	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
 	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x0998); /* shrug. */
-	aimbs_putle16(&fr->data, strlen(xml) + 1);
-	aimbs_putraw(&fr->data, xml, strlen(xml) + 1);
+	aimbs_putle16(&fr->data, 0x042e); /* shrug. */
+	aimbs_putle16(&fr->data, strlen(passwd)+1);
+	aimbs_putraw(&fr->data, passwd, strlen(passwd));
+	aimbs_putle8(&fr->data, '\0');
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!uin || uin[0] < '0' || uin[0] > '9')
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
+	aimbs_putle32(&fr->data, atoi(uin));
 
 	aim_tx_enqueue(sess, fr);
 
@@ -145,20 +183,20 @@
 	return 0;
 }
 
-faim_export int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
+faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	int bslen;
 
-	if (!uin || uin[0] < '0' || uin[0] > '9')
+	if (!xml || !strlen(xml))
 		return -EINVAL;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
 		return -EINVAL;
 
-	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+	bslen = 2 + 10 + 2 + strlen(xml) + 1;
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
 		return -ENOMEM;
@@ -174,16 +212,17 @@
 	aimbs_putle32(&fr->data, atoi(sess->sn));
 	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
 	aimbs_putle16(&fr->data, snacid); /* eh. */
-	aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
-	aimbs_putle32(&fr->data, atoi(uin));
+	aimbs_putle16(&fr->data, 0x0998); /* shrug. */
+	aimbs_putle16(&fr->data, strlen(xml) + 1);
+	aimbs_putraw(&fr->data, xml, strlen(xml) + 1);
 
 	aim_tx_enqueue(sess, fr);
 
 	return 0;
 }
 
-/*
- * Response to 15/2, contains an ICQ packet.
+/**
+ * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
  */
 static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -248,6 +287,10 @@
 		aim_bstream_advance(&qbs, 1); /* 0x0a */
 
 		switch (subtype) {
+		case 0x00aa: { /* password change status */
+			aimbs_getle8(&qbs); /* 0x000a for success */
+		} break;
+
 		case 0x00c8: { /* info summary (useful stuff) */
 			info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
 			info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
--- a/src/protocols/oscar/im.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/im.c	Wed Feb 26 05:01:37 2003 +0000
@@ -1,23 +1,29 @@
 /*
- *  Family 0x0004 - Routines for sending/receiving Instant Messages.
+ * Family 0x0004 - Routines for sending/receiving Instant Messages.
  *
- *  Note the term ICBM (Inter-Client Basic Message) which blankets
- *  all types of genericly routed through-server messages.  Within
- *  the ICBM types (family 4), a channel is defined.  Each channel
- *  represents a different type of message.  Channel 1 is used for
- *  what would commonly be called an "instant message".  Channel 2
- *  is used for negotiating "rendezvous".  These transactions end in
- *  something more complex happening, such as a chat invitation, or
- *  a file transfer.  Channel 3 is used for chat messages (not in 
- *  the same family as these channels).  Channel 4 is used for 
- *  various ICQ messages.  Examples are normal messages, URLs, and 
- *  old-style authorization.
+ * Note the term ICBM (Inter-Client Basic Message) which blankets
+ * all types of genericly routed through-server messages.  Within
+ * the ICBM types (family 4), a channel is defined.  Each channel
+ * represents a different type of message.  Channel 1 is used for
+ * what would commonly be called an "instant message".  Channel 2
+ * is used for negotiating "rendezvous".  These transactions end in
+ * something more complex happening, such as a chat invitation, or
+ * a file transfer.  Channel 3 is used for chat messages (not in 
+ * the same family as these channels).  Channel 4 is used for 
+ * various ICQ messages.  Examples are normal messages, URLs, and 
+ * old-style authorization.
  *
- *  In addition to the channel, every ICBM contains a cookie.  For
- *  standard IMs, these are only used for error messages.  However,
- *  the more complex rendezvous messages make suitably more complex
- *  use of this field.
+ * In addition to the channel, every ICBM contains a cookie.  For
+ * standard IMs, these are only used for error messages.  However,
+ * the more complex rendezvous messages make suitably more complex
+ * use of this field.
  *
+ * TODO: Split this up into an im.c file an an icbm.c file.  It 
+ *       will be beautiful, you'll see.
+ *
+ *       Need to rename all the mpmsg messages to aim_im_bleh.
+ *
+ *       Make sure aim_conn_findbygroup is used by all functions.
  */
 
 #define FAIM_INTERNAL
@@ -27,6 +33,25 @@
 #include "win32dep.h"
 #endif
 
+/**
+ * Add a standard ICBM header to the given bstream with the given 
+ * information.
+ *
+ * @param bs The bstream to write the ICBM header to.
+ * @param c c is for cookie, and cookie is for me.
+ * @param ch The ICBM channel (1 through 4).
+ * @param sn Null-terminated scrizeen nizame.
+ * @return The number of bytes written.  It's really not useful.
+ */
+static int aim_im_puticbm(aim_bstream_t *bs, const fu8_t *c, fu16_t ch, const char *sn)
+{
+	aimbs_putraw(bs, c, 8);
+	aimbs_put16(bs, ch);
+	aimbs_put8(bs, strlen(sn));
+	aimbs_putraw(bs, sn, strlen(sn));
+	return 8+2+1+strlen(sn);
+}
+
 /*
  * Takes a msghdr (and a length) and returns a client type
  * code.  Note that this is *only a guess* and has a low likelihood
@@ -51,7 +76,7 @@
  * the rest will always be the same.
  *
  */
-faim_export fu16_t aim_fingerprintclient(fu8_t *msghdr, int len)
+faim_export fu16_t aim_im_fingerprint(fu8_t *msghdr, int len)
 {
 	static const struct {
 		fu16_t clientid;
@@ -91,14 +116,14 @@
 	return AIM_CLIENTTYPE_UNKNOWN;
 }
 
-/*
- * Subtype 0x0002
+/** 
+ * Subtype 0x0002 - Set ICBM parameters.
  *
  * I definitly recommend sending this.  If you don't, you'll be stuck
- * with the rather unreasonable defaults.  You don't want those.  Send this.
+ * with the rather unreasonable defaults.
  * 
  */
-faim_export int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
+faim_export int aim_im_setparams(aim_session_t *sess, struct aim_icbmparameters *params)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
@@ -131,8 +156,11 @@
 	return 0;
 }
 
-/* Subtype 0x0004 - Request ICBM parameter information. */
-faim_export int aim_reqicbmparams(aim_session_t *sess)
+/**
+ * Subtype 0x0004 - Request ICBM parameter information.
+ *
+ */
+faim_export int aim_im_reqparams(aim_session_t *sess)
 {
 	aim_conn_t *conn;
 
@@ -142,11 +170,14 @@
 	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)
+/**
+ * Subtype 0x0005 - Receive parameter information.
+ *
+ */
+static int aim_im_paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
+	aim_rxcallback_t userfunc;
 	struct aim_icbmparameters params;
-	aim_rxcallback_t userfunc;
 
 	params.maxchan = aimbs_get16(bs);
 	params.flags = aimbs_get32(bs);
@@ -161,23 +192,7 @@
 	return 0;
 }
 
-/* This should be endian-safe now... but who knows... */
-faim_export fu16_t aim_iconsum(const fu8_t *buf, int buflen)
-{
-	fu32_t sum;
-	int i;
-
-	for (i = 0, sum = 0; i + 1 < buflen; i += 2)
-		sum += (buf[i+1] << 8) + buf[i];
-	if (i < buflen)
-		sum += buf[i];
-
-	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
-
-	return (fu16_t)sum;
-}
-
-/*
+/**
  * Subtype 0x0006 - Send an ICBM (instant message).  
  *
  *
@@ -220,19 +235,18 @@
  * supposed to be layed out. Most obviously, tlvlists should be used 
  * instead of writing out the bytes manually. 
  *
- * XXX more precise verification that we never send SNACs larger than 8192
- * XXX check SNAC size for multipart
+ * XXX - more precise verification that we never send SNACs larger than 8192
+ * XXX - check SNAC size for multipart
  *
  */
-faim_export int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args)
+faim_export int aim_im_sendch1_ext(aim_session_t *sess, struct aim_sendimext_args *args)
 {
-	static const fu8_t deffeatures[] = {
-		0x01, 0x01, 0x01, 0x02
-	};
 	aim_conn_t *conn;
-	int i, msgtlvlen;
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
+	int i, msgtlvlen;
+	static const fu8_t deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
 		return -EINVAL;
@@ -275,12 +289,12 @@
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
 		return -ENOMEM;
 
-	/* XXX should be optional */	
+	/* XXX - should be optional */	
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
 	/* 
-	 * Generate a random message cookie 
+	 * Generate a random message cookie
 	 *
 	 * We could cache these like we do SNAC IDs.  (In fact, it 
 	 * might be a good idea.)  In the message error functions, 
@@ -289,32 +303,17 @@
 	 *
 	 */
 	for (i = 0; i < 8; i++)
-		aimbs_put8(&fr->data, (fu8_t) rand());
-
-	/*
-	 * Channel ID
-	 */
-	aimbs_put16(&fr->data, 0x0001);
+		ck[i] = (fu8_t)rand();
 
-	/*
-	 * Destination SN (prepended with byte length)
-	 */
-	aimbs_put8(&fr->data, strlen(args->destsn));
-	aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0001, args->destsn);
 
-	/*
-	 * Message TLV (type 2).
-	 */
+	/* Message TLV (type 0x0002) */
 	aimbs_put16(&fr->data, 0x0002);
 	aimbs_put16(&fr->data, msgtlvlen);
 
-	/*
-	 * Features 
-	 *
-	 */
-	aimbs_put8(&fr->data, 0x05);
-	aimbs_put8(&fr->data, 0x01);
-
+	/* Features TLV (type 0x0501) */
+	aimbs_put16(&fr->data, 0x0501);
 	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
 		aimbs_put16(&fr->data, args->featureslen);
 		aimbs_putraw(&fr->data, args->features, args->featureslen);
@@ -326,6 +325,7 @@
 	if (args->flags & AIM_IMFLAGS_MULTIPART) {
 		aim_mpmsg_section_t *sec;
 
+		/* Insert each message part in a TLV (type 0x0101) */
 		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
 			aimbs_put16(&fr->data, 0x0101);
 			aimbs_put16(&fr->data, sec->datalen + 4);
@@ -336,21 +336,16 @@
 
 	} else {
 
+		/* Insert message text in a TLV (type 0x0101) */
 		aimbs_put16(&fr->data, 0x0101);
 
-		/* 
-		 * Message block length.
-		 */
+		/* Message block length */
 		aimbs_put16(&fr->data, args->msglen + 0x04);
 
-		/*
-		 * Character set.
-		 */
+		/* Character set */
 		if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
-
 			aimbs_put16(&fr->data, args->charset);
 			aimbs_put16(&fr->data, args->charsubset);
-
 		} else {
 			if (args->flags & AIM_IMFLAGS_UNICODE)
 				aimbs_put16(&fr->data, 0x0002);
@@ -362,23 +357,17 @@
 			aimbs_put16(&fr->data, 0x0000);
 		}
 
-		/*
-		 * Message.  Not terminated.
-		 */
+		/* Message.  Not terminated */
 		aimbs_putraw(&fr->data, args->msg, args->msglen);
 	}
 
-	/*
-	 * Set the Request Acknowledge flag.  
-	 */
+	/* Set the Request Acknowledge flag */
 	if (args->flags & AIM_IMFLAGS_ACK) {
 		aimbs_put16(&fr->data, 0x0003);
 		aimbs_put16(&fr->data, 0x0000);
 	}
 
-	/*
-	 * Set the Autoresponse flag.
-	 */
+	/* Set the Autoresponse flag */
 	if (args->flags & AIM_IMFLAGS_AWAY) {
 		aimbs_put16(&fr->data, 0x0004);
 		aimbs_put16(&fr->data, 0x0000);
@@ -414,6 +403,7 @@
 
 	aim_tx_enqueue(sess, fr);
 
+	/* Move this to receive aim_flap_nop and send aim_flap_nop */
 	if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM))
 		aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */
 
@@ -421,43 +411,43 @@
 }
 
 /*
- * Simple wrapper for aim_send_im_ext() 
+ * Simple wrapper for aim_im_sendch1_ext() 
  *
  * You cannot use aim_send_im if you need the HASICON flag.  You must
- * use aim_send_im_ext directly for that.
+ * use aim_im_sendch1_ext directly for that.
  *
  * aim_send_im also cannot be used if you require UNICODE messages, because
- * that requires an explicit message length.  Use aim_send_im_ext().
+ * that requires an explicit message length.  Use aim_im_sendch1_ext().
  *
  */
-faim_export int aim_send_im(aim_session_t *sess, const char *destsn, fu16_t flags, const char *msg)
+faim_export int aim_im_sendch1(aim_session_t *sess, const char *sn, fu16_t flags, const char *msg)
 {
 	struct aim_sendimext_args args;
 
-	args.destsn = destsn;
+	args.destsn = sn;
 	args.flags = flags;
 	args.msg = msg;
 	args.msglen = strlen(msg);
 
-	/* Make these don't get set by accident -- they need aim_send_im_ext */
+	/* Make these don't get set by accident -- they need aim_im_sendch1_ext */
 	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
 
-	return aim_send_im_ext(sess, &args);
+	return aim_im_sendch1_ext(sess, &args);
 }
 
-/*
- * Subtype 0x0006
+/**
+ * Subtype 0x0006 - Send your icon to a given user.
  *
  * This is also performance sensitive. (If you can believe it...)
  *
  */
-faim_export int aim_send_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
+faim_export int aim_im_sendch2_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
 {
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
+	int i;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
 		return -EINVAL;
@@ -466,7 +456,7 @@
 		return -EINVAL;
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
 		return -ENOMEM;
@@ -474,21 +464,8 @@
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	/*
 	 * TLV t(0005)
@@ -531,7 +508,7 @@
 }
 
 /*
- * Subtype 0x0006
+ * Subtype 0x0006 - Send a rich text message.
  *
  * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
  * send it to clients advertising the RTF capability.  In fact, if you send
@@ -545,15 +522,14 @@
  * make an interface similar to what AOL actually uses.  But I'm not using COM.
  *
  */
-faim_export int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
+faim_export int aim_im_sendch2_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
 {
-	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	int servdatalen;
+	fu8_t ck[8];
+	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+	int i, servdatalen;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
 		return -EINVAL;
@@ -564,7 +540,7 @@
 	servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
 		return -ENOMEM;
@@ -572,27 +548,10 @@
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, args->destsn);
 
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(args->destsn));
-	aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
-
-	/*
-	 * TLV t(0005)
-	 *
-	 * Encompasses everything below.
-	 */
+	/* TLV t(0005) - Encompasses everything below. */
 	aimbs_put16(&fr->data, 0x0005);
 	aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
 
@@ -600,22 +559,16 @@
 	aimbs_putraw(&fr->data, ck, 8);
 	aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
 
-	/*
-	 * t(000a) l(0002) v(0001)
-	 */
+	/* t(000a) l(0002) v(0001) */
 	aimbs_put16(&fr->data, 0x000a);
 	aimbs_put16(&fr->data, 0x0002);
 	aimbs_put16(&fr->data, 0x0001);
 
-	/*
-	 * t(000f) l(0000) v()
-	 */
+	/* t(000f) l(0000) v() */
 	aimbs_put16(&fr->data, 0x000f);
 	aimbs_put16(&fr->data, 0x0000);
 
-	/*
-	 * Service Data TLV
-	 */
+	/* Service Data TLV */
 	aimbs_put16(&fr->data, 0x2711);
 	aimbs_put16(&fr->data, servdatalen);
 
@@ -648,13 +601,16 @@
 	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)
+/**
+ * Subtype 0x0006 - Send an "I want to directly connect to you" message
+ *
+ */
+faim_export int aim_im_sendch2_odcrequest(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port)
 {
 	aim_conn_t *conn;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
+	fu8_t ck[8];
 	aim_tlvlist_t *tl = NULL, *itl = NULL;
 	int hdrlen, i;
 	fu8_t *hdr;
@@ -663,7 +619,7 @@
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
 		return -EINVAL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(sn))))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
@@ -675,25 +631,18 @@
 	 * This cookie needs to be alphanumeric and NULL-terminated to be 
 	 * TOC-compatible.
 	 *
-	 * XXX have I mentioned these should be generated in msgcookie.c?
+	 * XXX - have I mentioned these should be generated in msgcookie.c?
 	 *
 	 */
 	for (i = 0; i < 7; i++)
 	       	ck[i] = 0x30 + ((fu8_t) rand() % 10);
 	ck[7] = '\0';
 
-	if (ckret)
-		memcpy(ckret, ck, 8);
-
-	/* Cookie */
-	aimbs_putraw(&fr->data, ck, 8);
+	if (cookie)
+		memcpy(cookie, ck, 8);
 
-	/* Channel */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/* Destination SN */
-	aimbs_put8(&fr->data, strlen(destsn));
-	aimbs_putraw(&fr->data, destsn, strlen(destsn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	aim_addtlvtochain_noval(&tl, 0x0003);
 
@@ -725,15 +674,18 @@
 	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)
+/**
+ * Subtype 0x0006 - Send an "I want to send you this file" message
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_ask(aim_session_t *sess, fu8_t *cookie, const char *sn, const fu8_t *ip, fu16_t port, const char *filename, fu16_t numfiles, fu32_t totsize)
 {
 	aim_conn_t *conn;
-	int i;
-	fu8_t ck[8];
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	struct aim_snac_destructor snacdest;
+	aim_tlvlist_t *tl=NULL, *subtl=NULL;
+	fu8_t *ck;
+	int i;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
 		return -EINVAL;
@@ -741,90 +693,144 @@
 	if (!sn || !filename)
 		return -EINVAL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4)))
+	/* XXX - Should be like "21CBF95" and null terminated */
+	ck = (fu8_t *)malloc(8*sizeof(fu8_t));
+	for (i = 0; i < 7; i++)
+		ck[i] = 0x30 + ((fu8_t)rand() % 10);
+	ck[7] = '\0';
+	if (cookie)
+		memcpy(cookie, ck, 8);
+
+	{ /* Create the subTLV chain */
+		fu8_t *buf;
+		int buflen;
+		aim_bstream_t bs;
+
+		aim_addtlvtochain16(&subtl, 0x000a, 0x0001);
+		aim_addtlvtochain_noval(&subtl, 0x000f);
+/*		aim_addtlvtochain_raw(&subtl, 0x000e, 2, "en");
+		aim_addtlvtochain_raw(&subtl, 0x000d, 8, "us-ascii");
+		aim_addtlvtochain_raw(&subtl, 0x000c, 24, "Please accept this file."); */
+		aim_addtlvtochain_raw(&subtl, 0x0003, 4, ip);
+		aim_addtlvtochain16(&subtl, 0x0005, port);
+
+		/* TLV t(2711) */
+		buflen = 2+2+4+63;
+		buf = malloc(buflen);
+		aim_bstream_init(&bs, buf, buflen);
+		aimbs_put16(&bs, (numfiles > 1) ? 0x0002 : 0x0001);
+		aimbs_put16(&bs, numfiles);
+		aimbs_put32(&bs, totsize);
+
+		/* Filename is a fixed size of 63 bytes, so pad with 0's */
+		aimbs_putraw(&bs, filename, strlen(filename));
+		for (i=0; i<(63-strlen(filename)); i++)
+			aimbs_put8(&bs, 0x00);
+
+		aim_addtlvtochain_raw(&subtl, 0x2711, bs.len, bs.data);
+		free(buf);
+	}
+
+	{ /* Create the main TLV chain */
+		fu8_t *buf;
+		int buflen;
+		aim_bstream_t bs;
+
+		/* TLV t(0005) - Encompasses everything from above. Gee. */
+		buflen = 2+8+16+aim_sizetlvchain(&subtl);
+		buf = malloc(buflen);
+		aim_bstream_init(&bs, buf, buflen);
+		aimbs_put16(&bs, AIM_RENDEZVOUS_PROPOSE);
+		aimbs_putraw(&bs, ck, 8);
+		aim_putcap(&bs, AIM_CAPS_SENDFILE);
+		aim_writetlvchain(&bs, &subtl);
+		aim_freetlvchain(&subtl);
+		aim_addtlvtochain_raw(&tl, 0x0005, bs.len, bs.data);
+		free(buf);
+
+		/* TLV t(0003) - Request an ack */
+		aim_addtlvtochain_noval(&tl, 0x0003);
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(sn) + aim_sizetlvchain(&tl))))
 		return -ENOMEM;
 
-	for (i = 0; i < 7; i++)
-		aimutil_put8(ck+i, 0x30 + ((fu8_t) rand() % 10));
-	ck[7] = '\0';
-
-	if (ckret)
-		memcpy(ckret, ck, 8);
-
-	/* Fill in the snac destructor so we know if the request
-	 * times out.  Use the cookie in the data field, so we
-	 * know what request to cancel if there is an error.
-	 */
-	snacdest.data = malloc(8);
-	memcpy(snacdest.data, ck, 8);
-	snacdest.conn = conn;
-	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, &snacdest, sizeof(snacdest));
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, AIM_SNACFLAGS_DESTRUCTOR, ck, sizeof(ck));
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
-	aimbs_putraw(&fr->data, ck, 8);
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
-	/*
-	 * Channel (2)
-	 */
-	aimbs_put16(&fr->data, 0x0002);
+	/* All that crap from above (the 0x0005 TLV and the 0x0003 TLV) */
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
 
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+/**
+ * Subtype 0x0006 - Send an "I will accept this file" message?
+ *
+ * @param rendid Capability type (AIM_CAPS_GETFILE or AIM_CAPS_SENDFILE)
+ */
+faim_export int aim_im_sendch2_sendfile_accept(aim_session_t *sess, const fu8_t *cookie, const char *sn, fu16_t rendid)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-	/*
-	 * TLV t(0005)
-	 *
-	 * Encompasses everything below. Gee.
-	 */
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4);
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !cookie || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(sn) + 4+2+8+16)))
+		return -ENOMEM;
 
-	aimbs_put16(&fr->data, 0x0000);
-	aimbs_putraw(&fr->data, ck, 8);
-	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
 
-	/* TLV t(000a) */
-	aimbs_put16(&fr->data, 0x000a);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_ACCEPT);
+	aimbs_putraw(&fr->data, cookie, 8);
+	aim_putcap(&fr->data, rendid);
 
-	/* TLV t(0003) (IP) */
-	aimbs_put16(&fr->data, 0x0003);
-	aimbs_put16(&fr->data, 0x0004);
-	aimbs_putraw(&fr->data, ip, 4);
+	aim_tx_enqueue(sess, fr);
 
-	/* TLV t(0005) (port) */
-	aimbs_put16(&fr->data, 0x0005);
-	aimbs_put16(&fr->data, 0x0002);
-	aimbs_put16(&fr->data, port);
+	return 0;
+}
 
-	/* TLV t(000f) */
-	aimbs_put16(&fr->data, 0x000f);
-	aimbs_put16(&fr->data, 0x0000);
+/**
+ * Subtype 0x0006 - Send a "cancel this file transfer" message?
+ *
+ */
+faim_export int aim_im_sendch2_sendfile_cancel(aim_session_t *sess, const fu8_t *cookie, const char *sn, fu16_t rendid)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 
-	/* TLV t(2711) */
-	aimbs_put16(&fr->data, 0x2711);
-	aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4);
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !cookie || !sn)
+		return -EINVAL;
 
-	/* ? */
-	aimbs_put16(&fr->data, (numfiles > 1) ? 0x0002 : 0x0001);
-	aimbs_put16(&fr->data, numfiles);
-	aimbs_put32(&fr->data, totsize);
-	aimbs_putraw(&fr->data, filename, strlen(filename));
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 11+strlen(sn) + 4+2+8+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/* ? */
-	aimbs_put32(&fr->data, 0x00000000);
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, cookie, 0x0002, sn);
 
-#if 0
-	/* Newer clients seem to send this (?) -- wtm */
-	aimbs_put32(&fr->data, 0x00030000);
-#endif
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x001a);
+	aimbs_put16(&fr->data, AIM_RENDEZVOUS_CANCEL);
+	aimbs_putraw(&fr->data, cookie, 8);
+	aim_putcap(&fr->data, rendid);
 
 	aim_tx_enqueue(sess, fr);
 
@@ -840,19 +846,19 @@
  *        state of the user, as one of the AIM_ICQ_STATE_* defines.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type)
+faim_export int aim_im_sendch2_geticqaway(aim_session_t *sess, const char *sn, int type)
 {
 	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
 	int i;
 	fu8_t ck[8];
-	aim_frame_t *fr;
-	aim_snacid_t snacid;
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
 		return -EINVAL;
 
 	for (i = 0; i < 8; i++)
-		aimutil_put8(ck+i, (fu8_t) rand());
+		ck[i] = (fu8_t)rand();
 
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
 		return -ENOMEM;
@@ -860,15 +866,8 @@
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/* Cookie */
-	aimbs_putraw(&fr->data, ck, 8);
-
-	/* Channel (2) */
-	aimbs_put16(&fr->data, 0x0002);
-
-	/* Dest sn */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0002, sn);
 
 	/* TLV t(0005) - Encompasses almost everything below. */
 	aimbs_put16(&fr->data, 0x0005); /* T */
@@ -939,7 +938,7 @@
 }
 
 /**
- * Subtype 0x0006
+ * Subtype 0x0006 - Send an ICQ-esque ICBM.
  *
  * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."  
  * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack, 
@@ -952,12 +951,13 @@
  * @param message The message you want to send, it should be null terminated.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
+faim_export int aim_im_sendch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	int i;
+	char ck[8];
 
 	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
 		return -EINVAL;
@@ -971,22 +971,12 @@
 	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
 
-	/*
-	 * Cookie
-	 */
+	/* Cookie */
 	for (i=0; i<8; i++)
-		aimbs_put8(&fr->data, (fu8_t)rand());
+		ck[i] = (fu8_t)rand();
 
-	/*
-	 * Channel (4)
-	 */
-	aimbs_put16(&fr->data, 0x0004);
-
-	/*
-	 * Dest sn
-	 */
-	aimbs_put8(&fr->data, strlen(sn));
-	aimbs_putraw(&fr->data, sn, strlen(sn));
+	/* ICBM header */
+	aim_im_puticbm(&fr->data, ck, 0x0004, sn);
 
 	/*
 	 * TLV t(0005)
@@ -1019,6 +1009,9 @@
 	return 0;
 }
 
+/*
+ * XXX - I don't see when this would ever get called...
+ */
 static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int i, ret = 0;
@@ -1174,7 +1167,7 @@
 	return 0;
 }
 
-/* XXX should provide a way of saying ISO-8859-1 specifically */
+/* XXX - should provide a way of saying ISO-8859-1 specifically */
 faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
 {
 	fu8_t *dup;
@@ -1277,7 +1270,7 @@
 		 * UNICODE or binary data, as you should never read past
 		 * the specified data length, which will not include the pad.
 		 *
-		 * XXX There's an API bug here.  For sending, the UNICODE is
+		 * XXX - There's an API bug here.  For sending, the UNICODE is
 		 * given in host byte order (aim_mpmsg_addunicode), but here
 		 * the received messages are given in network byte order.
 		 *
@@ -1328,18 +1321,6 @@
 				args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
 			else if (args->charsubset == 0xffff)
 				; /* no subencoding */
-#if 0
-			/* XXX this isn't really necesary... */	
-			if (	((args.flag1 != 0x0000) &&
-				 (args.flag1 != 0x0002) &&
-				 (args.flag1 != 0x0003) &&
-				 (args.flag1 != 0xffff)) ||
-				((args.flag2 != 0x0000) &&
-				 (args.flag2 != 0x000b) &&
-				 (args.flag2 != 0xffff))) {
-				faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
-			}
-#endif
 
 			args->msg = sec->data;
 			args->msglen = sec->datalen;
@@ -1416,7 +1397,7 @@
 
 		} else if (type == 0x0006) { /* Message was received offline. */
 
-			/* XXX not sure if this actually gets sent. */
+			/* XXX - not sure if this actually gets sent. */
 			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
 
 		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
@@ -1552,7 +1533,7 @@
 static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
 {
 
-	/* XXX aim_chat_roominfo_free() */
+	/* XXX - aim_chat_roominfo_free() */
 	free(args->info.chat.roominfo.name);
 
 	return;
@@ -1613,7 +1594,7 @@
 	hdrlen = aimbs_getle32(servdata);
 	aim_bstream_advance(servdata, hdrlen);
 
-	/* XXX This is such a hack. */
+	/* XXX - This is such a hack. */
 	args->reqclass = AIM_CAPS_ICQRTF;
 
 	args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
@@ -1631,7 +1612,9 @@
 
 	args->destructor = (void *)incomingim_ch2_sendfile_free;
 
-	if (servdata) {
+	/* Maybe there is a better way to tell what kind of sendfile 
+	 * this is?  Maybe TLV t(000a)? */
+	if (servdata) { /* Someone is sending us a file */
 		int flen;
 
 		/* subtype is one of AIM_OFT_SUBTYPE_* */
@@ -1639,7 +1622,13 @@
 		args->info.sendfile.totfiles = aimbs_get16(servdata);
 		args->info.sendfile.totsize = aimbs_get32(servdata);
 
-		/* XXX - create an aimbs_getnullstr function */
+		/*
+		 * I hope to God I'm right when I guess that there is a 
+		 * 32 char max filename length for single files.  I think 
+		 * OFT tends to do that.  Gotta love inconsistency.  I saw 
+		 * a 26 byte filename?
+		 */
+		/* AAA - create an aimbs_getnullstr function (don't anymore)(maybe) */
 		/* Use an inelegant way of getting the null-terminated filename, 
 		 * since there's no easy bstream routine. */
 		for (flen = 0; aimbs_get8(servdata); flen++);
@@ -1648,6 +1637,7 @@
 
 		/* There is sometimes more after the null-terminated filename, 
 		 * but I'm unsure of its format. */
+		/* I don't believe him. */
 	}
 
 	return;
@@ -1709,6 +1699,8 @@
 
 	/*
 	 * IP address from the perspective of the client.
+	 *
+	 * XXX - I don't like this.  Maybe just read in an int?  Or inet_ntoa...
 	 */
 	if (aim_gettlv(list2, 0x0002, 1)) {
 		aim_tlv_t *iptlv;
@@ -1761,6 +1753,14 @@
 		args.port = aim_gettlv16(list2, 0x0005, 1);
 
 	/*
+	 * Something to do with ft -- two bytes
+	 * 0x0001 - "I want to send you this file"
+	 * 0x0002 - "I will accept this file from you"
+	 */
+	if (aim_gettlv(list2, 0x000a, 1))
+		;
+
+	/*
 	 * Error code.
 	 */
 	if (aim_gettlv(list2, 0x000b, 1))
@@ -1784,10 +1784,6 @@
 	if (aim_gettlv(list2, 0x000e, 1))
 		args.language = aim_gettlv_str(list2, 0x000e, 1);
 
-	/* Unknown -- two bytes = 0x0001 */
-	if (aim_gettlv(list2, 0x000a, 1))
-		;
-
 	/* Unknown -- no value */
 	if (aim_gettlv(list2, 0x000f, 1))
 		;
@@ -1800,7 +1796,7 @@
 		args.verifiedip = (char *)verifiedip;
 
 	/*
-	 * This is must be present in PROPOSALs, but will probably not
+	 * This must be present in PROPOSALs, but will probably not
 	 * exist in CANCELs and ACCEPTs.
 	 *
 	 * Service Data blocks are module-specific in format.
@@ -1904,15 +1900,15 @@
 	/*
 	 * Channel ID.
 	 *
-	 * Channel 0x0001 is the message channel.  There are 
-	 * other channels for things called "rendevous"
-	 * which represent chat and some of the other new
-	 * features of AIM2/3/3.5. 
+	 * Channel 0x0001 is the message channel.  It is 
+	 * used to send basic ICBMs.
 	 *
 	 * Channel 0x0002 is the Rendevous channel, which
 	 * is where Chat Invitiations and various client-client
 	 * connection negotiations come from.
 	 *
+	 * Channel 0x0003 is used for chat messages.
+	 *
 	 * Channel 0x0004 is used for ICQ authorization, or 
 	 * possibly any system notice.
 	 *
@@ -1979,7 +1975,7 @@
 }
 
 /*
- * Subtype 0x0008 - Send a warning to destsn.
+ * Subtype 0x0008 - Send a warning to sn.
  * 
  * Flags:
  *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
@@ -1987,25 +1983,23 @@
  * 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)
+faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu32_t flags)
 {
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
-	fu16_t outflags = 0x0000;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13)))
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(sn)+13)))
 		return -ENOMEM;
 
-	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
-
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
 	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
 
-	if (flags & AIM_WARN_ANON)
-		outflags |= 0x0001;
-
-	aimbs_put16(&fr->data, outflags); 
-	aimbs_put8(&fr->data, strlen(destsn));
-	aimbs_putraw(&fr->data, destsn, strlen(destsn));
+	aimbs_put16(&fr->data, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000); 
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
 
 	aim_tx_enqueue(sess, fr);
 
@@ -2043,7 +2037,7 @@
  *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
  * 
  */
-faim_export int aim_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code)
+faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
@@ -2095,6 +2089,8 @@
 	reason = aimbs_get16(bs);
 
 	if (channel == 0x0002) { /* File transfer declined */
+		aimbs_get16(bs); /* Unknown */
+		aimbs_get16(bs); /* Unknown */
 		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
 			ret = userfunc(sess, rx, channel, sn, reason, ck);
 	} else if (channel == 0x0004) { /* ICQ message */
@@ -2158,22 +2154,28 @@
 	return ret;
 }
 
-/* Subtype 0x000c */
+/*
+ * Subtype 0x000c - Receive an ack after sending an ICBM.
+ *
+ * You have to have send the message with the AIM_IMFLAGS_ACK flag set 
+ * (TLV t(0003)).  The ack contains the ICBM header of the message you 
+ * sent.
+ *
+ */
 static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	aim_rxcallback_t userfunc;
-	fu16_t type;
-	fu8_t snlen, *ck;
+	fu16_t ch;
+	fu8_t *ck;
 	char *sn;
 	int ret = 0;
 
 	ck = aimbs_getraw(bs, 8);
-	type = aimbs_get16(bs);
-	snlen = aimbs_get8(bs);
-	sn = aimbs_getstr(bs, snlen);
+	ch = aimbs_get16(bs);
+	sn = aimbs_getstr(bs, aimbs_get8(bs));
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, type, sn);
+		ret = userfunc(sess, rx, ch, sn);
 
 	free(sn);
 	free(ck);
@@ -2188,7 +2190,7 @@
  * and Gaim 0.60 and newer.
  *
  */
-faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2)
+faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2)
 {
 	aim_conn_t *conn;
 	aim_frame_t *fr;
@@ -2269,7 +2271,7 @@
 {
 
 	if (snac->subtype == 0x0005)
-		return paraminfo(sess, mod, rx, snac, bs);
+		return aim_im_paraminfo(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0006)
 		return outgoingim(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x0007)
@@ -2286,19 +2288,6 @@
 	return 0;
 }
 
-static int snacdestructor(aim_session_t *sess, aim_conn_t *conn, aim_modsnac_t *snac, void *data)
-{
-	aim_rxcallback_t userfunc;
-	int ret = 0;
-
-	if (snac->subtype == 0x0006) {
-		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_MSGTIMEOUT)))
-			ret = userfunc(sess, NULL, conn, data);
-	}
-	/* Note that we return 1 for success, 0 for failure. */
-	return ret;
-}
-
 faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
 {
 
@@ -2309,7 +2298,6 @@
 	mod->flags = 0;
 	strncpy(mod->name, "messaging", sizeof(mod->name));
 	mod->snachandler = snachandler;
-	mod->snacdestructor = snacdestructor;
 
 	return 0;
 }
--- a/src/protocols/oscar/oscar.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Wed Feb 26 05:01:37 2003 +0000
@@ -25,7 +25,7 @@
 #endif
 
 #include <sys/types.h>
-/*this must happen before sys/socket.h or freebsd won't compile*/
+/* this must happen before sys/socket.h or freebsd won't compile */
 
 #ifndef _WIN32
 #include <netdb.h>
@@ -91,8 +91,7 @@
 G_MODULE_IMPORT int report_idle;
 G_MODULE_IMPORT GSList *groups;
 
-static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON |
-	AIM_CAPS_IMIMAGE | AIM_CAPS_SENDFILE;
+static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_IMIMAGE | AIM_CAPS_SENDFILE;
 
 /* Set AIM caps, because Gaim can still do them over ICQ and 
  * Winicq doesn't mind. */
@@ -178,21 +177,15 @@
 	fu8_t cookie[8];
 };
 
-#if 0
-struct oscar_file_transfer {
-	enum { OFT_SENDFILE_IN, OFT_SENDFILE_OUT } type;
+/* BBB */
+struct oscar_xfer_data {
+	fu8_t cookie[8];
+	fu16_t modtime;
+	fu16_t checksum;
 	aim_conn_t *conn;
-	struct file_transfer *xfer;
-	char *sn;
-	char ip[64];
-	fu16_t port;
-	fu8_t cookie[8];
-	int totsize;
-	int filesdone;
-	int totfiles;
-	int watcher;
+	struct gaim_xfer *xfer;
+	struct gaim_connection *gc;
 };
-#endif
 
 struct icon_req {
 	char *user;
@@ -288,10 +281,248 @@
 	return c;
 }
 
+/* XXX - I really don't like forward declarations */
+static void oscar_callback(gpointer data, gint source, GaimInputCondition condition);
+
+/* BBB */
+/*
+ * This little area in oscar.c is the nexus of file transfer code, 
+ * so I wrote a little explanation of what happens.  I am such a 
+ * ninja.
+ *
+ * The series of events for a file send is:
+ *  -Create xfer and call gaim_xfer_request (this happens in oscar_ask_sendfile)
+ *  -User chooses a file and oscar_xfer_init is called.  It establishs a 
+ *   listening socket, then asks the remote user to connect to us (and 
+ *   gives them the file name, port, IP, etc.)
+ *  -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens 
+ *   in oscar_sendfile_established)
+ *  -They send us an AIM_CB_OFT_ACK and then we start sending data
+ *  -When we finish, they send us an AIM_CB_OFT_DONE and they close the 
+ *   connection.
+ *  -We get drunk because file transfer kicks ass.
+ *
+ * The series of events for a file receive is:
+ *  -Create xfer and call gaim_xfer request (this happens in incomingim_chan2)
+ *  -Gaim user selects file to name and location to save file to and 
+ *   oscar_xfer_init is called
+ *  -It connects to the remote user using the IP they gave us earlier
+ *  -After connecting, they send us an AIM_CB_OFT_PROMPT.  In reply, we send 
+ *   them an AIM_CB_OFT_ACK.
+ *  -They begin to send us lots of raw data.
+ *  -When they finish sending data we send an AIM_CB_OFT_DONE and then close 
+ *   the connectionn.
+ */
+static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition);
+static int oscar_sendfile_established(aim_session_t *, aim_frame_t *, ...);
+static int oscar_sendfile_prompt(aim_session_t *, aim_frame_t *, ...);
+static int oscar_sendfile_ack(aim_session_t *, aim_frame_t *, ...);
+static int oscar_sendfile_done(aim_session_t *, aim_frame_t *, ...);
+
+/* XXX - This function is pretty ugly */
+static void
+oscar_xfer_init(struct gaim_xfer *xfer)
+{
+	struct gaim_connection *gc;
+	struct oscar_data *od;
+	struct oscar_xfer_data *xfer_data;
+debug_printf("in oscar_xfer_init\n");
+
+	if (!(xfer_data = xfer->data))
+		return;
+	if (!(gc = xfer_data->gc))
+		return;
+	if (!(od = gc->proto_data))
+		return;
+
+	if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
+		xfer->filename = g_path_get_basename(xfer->local_filename);
+		if (xfer->local_port) {
+			int i;
+			char ip[4];
+			gchar **ipsplit = g_strsplit(xfer->local_ip, ".", 4);
+			for (i=0; ipsplit[i]; i++)
+				ip[i] = atoi(ipsplit[i]);
+			g_strfreev(ipsplit);
+			xfer_data->conn = aim_sendfile_listen(od->sess, xfer_data->cookie, ip, xfer->local_port);
+			if (!xfer_data->conn) {
+				/*
+				 * Try a few random ports.  Maybe we need a 
+				 * way to tell libfaim to listen for multiple 
+				 * connections on one listener socket.
+				 */
+				for (i=0; (i<5 && !xfer_data->conn); i++) {
+					xfer->local_port = (rand() % (65535-1024)) + 1024;
+					xfer_data->conn = aim_sendfile_listen(od->sess, xfer_data->cookie, ip, xfer->local_port);
+				}
+			}
+			if (xfer_data->conn) {
+				xfer->watcher = gaim_input_add(xfer_data->conn->fd, GAIM_INPUT_READ, oscar_callback, xfer_data->conn);
+				aim_im_sendch2_sendfile_ask(od->sess, xfer_data->cookie, xfer->who, ip, xfer->local_port, xfer->filename, 1, xfer->size);
+				aim_conn_addhandler(od->sess, xfer_data->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED, oscar_sendfile_established, 0);
+				/* Calculate a checksum thingy.  This is ugly.
+				if ((fd = open(xfer->local_filename, O_RDONLY))) {
+					int bytes;
+					char buf[1024];
+					xfer_data->checksum = 0xffff0000;
+					while ((bytes = aim_recv(fd, buf, 1024)) > 0)
+						xfer_data->checksum = aim_oft_checksum(buf, bytes, xfer_data->checksum);
+					close(fd);
+				} */
+			} else {
+				do_error_dialog(_("File Transfer Aborted"), _("Unable to establish listener socket."), GAIM_ERROR);
+				/* XXX - The below line causes a crash because the transfer is canceled before the "Ok" callback on the file selection thing exists, I think */
+				/* gaim_xfer_cancel(xfer); */
+			}
+		} else {
+			/* gaim_xfer_cancel(xfer); */
+		}
+	} else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
+		if (xfer->remote_ip && xfer->remote_port) {
+			xfer_data->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL);
+			if (xfer_data->conn) {
+				xfer_data->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+				aim_conn_addhandler(od->sess, xfer_data->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, oscar_sendfile_prompt, 0);
+				xfer_data->conn->fd = xfer->fd = proxy_connect(xfer->remote_ip, xfer->remote_port, oscar_sendfile_connected, xfer);
+				if (xfer->fd == -1) {
+					do_error_dialog(_("File Transfer Aborted"), _("Unable to establish file descriptor."), GAIM_ERROR);
+					/* gaim_xfer_cancel? */
+				}
+			} else {
+				do_error_dialog(_("File Transfer Aborted"), _("Unable to create new connection."), GAIM_ERROR);
+				/* gaim_xfer_cancel? */
+				/* Try a different port? Ask them to connect to us? */
+			}
+		} else {
+			/* I have a feeling this would crash */
+			/* gaim_xfer_cancel(xfer); */
+		}
+	}
+}
+
+static void
+oscar_xfer_start(struct gaim_xfer *xfer)
+{
+/*	struct gaim_connection *gc;
+	struct oscar_data *od;
+	struct oscar_xfer_data *xfer_data;
+
+	if (!(xfer_data = xfer->data))
+		return;
+	if (!(gc = xfer_data->gc))
+		return;
+	if (!(od = gc->proto_data))
+		return;
+
+	od = xfer_data->od;
+*/
+debug_printf("AAA - in oscar_xfer_start\n");
+
+	/* I'm pretty sure we don't need to do jack here.  Nor Jill. */
+}
+
+static void
+oscar_xfer_end(struct gaim_xfer *xfer)
+{
+	struct gaim_connection *gc;
+	struct oscar_data *od;
+	struct oscar_xfer_data *xfer_data;
+debug_printf("AAA - in oscar_xfer_end\n");
+
+	if (!(xfer_data = xfer->data))
+		return;
+
+	if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE)
+		aim_oft_sendheader(xfer_data->conn->sessv, xfer_data->conn, AIM_CB_OFT_DONE, xfer_data->cookie, xfer->filename, 1, 1, xfer->size, xfer->size, xfer_data->modtime, xfer_data->checksum, 0x21);
+
+	if ((gc = xfer_data->gc)) {
+		if ((od = gc->proto_data))
+			od->file_transfers = g_slist_remove(od->file_transfers, xfer);
+	}
+
+	g_free(xfer_data);
+	xfer->data = NULL;
+}
+
+static void
+oscar_xfer_cancel(struct gaim_xfer *xfer)
+{
+	struct gaim_connection *gc;
+	struct oscar_data *od;
+	struct oscar_xfer_data *xfer_data;
+	aim_conn_t *conn;
+debug_printf("AAA - in oscar_xfer_cancel\n");
+
+	if (!(xfer_data = xfer->data))
+		return;
+
+	if ((conn = xfer_data->conn)) {
+		aim_session_t *sess;
+		if ((sess = conn->sessv))
+			if (xfer_data->cookie && xfer->who)
+				aim_im_sendch2_sendfile_cancel(sess, xfer_data->cookie, xfer->who, AIM_CAPS_SENDFILE);
+	}
+
+	if ((gc = xfer_data->gc))
+		if ((od = gc->proto_data))
+			od->file_transfers = g_slist_remove(od->file_transfers, xfer);
+
+	g_free(xfer_data);
+	xfer->data = NULL;
+}
+
+static void
+oscar_xfer_ack(struct gaim_xfer *xfer, const char *buffer, size_t size)
+{
+	struct oscar_xfer_data *xfer_data;
+
+	if (!(xfer_data = xfer->data))
+		return;
+
+	aim_oft_checksum(buffer, size, xfer_data->checksum);
+}
+
+static struct gaim_xfer *
+oscar_find_xfer_by_cookie(GSList *fts, const char *ck)
+{
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *data;
+
+	while (fts) {
+		xfer = fts->data;
+		data = xfer->data;
+
+		if (data && !strcmp(data->cookie, ck))
+			return xfer;
+
+		fts = g_slist_next(fts);
+	}
+
+	return NULL;
+}
+
+static struct gaim_xfer *
+oscar_find_xfer_by_conn(GSList *fts, aim_conn_t *conn)
+{
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *data;
+
+	while (fts) {
+		xfer = fts->data;
+		data = xfer->data;
+
+		if (data && (conn == data->conn))
+			return xfer;
+
+		fts = g_slist_next(fts);
+	}
+
+	return NULL;
+}
+
 #if 0
 /* XXX there must be a better way than this.... -- wtm */
-static struct oscar_file_transfer *find_oft_by_conn(struct gaim_connection *gc,
-		aim_conn_t *conn) {
+static struct oscar_file_transfer *find_oft_by_conn(struct gaim_connection *gc, aim_conn_t *conn) {
 	GSList *g = ((struct oscar_data *)gc->proto_data)->file_transfers;
 	struct oscar_file_transfer *f = NULL;
 
@@ -306,8 +537,7 @@
 	return f;
 }
 
-static struct oscar_file_transfer *find_oft_by_xfer(struct gaim_connection *gc,
-		struct file_transfer *xfer) {
+static struct oscar_file_transfer *find_oft_by_xfer(struct gaim_connection *gc, struct file_transfer *xfer) {
 	GSList *g = ((struct oscar_data *)gc->proto_data)->file_transfers;
 	struct oscar_file_transfer *f = NULL;
 
@@ -321,22 +551,6 @@
 
 	return f;
 }
-
-static struct oscar_file_transfer *find_oft_by_cookie(struct gaim_connection *gc,
-		const char *cookie) {
-	GSList *g = ((struct oscar_data *)gc->proto_data)->file_transfers;
-	struct oscar_file_transfer *f = NULL;
-
-	while (g) {
-		f = (struct oscar_file_transfer *)g->data;
-		if (!strncmp(f->cookie, cookie, 8))
-			break;
-		g = g->next;
-		f = NULL;
-	}
-
-	return f;
-}
 #endif
 
 static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
@@ -392,21 +606,10 @@
 static int gaim_ssi_gotadded     (aim_session_t *, aim_frame_t *, ...);
 #endif
 
-static int gaim_directim_initiate(aim_session_t *, aim_frame_t *, ...);
-static int gaim_directim_incoming(aim_session_t *, aim_frame_t *, ...);
-static int gaim_directim_typing  (aim_session_t *, aim_frame_t *, ...);
-static int gaim_update_ui       (aim_session_t *, aim_frame_t *, ...);
-
-#if 0
-static int oscar_file_transfer_do(aim_session_t *, aim_frame_t *, ...);
-static void oscar_file_transfer_disconnect(aim_session_t *,
-		aim_conn_t *);
-static void oscar_file_transfer_cancel(struct gaim_connection *,
-		struct file_transfer *);
-static int oscar_sendfile_request(aim_session_t *sess,
-		struct oscar_file_transfer *oft);
-static int oscar_sendfile_timeout(aim_session_t *sess, aim_frame_t *fr, ...);
-#endif
+static int gaim_odc_initiate     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_odc_incoming     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_odc_typing       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_update_ui        (aim_session_t *, aim_frame_t *, ...);
 
 static fu32_t check_encoding(const char *utf8);
 static fu32_t parse_encoding(const char *enc);
@@ -440,32 +643,7 @@
 };
 static int msgerrreasonlen = 25;
 
-#if 0
-/*
- * 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,
-			conn);
-
-	od->file_transfers = g_slist_remove(od->file_transfers, oft);
-
-	if (oft->watcher) {
-		gaim_input_remove(oft->watcher);
-		oft->watcher = 0;
-	}
-	
-	aim_conn_kill(sess, &conn);
-
-	g_free(oft->sn);
-	g_free(oft);
-}
-#endif
-
-static void gaim_directim_disconnect(aim_session_t *sess, aim_conn_t *conn) {
+static void gaim_odc_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 gaim_conversation *cnv;
@@ -473,7 +651,7 @@
 	char *sn;
 	char buf[256];
 
-	sn = g_strdup(aim_directim_getsn(conn));
+	sn = g_strdup(aim_odc_getsn(conn));
 
 	debug_printf("%s disconnected Direct IM.\n", sn);
 
@@ -497,12 +675,11 @@
 	return;
 }
 
-static void oscar_callback(gpointer data, gint source,
-				GaimInputCondition condition) {
+static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) {
 	aim_conn_t *conn = (aim_conn_t *)data;
 	aim_session_t *sess = aim_conn_getsess(conn);
 	struct gaim_connection *gc = sess ? sess->aux_data : NULL;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 
 	if (!gc) {
 		/* gc is null. we return, else we seg SIGSEG on next line. */
@@ -510,7 +687,7 @@
 		return;
 	}
       
-	odata = (struct oscar_data *)gc->proto_data;
+	od = (struct oscar_data *)gc->proto_data;
 
 	if (!g_slist_find(connections, gc)) {
 		/* oh boy. this is probably bad. i guess the only thing we 
@@ -520,20 +697,20 @@
 	}
 
 	if (condition & GAIM_INPUT_READ) {
-		if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
-			debug_printf("got information on rendezvous\n");
-			if (aim_handlerendconnect(odata->sess, conn) < 0) {
-				debug_printf("connection error (rend)\n");
-				aim_conn_kill(odata->sess, &conn);
+		if (conn->type == AIM_CONN_TYPE_LISTENER) {
+			debug_printf("got information on rendezvous listener\n");
+			if (aim_handlerendconnect(od->sess, conn) < 0) {
+				debug_printf("connection error (rendezvous listener)\n");
+				aim_conn_kill(od->sess, &conn);
 			}
 		} else {
-			if (aim_get_command(odata->sess, conn) >= 0) {
-				aim_rxdispatch(odata->sess);
-                                if (odata->killme)
+			if (aim_get_command(od->sess, conn) >= 0) {
+				aim_rxdispatch(od->sess);
+                                if (od->killme)
                                         signoff(gc);
 			} else {
 				if ((conn->type == AIM_CONN_TYPE_BOS) ||
-					   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
+					   !(aim_getconn_type(od->sess, AIM_CONN_TYPE_BOS))) {
 					debug_printf("major connection error\n");
 					hide_login_progress_error(gc, _("Disconnected."));
 					signoff(gc);
@@ -546,56 +723,43 @@
 						gaim_input_remove(c->inpa);
 					c->inpa = 0;
 					c->fd = -1;
-					aim_conn_kill(odata->sess, &conn);
+					aim_conn_kill(od->sess, &conn);
 					snprintf(buf, sizeof(buf), _("You have been disconnected from chat room %s."), c->name);
 					do_error_dialog(buf, NULL, GAIM_ERROR);
 				} else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
-					if (odata->cnpa > 0)
-						gaim_input_remove(odata->cnpa);
-					odata->cnpa = 0;
+					if (od->cnpa > 0)
+						gaim_input_remove(od->cnpa);
+					od->cnpa = 0;
 					debug_printf("removing chatnav input watcher\n");
-					while (odata->create_rooms) {
-						struct create_room *cr = odata->create_rooms->data;
+					while (od->create_rooms) {
+						struct create_room *cr = od->create_rooms->data;
 						g_free(cr->name);
-						odata->create_rooms =
-							g_slist_remove(odata->create_rooms, cr);
+						od->create_rooms =
+							g_slist_remove(od->create_rooms, cr);
 						g_free(cr);
 						do_error_dialog(_("Chat is currently unavailable"), NULL, GAIM_ERROR);
 					}
-					aim_conn_kill(odata->sess, &conn);
+					aim_conn_kill(od->sess, &conn);
 				} else if (conn->type == AIM_CONN_TYPE_AUTH) {
-					if (odata->paspa > 0)
-						gaim_input_remove(odata->paspa);
-					odata->paspa = 0;
+					if (od->paspa > 0)
+						gaim_input_remove(od->paspa);
+					od->paspa = 0;
 					debug_printf("removing authconn input watcher\n");
-					aim_conn_kill(odata->sess, &conn);
+					aim_conn_kill(od->sess, &conn);
 				} else if (conn->type == AIM_CONN_TYPE_EMAIL) {
-					if (odata->emlpa > 0)
-						gaim_input_remove(odata->emlpa);
-					odata->emlpa = 0;
+					if (od->emlpa > 0)
+						gaim_input_remove(od->emlpa);
+					od->emlpa = 0;
 					debug_printf("removing email input watcher\n");
-					aim_conn_kill(odata->sess, &conn);
+					aim_conn_kill(od->sess, &conn);
 				} else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
 					if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
-						gaim_directim_disconnect(odata->sess, conn);
-					else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
-#if 0
-						struct oscar_file_transfer *oft = find_oft_by_conn(gc, conn);
-						if (oft) {
-							transfer_abort(oft->xfer, _("Buddy canceled transfer"));
-						}
-						oscar_file_transfer_disconnect(odata->sess, conn);
-#endif
-					}
-					else {
-						debug_printf("No handler for rendezvous disconnect (%d).\n",
-								source);
-					}
-					aim_conn_kill(odata->sess, &conn);
+						gaim_odc_disconnect(od->sess, conn);
+					aim_conn_kill(od->sess, &conn);
 				} else {
 					debug_printf("holy crap! generic connection error! %hu\n",
 							conn->type);
-					aim_conn_kill(odata->sess, &conn);
+					aim_conn_kill(od->sess, &conn);
 				}
 			}
 		}
@@ -620,7 +784,7 @@
 static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
 {
 	struct gaim_connection *gc = data;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *conn;
 
@@ -629,8 +793,8 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
+	od = gc->proto_data;
+	sess = od->sess;
 	conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
 	
 	conn->fd = source;
@@ -642,8 +806,7 @@
 	}
 
 	aim_conn_completeconnect(sess, conn);
-	gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ,
-			oscar_callback, conn);
+	gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ, oscar_callback, conn);
 	debug_printf("Password sent, waiting for response\n");
 }
 
@@ -652,16 +815,16 @@
 	aim_conn_t *conn;
 	char buf[256];
 	struct gaim_connection *gc = new_gaim_conn(account);
-	struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
+	struct oscar_data *od = gc->proto_data = g_new0(struct oscar_data, 1);
 
 	if (isdigit(*account->username)) {
-		odata->icq = TRUE;
+		od->icq = TRUE;
 		gc->password[8] = 0;
 	} else {
 		gc->flags |= OPT_CONN_HTML;
 		gc->flags |= OPT_CONN_AUTO_RESP;
 	}
-	odata->supports_tn = g_hash_table_new(g_str_hash, g_str_equal);
+	od->supports_tn = g_hash_table_new(g_str_hash, g_str_equal);
 
 	sess = g_new0(aim_session_t, 1);
 
@@ -671,7 +834,7 @@
 	/* we need an immediate queue because we don't use a while-loop to
 	 * see if things need to be sent. */
 	aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
-	odata->sess = sess;
+	od->sess = sess;
 	sess->aux_data = gc;
 
 	conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
@@ -702,69 +865,66 @@
 }
 
 static void oscar_close(struct gaim_connection *gc) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
-	
-	while (odata->oscar_chats) {
-		struct chat_connection *n = odata->oscar_chats->data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+
+	while (od->oscar_chats) {
+		struct chat_connection *n = od->oscar_chats->data;
 		if (n->inpa > 0)
 			gaim_input_remove(n->inpa);
 		g_free(n->name);
 		g_free(n->show);
-		odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
+		od->oscar_chats = g_slist_remove(od->oscar_chats, n);
 		g_free(n);
 	}
-	while (odata->direct_ims) {
-		struct direct_im *n = odata->direct_ims->data;
+	while (od->direct_ims) {
+		struct direct_im *n = od->direct_ims->data;
 		if (n->watcher > 0)
 			gaim_input_remove(n->watcher);
-		odata->direct_ims = g_slist_remove(odata->direct_ims, n);
+		od->direct_ims = g_slist_remove(od->direct_ims, n);
 		g_free(n);
 	}
 
-#if 0
-	while (odata->file_transfers) {
-		struct oscar_file_transfer *n = odata->file_transfers->data;
-		if (n->watcher > 0)
-			gaim_input_remove(n->watcher);
-		odata->file_transfers = g_slist_remove(odata->file_transfers, n);
-		g_free(n);
+/* BBB */
+	while (od->file_transfers) {
+		struct gaim_xfer *xfer;
+		xfer = (struct gaim_xfer *)od->file_transfers->data;
+		gaim_xfer_destroy(xfer);
 	}
-#endif
-
-	while (odata->hasicons) {
-		struct icon_req *n = odata->hasicons->data;
+
+	while (od->hasicons) {
+		struct icon_req *n = od->hasicons->data;
 		g_free(n->user);
-		odata->hasicons = g_slist_remove(odata->hasicons, n);
+		od->hasicons = g_slist_remove(od->hasicons, n);
 		g_free(n);
 	}
-	g_hash_table_destroy(odata->supports_tn);
-	while (odata->evilhack) {
-		g_free(odata->evilhack->data);
-		odata->evilhack = g_slist_remove(odata->evilhack, odata->evilhack->data);
+	g_hash_table_destroy(od->supports_tn);
+	while (od->evilhack) {
+		g_free(od->evilhack->data);
+		od->evilhack = g_slist_remove(od->evilhack, od->evilhack->data);
 	}
-	while (odata->create_rooms) {
-		struct create_room *cr = odata->create_rooms->data;
+	while (od->create_rooms) {
+		struct create_room *cr = od->create_rooms->data;
 		g_free(cr->name);
-		odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+		od->create_rooms = g_slist_remove(od->create_rooms, cr);
 		g_free(cr);
 	}
-	if (odata->email)
-		g_free(odata->email);
-	if (odata->newp)
-		g_free(odata->newp);
-	if (odata->oldp)
-		g_free(odata->oldp);
+	if (od->email)
+		g_free(od->email);
+	if (od->newp)
+		g_free(od->newp);
+	if (od->oldp)
+		g_free(od->oldp);
 	if (gc->inpa > 0)
 		gaim_input_remove(gc->inpa);
-	if (odata->cnpa > 0)
-		gaim_input_remove(odata->cnpa);
-	if (odata->paspa > 0)
-		gaim_input_remove(odata->paspa);
-	if (odata->emlpa > 0)
-		gaim_input_remove(odata->emlpa);
-	aim_session_kill(odata->sess);
-	g_free(odata->sess);
-	odata->sess = NULL;
+	if (od->cnpa > 0)
+		gaim_input_remove(od->cnpa);
+	if (od->paspa > 0)
+		gaim_input_remove(od->paspa);
+	if (od->emlpa > 0)
+		gaim_input_remove(od->emlpa);
+	aim_session_kill(od->sess);
+	g_free(od->sess);
+	od->sess = NULL;
 	g_free(gc->proto_data);
 	gc->proto_data = NULL;
 	debug_printf("Signed off.\n");
@@ -772,7 +932,7 @@
 
 static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
 	struct gaim_connection *gc = data;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *bosconn;
 
@@ -781,9 +941,9 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
-	bosconn = odata->conn;	
+	od = gc->proto_data;
+	sess = od->sess;
+	bosconn = od->conn;	
 	bosconn->fd = source;
 
 	if (source < 0) {
@@ -793,26 +953,52 @@
 	}
 
 	aim_conn_completeconnect(sess, bosconn);
-	gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ,
-			oscar_callback, bosconn);
+	gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, oscar_callback, bosconn);
 	set_login_progress(gc, 4, _("Connection established, cookie sent"));
 }
 
-#if 0
-static void oscar_ask_send_file(struct gaim_connection *gc, char *destsn) {
+/* BBB */
+static void oscar_ask_sendfile(struct gaim_connection *gc, char *destsn) {
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 
-	struct oscar_file_transfer *oft = g_new0(struct oscar_file_transfer,
-			1);
-
-	oft->type = OFT_SENDFILE_OUT;
-	oft->sn = g_strdup(destsn);
-
-	od->file_transfers = g_slist_append(od->file_transfers, oft);
-
-	oft->xfer = transfer_out_add(gc, oft->sn);
+	/* You want to send a file to someone else, you're so generous */
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *xfer_data;
+
+	/* Create the oscar-specific data */
+	xfer_data = g_malloc0(sizeof(struct oscar_xfer_data));
+	xfer_data->gc = gc;
+
+	/* Build the file transfer handle */
+	xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, destsn);
+	xfer_data->xfer = xfer;
+	xfer->data = xfer_data;
+
+	/* Set the info about the incoming file */
+	if (od) {
+		/* XXX - Create core gaim functions for getting ip and getting port from an fd */
+		aim_session_t *sess;
+		aim_conn_t *conn;
+		struct sockaddr addr;
+		socklen_t namelen = sizeof(addr);
+		if ((sess = od->sess) && (conn = aim_conn_findbygroup(sess, 0x0004)))
+			if (!getsockname(conn->fd, &addr, &namelen))
+				xfer->local_ip = g_strdup(inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr));
+	}
+	xfer->local_port = 5190;
+
+	 /* Setup our I/O op functions */
+	gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
+	gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
+	gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
+	gaim_xfer_set_cancel_fnc(xfer, oscar_xfer_cancel);
+
+	/* Keep track of this transfer for later */
+	od->file_transfers = g_slist_append(od->file_transfers, xfer);
+
+	/* Now perform the request */
+	gaim_xfer_request(xfer);
 }
-#endif
 
 static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
@@ -840,7 +1026,6 @@
 		case 0x05:
 			/* Incorrect nick/password */
 			hide_login_progress(gc, _("Incorrect nickname or password."));
-			plugin_event(event_error, (void *)980, 0, 0, 0);
 			break;
 		case 0x11:
 			/* Suspended account */
@@ -853,13 +1038,11 @@
 		case 0x18:
 			/* connecting too frequently */
 			hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
-			plugin_event(event_error, (void *)983, 0, 0, 0);
 			break;
 		case 0x1c:
 			/* client too old */
 			g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), WEBSITE);
 			hide_login_progress(gc, buf);
-			plugin_event(event_error, (void *)989, 0, 0, 0);
 			break;
 		default:
 			hide_login_progress(gc, _("Authentication Failed"));
@@ -932,10 +1115,6 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0);
 #endif
 
-#if 0
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_MSGTIMEOUT, oscar_sendfile_timeout, 0);
-#endif
-
 	((struct oscar_data *)gc->proto_data)->conn = bosconn;
 	for (i = 0; i < (int)strlen(info->bosip); i++) {
 		if (info->bosip[i] == ':') {
@@ -1111,13 +1290,13 @@
 	char *key;
 	va_list ap;
 	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *odata = gc->proto_data;
+	struct oscar_data *od = gc->proto_data;
 
 	va_start(ap, fr);
 	key = va_arg(ap, char *);
 	va_end(ap);
 
-	if (odata->icq) {
+	if (od->icq) {
 		struct client_info_s info = CLIENTINFO_ICQ_KNOWNGOOD;
 		aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
 	} else {
@@ -1177,7 +1356,7 @@
 
 static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
 	struct gaim_connection *gc = data;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *tstconn;
 
@@ -1186,8 +1365,8 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
+	od = gc->proto_data;
+	sess = od->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
 	tstconn->fd = source;
 
@@ -1198,15 +1377,14 @@
 	}
 
 	aim_conn_completeconnect(sess, tstconn);
-	odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
-					oscar_callback, tstconn);
+	od->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
 	debug_printf("chatnav: connected\n");
 }
 
 static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
 {
 	struct gaim_connection *gc = data;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *tstconn;
 
@@ -1215,8 +1393,8 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
+	od = gc->proto_data;
+	sess = od->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
 	tstconn->fd = source;
 
@@ -1227,8 +1405,7 @@
 	}
 
 	aim_conn_completeconnect(sess, tstconn);
-	odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
-				oscar_callback, tstconn);
+	od->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
 	debug_printf("chatnav: connected\n");
 }
 
@@ -1236,7 +1413,7 @@
 {
 	struct chat_connection *ccon = data;
 	struct gaim_connection *gc = ccon->gc;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *tstconn;
 
@@ -1248,8 +1425,8 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
+	od = gc->proto_data;
+	sess = od->sess;
 	tstconn = ccon->conn;
 	tstconn->fd = source;
 
@@ -1262,15 +1439,13 @@
 	}
 
 	aim_conn_completeconnect(sess, ccon->conn);
-	ccon->inpa = gaim_input_add(tstconn->fd,
-			GAIM_INPUT_READ,
-			oscar_callback, tstconn);
-	odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
+	ccon->inpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
+	od->oscar_chats = g_slist_append(od->oscar_chats, ccon);
 }
 
 static void oscar_email_connect(gpointer data, gint source, GaimInputCondition cond) {
 	struct gaim_connection *gc = data;
-	struct oscar_data *odata;
+	struct oscar_data *od;
 	aim_session_t *sess;
 	aim_conn_t *tstconn;
 
@@ -1279,8 +1454,8 @@
 		return;
 	}
 
-	odata = gc->proto_data;
-	sess = odata->sess;
+	od = gc->proto_data;
+	sess = od->sess;
 	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL);
 	tstconn->fd = source;
 
@@ -1291,7 +1466,7 @@
 	}
 
 	aim_conn_completeconnect(sess, tstconn);
-	odata->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
+	od->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
 	debug_printf("email: connected\n");
 }
 
@@ -1507,7 +1682,7 @@
 	g_free(d);
 }
 
-static void oscar_directim_callback(gpointer data, gint source, GaimInputCondition condition) {
+static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) {
 	struct direct_im *dim = data;
 	struct gaim_connection *gc = dim->gc;
 	struct oscar_data *od = gc->proto_data;
@@ -1539,205 +1714,183 @@
 	}
 	od->direct_ims = g_slist_append(od->direct_ims, dim);
 	
-	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
-				      oscar_callback, dim->conn);
+	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
 }
 
-#if 0
+/* BBB */
 /*
- * 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.
+ * This is called after a remote AIM user has connected to us.  We 
+ * want to do some voodoo with the socket file descriptors, add a 
+ * callback or two, and then send the AIM_CB_OFT_PROMPT.
  */
-static int oscar_sendfile_out_done(aim_session_t *sess, aim_frame_t *fr, ...) {
-	struct gaim_connection *gc = sess->aux_data;
-	va_list ap;
-	aim_conn_t *conn;
-	const char *cook;
-	struct oscar_file_transfer *oft;
-
-	va_start(ap, fr);
-	conn = va_arg(ap, aim_conn_t *);
-	cook = va_arg(ap, const char *);
-	va_end(ap);
-
-	oft = find_oft_by_cookie(gc, cook);
-	if (oft->filesdone == oft->totfiles)
-		oscar_file_transfer_disconnect(sess, conn);
-	else 
-		/* Send header for next file */
-		oscar_sendfile_request(sess, oft);
-
-	return 0;
-}
-
-/* Called once for each file before sending the raw data. */
-static int oscar_sendfile_request(aim_session_t *sess,
-		struct oscar_file_transfer *oft) {
-	char *name;
-	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, ...) {
+static int oscar_sendfile_established(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;
-	struct oscar_file_transfer *oft;
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *xfer_data;
 	va_list ap;
 	aim_conn_t *conn, *listenerconn;
+debug_printf("AAA - in oscar_sendfile_established\n");
 
 	va_start(ap, fr);
 	conn = va_arg(ap, aim_conn_t *);
 	listenerconn = va_arg(ap, aim_conn_t *);
 	va_end(ap);
 
-	oft = find_oft_by_conn(gc, listenerconn);
-	oft->conn = conn;
+	if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn)))
+		return 1;
+
+	if (!(xfer_data = xfer->data))
+		return 1;
+
 	/* Stop watching listener conn; watch transfer conn instead */
-	gaim_input_remove(oft->watcher);
+	gaim_input_remove(xfer->watcher);
 	aim_conn_kill(sess, &listenerconn);
 
-	aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT,
-			AIM_CB_OFT_SENDFILEFILESEND,
-			oscar_file_transfer_do,
-			0);
-	aim_conn_addhandler(sess, conn,
-			AIM_CB_FAM_OFT,
-			AIM_CB_OFT_SENDFILECOMPLETE,
-			oscar_sendfile_out_done,
-			0);
-	oft->watcher = gaim_input_add(oft->conn->fd, GAIM_INPUT_READ,
-			oscar_callback, oft->conn);
-
-	oscar_sendfile_request(sess, oft);
+	xfer_data->conn = conn;
+	xfer->fd = xfer_data->conn->fd;
+
+	aim_conn_addhandler(sess, xfer_data->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, oscar_sendfile_ack, 0);
+	aim_conn_addhandler(sess, xfer_data->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, oscar_sendfile_done, 0);
+	xfer->watcher = gaim_input_add(xfer_data->conn->fd, GAIM_INPUT_READ, oscar_callback, xfer_data->conn);
+
+	/* Inform the other user that we are connected and ready to transfer */
+	aim_oft_sendheader(sess, xfer_data->conn, AIM_CB_OFT_PROMPT, NULL, xfer->filename, 0, 1,  xfer->size, xfer->size, time(NULL), xfer_data->checksum, 0x02);
 
 	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.
+ * This is the gaim callback passed to proxy_connect when connecting to another AIM 
+ * user in order to transfer a file.
  */
-static int oscar_sendfile_timeout(aim_session_t *sess, aim_frame_t *fr, ...) {
-	struct gaim_connection *gc = sess->aux_data;
-	va_list ap;
-	struct oscar_file_transfer *oft;
-	char *cookie;
-	aim_conn_t *bosconn;
-
-	va_start(ap, fr);
-	bosconn = va_arg(ap, aim_conn_t *);
-	cookie = va_arg(ap, char *);
-	va_end(ap);
-
-	if ((oft = find_oft_by_cookie(gc, cookie))) {
-		aim_canceltransfer(sess, bosconn, oft->cookie,
-				oft->sn, AIM_CAPS_SENDFILE);
-
-		transfer_abort(oft->xfer, _("Transfer timed out"));
-		oscar_file_transfer_disconnect(sess, oft->conn);
-	}
-
-	return 1; /* success */
-}
-
-/* Called once at the beginning of an outgoing transfer session. */
-static void oscar_file_transfer_out(struct gaim_connection *gc,
-		struct file_transfer *xfer, const char *name, int totfiles,
-		int totsize) {
-	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
-	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
-
-	oft->xfer = xfer;
-	oft->totsize = totsize;
-	oft->totfiles = totfiles;
-	oft->filesdone = 0;
-
-	oft->conn = aim_sendfile_initiate(od->sess, oft->sn,
-			name, totfiles, oft->totsize, oft->cookie);
-	if (!oft->conn) {
-		do_error_dialog(_("Couldn't open listener to send file"),
-				_("File transfer aborted"),
-				GAIM_ERROR);
+static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) {
+	struct gaim_connection *gc;
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *xfer_data;
+debug_printf("AAA - in oscar_sendfile_connected\n");
+
+	if (!(xfer = data))
+		return;
+	if (!(xfer_data = xfer->data))
+		return;
+	if (!(gc = xfer_data->gc))
 		return;
-	}
-
-	aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT,
-			AIM_CB_OFT_SENDFILEINITIATE,
-			oscar_sendfile_accepted,
-			0);
-	oft->watcher = gaim_input_add(oft->conn->fd, GAIM_INPUT_READ,
-			oscar_callback, oft->conn);
+	if (source < 0)
+		return;
+
+	xfer->fd = source;
+	xfer_data->conn->fd = source;
+
+	aim_conn_completeconnect(xfer_data->conn->sessv, xfer_data->conn);
+	xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, xfer_data->conn);
+
+	/* Inform the other user that we are connected and ready to transfer */
+	aim_im_sendch2_sendfile_accept(xfer_data->conn->sessv, xfer_data->cookie, xfer->who, AIM_CAPS_SENDFILE);
+
+	return;
 }
 
 /*
- * This is called after a chunk of data has been sent out or received; it is used 
- * to update the checksum.
+ * This is called when a buddy sends us some file info.  This happens when they 
+ * are sending a file to you, and you have just established a connection to them.
+ * You should send them the exactly same info except use the real cookie.  We also 
+ * get like totally ready to like, receive the file, kay?
  */
-static void oscar_file_transfer_data_chunk(struct gaim_connection *gc,
-		struct file_transfer *xfer, const char *buf, int len)
-{
-	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
-	aim_session_t *sess = aim_conn_getsess(oft->conn);
-
-	if (oft->type == OFT_SENDFILE_IN)
-		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;
-	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
-
-	oft->xfer = xfer;
-	oft->conn = aim_accepttransfer(od->sess, od->conn, oft->sn,
-			oft->cookie, oft->ip,
-			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);
-		return;
-	}
-
-	aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT,
-			AIM_CB_OFT_SENDFILEFILEREQ, oscar_file_transfer_do,
-			0);
-
-	oft->watcher = gaim_input_add(oft->conn->fd, GAIM_INPUT_READ,
-			oscar_callback, oft->conn);
+static int oscar_sendfile_prompt(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
+	struct gaim_xfer *xfer;
+	struct oscar_xfer_data *xfer_data;
+	va_list ap;
+	aim_conn_t *conn;
+	fu8_t *cookie;
+	struct aim_fileheader_t *fh;
+debug_printf("AAA - in oscar_sendfile_prompt\n");
+
+	va_start(ap, fr);
+	conn = va_arg(ap, aim_conn_t *);
+	cookie = va_arg(ap, fu8_t *);
+	fh = va_arg(ap, struct aim_fileheader_t *);
+	va_end(ap);
+
+	if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
+		return 1;
+
+	if (!(xfer_data = xfer->data))
+		return 1;
+
+	/* Jot down some data we'll need later */
+	xfer_data->modtime = fh->modtime;
+	xfer_data->checksum = fh->checksum;
+
+	/* We want to stop listening with a normal thingy */
+	gaim_input_remove(xfer->watcher);
+	xfer->watcher = 0;
+
+	/* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */
+	aim_oft_sendheader(xfer_data->conn->sessv, xfer_data->conn, AIM_CB_OFT_ACK, xfer_data->cookie, xfer->filename, 0, 1, xfer->size, xfer->size, fh->modtime, fh->checksum, 0x02);
+	gaim_xfer_start(xfer, xfer->fd, NULL, 0);
+
+	return 0;
 }
 
 /*
- * This is called when the user began a file transfer, but subsequently canceled.
+ * We are sending a file to someone else.  They have just acknowledged are 
+ * prompt, so we want to start sending data like there's no tomorrow.
  */
-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);
-
-	if (oft->type == OFT_SENDFILE_IN)
-		aim_denytransfer(od->sess, oft->sn, oft->cookie,
-				AIM_TRANSFER_DENY_DECLINE);
-
-	od->file_transfers = g_slist_remove(od->file_transfers, oft);
-	aim_conn_kill(od->sess, &oft->conn);
-	g_free(oft->sn);
-	g_free(oft);
+static int oscar_sendfile_ack(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
+	struct gaim_xfer *xfer;
+	va_list ap;
+	aim_conn_t *conn;
+	fu8_t *cookie;
+	struct aim_fileheader_t *fh;
+debug_printf("AAA - in oscar_sendfile_ack\n");
+
+	va_start(ap, fr);
+	conn = va_arg(ap, aim_conn_t *);
+	cookie = va_arg(ap, fu8_t *);
+	fh = va_arg(ap, struct aim_fileheader_t *);
+	va_end(ap);
+
+	if (!(xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
+		return 1;
+
+	gaim_xfer_start(xfer, xfer->fd, NULL, 0);
+
+	return 0;
 }
-#endif
+
+/*
+ * We just sent a file to someone.  They said they got it and everything, 
+ * so we can close our direct connection and what not.
+ */
+static int oscar_sendfile_done(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
+	struct gaim_xfer *xfer;
+	va_list ap;
+	aim_conn_t *conn;
+	fu8_t *cookie;
+	struct aim_fileheader_t *fh;
+debug_printf("AAA - in oscar_sendfile_done\n");
+
+	va_start(ap, fr);
+	conn = va_arg(ap, aim_conn_t *);
+	cookie = va_arg(ap, fu8_t *);
+	fh = va_arg(ap, struct aim_fileheader_t *);
+	va_end(ap);
+
+	if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
+		return 1;
+
+	gaim_xfer_set_completed(xfer, TRUE);
+	gaim_xfer_end(xfer);
+
+	return 0;
+}
 
 static void accept_direct_im(struct ask_direct *d) {
 	struct gaim_connection *gc = d->gc;
@@ -1763,7 +1916,7 @@
 	dim->gc = d->gc;
 	g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
 
-	dim->conn = aim_directim_connect(od->sess, d->sn, NULL, d->cookie);
+	dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie);
 	if (!dim->conn) {
 		g_free(dim);
 		cancel_direct_im(d);
@@ -1771,9 +1924,9 @@
 	}
 
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
-				gaim_directim_incoming, 0);
+				gaim_odc_incoming, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
-				gaim_directim_typing, 0);
+				gaim_odc_typing, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
 			        gaim_update_ui, 0);
 	for (i = 0; i < (int)strlen(d->ip); i++) {
@@ -1784,7 +1937,7 @@
 	}
 	host = g_strndup(d->ip, i);
 	dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
-	rc = proxy_connect(host, port, oscar_directim_callback, dim);
+	rc = proxy_connect(host, port, oscar_odc_callback, dim);
 	g_free(host);
 	if (rc < 0) {
 		aim_conn_kill(od->sess, &dim->conn);
@@ -1844,9 +1997,9 @@
 			if (file) {
 				int len = fread(buf, 1, st.st_size, file);
 				debug_printf("Sending buddy icon to %s (%d bytes, %lu reported)\n",
-						userinfo->sn, len, st.st_size);
-				aim_send_icon(sess, userinfo->sn, buf, st.st_size,
-					      st.st_mtime, aim_iconsum(buf, st.st_size));
+					userinfo->sn, len, st.st_size);
+				aim_im_sendch2_icon(sess, userinfo->sn, buf, st.st_size,
+					st.st_mtime, aimutil_iconsum(buf, st.st_size));
 				fclose(file);
 			} else
 				debug_printf("Can't open buddy icon file!\n");
@@ -1912,38 +2065,12 @@
 
 static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
 	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
 
 	if (!args)
 		return 0;
 
-	debug_printf("rendezvous status %hu (%s)\n", args->status, userinfo->sn);
-
-	if (args->status == AIM_RENDEZVOUS_CANCEL) {
-#if 0
-		struct oscar_file_transfer *oft;
-		if (!args->cookie)
-			return 1;
-		oft = find_oft_by_cookie(gc, args->cookie);
-		if (oft) {
-			transfer_abort(oft->xfer, _("Buddy canceled transfer"));
-			oscar_file_transfer_disconnect(sess, oft->conn);
-		}
-		return 0;
-#endif
-	}
-	else if (args->status == AIM_RENDEZVOUS_ACCEPT) {
-#if 0
-		/* The user accepted our transfer request, but we don't
-		 * really need to do anything yet.
-		 * -- wtm
-		 */
-		return 0;
-#endif
-	}
-	else if (args->status != AIM_RENDEZVOUS_PROPOSE) {
-		debug_printf("unknown rendezvous status\n");
-		return 1;
-	}
+	debug_printf("rendezvous with %s, status is %hu\n", userinfo->sn, args->status);
 
 	if (args->reqclass & AIM_CAPS_CHAT) {
 		char *name;
@@ -1965,96 +2092,78 @@
 		if (name)
 			g_free(name);
 	} else if (args->reqclass & AIM_CAPS_SENDFILE) {
-#if 0
-		struct oscar_file_transfer *oft;
-		struct oscar_data *od = gc->proto_data;
-		char *tmp;
-
-		if (!args->cookie || !args->verifiedip || !args->port ||
-		    !args->info.sendfile.filename || !args->info.sendfile.totsize ||
-		    !args->info.sendfile.totfiles || !args->reqclass)
-			return 1;
-		if ((oft = find_oft_by_cookie(sess->aux_data, args->cookie)))
-		{
-			/* This is a request for a reverse connection,
-			 * which is used by newer clients when for some
-			 * reason they are unable to connect to our listener
-			 * (e.g. they are behind a firewall).
-			 */
-			if (oft->type != OFT_SENDFILE_OUT)
-				return -1;
-
-			/* It seems that Trillian sends some weird
-			 * packets.  Sanity check.
-			 */
-			if (!args->verifiedip)
-				return -1;
-
-			/* This connection isn't used for anything, since
-			 * we're using a reverse connection instead.
-			 */
-			gaim_input_remove(oft->watcher);
-			aim_conn_kill(sess, &oft->conn);
-
-			debug_printf("sendfile: doing reverse connection to %s:%hu\n", args->verifiedip, args->port);
-
-			oft->conn = aim_accepttransfer(sess, od->conn,
-				userinfo->sn,
-				args->cookie, args->verifiedip,
-				args->port,
-				AIM_CAPS_SENDFILE);
-
-			/* XXX: this is a bit of a hack: ideally
-			 * we should wait on GAIM_INPUT_WRITE. -- wtm
+/* BBB */
+		if (args->status == AIM_RENDEZVOUS_PROPOSE) {
+			/* Someone wants to send a file (or files) to us */
+			struct gaim_xfer *xfer;
+			struct oscar_xfer_data *xfer_data;
+
+			/* XXX - Should do something with args->clientip or args->clientip2 */
+			if (!args->cookie || !args->verifiedip || !args->port ||
+			    !args->info.sendfile.filename || !args->info.sendfile.totsize ||
+			    !args->info.sendfile.totfiles || !args->reqclass)
+				return 1;
+
+			if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) {
+				/* last char of the ft req is a star, they are sending us a
+				 * directory -- remove the star and trailing slash so we dont save
+				 * directories that look like 'dirname\*'  -- arl */
+				char *tmp = strrchr(args->info.sendfile.filename, '\\');
+				if (tmp && (tmp[1] == '*')) {
+					tmp[0] = '\0';
+				}
+			}
+
+			/* Setup the oscar-specific transfer xfer_data */
+			xfer_data = g_malloc0(sizeof(struct oscar_xfer_data));
+			xfer_data->gc = gc;
+			memcpy(xfer_data->cookie, args->cookie, 8);
+
+			/* Build the file transfer handle */
+			xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, userinfo->sn);
+			xfer_data->xfer = xfer;
+			xfer->data = xfer_data;
+
+			/* Set the info about the incoming file */
+			gaim_xfer_set_filename(xfer, args->info.sendfile.filename);
+			gaim_xfer_set_size(xfer, args->info.sendfile.totsize);
+			/* XXX - xfer->remote_ip = g_strdup(args->verifiedip); */
+			xfer->remote_ip = g_strdup(args->clientip2);
+			xfer->remote_port = args->port;
+
+			 /* Setup our I/O op functions */
+			gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
+			gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
+			gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
+			gaim_xfer_set_cancel_fnc(xfer, oscar_xfer_cancel);
+			gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack);
+
+			/*
+			 * XXX - Should do something with args->msg, args->encoding, and args->language
+			 *       probably make it apply to all ch2 messages.
 			 */
-			aim_conn_completeconnect(sess, oft->conn);
-
-			oscar_sendfile_request(sess, oft);
-
-			aim_conn_addhandler(sess, oft->conn,
-				AIM_CB_FAM_OFT,
-				AIM_CB_OFT_SENDFILECOMPLETE,
-				oscar_sendfile_out_done,
-				0);
-			aim_conn_addhandler(sess, oft->conn,
-				AIM_CB_FAM_OFT,
-				AIM_CB_OFT_SENDFILEFILESEND,
-				oscar_file_transfer_do,
-				0);
-			oft->watcher = gaim_input_add(oft->conn->fd,
-				GAIM_INPUT_READ, oscar_callback,
-				oft->conn);
-			return 0;
+
+			/* Keep track of this transfer for later */
+			od->file_transfers = g_slist_append(od->file_transfers, xfer);
+
+			/* Now perform the request */
+			gaim_xfer_request(xfer);
+		} else if (args->status == AIM_RENDEZVOUS_CANCEL) {
+			/* The other user wants to cancel a file transfer */
+			struct gaim_xfer *xfer;
+			debug_printf("AAA - File transfer canceled by remote user\n");
+			if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie)))
+				gaim_xfer_cancel(xfer);
+		} else if (args->status == AIM_RENDEZVOUS_ACCEPT) {
+			/*
+			 * This gets sent by the receiver of a file 
+			 * as they connect directly to us.  If we don't 
+			 * get this, then maybe a third party connected 
+			 * to us, and we shouldn't send them anything.
+			 */
+		} else {
+			debug_printf("unknown rendezvous status!\n");
 		}
-
-		/* 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);
-
-		oft = g_new0(struct oscar_file_transfer, 1);
-		
-		oft->type = OFT_SENDFILE_IN;
-		oft->sn = g_strdup(userinfo->sn);
-		strncpy(oft->ip, args->verifiedip, sizeof(oft->ip));
-		oft->port = args->port;
-		memcpy(oft->cookie, args->cookie, 8);
-
-		od->file_transfers = g_slist_append(od->file_transfers, oft);
-
-		/* last char of the ft req is a star, they are sending us a
-		 * directory -- remove the star and trailing slash so we dont save
-		 * directories that look like 'dirname\*'  -- arl */
-		tmp = strrchr(args->info.sendfile.filename, '\\');
-		if (tmp && (tmp[1] == '*')) {
-			tmp[0] = '\0';
-		}
-
-		oft->xfer = transfer_in_add(gc, userinfo->sn, 
-				args->info.sendfile.filename,
-				args->info.sendfile.totsize,
-				args->info.sendfile.totfiles,
-				args->msg);
-#endif
 	} else if (args->reqclass & AIM_CAPS_GETFILE) {
 	} else if (args->reqclass & AIM_CAPS_VOICE) {
 	} else if (args->reqclass & AIM_CAPS_BUDDYICON) {
@@ -2155,7 +2264,7 @@
 		gchar message;
 		message = 0;
 		buddy = find_buddy(gc->account, data->name);
-		aim_send_im_ch4(od->sess, data->name, AIM_ICQMSG_AUTHGRANTED, &message);
+		aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHGRANTED, &message);
 		show_got_added(gc, NULL, data->name, (buddy ? get_buddy_alias_only(buddy) : NULL), NULL);
 #else
 		aim_ssi_sendauthreply(od->sess, od->conn, data->name, 0x01, NULL);
@@ -2172,7 +2281,7 @@
 	if (g_slist_find(connections, gc)) {
 		struct oscar_data *od = gc->proto_data;
 #ifdef NOSSI
-		aim_send_im_ch4(od->sess, data->name, AIM_ICQMSG_AUTHDENIED, msg ? msg : _("No reason given."));
+		aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHDENIED, msg ? msg : _("No reason given."));
 #else
 		aim_ssi_sendauthreply(od->sess, od->conn, data->name, 0x00, msg ? msg : _("No reason given."));
 #endif
@@ -2478,20 +2587,15 @@
 
 static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const char *cookie) {
 	struct gaim_connection *gc = sess->aux_data;
-
+	struct oscar_data *od = gc->proto_data;
+
+/* BBB */
 	switch (reason) {
 		case 3: { /* Decline sendfile. */
-#if 0
-			struct oscar_file_transfer *oft = find_oft_by_cookie(gc, cookie);
-
-			if (oft) {
-				char *buf = g_strdup_printf(_("%s has declined to receive a file from %s.\n"),
-						who, gc->username);
-				transfer_abort(oft->xfer, buf);
-				g_free(buf);
-				oscar_file_transfer_disconnect(sess, oft->conn);
-			}
-#endif
+			struct gaim_xfer *xfer;
+			debug_printf("AAA - Other user declined file transfer\n");
+			if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
+				gaim_xfer_cancel(xfer);
 		} break;
 
 		default: {
@@ -2598,35 +2702,35 @@
 }
 
 static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
-	va_list ap;
-	char *data;
-	fu16_t reason;
-	char buf[1024];
+#if 0
 	struct gaim_connection *gc = sess->aux_data;
-
-#if 0
-	struct oscar_file_transfer *oft;
+	struct oscar_data *od = gc->proto_data;
+	struct gaim_xfer *xfer;
 #endif
+	va_list ap;
+	fu16_t reason;
+	char *data, *buf;
 
 	va_start(ap, fr);
-	reason = (fu16_t) va_arg(ap, unsigned int);
+	reason = (fu16_t)va_arg(ap, unsigned int);
 	data = va_arg(ap, char *);
 	va_end(ap);
 
+	debug_printf("Message error with data %s and reason %hu\n", data, reason);
+
+/* BBB */
 #if 0
-	/* If this was a file transfer request, data is a cookie. */
-	if ((oft = find_oft_by_cookie(gc, data))) {
-		transfer_abort(oft->xfer,
-				(reason < msgerrreasonlen) ? gettext(msgerrreason[reason]) : _("No reason given."));
-
-		oscar_file_transfer_disconnect(sess, oft->conn);
+	/* If this was a file transfer request, data is a cookie */
+	if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) {
+		gaim_xfer_cancel(xfer);
 		return 1;
 	}
 #endif
 
-	/* Data is assumed to be the destination sn. */
-	snprintf(buf, sizeof(buf), _("Your message to %s did not get sent:"), data);
+	/* Data is assumed to be the destination sn */
+	buf = g_strdup_printf(_("Your message to %s did not get sent:"), data);
 	do_error_dialog(buf, (reason < msgerrreasonlen) ? gettext(msgerrreason[reason]) : _("No reason given."), GAIM_ERROR);
+	g_free(buf);
 
 	return 1;
 }
@@ -2907,7 +3011,7 @@
 	va_list ap;
 	fu16_t type;
 	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 
 	va_start(ap, fr);
 	type = (fu16_t) va_arg(ap, unsigned int);
@@ -2927,12 +3031,12 @@
 			debug_printf("chat info: \tExchange List: (%d total)\n", exchangecount);
 			for (i = 0; i < exchangecount; i++)
 				debug_printf("chat info: \t\t%hu    %s\n", exchanges[i].number, exchanges[i].name ? exchanges[i].name : "");
-			while (odata->create_rooms) {
-				struct create_room *cr = odata->create_rooms->data;
+			while (od->create_rooms) {
+				struct create_room *cr = od->create_rooms->data;
 				debug_printf("creating room %s\n", cr->name);
 				aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
 				g_free(cr->name);
-				odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+				od->create_rooms = g_slist_remove(od->create_rooms, cr);
 				g_free(cr);
 			}
 			}
@@ -2961,7 +3065,7 @@
 					createtime,
 					maxmsglen, maxoccupancy, createperms, unknown,
 					name, ck);
-			aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
+			aim_chat_join(od->sess, od->conn, exchange, ck, instance);
 			}
 			break;
 		default:
@@ -3084,7 +3188,6 @@
 	va_end(ap);
 
 	if (emailinfo) {
-		debug_printf("Got email info. domain is %s,  webmail is %s,  new email: %hhu,  number new: %hu,  flag is %hu, havenewmail is %d\n", emailinfo->domain, emailinfo->url, emailinfo->unread, emailinfo->nummsgs, emailinfo->flag, havenewmail);
 		if (emailinfo->unread) {
 			if (havenewmail)
 				connection_has_mail(gc, emailinfo->nummsgs ? emailinfo->nummsgs : -1, NULL, NULL, emailinfo->url);
@@ -3207,7 +3310,7 @@
 
 	aim_bos_reqlocaterights(sess, fr->conn);
 	aim_bos_reqbuddyrights(sess, fr->conn);
-	aim_reqicbmparams(sess);
+	aim_im_reqparams(sess);
 	aim_bos_reqrights(sess, fr->conn);
 
 #ifdef NOSSI
@@ -3282,7 +3385,7 @@
 	params->maxmsglen = 8000;
 	params->minmsginterval = 0;
 
-	aim_seticbmparam(sess, params);
+	aim_im_setparams(sess, params);
 
 	return 1;
 }
@@ -3292,7 +3395,7 @@
 	va_list ap;
 	fu16_t maxsiglen;
 	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	char *unicode;
 	int unicode_len;
 	fu32_t flags;
@@ -3303,9 +3406,9 @@
 
 	debug_printf("locate rights: max sig len = %d\n", maxsiglen);
 
-	odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
-
-	if (odata->icq)
+	od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
+
+	if (od->icq)
 		aim_bos_setprofile(sess, fr->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
 	else {
 		flags = check_encoding (gc->account->user_info);
@@ -3329,7 +3432,7 @@
 	va_list ap;
 	fu16_t maxbuddies, maxwatchers;
 	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 
 	va_start(ap, fr);
 	maxbuddies = (fu16_t) va_arg(ap, unsigned int);
@@ -3338,8 +3441,8 @@
 
 	debug_printf("buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
 
-	odata->rights.maxbuddies = (guint)maxbuddies;
-	odata->rights.maxwatchers = (guint)maxwatchers;
+	od->rights.maxbuddies = (guint)maxbuddies;
+	od->rights.maxwatchers = (guint)maxwatchers;
 
 	return 1;
 }
@@ -3348,7 +3451,7 @@
 	fu16_t maxpermits, maxdenies;
 	va_list ap;
 	struct gaim_connection *gc = sess->aux_data;
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 
 	va_start(ap, fr);
 	maxpermits = (fu16_t) va_arg(ap, unsigned int);
@@ -3357,8 +3460,8 @@
 
 	debug_printf("BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
 
-	odata->rights.maxpermits = (guint)maxpermits;
-	odata->rights.maxdenies = (guint)maxdenies;
+	od->rights.maxpermits = (guint)maxpermits;
+	od->rights.maxdenies = (guint)maxdenies;
 
 	account_online(gc);
 	serv_finish_login(gc);
@@ -3367,9 +3470,9 @@
 
 	aim_clientready(sess, fr->conn);
 
-	/* AAA - Should call aim_bos_setidle with 0x0000 */
-
-	/* AAA - Should only call reqofflinemsgs when using ICQ? */
+	/* 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);
@@ -3420,6 +3523,9 @@
 	info = va_arg(ap, struct aim_icq_info *);
 	va_end(ap);
 
+	if (!info || !info->uin)
+		return 1;
+
 	g_snprintf(who, sizeof(who), "%lu", info->uin);
 	buf = g_strdup_printf("<b>UIN:</b> %s<br>", who);
 	if (info->nick) {
@@ -3484,6 +3590,9 @@
 	info = va_arg(ap, struct aim_icq_info *);
 	va_end(ap);
 
+	if (!info || !info->uin)
+		return 1;
+
 	g_snprintf(who, sizeof(who), "%lu", info->uin);
 	buf = g_strdup_printf("<b>UIN:</b> %s<br>", who);
 	if (info->nick) {
@@ -3675,23 +3784,23 @@
 }
 
 static void oscar_keepalive(struct gaim_connection *gc) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
-	aim_flap_nop(odata->sess, odata->conn);
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	aim_flap_nop(od->sess, od->conn);
 }
 
 static int oscar_send_typing(struct gaim_connection *gc, char *name, int typing) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
-	struct direct_im *dim = find_direct_im(odata, name);
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	struct direct_im *dim = find_direct_im(od, name);
 	if (dim)
-		aim_send_typing(odata->sess, dim->conn, typing);
+		aim_odc_send_typing(od->sess, dim->conn, typing);
 	else {
-		if (g_hash_table_lookup(odata->supports_tn, normalize(name))) {
+		if (g_hash_table_lookup(od->supports_tn, normalize(name))) {
 			if (typing == TYPING)
-				aim_mtn_send(odata->sess, 0x0001, name, 0x0002);
+				aim_im_sendmtn(od->sess, 0x0001, name, 0x0002);
 			else if (typing == TYPED)
-				aim_mtn_send(odata->sess, 0x0001, name, 0x0001);
+				aim_im_sendmtn(od->sess, 0x0001, name, 0x0001);
 			else
-				aim_mtn_send(odata->sess, 0x0001, name, 0x0000);
+				aim_im_sendmtn(od->sess, 0x0001, name, 0x0000);
 		}
 	}
 	return 0;
@@ -3699,15 +3808,15 @@
 static void oscar_ask_direct_im(struct gaim_connection *gc, char *name);
 
 static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
-	struct direct_im *dim = find_direct_im(odata, name);
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	struct direct_im *dim = find_direct_im(od, name);
 	int ret = 0;
 	GError *err = NULL;
 
 	if (dim) {
 		if (dim->connected) {  /* If we're not connected yet, send through server */
-			/* 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);
+			/* XXX - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
+			ret =  aim_odc_send_im(od->sess, dim->conn, message, len == -1 ? strlen(message) : len, 0);
 			if (ret == 0)
 				return 1;
 			else return ret;
@@ -3719,17 +3828,17 @@
 		return -ENOTCONN;
 	}
 	if (imflags & IM_FLAG_AWAY) {
-		ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
+		ret = aim_im_sendch1(od->sess, name, AIM_IMFLAGS_AWAY, message);
 	} else {
 		struct aim_sendimext_args args;
-		GSList *h = odata->hasicons;
+		GSList *h = od->hasicons;
 		struct icon_req *ir = NULL;
 		char *who = normalize(name);
 		struct stat st;
 		int len;
 
 		args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
-		if (odata->icq)
+		if (od->icq)
 			args.flags |= AIM_IMFLAGS_OFFLINE;
 
 		args.features = gaim_features;
@@ -3754,7 +3863,7 @@
 				fread(buf, 1, st.st_size, file);
 
 				args.iconlen   = st.st_size;
-				args.iconsum   = aim_iconsum(buf, st.st_size);
+				args.iconsum   = aimutil_iconsum(buf, st.st_size);
 				args.iconstamp = st.st_mtime;
 
 				args.flags |= AIM_IMFLAGS_HASICON;
@@ -3796,7 +3905,7 @@
 		}
 		args.msglen = len;
 
-		ret = aim_send_im_ext(odata->sess, &args);
+		ret = aim_im_sendch1_ext(od->sess, &args);
 	}
 	if (ret >= 0)
 		return 1;
@@ -3804,24 +3913,24 @@
 }
 
 static void oscar_get_info(struct gaim_connection *g, char *name) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	if (odata->icq)
-		aim_icq_getsimpleinfo(odata->sess, name);
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
+	if (od->icq)
+		aim_icq_getsimpleinfo(od->sess, name);
 	else
 		/* people want the away message on the top, so we get the away message
 		 * first and then get the regular info, since it's too difficult to
 		 * insert in the middle. i hate people. */
-		aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
+		aim_getinfo(od->sess, od->conn, name, AIM_GETINFO_AWAYMESSAGE);
 }
 
 static void oscar_get_away(struct gaim_connection *g, char *who) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	if (odata->icq) {
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
+	if (od->icq) {
 		struct buddy *budlight = find_buddy(g->account, who);
 		if (budlight)
 			if ((budlight->uc & 0xffff0000) >> 16)
 				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
-					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xffff0000) >> 16);
+					aim_im_sendch2_geticqaway(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
 				else
 					debug_printf("Error: Remote client does not support retrieval of status messages.\n");
 			else
@@ -3829,63 +3938,65 @@
 		else
 			debug_printf("Error: Could not find %s in local contact list, therefore unable to request status message.\n", who);
 	} else
-		aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_GENERALINFO);
+		aim_getinfo(od->sess, od->conn, who, AIM_GETINFO_GENERALINFO);
 }
 
+#if 0
 static void oscar_get_caps(struct gaim_connection *g, char *name) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_CAPABILITIES);
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
+	aim_getinfo(od->sess, od->conn, name, AIM_GETINFO_CAPABILITIES);
 }
+#endif
 
 static void oscar_set_dir(struct gaim_connection *g, const char *first, const char *middle, const char *last,
 			  const char *maiden, const char *city, const char *state, const char *country, int web) {
-	/* AAA: some of these things are wrong, but i'm lazy */
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	aim_setdirectoryinfo(odata->sess, odata->conn, first, middle, last,
+	/* XXX - some of these things are wrong, but i'm lazy */
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
+	aim_setdirectoryinfo(od->sess, od->conn, first, middle, last,
 				maiden, NULL, NULL, city, state, NULL, 0, web);
 }
 
 
 static void oscar_set_idle(struct gaim_connection *g, int time) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	aim_bos_setidle(odata->sess, odata->conn, time);
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
+	aim_bos_setidle(od->sess, od->conn, time);
 }
 
 static void oscar_set_info(struct gaim_connection *g, char *info) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
 	gchar *inforeal, *unicode;
 	fu32_t flags;
 	int unicode_len;
 
-	if (odata->rights.maxsiglen == 0)
+	if (od->rights.maxsiglen == 0)
 		do_error_dialog(_("Unable to set AIM profile."), 
 				_("You have probably requested to set your profile before the login procedure completed.  "
 				  "Your profile remains unset; try setting it again when you are fully connected."), GAIM_ERROR);
 
-	if (strlen(info) > odata->rights.maxsiglen) {
+	if (strlen(info) > od->rights.maxsiglen) {
 		gchar *errstr;
 
 		errstr = g_strdup_printf(_("The maximum profile length of %d bytes has been exceeded.  "
-					   "Gaim has truncated and set it."), odata->rights.maxsiglen);
+					   "Gaim has truncated and set it."), od->rights.maxsiglen);
 		do_error_dialog("Profile too long.", errstr, GAIM_WARNING);
 
 		g_free(errstr);
 	}
 
-	inforeal = g_strndup(info, odata->rights.maxsiglen);
-
-	if (odata->icq)
-		aim_bos_setprofile(odata->sess, odata->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
+	inforeal = g_strndup(info, od->rights.maxsiglen);
+
+	if (od->icq)
+		aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
 	else {
 		flags = check_encoding(inforeal);
 
 		if (flags == 0) {
-			aim_bos_setprofile(odata->sess, odata->conn, "us-ascii", inforeal, strlen (inforeal),
+			aim_bos_setprofile(od->sess, od->conn, "us-ascii", inforeal, strlen (inforeal),
 					   NULL, NULL, 0, caps_aim);
 		} else {
 			unicode = g_convert (inforeal, strlen(inforeal), "UCS-2BE", "UTF-8", NULL,
 					     &unicode_len, NULL);
-			aim_bos_setprofile(odata->sess, odata->conn, "unicode-2-0", unicode, unicode_len,
+			aim_bos_setprofile(od->sess, od->conn, "unicode-2-0", unicode, unicode_len,
 					   NULL, NULL, 0, caps_aim);
 			g_free(unicode);
 		}
@@ -4005,15 +4116,15 @@
 }
 
 static void oscar_warn(struct gaim_connection *gc, char *name, int anon) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
-	aim_send_warning(odata->sess, odata->conn, name, anon ? AIM_WARN_ANON : 0);
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	aim_im_warn(od->sess, od->conn, name, anon ? AIM_WARN_ANON : 0);
 }
 
 static void oscar_dir_search(struct gaim_connection *gc, const char *first, const char *middle, const char *last,
 			     const char *maiden, const char *city, const char *state, const char *country, const char *email) {
-	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	if (strlen(email))
-		aim_usersearch_address(odata->sess, odata->conn, email);
+		aim_usersearch_address(od->sess, od->conn, email);
 }
 
 static void oscar_add_buddy(struct gaim_connection *gc, const char *name) {
@@ -4139,7 +4250,7 @@
 
 	debug_printf("ssi rights:");
 	for (i=0; i<numtypes; i++)
-		debug_printf(" max type 0x%04x = %hx, ", i, maxitems[i]);
+		debug_printf(" max type 0x%04x=%hd,", i, maxitems[i]);
 	debug_printf("\n");
 
 	if (numtypes >= 0)
@@ -4159,7 +4270,7 @@
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct aim_ssi_item *curitem;
 	int tmp;
-	/* AAA - use these?
+	/* XXX - use these?
 	va_list ap;
 
 	va_start(ap, fr);
@@ -4510,7 +4621,7 @@
 }
 
 static void oscar_join_chat(struct gaim_connection *g, GList *data) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
 	aim_conn_t *cur;
 	char *name;
 	int *exchange;
@@ -4522,33 +4633,33 @@
 	exchange = data->next->data;
 
 	debug_printf("Attempting to join chat room %s.\n", name);
-	if ((cur = aim_getconn_type(odata->sess, AIM_CONN_TYPE_CHATNAV))) {
+	if ((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
 		debug_printf("chatnav exists, creating room\n");
-		aim_chatnav_createroom(odata->sess, cur, name, *exchange);
+		aim_chatnav_createroom(od->sess, cur, name, *exchange);
 	} else {
 		/* this gets tricky */
 		struct create_room *cr = g_new0(struct create_room, 1);
 		debug_printf("chatnav does not exist, opening chatnav\n");
 		cr->exchange = *exchange;
 		cr->name = g_strdup(name);
-		odata->create_rooms = g_slist_append(odata->create_rooms, cr);
-		aim_reqservice(odata->sess, odata->conn, AIM_CONN_TYPE_CHATNAV);
+		od->create_rooms = g_slist_append(od->create_rooms, cr);
+		aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
 	}
 }
 
 static void oscar_chat_invite(struct gaim_connection *g, int id, const char *message, const char *name) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
 	struct chat_connection *ccon = find_oscar_chat(g, id);
 	
 	if (!ccon)
 		return;
 	
-	aim_chat_invite(odata->sess, odata->conn, name, message ? message : "",
+	aim_chat_invite(od->sess, od->conn, name, message ? message : "",
 			ccon->exchange, ccon->name, 0x0);
 }
 
 static void oscar_chat_leave(struct gaim_connection *g, int id) {
-	struct oscar_data *odata = g ? (struct oscar_data *)g->proto_data : NULL;
+	struct oscar_data *od = g ? (struct oscar_data *)g->proto_data : NULL;
 	GSList *bcs = g->buddy_chats;
 	struct gaim_conversation *b = NULL;
 	struct chat_connection *c = NULL;
@@ -4570,12 +4681,12 @@
 	
 	c = find_oscar_chat(g, gaim_chat_get_id(GAIM_CHAT(b)));
 	if (c != NULL) {
-		if (odata)
-			odata->oscar_chats = g_slist_remove(odata->oscar_chats, c);
+		if (od)
+			od->oscar_chats = g_slist_remove(od->oscar_chats, c);
 		if (c->inpa > 0)
 			gaim_input_remove(c->inpa);
-		if (g && odata->sess)
-			aim_conn_kill(odata->sess, &c->conn);
+		if (g && od->sess)
+			aim_conn_kill(od->sess, &c->conn);
 		g_free(c->name);
 		g_free(c->show);
 		g_free(c);
@@ -4585,7 +4696,7 @@
 }
 
 static int oscar_chat_send(struct gaim_connection *g, int id, char *message) {
-	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	struct oscar_data *od = (struct oscar_data *)g->proto_data;
 	GSList *bcs = g->buddy_chats;
 	struct gaim_conversation *b = NULL;
 	struct chat_connection *c = NULL;
@@ -4602,7 +4713,7 @@
 	if (!b)
 		return -EINVAL;
 
-	bcs = odata->oscar_chats;
+	bcs = od->oscar_chats;
 	while (bcs) {
 		c = (struct chat_connection *)bcs->data;
 		if (b == c->cnv)
@@ -4636,7 +4747,7 @@
 	}
 	g_free(buf2);
 
-	aim_chat_send_im(odata->sess, c->conn, 0, buf, strlen(buf));
+	aim_chat_send_im(od->sess, c->conn, 0, buf, strlen(buf));
 	g_free(buf);
 	return 0;
 }
@@ -4677,108 +4788,18 @@
 	return NULL;
 }
 
-#if 0
 /*
- * 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);
-	aim_conn_t *conn = oft->conn;
-	aim_session_t *sess = aim_conn_getsess(conn);
-
-	oft->filesdone++;
-	oft->watcher = gaim_input_add(conn->fd, GAIM_INPUT_READ,
-			oscar_callback, conn);
-
-	/* If this is an incoming sendfile transfer, we send an OK
-	 * message to the sender; if this is an outgoing sendfile, we
-	 * will get an OK from the receiver that will be handled in
-	 * oscar_sendfile_out_done(), so we don't need to do anything
-	 * yet.
-	 */
-
-	if (oft->type == OFT_SENDFILE_IN)
-		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.
+ * We have just established a socket with the other dude, so set up some handlers.
  */
-void oscar_file_transfer_done(struct gaim_connection *gc,
-		struct file_transfer *xfer) {
-	struct oscar_file_transfer *oft = find_oft_by_xfer(gc, xfer);
-	aim_conn_t *conn = oft->conn;
-	aim_session_t *sess = aim_conn_getsess(conn);
-
-	oft->filesdone++;
-	if (oft->type == OFT_SENDFILE_IN) {
-		aim_oft_end(sess, conn);
-		oscar_file_transfer_disconnect(sess, conn);
-	}
-	else if (oft->type == OFT_SENDFILE_OUT) { 
-		/* Wait for response before closing connection. */
-		oft->watcher = gaim_input_add(conn->fd, GAIM_INPUT_READ,
-				oscar_callback, conn);
-	}
-}
-
-/*
- * 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;
-	aim_conn_t *conn;
-	struct oscar_file_transfer *oft;
-	struct aim_fileheader_t *fh;
-	int err;
-
-	va_start(ap, fr);
-	conn = va_arg(ap, aim_conn_t *);
-	fh = va_arg(ap, struct aim_fileheader_t *);
-	va_end(ap);
-
-	oft = find_oft_by_conn(gc, conn);
-
-	/* Don't use the regular input handler for the raw data. */
-	gaim_input_remove(oft->watcher);
-	oft->watcher = 0;
-
-	if (oft->type == OFT_SENDFILE_IN) {
-		/* 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);
-	}
-
-	if (err) {
-		/* There was an error; cancel the transfer. */
-		struct oscar_data *od = (struct oscar_data *)gc->proto_data;
-		aim_conn_t *bosconn = od->conn;
-		aim_canceltransfer(sess, bosconn, oft->cookie,
-			oft->sn, AIM_CAPS_SENDFILE);
-		oscar_file_transfer_disconnect(sess, oft->conn);
-	}
-
-	return 0;
-}
-#endif
-
-static int gaim_directim_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
-	va_list ap;
+static int gaim_odc_initiate(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;
-	aim_conn_t *newconn, *listenerconn;
 	struct gaim_conversation *cnv;
 	struct direct_im *dim;
 	char buf[256];
 	char *sn;
+	va_list ap;
+	aim_conn_t *newconn, *listenerconn;
 
 	va_start(ap, fr);
 	newconn = va_arg(ap, aim_conn_t *);
@@ -4788,7 +4809,7 @@
 	aim_conn_close(listenerconn);
 	aim_conn_kill(sess, &listenerconn);
 
-	sn = g_strdup(aim_directim_getsn(newconn));
+	sn = g_strdup(aim_odc_getsn(newconn));
 
 	debug_printf("DirectIM: initiate success to %s\n", sn);
 	dim = find_direct_im(od, sn);
@@ -4797,19 +4818,16 @@
 		cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, sn);
 	gaim_input_remove(dim->watcher);
 	dim->conn = newconn;
-	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
-					oscar_callback, dim->conn);
+	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
 	dim->connected = TRUE;
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
 	g_free(sn);
 	gaim_conversation_write(cnv, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
 
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
-				gaim_directim_incoming, 0);
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
-				gaim_directim_typing, 0);
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
-			    gaim_update_ui, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_update_ui, 0);
+
 	return 1;
 }
 
@@ -4844,7 +4862,7 @@
 	return 1;
 }
 
-static int gaim_directim_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
+static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *msg, *sn;
 	int len, encoding;
@@ -4859,13 +4877,13 @@
 
 	debug_printf("Got DirectIM message from %s\n", sn);
 
-	/* AAA - I imagine Paco-Paco will want to do some voodoo with the encoding here */
+	/* XXX - 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;
 }
 
-static int gaim_directim_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
+static int gaim_odc_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *sn;
 	int typing;
@@ -4923,13 +4941,13 @@
 	dim->gc = gc;
 	g_snprintf(dim->name, sizeof dim->name, "%s", data->who);
 
-	dim->conn = aim_directim_initiate(od->sess, data->who);
+	dim->conn = aim_odc_initiate(od->sess, data->who);
 	if (dim->conn != NULL) {
 		od->direct_ims = g_slist_append(od->direct_ims, dim);
 		dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
 						oscar_callback, dim->conn);
-		aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE,
-					gaim_directim_initiate, 0);
+		aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED,
+					gaim_odc_initiate, 0);
 	} else {
 		do_error_dialog(_("Unable to open Direct IM"), NULL, GAIM_ERROR);
 		g_free(dim);
@@ -4955,7 +4973,7 @@
 		if (budlight)
 			if ((budlight->uc >> 16) & (AIM_ICQ_STATE_AWAY || AIM_ICQ_STATE_DND || AIM_ICQ_STATE_OUT || AIM_ICQ_STATE_BUSY || AIM_ICQ_STATE_CHAT))
 				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
-					aim_send_im_ch2_geticqmessage(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
+					aim_im_sendch2_geticqaway(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<HR><I>Remote client does not support sending status messages.</I><BR>"), who, state_msg);
@@ -5134,22 +5152,23 @@
 			pbm->callback = oscar_ask_direct_im;
 			pbm->gc = gc;
 			m = g_list_append(m, pbm);
-		
 #if 0
 			pbm = g_new0(struct proto_buddy_menu, 1);
 			pbm->label = _("Send File");
-			pbm->callback = oscar_ask_send_file;
+			pbm->callback = oscar_ask_sendfile;
 			pbm->gc = gc;
 			m = g_list_append(m, pbm);
 #endif
 		}
 	}
 
+#if 0
 	pbm = g_new0(struct proto_buddy_menu, 1);
 	pbm->label = _("Get Capabilities");
 	pbm->callback = oscar_get_caps;
 	pbm->gc = gc;
 	m = g_list_append(m, pbm);
+#endif
 
 	return m;
 }
@@ -5306,15 +5325,25 @@
 	pam->gc = gc;
 	m = g_list_append(m, pam);
 
-	if (od->sess->authinfo->regstatus == 0x0003) {
-		/* AIM actions */
-		m = g_list_append(m, NULL);
-
+	if ((od->sess->authinfo->regstatus == 0x0003) || (od->icq)) {
 		pam = g_new0(struct proto_actions_menu, 1);
 		pam->label = _("Change Password");
 		pam->callback = show_change_passwd;
 		pam->gc = gc;
 		m = g_list_append(m, pam);
+	}
+
+	if (od->sess->authinfo->chpassurl) {
+		pam = g_new0(struct proto_actions_menu, 1);
+		pam->label = _("Change Password (URL)");
+		pam->callback = oscar_show_chpassurl;
+		pam->gc = gc;
+		m = g_list_append(m, pam);
+	}
+
+	if (od->sess->authinfo->regstatus == 0x0003) {
+		/* AIM actions */
+		m = g_list_append(m, NULL);
 
 		pam = g_new0(struct proto_actions_menu, 1);
 		pam->label = _("Format Screenname");
@@ -5339,12 +5368,6 @@
 		pam->callback = oscar_show_change_email;
 		pam->gc = gc;
 		m = g_list_append(m, pam);
-	} else if (od->sess->authinfo->chpassurl) {
-		pam = g_new0(struct proto_actions_menu, 1);
-		pam->label = _("Change Password");
-		pam->callback = oscar_show_chpassurl;
-		pam->gc = gc;
-		m = g_list_append(m, pam);
 	}
 
 	m = g_list_append(m, NULL);
@@ -5375,14 +5398,19 @@
 static void oscar_change_passwd(struct gaim_connection *gc, const char *old, const char *new)
 {
 	struct oscar_data *od = gc->proto_data;
-	if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH)) {
-		od->chpass = TRUE;
-		od->oldp = g_strdup(old);
-		od->newp = g_strdup(new);
-		aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
+
+	if (od->icq) {
+		aim_icq_changepasswd(od->sess, new);
 	} else {
-		aim_admin_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH),
-				new, old);
+		aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
+		if (conn) {
+			aim_admin_changepasswd(od->sess, conn, new, old);
+		} else {
+			od->chpass = TRUE;
+			od->oldp = g_strdup(old);
+			od->newp = g_strdup(new);
+			aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
+		}
 	}
 }
 
@@ -5497,15 +5525,6 @@
 	ret->rem_deny = oscar_rem_deny;
 	ret->set_permit_deny = oscar_set_permit_deny;
 
-#if 0
-	ret->file_transfer_cancel = oscar_file_transfer_cancel;
-	ret->file_transfer_in = oscar_file_transfer_in;
-	ret->file_transfer_out = oscar_file_transfer_out;
-	ret->file_transfer_data_chunk = oscar_file_transfer_data_chunk;
-	ret->file_transfer_nextfile = oscar_file_transfer_nextfile;
-	ret->file_transfer_done = oscar_file_transfer_done;
-#endif
-
 	ret->warn = oscar_warn;
 	ret->chat_info = oscar_chat_info;
 	ret->join_chat = oscar_join_chat;
--- a/src/protocols/oscar/rxhandlers.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/rxhandlers.c	Wed Feb 26 05:01:37 2003 +0000
@@ -539,56 +539,37 @@
 		if (cur->handled)
 			continue;
 
-		/*
-		 * This is a debugging/sanity check only and probably 
-		 * could/should be removed for stable code.
-		 */
-		if (((cur->hdrtype == AIM_FRAMETYPE_OFT) && 
-		   (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
-		  ((cur->hdrtype == AIM_FRAMETYPE_FLAP) && 
-		   (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
-			faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", cur->hdrtype, cur->conn->type);
-			cur->handled = 1;
-			continue;
-		}
+		if (cur->hdrtype == AIM_FRAMETYPE_FLAP) {
+			if (cur->hdr.flap.type == 0x01) {
+				cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
+				continue;
+
+			} else if (cur->hdr.flap.type == 0x02) {
+				if ((cur->handled = consumesnac(sess, cur)))
+					continue;
 
-		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
-			if (cur->hdrtype != AIM_FRAMETYPE_OFT) {
-				faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
-				cur->handled = 1; /* get rid of it */
-			} else {
+			} else if (cur->hdr.flap.type == 0x04) {
+				cur->handled = negchan_middle(sess, cur);
+				continue;
+
+			} else if (cur->hdr.flap.type == 0x05) {
+
+			}
+
+		} else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
+			if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
 				aim_rxdispatch_rendezvous(sess, cur);
 				cur->handled = 1;
+				continue;
+
+			} else if (cur->conn->type == AIM_CONN_TYPE_LISTENER) {
+				/* not possible */
+				faimdprintf(sess, 0, "rxdispatch called on LISTENER connection!\n");
+				cur->handled = 1;
+				continue;
 			}
-			continue;
 		}
 
-		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
-			/* not possible */
-			faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
-			cur->handled = 1;
-			continue;
-		}
-
-		if (cur->hdr.flap.type == 0x01) {
-			
-			cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
-			
-			continue;
-			
-		} else if (cur->hdr.flap.type == 0x02) {
-
-			if ((cur->handled = consumesnac(sess, cur)))
-				continue;
-
-		} else if (cur->hdr.flap.type == 0x04) {
-
-			cur->handled = negchan_middle(sess, cur);
-			continue;
-
-		} else if (cur->hdr.flap.type == 0x05)
-			;
-		
 		if (!cur->handled) {
 			consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */
 			cur->handled = 1;
--- a/src/protocols/oscar/rxqueue.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/rxqueue.c	Wed Feb 26 05:01:37 2003 +0000
@@ -164,7 +164,7 @@
 	fu16_t payloadlen;
 
 	if (!sess || !conn)
-		return -1;
+		return -EINVAL;
 
 	if (conn->fd == -1)
 		return -1; /* it's an aim_conn_close()'d connection */
@@ -176,7 +176,7 @@
 		return aim_conn_completeconnect(sess, conn);
 
 	if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1)))
-		return -1;
+		return -ENOMEM;
 
 	/*
 	 * Rendezvous (client to client) connections do not speak FLAP, so this 
@@ -184,8 +184,8 @@
 	 */
 	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);
+	else if (conn->type == AIM_CONN_TYPE_LISTENER) {
+		faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd);
 		free(newrx);
 		return -1;
 	} else
--- a/src/protocols/oscar/snac.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/snac.c	Wed Feb 26 05:01:37 2003 +0000
@@ -90,9 +90,8 @@
 		if (cur->id == id) {
 			*prev = cur->next;
 			if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) {
-				struct aim_snac_destructor *asd = cur->data;
-				cur->data = asd->data;
-				free(asd);
+				free(cur->data);
+				cur->data = NULL;
 			}
 			return cur;
 		} else
@@ -102,40 +101,6 @@
 	return cur;
 }
 
-/* Free a SNAC, and call the appropriate destructor if necessary.
- */
-faim_internal faim_shortfunc void aim_cleansnac(aim_session_t *sess, aim_snac_t *snac)
-{
-	aim_module_t *cur;
-
-	if (snac->flags & AIM_SNACFLAGS_DESTRUCTOR) {
-		struct aim_snac_destructor *d = snac->data;
-		aim_modsnac_t modsnac;
-
-		modsnac.id = snac->id;
-		modsnac.subtype = snac->type;
-		modsnac.family = snac->family;
-		modsnac.flags = snac->flags;
-
-		for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next)
-		{
-			if (!cur->snacdestructor)
-				continue;
-			if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
-				(cur->family != modsnac.family))
-				continue;
-			if (cur->snacdestructor(sess, d->conn, &modsnac,
-						d->data))
-				break;
-		}
-		free(d->data);
-	}
-
-	free(snac->data);
-	free(snac);
-}
-
-
 /*
  * This is for cleaning up old SNACs that either don't get replies or
  * a reply was never received for.  Garabage collection. Plain and simple.
@@ -161,7 +126,8 @@
 
 				*prev = cur->next;
 
-				aim_cleansnac(sess, cur);
+				free(cur->data);
+				free(cur);
 			} else
 				prev = &cur->next;
 		}
--- a/src/protocols/oscar/tlv.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/tlv.c	Wed Feb 26 05:01:37 2003 +0000
@@ -549,10 +549,7 @@
 	aim_tlvlist_t *cur;
 
 	/* do an initial run to test total length */
-	for (cur = *list, goodbuflen = 0; cur; cur = cur->next) {
-		goodbuflen += 2 + 2; /* type + len */
-		goodbuflen += cur->tlv->length;
-	}
+	goodbuflen = aim_sizetlvchain(list);
 
 	if (goodbuflen > aim_bstream_empty(bs))
 		return 0; /* not enough buffer */
--- a/src/protocols/oscar/txqueue.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/txqueue.c	Wed Feb 26 05:01:37 2003 +0000
@@ -36,8 +36,7 @@
 	}
 
 	/* For sanity... */
-	if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || 
-			(conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
+	if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_LISTENER)) {
 		if (framing != AIM_FRAMETYPE_OFT) {
 			faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n");
 			return NULL;
@@ -242,13 +241,13 @@
 		    (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
 			/* I strongly suspect that this is a horrible thing to do
 			 * and I feel really guilty doing it. */
-			const char *sn = aim_directim_getsn(conn);
+			const char *sn = aim_odc_getsn(conn);
 			aim_rxcallback_t userfunc;
 			while (count - wrote > 1024) {
 				wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
 				if ((userfunc=aim_callhandler(conn->sessv, conn, 
-							      AIM_CB_FAM_SPECIAL, 
-							      AIM_CB_SPECIAL_IMAGETRANSFER)))
+								AIM_CB_FAM_SPECIAL, 
+								AIM_CB_SPECIAL_IMAGETRANSFER)))
 				  userfunc(conn->sessv, NULL, sn, 
 					   count-wrote>1024 ? ((double)wrote / count) : 1);
 			}
@@ -256,9 +255,8 @@
 		if (count - wrote) {
 			wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
 		}
-		
+
 	}
-	
 
 	if (((aim_session_t *)conn->sessv)->debug >= 2) {
 		int i;
@@ -273,10 +271,9 @@
 		faimdprintf(sess, 2, "\n");
 	}
 
-
 	bs->offset += wrote;
 
-	return wrote;	
+	return wrote;
 }
 
 static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
--- a/src/protocols/oscar/util.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/oscar/util.c	Wed Feb 26 05:01:37 2003 +0000
@@ -121,6 +121,42 @@
 	return toReturn;
 }
 
+/**
+ * Calculate the checksum of a given icon.
+ *
+ */
+faim_export fu16_t aimutil_iconsum(const fu8_t *buf, int buflen)
+{
+	fu32_t sum;
+	int i;
+
+	for (i=0, sum=0; i+1<buflen; i+=2)
+		sum += (buf[i+1] << 8) + buf[i];
+	if (i < buflen)
+		sum += buf[i];
+	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
+
+	return sum;
+}
+
+faim_export int aim_util_getlocalip(fu8_t *ip)
+{
+	struct hostent *hptr;
+	char localhost[129];
+
+	/* XXX if available, use getaddrinfo() */
+	/* XXX allow client to specify which IP to use for multihomed boxes */
+
+	if (gethostname(localhost, 128) < 0)
+		return -1;
+
+	if (!(hptr = gethostbyname(localhost)))
+		return -1;
+	memcpy(ip, hptr->h_addr_list[0], 4);
+
+	return 0;
+}
+
 /*
 * int snlen(const char *)
 * 
--- a/src/protocols/toc/toc.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/protocols/toc/toc.c	Wed Feb 26 05:01:37 2003 +0000
@@ -448,8 +448,6 @@
 	char *w = strtok(NULL, ":");
 	static char buf[256];
 
-	plugin_event(event_error, (void *)no, 0, 0, 0);
-
         switch(no) {
         case 69:
                 g_snprintf(buf, sizeof(buf), _("Unable to write file %s."), w);