changeset 237:6ced2f1c8b24

[gaim-migrate @ 247] How cool is this, libfaim is making a comeback. I completely redid everything, as was necessary because of the updates to libfaim since gaim 0.9.7. You can sign on and send/recv IMs, but there's a bad lag between display updates that I haven't figured out how to fix yet. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sat, 20 May 2000 00:30:53 +0000
parents 62d470738cc7
children fbf1d60668d1
files libfaim/BUGS libfaim/CHANGES libfaim/CHANGES.gaim libfaim/Makefile.am libfaim/README libfaim/README.gaim libfaim/aim.h libfaim/aim_auth.c libfaim/aim_buddylist.c libfaim/aim_cbtypes.h libfaim/aim_chat.c libfaim/aim_chatnav.c libfaim/aim_conn.c libfaim/aim_global.c libfaim/aim_im.c libfaim/aim_info.c libfaim/aim_login.c libfaim/aim_logoff.c libfaim/aim_misc.c libfaim/aim_rxhandlers.c libfaim/aim_rxqueue.c libfaim/aim_search.c libfaim/aim_snac.c libfaim/aim_tlv.c libfaim/aim_txqueue.c libfaim/aim_util.c libfaim/faimconfig.h src/dialogs.c src/gaim.h src/oscar.c src/rvous.c src/server.c src/toc.c
diffstat 33 files changed, 5631 insertions(+), 2582 deletions(-) [+]
line wrap: on
line diff
--- a/libfaim/BUGS	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/BUGS	Sat May 20 00:30:53 2000 +0000
@@ -27,3 +27,11 @@
 aim_snac.c
 ----------
   - Should implement better SNAC handling
+
+aim_tlv.c
+---------
+  - Newer TLV bulk-read routines most likely have leakage somewhere.
+
+aim_rxhandlers.c
+----------------
+  - Need a better solution than sleep()
--- a/libfaim/CHANGES	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/CHANGES	Sat May 20 00:30:53 2000 +0000
@@ -1,6 +1,183 @@
 
 No release numbers
 ------------------
+ - Sun Apr  2 07:29:11 UTC 2000
+   - Redid much of the tx path to remove the superfluous copy.  But
+       it touches every function that transmits.  Its been a long 
+       time in coming.
+   - Added caps parameter to aim_bos_setprofile().  You can now
+       control what capabilties you show up as others to
+   - Added ->capabilities to userinfo.  It should be nonzero when
+       its coming off buddy oncoming information, but NOT requested
+       user info.
+
+ - Sun Apr  2 01:45:15 UTC 2000
+   - Increased AIM_CONN_MAX from 5 to 7.  Precausionary only
+   - Removed deprecated TIS_TELNET_PROXY from faim/faimconfig.h
+   - Added macros for the simple integer functions in aim_util.c
+   - Removed usage of aim_failsaferead() 
+      - Still not sure why I had it to begin with.  I think it 
+        was a remament from when we used non-blocking sockets
+        (which I don't know why I did either)
+   - Removed some of the oddities in aim_get_command()
+      - gprof showed this taking 20kns on my k6-300.  Should be faster now.
+      - Added conn parameter.  This should prevent aim_select() from
+        getting called too many times in a row.
+
+ - Thu Mar 23 08:45:40 UTC 2000
+   - Removed aim_countconn() > 0 check in aim_select(), its logically redundent
+   - Added aim_putuserinfo() (inverse of aim_extractuserinfo())
+   - Added aim_sendbuddyoncoming/offgoing() 
+   - Rearranged loop in rxdispatch()
+   - Remove aim_conn_close() if connections dead in aim_get_command()
+
+ - Thu Mar 23 00:44:32 UTC 2000
+   - Added a check to purge_rxqueue to skip handled commands
+
+ - Mon Mar 20 05:30:59 UTC 2000
+   - Added some server-only functions for login
+   - Added aim_counttlvchain()
+   - Added aim_sncmp() and aim_snlen()
+
+ - Sun Mar 19 06:07:52 UTC 2000
+   - Added a parameter to aim_select to return event type 
+     - REQUIRES CLIENT CHANGES.
+   - For the most part rewrote the tx and rx queuing code 
+     - Should fix many, many outstanding problems in and related
+         to that code, including one that keeps memory from freeing 
+   - Fixed several bugs in various places
+   - Reformated a lot of code, and did general cleanups
+   - Should have a generally more robust lib now.
+
+ - Sun Mar 12 00:07:40 UTC 2000
+   - Fixed a robustness problem in aim_handleredirect_middle()
+   - Added TLV chain creation routines (yes, aimd is progressing)
+
+ - Mon Jan  3 04:07:55 UTC 2000
+   - Fixed bug in aim_snac.c
+   - Fixed condition where commands read from connections that have 
+       been closed were still left in the queue.  Now cancelled.
+   - Added some printfs to aim_info to get more informative crahes
+   - Fixed a bug in aim_rxqueue::purge
+
+ - Sun Jan  2 10:31:19 UTC 2000
+   - Cleanups in aim_info.c
+   - Can compile with -Ddebug=100 again
+   - Implemented chat: Joining/Leaving, info parsing, IM parsing/sending
+   - Implemented some chatnav: rights req/parsing, room creation
+
+ - Thu Dec 30 10:08:42 UTC 1999
+   - Fixed bug in aim_im.c when (encoding == NULL) || (lang == NULL)
+   - Added detection of voice chat requests
+   - Added AIM_CLASS_* defines, including new Away flag
+   - Added awaymsg parameter to bos_setprofile.
+     - If awaymsg is nonnull, you will be advertised as being away (your
+         class will be ORed with AIM_CLASS_AWAY), otherwise you'll show 
+         up normal.
+
+ - Wed Dec 29 10:06:35 UTC 1999
+   - Fixed small bug in IM parser
+   - Added stubs for the capability TLVs in userinfo.
+
+ - Wed Dec 29 09:14:45 UTC 1999
+   - Added a capability block to aim_bos_setprofile.  Can now get chat 
+       invites again.
+   - Extended ICBM parser to support channel 2 messages (chat invites)
+     - A channel parameter has been prepended to the varargs -- REQUIRES 
+         CLIENT CHANGES.
+     - Extended faimtest to support chat invites.
+   - Changed faimtest to get sn/password from environment
+
+ - Wed Dec 29 04:17:03 UTC 1999
+   - Added -g to CFLAGS
+   - Added aim_sendconnack() to aim_login.c (needed for newer login)
+   - Added code for the new SNAC-based login/auth procedure. (see SNACLOGIN
+       in faim/faimconfig.h for why its not enabled)
+   - Reimplemented aim_authparse(), aim_handleredirect() using TLVlists
+       - The old auth_failed callback is now integrated into the
+           success one.  If there was an error, logininfo->errorcode is nonzero
+   - Fiddled with version information.  Added aim_setversions()
+   - Added table of SNAC names for showing unknown snacs (jbm)
+   - Added a middle handler for MOTD
+   - Added new authorization SNACs to faim/aim_cbtypes.h   
+
+ - Sun Dec 26 22:59:10 UTC 1999
+   - Renamed login_phase1_struct to aim_login_struct
+   - Changed cookie and sn to be static arrays in aim_login_struct
+   - Integrated the Jabber-faim changes.  (temas)  [BIG CLIENT CHANGES]
+     - Added aim_session_t, removed all global variables
+     - Changed all functions to accept a session pointer
+     - Removed aim_global.c
+     - Updated faimtest to use an aim_session_t.
+     - Removed all cases where logininfo was passed as vararg to client
+   - Fixed small bug in aim_newconn's 'fixing' of host:port addresses
+   - Added an install rule to the makefile (installs headers+so only!)
+   - Enabled USE_SNAC_FOR_IMS by default, assuming it got fixed
+       by n's new aim_snac.c from ages ago
+   - Implemented a middle handler for 0004/0001 message errors, added
+       snacid lookup to get illfated destination SN and pass to client
+   - Implemented a short middle handler for offgoing buddy.
+
+ - Fri Dec 24 21:30:06 UTC 1999
+   - Added an error-counting Read() that has been sitting in my inbox
+   - Cleaned up header files, created aim_cbtypes.h.
+   - Added void * to aim_conn_t for private client use if they want. (Orb)
+   - Removed all stderr output.  All output is important, and goes to stdout.
+   - Renamed isautoresponse in IM parser to icbmflags.
+   - Added Orb's fix for the new login code, deleted old (see Orb, I do read
+       your mail....eventually).
+   - Added mailing lists to README.
+
+ - Fri Dec 24 11:12:34 UTC 1999
+   - Cleaned up both outgoing and incoming ICBM handling.  Anything
+       that crashes around there is no longer libfaims fault!
+       - The encoding flags are now passed up to the client.
+   - Added several TLV routines to parse large blocks of continuous
+       TLV triplets.  Not terribly effecient, but quite elegent in usage.
+   - Added icbm_setparams() back in from way-back-long-ago.  It hasn't
+       been implemented in a long time, but I think we should still send it.
+
+ - Fri Dec 24 01:23:06 UTC 1999
+   - Fixed a very minor bug in aim_newconn().
+   - Added aimutil_get{16,32}()
+   - Added aim_extractuserinfo() for extracting  user data
+       blocks and putting them into struct aim_userinfo_s's.
+         - Added a loop to print out info on extraneous TLVs.
+         - Put in lots of comments.
+   - Added parse_oncoming_middle() to parse the user data
+       block of that packet.  Now passes struct aim_userinfo_s
+       to client.
+   - Rearranged parse_userinfo_middle().  Now passes an entire
+       userinfo struct to client instead of individual variables.
+   - Convered the version of parse_im_middle() thats actually getting
+       used to pass up a userinfo struct.  
+   - Updated faimtest to accept new structs.
+
+ - Tue Dec 21 06:18:50 UTC 1999
+   - Fixed a Win32 header problem
+ - Tue Dec 21 03:44:13 UTC 1999
+   - Latency timers now update on rx as well as tx.  Gets rid of even more
+       of the rate problems that most clients are having.  
+   - Renamed lasttx and settxlatency to lastactivity and setlatency, respec.
+   - Integrated fixes needed for Win32 -- should compile cleanly now (DMP)
+   - Cleaned up some places, use aim_putsnac everywhere now.
+
+ - Sun Sep 26 20:04:20 MST 1999
+   - Reduced the IM parsing to look for 3 zeros instead of 4 -- NEEDS WORK
+       - This was needed to work with MacAIM3 and some WinAIM3s.
+   - Added aim_conn_settxlatency() for throttling outgoing frames -- NEEDS WORK
+   - Added an int to the userinfo and incoming IM user callbacks for new
+       TLV that AOL put it in -- its the number of seconds elapsed since
+       the user logged in
+   - Worked more on the callbacks (more internal rearrangements)
+   - Fixed bug in aim_select() (returning negative fds)
+   - Clear out logininfo struct before putting more data into it
+   - Other bugfixes that I can't particularly remember.
+
+ - Tue Aug 24 03:13:12 UTC 1999   --- TRANSITION RELEASE!!
+   - Added jbm's new aim_rxqueue.c, which should crash less
+   - Started the overhaul on the callback system.  No where near complete yet.
+
  - Sun Aug  1 03:02:17 UTC 1999
    - Added aimutil_*()s in aim_util.c for raw byte placement
    - Cleaned up aim_im.c, aim_auth.c, and aim_login.c using aimutil_*
@@ -11,6 +188,7 @@
        changes left to do)
    - Some Chat stuff changed, still no where near functional
    - Finally remembered to switch the license to LGPL (from GPL)
+   - Permit/Deny (blocking) list support added
    - Released a snapshot
 
  - Sat Jul 31 05:28:38 UTC 1999
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/CHANGES.gaim	Sat May 20 00:30:53 2000 +0000
@@ -0,0 +1,10 @@
+Sat May 20 00:16:25 UTC 2000 EWarmenhoven
+	- First log entry! Yay!
+	- Updated the libfaim code to the latest in CVS
+	- Rewrote much of oscar.c (if not all).
+	- Current supported features:
+		Signing On
+		Receiving IMs
+		Sending IMs
+	- There is a 2 second delay between GTK updates. Please, someone
+	  fix this before I go insane.
--- a/libfaim/Makefile.am	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/Makefile.am	Sat May 20 00:30:53 2000 +0000
@@ -2,13 +2,17 @@
 
 EXTRA_LIBRARIES = libfaim.a
 
-EXTRA_DIST = aim.h faimconfig.h
+EXTRA_DIST = aim.h aim_cbtypes.h faimconfig.h
 
-libfaim_a_SOURCES = aim_chatnav.c aim_info.c aim_rxhandlers.c \
-                    aim_tlv.c aim_auth.c aim_conn.c aim_login.c \
-                    aim_rxqueue.c aim_txqueue.c aim_buddylist.c \
-                    aim_global.c aim_logoff.c aim_search.c aim_util.c \
-                    aim_chat.c aim_im.c aim_misc.c aim_snac.c
+#libfaim_a_SOURCES = aim_chatnav.c aim_info.c aim_rxhandlers.c \
+#                    aim_tlv.c aim_auth.c aim_conn.c aim_login.c \
+#                    aim_rxqueue.c aim_txqueue.c aim_buddylist.c \
+#                    aim_global.c aim_logoff.c aim_search.c aim_util.c \
+#                    aim_chat.c aim_im.c aim_misc.c aim_snac.c
+libfaim_a_SOURCES = aim_auth.c aim_buddylist.c aim_chat.c aim_chatnav.c \
+		    aim_conn.c aim_im.c aim_info.c aim_login.c \
+		    aim_logoff.c aim_misc.c aim_rxhandlers.c aim_rxqueue.c \
+		    aim_search.c aim_snac.c aim_tlv.c aim_txqueue.c aim_util.c
 
 CFLAGS += $(GAIM_CFLAGS) -I../src
 
--- a/libfaim/README	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/README	Sat May 20 00:30:53 2000 +0000
@@ -4,7 +4,7 @@
 
 This is libfaim, the purpose of which is to implement as much as the
 AOL AIM/OSCAR protocol as possible (which should be all of it).  After
-nearly a year of development, its still nowhere close.  
+over a year of development, its still nowhere close.  
 
 This is not a full client and never will be.  libfaim only implements
 the routines to implement a client (ie, there's no user interface).  
@@ -14,7 +14,9 @@
 
 I would not recommend using this version of libfaim in any form yet.  It's
 beta-quality and I know it leaks memory quite badly.  It seems fairly
-stable, however.  YMMV, YAYOR, etc.
+stable, however.  YMMV, YAYOR, etc.  I suppose I should say regardless of
+that warning, that several clients use it and people use those clients
+on a daily basis (in particular, me).
 
 
 Building
@@ -22,8 +24,8 @@
 
 Everything in this libfaim dist should build cleanly on any UNIX(-like)
 operating system.  Originally developed on Linux+glibc.  Past versions 
-known to work on Linux+libc5, FreeBSD, HP/UX, Solaris, Mac OS X Server,
-and others.
+known to work on Linux+libc5, FreeBSD, HP/UX, Solaris, Mac OS X Server, 
+Win32 using VC++ 98/6 and others.
 
 libfaim builds as both libfaim.a and libfaim.so.  If your platform for
 some reason does not support dynamic libraries (eg, you get errors when
@@ -63,15 +65,21 @@
 Use the source and utils/faimtest/faimtest.c as a reference when coding
 front-ends.  
 
+Mailing Lists
+-------------
+
+Thanks to Sourceforge, we have our mailing lists back. See:
+http://www.sourceforge.org/mail/?group_id=920  for instructions
+on subscribing to the lists:
+
+  libfaim-devel: Discussion of libfaim and its developement.
+  libfaim-aim-protocol: Discussion of the finer points of OSCAR hacking
+ 
 
 Contact Info
 ------------
 
-The author (Adam Fritzler), can be reached at afritz@iname.com or mid@auk.cx.
-
-I did have mailing lists available for faim-related discussion, but they
-have dwindled and eventually broke and to my knowledge have yet to fix 
-themselves.
+The author (Adam Fritzler), can be reached at mid@auk.cx.
 
 Front-end information:
   http://www.auk.cx/faim/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/README.gaim	Sat May 20 00:30:53 2000 +0000
@@ -0,0 +1,73 @@
+Hello, your good friend EW here with a nice little notice that I'm sure will
+affect the ten of you who actually read this.
+
+I'm going to start trying to get gaim to use Oscar through libfaim. As far as I
+can tell, the only thing it used to be able to do is sign on and receive IMs. I
+updated libfaim to what's currently in the libfaim CVS on sourceforge. As of
+right now, I haven't even gotten it to sign on, but theoretically it can receive
+IMs.
+
+I'm going to try to make as few modifications as possible to the libfaim code.
+The only two modifications I'll probably ever make to it are 1) to make my life
+easier (like putting all the .h files in the same directory as the .c files) or
+2) to fix a compilation error that I happen to be able to fix very easily (like
+with a typo or something). That means that what you're getting when you enable
+oscar is basically faimtest (the very instructional program included with the
+libfaim source on sourceforge) with the Gaim GTK front-end. I'll put any changes
+I make into a file, but so far, I haven't made any changes other than moving the
+.h files down a directory.
+
+HOW TO HELP
+===========
+So here's what you can do in order to help gaim use libfaim. There are basically
+3 steps:
+
+1) In server.c, find an #ifndef USE_OSCAR tag that doesn't have a corresponding
+#else. Find it in a good fun function that you want to implement. Basically
+copy the code from the TOC side for the Oscar side. For example:
+
+void serv_send_im(char *name, char *message, int away)
+{
+	char buf[MSG_LEN - 7];
+
+#ifndef USE_OSCAR
+	g_snprintf(buf, MSG_LEN - 8, "toc_send_im %s \"%s\"%s", normalize(name),		   message, ((away) ? " auto" : ""));
+	sflap_send(buf, strlen(buf), TYPE_DATA);
+#endif
+	if (!away)
+		serv_touch_idle();
+}
+
+becomes:
+
+void serv_send_im(char *name, char *message, int away)
+{
+        char buf[MSG_LEN - 7];
+
+#ifndef USE_OSCAR
+        g_snprintf(buf, MSG_LEN - 8, "toc_send_im %s \"%s\"%s", normalize(name),
+	           message, ((away) ? " auto" : ""));
+	sflap_send(buf, strlen(buf), TYPE_DATA);
+#else
+	oscar_send_im(name, message, away);
+#endif
+	if (!away)
+		serv_touch_idle();
+}
+
+2) Edit gaim.h to add the new function (you'll see a list of them in there)
+
+3) Edit oscar.c to implement the new function
+
+Most of the functions you're going to need to call use a session and connection
+structure. These are kept statically in oscar.c as gaim_sess and gaim_conn. For
+example, from above:
+
+void oscar_send_im(char *name, char *msg, int away) {
+	if (away)
+		aim_send_im(gaim_sess, gaim_conn, name, AIM_IMFLAGS_AWAY, msg);
+	else
+		aim_send_im(gaim_sess, gaim_conn, name, 0, msg);
+}
+
+That should be all that's needed. And that's that. Happy hacking.
--- a/libfaim/aim.h	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim.h	Sat May 20 00:30:53 2000 +0000
@@ -1,46 +1,88 @@
+/* 
+ * Main libfaim header.  Must be included in client for prototypes/macros.
+ *
+ */
+
 #ifndef __AIM_H__
 #define __AIM_H__
 
 #include <faimconfig.h>
+#include <aim_cbtypes.h>
 
-/* some global includes */
 #include <stdio.h>
 #include <string.h>
 #include <fcntl.h>
-#include <netdb.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/time.h>
-#include <unistd.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <errno.h>
 
-#define CONNECT_SIG_LEN 10 /* not used anymore, hopefully */
-#define LOGIN_RESP_LEN 512 /* should only be 334b but no segfault for us! */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <io.h>
+#else
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+/* Portability stuff (DMP) */
 
+#ifdef _WIN32
+#define sleep Sleep
+#define strlen(x) (int)strlen(x)  /* win32 has a unsigned size_t */
+#endif
+
+#if defined(_WIN32) || (defined(mach) && defined(__APPLE__)) 
+#define gethostbyname2(x,y) gethostbyname(x) /* revert to IPv4 */
+#endif 
+
+/* 
+ * Current Maximum Length for Screen Names (not including NULL) 
+ */
+#define MAXSNLEN 16
 
 /*
- * Error codes
+ * Standard size of an AIM authorization cookie
  */
-#define AIM_CONNECT_ERROR	-0x1
-#define AIM_SIGNON_TOO_SOON	-0x4
-#define AIM_SERVICE_FULL	-0x6f
+#define AIM_COOKIELEN            0x100
 
+#if debug > 0
+#define faimdprintf(l, x...) {if (l >= debug) printf(x); }
+#else
+#define faimdprintf(l, x...)
+#endif
 
-struct login_phase1_struct {
-  char *screen_name;
+/*
+ * Login info.  Passes information from the Authorization
+ * stage of login to the service (BOS, etc) connection
+ * phase.
+ *
+ */
+struct aim_login_struct {
+  char screen_name[MAXSNLEN+1];
   char *BOSIP;
-  char *cookie;
+  char cookie[AIM_COOKIELEN];
   char *email;
-  ushort regstatus;
+  u_short regstatus;
+  char *errorurl;
+  u_short errorcode;
 };
 
-extern struct login_phase1_struct aim_logininfo;
-
+/*
+ * Client info.  Filled in by the client and passed
+ * in to aim_login().  The information ends up
+ * getting passed to OSCAR through the initial
+ * login command.
+ *
+ * XXX: Should this be per-session? -mid
+ *
+ */
 struct client_info_s {
-  char clientstring[100]; /* arbitrary number */
+  char clientstring[100]; /* arbitrary size */
   int major;
   int minor;
   int build;
@@ -48,55 +90,49 @@
   char lang[3];
 };
 
-struct connection_info_struct {
-  unsigned int local_seq_num_origin; /* our first seq num */
-  int local_command_count;
-
-  unsigned int remote_seq_num_origin; /* oscar's first seqnum */
-  int remote_command_count; /* command_count + seq_num_origin = cur_seq_num */
-
-  char *sn; /* our screen name */
-
-  int fd;                   /* socket descriptor */
-};
-
 #ifndef TRUE
 #define TRUE 1
 #define FALSE 0
 #endif
 
-#define AIM_CONN_MAX 5 
-/* 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 2
-#define AIM_CONN_TYPE_CHAT 0x000e
-#define AIM_CONN_TYPE_CHATNAV 0x000d
+/* 
+ * 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_STATUS_READY 0x0001
+/*
+ * Status values returned from aim_conn_new().  ORed together.
+ */
+#define AIM_CONN_STATUS_READY       0x0001
 #define AIM_CONN_STATUS_INTERNALERR 0x0002
-#define AIM_CONN_STATUS_RESOLVERR 0x80
-#define AIM_CONN_STATUS_CONNERR 0x40
-
+#define AIM_CONN_STATUS_RESOLVERR   0x0080
+#define AIM_CONN_STATUS_CONNERR     0x0040
 
 struct aim_conn_t {
   int fd;
   int type;
   int seqnum;
   int status;
+  void *priv; /* misc data the client may want to store */
+  time_t lastactivity; /* time of last transmit */
+  int forcedlatency; 
+  struct aim_rxcblist_t *handlerlist;
 };
-struct aim_conn_t aim_conns[AIM_CONN_MAX];
-
 
 /* struct for incoming commands */
 struct command_rx_struct {
                             /* byte 1 assumed to always be 0x2a */
   char type;                /* type code (byte 2) */
-  unsigned int seqnum;      /* sequence number (bytes 3 and 4) */
-  unsigned int commandlen;  /* total packet len - 6 (bytes 5 and 6) */
-  char *data;               /* packet data (from 7 byte on) */
-  unsigned int lock;        /* 1 = locked, 0 = open */
-  unsigned int handled;     /* 1 = been handled, 0 = new */
+  u_int seqnum;             /* sequence number (bytes 3 and 4) */
+  u_int commandlen;         /* total packet len - 6 (bytes 5 and 6) */
+  u_char *data;             /* packet data (from 7 byte on) */
+  u_int lock;               /* 0 = open, !0 = locked */
+  u_int handled;            /* 0 = new, !0 = been handled */
+  u_int nofree;		    /* 0 = free data on purge, 1 = only unlink */
   struct aim_conn_t *conn;  /* the connection it came in on... */
   struct command_rx_struct *next; /* ptr to next struct in list */
 };
@@ -105,103 +141,189 @@
 struct command_tx_struct {
                             /* byte 1 assumed to be 0x2a */
   char type;                /* type/family code */
-  unsigned int seqnum;      /* seqnum dynamically assigned on tx */
-  unsigned int commandlen;  /* SNAC length */
-  char *data;               /* packet data */
-  unsigned int lock;        /* 1 = locked, 0 = open */
-  unsigned int sent;        /* 1 = has been sent, 0 = new */
+  u_int seqnum;             /* seqnum dynamically assigned on tx */
+  u_int commandlen;         /* SNAC length */
+  u_char *data;             /* packet data */
+  u_int lock;               /* 0 = open, !0 = locked */
+  u_int sent;               /* 0 = pending, !0 = has been sent */
   struct aim_conn_t *conn; 
   struct command_tx_struct *next; /* ptr to next struct in list */
 };
 
-/* TLV-related tidbits */
+
+/*
+ * AIM Session: The main client-data interface.  
+ *
+ */
+struct aim_session_t {
+
+  /* ---- Client Accessible ------------------------ */
+  /* 
+   * Login information.  See definition above.
+   *
+   */
+  struct aim_login_struct logininfo;
+  
+  /*
+   * Pointer to anything the client wants to 
+   * explicitly associate with this session.
+   */
+  void *aux_data;
+
+
+  /* ---- Internal Use Only ------------------------ */
+  /* 
+   * Connection information
+   */
+  struct aim_conn_t conns[AIM_CONN_MAX];
+  
+  /* 
+   * TX/RX queues 
+   */
+  struct command_tx_struct *queue_outgoing; 
+  struct command_rx_struct *queue_incoming; 
+  
+  /*
+   * This is a dreadful solution to the what-room-are-we-joining
+   * problem.  (There's no connection between the service
+   * request and the resulting redirect.)
+   */ 
+  char *pendingjoin;
+
+  /*
+   * Outstanding snac handling 
+   *
+   * XXX: Should these be per-connection? -mid
+   **/
+  struct aim_snac_t *outstanding_snacs;
+  u_long snac_nextid;
+};
+
+
+/*
+ * AIM User Info, Standard Form.
+ */
+struct aim_userinfo_s {
+  char sn[MAXSNLEN+1];
+  u_short warnlevel;
+  u_short idletime;
+  u_short class;
+  u_long membersince;
+  u_long onlinesince;
+  u_long sessionlen;  
+  u_short capabilities;
+};
+
+#define AIM_CLASS_TRIAL 	0x0001
+#define AIM_CLASS_UNKNOWN2	0x0002
+#define AIM_CLASS_AOL		0x0004
+#define AIM_CLASS_UNKNOWN4	0x0008
+#define AIM_CLASS_FREE 		0x0010
+#define AIM_CLASS_AWAY		0x0020
+#define AIM_CLASS_UNKNOWN40	0x0040
+#define AIM_CLASS_UNKNOWN80	0x0080
+
+/*
+ * TLV handling
+ */
+
+/* Generic TLV structure. */
 struct aim_tlv_t {
   u_short type;
   u_short length;
   u_char *value;
 };
 
+/* List of above. */
+struct aim_tlvlist_t {
+  struct aim_tlv_t *tlv;
+  struct aim_tlvlist_t *next;
+};
+
+/* TLV-handling functions */
+struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen);
+void aim_freetlvchain(struct aim_tlvlist_t **list);
 struct aim_tlv_t *aim_grabtlv(u_char *src);
 struct aim_tlv_t *aim_grabtlvstr(u_char *src);
+struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, u_short, int);
+char *aim_gettlv_str(struct aim_tlvlist_t *, u_short, int);
 int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv);
 struct aim_tlv_t *aim_createtlv(void);
 int aim_freetlv(struct aim_tlv_t **oldtlv);
 int aim_puttlv_16(u_char *, u_short, u_short);
-
-/* some prototypes... */
-
-/*   implicitly or explicitly called */
-int aim_get_command(void);
-int aim_rxdispatch(void);
-int aim_logoff(void);
-
-typedef int (*rxcallback_t)(struct command_rx_struct *, ...);
-int aim_register_callbacks(rxcallback_t *);
+int aim_puttlv_32(u_char *, u_short, u_long);
+int aim_puttlv_str(u_char *buf, u_short t, u_short l, u_char *v);
+int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list);
+int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val);
+int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val);
+int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len);
+int aim_counttlvchain(struct aim_tlvlist_t **list);
 
-u_long aim_genericreq_n(struct aim_conn_t *conn, u_short family, u_short subtype);
-u_long aim_genericreq_l(struct aim_conn_t *conn, u_short family, u_short subtype, u_long *);
-u_long aim_genericreq_s(struct aim_conn_t *conn, u_short family, u_short subtype, u_short *);
+/*
+ * Get command from connections / Dispatch commands
+ * already in queue.
+ */
+int aim_get_command(struct aim_session_t *, struct aim_conn_t *);
+int aim_rxdispatch(struct aim_session_t *);
 
-/* aim_login.c */
-int aim_send_login (struct aim_conn_t *, char *, char *, struct client_info_s *);
-int aim_encode_password(const char *, char *);
-
-
-struct command_rx_struct *aim_purge_rxqueue(struct command_rx_struct *queue);
+int aim_logoff(struct aim_session_t *);
 
 
-int aim_parse_unknown(struct command_rx_struct *command, ...);
-int aim_parse_missed_im(struct command_rx_struct *, ...);
-int aim_parse_last_bad(struct command_rx_struct *, ...);
+typedef int (*rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...);
+int aim_register_callbacks(rxcallback_t *);
 
-int aim_tx_enqueue(struct command_tx_struct *);
-unsigned int aim_get_next_txseqnum(struct aim_conn_t *);
-int aim_tx_flushqueue(void);
-int aim_tx_printqueue(void);
-int aim_tx_purgequeue(void);
+u_long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype);
+u_long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *);
+u_long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *);
 
-/* queue (linked list) pointers */
-extern struct command_tx_struct *aim_queue_outgoing; /* incoming commands */
-extern struct command_rx_struct *aim_queue_incoming; /* outgoing commands */
+/* aim_login.c */
+int aim_sendconnack(struct aim_session_t *sess, struct aim_conn_t *conn);
+int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
+int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *);
+int aim_encode_password(const char *, u_char *);
+unsigned long aim_sendauthresp(struct aim_session_t *sess, 
+			       struct aim_conn_t *conn, 
+			       char *sn, char *bosip, 
+			       char *cookie, char *email, 
+			       int regstatus);
+int aim_gencookie(unsigned char *buf);
+int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn);
+unsigned long aim_sendredirect(struct aim_session_t *sess, 
+			       struct aim_conn_t *conn, 
+			       unsigned short servid, 
+			       char *ip,
+			       char *cookie);
+void aim_purge_rxqueue(struct aim_session_t *);
 
-/* The default callback handler array */
-extern rxcallback_t aim_callbacks[];
-
-extern struct aim_snac_t *aim_outstanding_snacs;
-extern u_long aim_snac_nextid;
 
-#define AIM_CB_INCOMING_IM 0
-#define AIM_CB_ONCOMING_BUDDY 1
-#define AIM_CB_OFFGOING_BUDDY 2
-#define AIM_CB_MISSED_IM 3
-#define AIM_CB_MISSED_CALL 4
-#define AIM_CB_LOGIN_P4_C1 5
-#define AIM_CB_LOGIN_P4_C2 6
-#define AIM_CB_LOGIN_P2_1 7
-#define AIM_CB_LOGIN_P2_2 8
-#define AIM_CB_LOGIN_P3_B 9
-#define AIM_CB_LOGIN_P3D_A 10
-#define AIM_CB_LOGIN_P3D_B 11
-#define AIM_CB_LOGIN_P3D_C 12
-#define AIM_CB_LOGIN_P3D_D 13
-#define AIM_CB_LOGIN_P3D_E 14
-#define AIM_CB_LOGIN_P3D_F 15
-#define AIM_CB_RATECHANGE 16
-#define AIM_CB_USERERROR 17
-#define AIM_CB_UNKNOWN 18
-#define AIM_CB_USERINFO 19
-#define AIM_CB_SEARCH_ADDRESS 20
-#define AIM_CB_SEARCH_NAME 21
-#define AIM_CB_SEARCH_FAIL 22
-#define AIM_CB_AUTH_ERROR 23
-#define AIM_CB_AUTH_SUCCESS 24
-#define AIM_CB_AUTH_SVRREADY 25
-#define AIM_CB_AUTH_OTHER 26
-#define AIM_CB_AUTH_INFOCHNG_REPLY 27
-#define AIM_CB_CHATNAV_SVRREADY 28
+int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *command, ...);
+int aim_parse_missed_im(struct aim_session_t *, struct command_rx_struct *, ...);
+int aim_parse_last_bad(struct aim_session_t *, struct command_rx_struct *, ...);
+
+struct command_tx_struct *aim_tx_new(int, struct aim_conn_t *, int);
+int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *);
+u_int aim_get_next_txseqnum(struct aim_conn_t *);
+int aim_tx_flushqueue(struct aim_session_t *);
+int aim_tx_printqueue(struct aim_session_t *);
+void aim_tx_purgequeue(struct aim_session_t *);
 
-int Read(int, u_char *, int);
+struct aim_rxcblist_t {
+  u_short family;
+  u_short type;
+  rxcallback_t handler;
+  u_short flags;
+  struct aim_rxcblist_t *next;
+};
 
+int aim_conn_setlatency(struct aim_conn_t *conn, int newval);
+
+int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, rxcallback_t newhandler, u_short flags);
+rxcallback_t aim_callhandler(struct aim_conn_t *conn, u_short family, u_short type);
+int aim_clearhandlers(struct aim_conn_t *conn);
+
+/*
+ * Generic SNAC structure.  Rarely if ever used.
+ */
 struct aim_snac_t {
   u_long id;
   u_short family;
@@ -211,87 +333,170 @@
   time_t issuetime;
   struct aim_snac_t *next;
 };
-u_long aim_newsnac(struct aim_snac_t *newsnac);
-struct aim_snac_t *aim_remsnac(u_long id);
-int aim_cleansnacs(int maxage);
+u_long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac);
+struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id);
+int aim_cleansnacs(struct aim_session_t *, int maxage);
 int aim_putsnac(u_char *, int, int, int, u_long);
 
-void aim_connrst(void);
-struct aim_conn_t *aim_conn_getnext(void);
+
+void aim_connrst(struct aim_session_t *);
+struct aim_conn_t *aim_conn_getnext(struct aim_session_t *);
 void aim_conn_close(struct aim_conn_t *deadconn);
-struct aim_conn_t *aim_getconn_type(int type);
-struct aim_conn_t *aim_newconn(int type, char *dest);
-int aim_conngetmaxfd(void);
-struct aim_conn_t *aim_select(struct timeval *);
+struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type);
+struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest);
+int aim_conngetmaxfd(struct aim_session_t *);
+struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *);
 int aim_conn_isready(struct aim_conn_t *);
 int aim_conn_setstatus(struct aim_conn_t *, int);
+void aim_session_init(struct aim_session_t *);
 
 /* aim_misc.c */
 
-#define AIM_VISIBILITYCHANGE_PERMITADD 0x05
+#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
 #define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
-#define AIM_VISIBILITYCHANGE_DENYADD 0x07
-#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08
+#define AIM_VISIBILITYCHANGE_DENYADD      0x07
+#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
 
-u_long aim_bos_setidle(struct aim_conn_t *, u_long);
-u_long aim_bos_changevisibility(struct aim_conn_t *, int, char *);
-u_long aim_bos_setbuddylist(struct aim_conn_t *, char *);
-u_long aim_bos_setprofile(struct aim_conn_t *, char *);
-u_long aim_bos_setgroupperm(struct aim_conn_t *, u_long);
-u_long aim_bos_clientready(struct aim_conn_t *);
-u_long aim_bos_reqrate(struct aim_conn_t *);
-u_long aim_bos_ackrateresp(struct aim_conn_t *);
-u_long aim_bos_setprivacyflags(struct aim_conn_t *, u_long);
-u_long aim_bos_reqpersonalinfo(struct aim_conn_t *);
-u_long aim_bos_reqservice(struct aim_conn_t *, u_short);
-u_long aim_bos_reqrights(struct aim_conn_t *);
-u_long aim_bos_reqbuddyrights(struct aim_conn_t *);
-u_long aim_bos_reqlocaterights(struct aim_conn_t *);
-u_long aim_bos_reqicbmparaminfo(struct aim_conn_t *);
+u_long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long);
+u_long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *);
+u_long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *);
+u_long aim_bos_setprofile(struct aim_session_t *, struct aim_conn_t *, char *, char *, unsigned int);
+u_long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long);
+u_long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long);
+u_long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short);
+u_long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_bos_reqicbmparaminfo(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
 
 /* aim_rxhandlers.c */
-int aim_register_callbacks(rxcallback_t *);
-int aim_rxdispatch(void);
-int aim_authparse(struct command_rx_struct *);
-int aim_handleredirect_middle(struct command_rx_struct *, ...);
-int aim_parse_unknown(struct command_rx_struct *, ...);
-int aim_parse_missed_im(struct command_rx_struct *, ...);
-int aim_parse_last_bad(struct command_rx_struct *, ...);
-int aim_parse_generalerrs(struct command_rx_struct *command, ...);
+int aim_rxdispatch(struct aim_session_t *);
+int aim_authparse(struct aim_session_t *, struct command_rx_struct *);
+int aim_handleredirect_middle(struct aim_session_t *, struct command_rx_struct *, ...);
+int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *, ...);
+int aim_parse_last_bad(struct aim_session_t *, struct command_rx_struct *, ...);
+int aim_parse_generalerrs(struct aim_session_t *, struct command_rx_struct *command, ...);
+int aim_parsemotd_middle(struct aim_session_t *sess, struct command_rx_struct *command, ...);
 
 /* aim_im.c */
 #define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */
-#define AIM_IMFLAGS_ACK 0x02 /* request a receipt notice */
-u_long aim_send_im(struct aim_conn_t *, char *, int, char *);
-int aim_parse_incoming_im_middle(struct command_rx_struct *);
+#define AIM_IMFLAGS_ACK  0x02 /* request a receipt notice */
+
+u_long aim_send_im(struct aim_session_t *, struct aim_conn_t *, char *, u_int, char *);
+int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *);
+u_long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn);
+int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *);
 
 /* aim_info.c */
-u_long aim_getinfo(struct aim_conn_t *, const char *);
-int aim_parse_userinfo_middle(struct command_rx_struct *);
+#define AIM_CAPS_BUDDYICON 0x01
+#define AIM_CAPS_VOICE 0x02
+#define AIM_CAPS_IMIMAGE 0x04
+#define AIM_CAPS_CHAT 0x08
+#define AIM_CAPS_GETFILE 0x10
+#define AIM_CAPS_SENDFILE 0x20
+extern u_char aim_caps[6][16];
+u_long aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *);
+int aim_extractuserinfo(u_char *, struct aim_userinfo_s *);
+int aim_parse_userinfo_middle(struct aim_session_t *, struct command_rx_struct *);
+int aim_parse_oncoming_middle(struct aim_session_t *, struct command_rx_struct *);
+int aim_parse_offgoing_middle(struct aim_session_t *, struct command_rx_struct *);
+int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info);
+int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info);
+int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
+
 
 /* aim_auth.c */
-int aim_auth_sendcookie(struct aim_conn_t *, char *);
-u_long aim_auth_clientready(struct aim_conn_t *);
-u_long aim_auth_changepasswd(struct aim_conn_t *, char *, char *);
+int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *);
+u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *);
+u_long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *);
 
 /* aim_buddylist.c */
-u_long aim_add_buddy(struct aim_conn_t *, char *);
-u_long aim_remove_buddy(struct aim_conn_t *, char *);
+u_long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
+u_long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
 
 /* aim_search.c */
-u_long aim_usersearch_address(struct aim_conn_t *, char *);
-/* u_long aim_usersearch_name(struct aim_conn_t *, char *); */
+u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *);
+/* u_long aim_usersearch_name(struct aim_session_t *, struct aim_conn_t *, char *); */
+
+
+struct aim_chat_roominfo {
+  u_short exchange;
+  char *name;
+  u_short instance;
+};
+int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo);
+int aim_chat_parse_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command);
+int aim_chat_parse_joined(struct aim_session_t *sess, struct command_rx_struct *command);
+int aim_chat_parse_leave(struct aim_session_t *sess, struct command_rx_struct *command);
+int aim_chat_parse_incoming(struct aim_session_t *sess, struct command_rx_struct *command);
+u_long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg);
+u_long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname);
+u_long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+int aim_chat_attachname(struct aim_conn_t *conn, char *roomname);
+char *aim_chat_getname(struct aim_conn_t *conn);
+struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name);
+
+u_long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn);
+u_long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+u_long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance);
+
+struct aim_chat_exchangeinfo {
+  u_short number;
+  char *name;
+  char *charset1;
+  char *lang1;
+  char *charset2;
+  char *lang2;
+};
+int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command);
+u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange);
+int aim_chat_leaveroom(struct aim_session_t *sess, char *name);
 
 /* aim_util.c */
-int aimutil_put8(u_char *, u_short);
+#ifdef AIMUTIL_USEMACROS
+/*
+ * These are really ugly.  You'd think this was LISP.  I wish it was.
+ */
+#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1)
+#define aimutil_get8(buf) ((*(buf))&0xff)
+#define aimutil_put16(buf, data) ( \
+                                  (*(buf) = (u_char)((data)>>8)&0xff), \
+				  (*((buf)+1) = (u_char)(data)&0xff),  \
+				  2)
+#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define aimutil_put32(buf, data) ( \
+                                  (*((buf)) = (u_char)((data)>>24)&0xff), \
+				  (*((buf)+1) = (u_char)((data)>>16)&0xff), \
+				  (*((buf)+2) = (u_char)((data)>>8)&0xff), \
+				  (*((buf)+3) = (u_char)(data)&0xff), \
+                                  4)
+#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+                            (((*((buf)+1))<<16)&0x00ff0000) + \
+                            (((*((buf)+2))<< 8)&0x0000ff00) + \
+                            (((*((buf)+3)    )&0x000000ff)))
+#else
+#warning Not using aimutil macros.  May have performance problems.
+int aimutil_put8(u_char *, u_char);
+u_char aimutil_get8(u_char *buf);
 int aimutil_put16(u_char *, u_short);
+u_short aimutil_get16(u_char *);
 int aimutil_put32(u_char *, u_long);
-int aimutil_putstr(u_char *, u_char *, int);
+u_long aimutil_get32(u_char *);
+#endif
 
-/* proxy support */
-#ifdef ENABLE_PROXY_SUPPORT
-#include "proxy.h"
-#endif
+int aimutil_putstr(u_char *, const u_char *, int);
+int aimutil_tokslen(char *toSearch, int index, char dl);
+int aimutil_itemcnt(char *toSearch, char dl);
+char *aimutil_itemidx(char *toSearch, int index, char dl);
+
+int aim_snlen(const char *sn);
+int aim_sncmp(const char *sn1, const char *sn2);
 
 #endif /* __AIM_H__ */
 
--- a/libfaim/aim_auth.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_auth.c	Sat May 20 00:30:53 2000 +0000
@@ -5,145 +5,103 @@
 
  */
 
-#include "aim.h"
+#include <aim.h> 
 
 /* this just pushes the passed cookie onto the passed connection -- NO SNAC! */
-int aim_auth_sendcookie(struct aim_conn_t *conn, char *chipsahoy)
+int aim_auth_sendcookie(struct aim_session_t *sess, 
+			struct aim_conn_t *conn, 
+			u_char *chipsahoy)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   int curbyte=0;
   
-  newpacket.lock = 1;
+  if (!(newpacket = aim_tx_new(0x0001, conn, 4+2+2+AIM_COOKIELEN)))
+    return -1;
 
-  if (conn==NULL)
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_AUTH);
-  else
-    newpacket.conn = conn;
+  newpacket->lock = 1;
 
-  newpacket.type = 0x0001;  /* channel 1 (no SNACs, you know) */
-  
-  newpacket.commandlen = 4 + 2 + 2 + 0x100;
-  newpacket.data = (char *) calloc(1, newpacket.commandlen);
-  
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0006);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0100);
-  memcpy(&(newpacket.data[curbyte]), chipsahoy, 0x100);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
+  curbyte += aimutil_put16(newpacket->data+curbyte, AIM_COOKIELEN);
+  memcpy(newpacket->data+curbyte, chipsahoy, AIM_COOKIELEN);
 
-  aim_tx_enqueue(&newpacket);
-  
-  return 0;
+  return aim_tx_enqueue(sess, newpacket);
 }
 
-u_long aim_auth_clientready(struct aim_conn_t *conn)
+u_long aim_auth_clientready(struct aim_session_t *sess,
+			    struct aim_conn_t *conn)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   int curbyte = 0;
 
-  newpacket.lock = 1;
+  if (!(newpacket = aim_tx_new(0x0002, conn, 26)))
+    return -1;
 
-  if (conn==NULL)
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_AUTH);
-  else
-    newpacket.conn = conn;
+  newpacket->lock = 1;
 
-  newpacket.type = 0x0002;
-  
-  newpacket.commandlen = 26;
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  
-  curbyte += aim_putsnac(newpacket.data+curbyte, 0x0001, 0x0002, 0x0000, aim_snac_nextid);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0013);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0007);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
+  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0013);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0007);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
 
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
   {
     struct aim_snac_t snac;
     
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x0001;
     snac.type = 0x0004;
     snac.flags = 0x0000;
 
     snac.data = NULL;
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
-u_long aim_auth_changepasswd(struct aim_conn_t *conn, char *new, char *current)
+u_long aim_auth_changepasswd(struct aim_session_t *sess,
+			     struct aim_conn_t *conn, 
+			     char *new, char *current)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   int i;
 
-  newpacket.lock = 1;
-
-  if (conn==NULL)
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_AUTH);
-  else
-    newpacket.conn = conn;
-
-  newpacket.type = 0x0002;
-  
-  newpacket.commandlen = 10 + 4 + strlen(current) + 4 + strlen(new);
-  newpacket.data = (char *) malloc(newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+4+strlen(current)+4+strlen(new))))
+    return -1;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x07;
-
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x04;
+  newpacket->lock = 1;
 
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
 
   /* current password TLV t(0002) */
-  i = 10;
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x02;
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = strlen(current) & 0xff;
-  memcpy(&(newpacket.data[i]), current, strlen(current));
-  i += strlen(current);
+  i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(current), current);
 
   /* new password TLV t(0012) */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x12;
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = strlen(new) & 0xff;
-  memcpy(&(newpacket.data[i]), new, strlen(new));
-  i+=strlen(new);
+  i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(new), new);
 
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
   {
     struct aim_snac_t snac;
     
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x0001;
     snac.type = 0x0004;
     snac.flags = 0x0000;
 
     snac.data = NULL;
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
--- a/libfaim/aim_buddylist.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_buddylist.c	Sat May 20 00:30:53 2000 +0000
@@ -7,47 +7,31 @@
  * Adds a single buddy to your buddy list after login.
  *
  */
-u_long aim_add_buddy(struct aim_conn_t *conn, char *sn )
+u_long aim_add_buddy(struct aim_session_t *sess,
+		     struct aim_conn_t *conn, 
+		     char *sn )
 {
-   struct command_tx_struct newpacket;
-
-   if( !sn )
-      return -1;
+   struct command_tx_struct *newpacket;
+   int i;
 
-   if (conn)
-     newpacket.conn = conn;
-   else
-     newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-
-   newpacket.lock = 1;
-   newpacket.type = 0x0002;
-   newpacket.commandlen = 11 + strlen( sn );
-   newpacket.data = (char *)malloc( newpacket.commandlen );
+   if(!sn)
+     return -1;
 
-   newpacket.data[0] = 0x00;
-   newpacket.data[1] = 0x03;
-   newpacket.data[2] = 0x00;
-   newpacket.data[3] = 0x04;
-   newpacket.data[4] = 0x00;
-   newpacket.data[5] = 0x00;
+   if (!(newpacket = aim_tx_new(0x0002, conn, 10+1+strlen(sn))))
+     return -1;
+
+   newpacket->lock = 1;
 
-   /* SNAC reqid */
-   newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-   newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-   newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-   newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+   i = aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, sess->snac_nextid);
+   i += aimutil_put8(newpacket->data+i, strlen(sn));
+   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
 
-   /* length of screenname */ 
-   newpacket.data[10] = strlen( sn );
-
-   memcpy( &(newpacket.data[11]), sn, strlen( sn ) );
-
-   aim_tx_enqueue( &newpacket );
+   aim_tx_enqueue(sess, newpacket );
 
    {
       struct aim_snac_t snac;
     
-      snac.id = aim_snac_nextid;
+      snac.id = sess->snac_nextid;
       snac.family = 0x0003;
       snac.type = 0x0004;
       snac.flags = 0x0000;
@@ -55,53 +39,38 @@
       snac.data = malloc( strlen( sn ) + 1 );
       memcpy( snac.data, sn, strlen( sn ) + 1 );
 
-      aim_newsnac( &snac );
+      aim_newsnac(sess, &snac);
    }
 
-   return( aim_snac_nextid++ );
+   return( sess->snac_nextid++ );
 }
 
-u_long aim_remove_buddy(struct aim_conn_t *conn, char *sn )
+u_long aim_remove_buddy(struct aim_session_t *sess,
+			struct aim_conn_t *conn, 
+			char *sn )
 {
-   struct command_tx_struct newpacket;
-
-   if( !sn )
-      return -1;
+   struct command_tx_struct *newpacket;
+   int i;
 
-   if (conn)
-     newpacket.conn = conn;
-   else
-     newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-
-   newpacket.lock = 1;
-   newpacket.type = 0x0002;
-   newpacket.commandlen = 11 + strlen(sn);
-   newpacket.data = (char *)malloc( newpacket.commandlen );
+   if(!sn)
+     return -1;
 
-   newpacket.data[0] = 0x00;
-   newpacket.data[1] = 0x03;
-   newpacket.data[2] = 0x00;
-   newpacket.data[3] = 0x05;
-   newpacket.data[4] = 0x00;
-   newpacket.data[5] = 0x00;
+   if (!(newpacket = aim_tx_new(0x0002, conn, 10+1+strlen(sn))))
+     return -1;
+
+   newpacket->lock = 1;
 
-   /* SNAC reqid */
-   newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-   newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-   newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-   newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+   i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid);
 
-   /* length of screenname */ 
-   newpacket.data[10] = strlen( sn );
+   i += aimutil_put8(newpacket->data+i, strlen(sn));
+   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
 
-   memcpy( &(newpacket.data[11]), sn, strlen( sn ) );
-
-   aim_tx_enqueue( &newpacket );
+   aim_tx_enqueue(sess, newpacket);
 
    {
       struct aim_snac_t snac;
     
-      snac.id = aim_snac_nextid;
+      snac.id = sess->snac_nextid;
       snac.family = 0x0003;
       snac.type = 0x0005;
       snac.flags = 0x0000;
@@ -109,9 +78,9 @@
       snac.data = malloc( strlen( sn ) + 1 );
       memcpy( snac.data, sn, strlen( sn ) + 1 );
 
-      aim_newsnac( &snac );
+      aim_newsnac(sess, &snac );
    }
 
-   return( aim_snac_nextid++ );
+   return( sess->snac_nextid++ );
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/aim_cbtypes.h	Sat May 20 00:30:53 2000 +0000
@@ -0,0 +1,197 @@
+/*
+ * AIM Callback Types
+ *
+ */
+#ifndef __AIM_CBTYPES_H__
+#define __AIM_CBTYPES_H__
+
+/*
+ * SNAC Families.
+ */
+#define AIM_CB_FAM_ACK 0x0000
+#define AIM_CB_FAM_GEN 0x0001
+#define AIM_CB_FAM_LOC 0x0002
+#define AIM_CB_FAM_BUD 0x0003
+#define AIM_CB_FAM_MSG 0x0004
+#define AIM_CB_FAM_ADS 0x0005
+#define AIM_CB_FAM_INV 0x0006
+#define AIM_CB_FAM_ADM 0x0007
+#define AIM_CB_FAM_POP 0x0008
+#define AIM_CB_FAM_BOS 0x0009
+#define AIM_CB_FAM_LOK 0x000a
+#define AIM_CB_FAM_STS 0x000b
+#define AIM_CB_FAM_TRN 0x000c
+#define AIM_CB_FAM_CTN 0x000d /* ChatNav */
+#define AIM_CB_FAM_CHT 0x000e /* Chat */
+#define AIM_CB_FAM_ATH 0x0017
+#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
+
+/*
+ * SNAC Family: Ack.
+ * 
+ * Not really a family, but treating it as one really
+ * helps it fit into the libfaim callback structure better.
+ *
+ */
+#define AIM_CB_ACK_ACK 0x0001
+
+/*
+ * SNAC Family: General.
+ */ 
+#define AIM_CB_GEN_ERROR 0x0001
+#define AIM_CB_GEN_CLIENTREADY 0x0002
+#define AIM_CB_GEN_SERVERREADY 0x0003
+#define AIM_CB_GEN_SERVICEREQ 0x0004
+#define AIM_CB_GEN_REDIRECT 0x0005
+#define AIM_CB_GEN_RATEINFOREQ 0x0006
+#define AIM_CB_GEN_RATEINFO 0x0007
+#define AIM_CB_GEN_RATEINFOACK 0x0008
+#define AIM_CB_GEN_RATECHANGE 0x000a
+#define AIM_CB_GEN_SERVERPAUSE 0x000b
+#define AIM_CB_GEN_SERVERRESUME 0x000d
+#define AIM_CB_GEN_REQSELFINFO 0x000e
+#define AIM_CB_GEN_SELFINFO 0x000f
+#define AIM_CB_GEN_EVIL 0x0010
+#define AIM_CB_GEN_SETIDLE 0x0011
+#define AIM_CB_GEN_MIGRATIONREQ 0x0012
+#define AIM_CB_GEN_MOTD 0x0013
+#define AIM_CB_GEN_SETPRIVFLAGS 0x0014
+#define AIM_CB_GEN_WELLKNOWNURL 0x0015
+#define AIM_CB_GEN_NOP 0x0016
+#define AIM_CB_GEN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Location Services.
+ */ 
+#define AIM_CB_LOC_ERROR 0x0001
+#define AIM_CB_LOC_REQRIGHTS 0x0002
+#define AIM_CB_LOC_RIGHTSINFO 0x0003
+#define AIM_CB_LOC_SETUSERINFO 0x0004
+#define AIM_CB_LOC_REQUSERINFO 0x0005
+#define AIM_CB_LOC_USERINFO 0x0006
+#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
+#define AIM_CB_LOC_WATCHERNOT 0x0008
+#define AIM_CB_LOC_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Buddy List Management Services.
+ */ 
+#define AIM_CB_BUD_ERROR 0x0001
+#define AIM_CB_BUD_REQRIGHTS 0x0002
+#define AIM_CB_BUD_RIGHTSINFO 0x0003
+#define AIM_CB_BUD_ADDBUDDY 0x0004
+#define AIM_CB_BUD_REMBUDDY 0x0005
+#define AIM_CB_BUD_REJECT 0x000a
+#define AIM_CB_BUD_ONCOMING 0x000b
+#define AIM_CB_BUD_OFFGOING 0x000c
+#define AIM_CB_BUD_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Messeging Services.
+ */ 
+#define AIM_CB_MSG_ERROR 0x0001
+#define AIM_CB_MSG_PARAMINFO 0x0005
+#define AIM_CB_MSG_INCOMING 0x0007
+#define AIM_CB_MSG_EVIL 0x0009
+#define AIM_CB_MSG_MISSEDCALL 0x000a
+#define AIM_CB_MSG_CLIENTERROR 0x000b
+#define AIM_CB_MSG_ACK 0x000c
+#define AIM_CB_MSG_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Advertisement Services
+ */ 
+#define AIM_CB_ADS_ERROR 0x0001
+#define AIM_CB_ADS_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Invitation Services.
+ */ 
+#define AIM_CB_INV_ERROR 0x0001
+#define AIM_CB_INV_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Administrative Services.
+ */ 
+#define AIM_CB_ADM_ERROR 0x0001
+#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005
+#define AIM_CB_ADM_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Popup Messages
+ */ 
+#define AIM_CB_POP_ERROR 0x0001
+#define AIM_CB_POP_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Misc BOS Services.
+ */ 
+#define AIM_CB_BOS_ERROR 0x0001
+#define AIM_CB_BOS_DEFAULT 0xffff
+
+/*
+ * SNAC Family: User Lookup Services
+ */ 
+#define AIM_CB_LOK_ERROR 0x0001
+#define AIM_CB_LOK_DEFAULT 0xffff
+
+/*
+ * SNAC Family: User Status Services
+ */ 
+#define AIM_CB_STS_ERROR 0x0001
+#define AIM_CB_STS_SETREPORTINTERVAL 0x0002
+#define AIM_CB_STS_REPORTACK 0x0004
+#define AIM_CB_STS_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Translation Services
+ */ 
+#define AIM_CB_TRN_ERROR 0x0001
+#define AIM_CB_TRN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Chat Navigation Services
+ */ 
+#define AIM_CB_CTN_ERROR 0x0001
+#define AIM_CB_CTN_CREATE 0x0008
+#define AIM_CB_CTN_INFO 0x0009
+#define AIM_CB_CTN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Chat Services
+ */ 
+#define AIM_CB_CHT_ERROR 0x0001
+#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002
+#define AIM_CB_CHT_USERJOIN 0x0003
+#define AIM_CB_CHT_USERLEAVE 0x0004
+#define AIM_CB_CHT_OUTGOINGMSG 0x0005
+#define AIM_CB_CHT_INCOMINGMSG 0x0006
+#define AIM_CB_CHT_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Authorizer
+ *
+ * Used only in protocol versions three and above.
+ *
+ */
+#define AIM_CB_ATH_ERROR 0x0001
+#define AIM_CB_ATH_LOGINREQEST 0x0002
+#define AIM_CB_ATH_LOGINRESPONSE 0x0003
+#define AIM_CB_ATH_AUTHREQ 0x0006
+#define AIM_CB_ATH_AUTHRESPONSE 0x0007
+
+/*
+ * SNAC Family: Internal Messages
+ *
+ * This isn't truely a SNAC family either, but using
+ * these, we can integrated non-SNAC services into
+ * the SNAC-centered libfaim callback structure.
+ *
+ */ 
+#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
+#define AIM_CB_SPECIAL_AUTHOTHER 0x0002
+#define AIM_CB_SPECIAL_UNKNOWN 0xffff
+#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
+
+
+#endif/*__AIM_CBTYPES_H__ */
--- a/libfaim/aim_chat.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_chat.c	Sat May 20 00:30:53 2000 +0000
@@ -5,76 +5,645 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h> 
+
+char *aim_chat_getname(struct aim_conn_t *conn)
+{
+  if (!conn)
+    return NULL;
+  if (conn->type != AIM_CONN_TYPE_CHAT)
+    return NULL;
+
+  return (char *)conn->priv; /* yuck ! */
+}
+
+struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
+{
+  int i;
+
+  for (i=0;i<AIM_CONN_MAX;i++)
+    {
+      if (sess->conns[i].type == AIM_CONN_TYPE_CHAT)
+	{
+	  if (sess->conns[i].priv)
+	    if (!strcmp((char *)sess->conns[i].priv, name))
+	      {
+		return &sess->conns[i];
+	      }
+	}
+    }
+  return NULL;
+}
+
+int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
+{
+  if (!conn || !roomname)
+    return -1;
+
+  conn->priv = malloc(strlen(roomname)+1);
+  strcpy(conn->priv, roomname);
+
+  return 0;
+}
+
+u_long aim_chat_send_im(struct aim_session_t *sess,
+			struct aim_conn_t *conn, 
+			char *msg)
+{   
+
+  int curbyte,i;
+  struct command_tx_struct *newpacket;
+
+  if (!sess || !conn || !msg)
+    return 0;
+  
+  if (!(newpacket = aim_tx_new(0x0002, conn, 1152)))
+    return -1;
+
+  newpacket->lock = 1; /* lock struct */
+
+  curbyte  = 0;
+  curbyte += aim_putsnac(newpacket->data+curbyte, 
+			 0x000e, 0x0005, 0x0000, sess->snac_nextid);
+
+  /* 
+   * Generate a random message cookie 
+   */
+  for (i=0;i<8;i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random());
+
+  /*
+   * metaTLV start.  -- i assume this is a metaTLV.  it could be the
+   *                    channel ID though.
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+
+  /*
+   * Type 1: Unknown.  Blank.
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  
+  /*
+   * Type 6: Unknown.  Blank.
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /*
+   * Type 5: Message block.  Contains more TLVs.
+   *
+   * This could include other information... We just
+   * put in a message TLV however.  
+   * 
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4);
+
+  /*
+   * SubTLV: Type 1: Message
+   */
+  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg);
+  
+  newpacket->commandlen = curbyte;
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
 
 /*
- * FIXME: Doesn't work.
+ * Join a room of name roomname.  This is the first
+ * step to joining an already created room.  It's 
+ * basically a Service Request for family 0x000e, 
+ * with a little added on to specify the exchange
+ * and room name.
  *
  */
-u_long aim_chat_join(struct aim_conn_t *conn, const char *roomname)
+u_long aim_chat_join(struct aim_session_t *sess,
+		     struct aim_conn_t *conn, 
+		     u_short exchange,
+		     const char *roomname)
 {
-  struct command_tx_struct newpacket;
-  
-  newpacket.lock = 1;
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x0002;
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!sess || !conn || !roomname)
+    return 0;
   
-  newpacket.commandlen = 12+7+strlen(roomname)+6;
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  memset(newpacket.data, 0x00, newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+9+strlen(roomname)+2)))
+    return -1;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x01;
+  newpacket->lock = 1;
+  
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);
 
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x04;
-
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
+  i+= aimutil_put16(newpacket->data+i, 0x000e);
 
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  /* 
+   * this is techinally a TLV, but we can't use normal functions
+   * because we need the extraneous nulls and other weird things.
+   */
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
+  i+= aimutil_put16(newpacket->data+i, exchange);
+  i+= aimutil_put8(newpacket->data+i, strlen(roomname));
+  memcpy(newpacket->data+i, roomname, strlen(roomname));
+  i+= strlen(roomname);
+  //i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
+  i+= aimutil_put16(newpacket->data+i, 0x0000);
 
-  newpacket.data[10] = 0x00;
-  newpacket.data[11] = 0x0e;
-  newpacket.data[12] = 0x00;
-  newpacket.data[13] = 0x01;
-  newpacket.data[14] = 0x00;
-  newpacket.data[15] = 0x0c;
-  newpacket.data[16] = 0x00;
-  newpacket.data[17] = 0x04;
-  newpacket.data[18] = strlen(roomname) & 0x00ff;
-  memcpy(&(newpacket.data[19]), roomname, strlen(roomname));
-  
-  {
-    int i = 0;
-    printf("\n\n\n");
-    for (i = 0;i < newpacket.commandlen; i++)
-      printf("0x%02x ", newpacket.data[i]);
-    printf("\n\n\n");
-  }
+  /*
+   * Chat hack.
+   *
+   * XXX: A problem occurs here if we request a channel
+   *      join but it fails....pendingjoin will be nonnull
+   *      even though the channel is never going to get a
+   *      redirect!
+   *
+   */
+  sess->pendingjoin = (char *)malloc(strlen(roomname)+1);
+  strcpy(sess->pendingjoin, roomname);
 
-  aim_tx_enqueue(&newpacket);
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
 
+#if 0
   {
     struct aim_snac_t snac;
     
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x0001;
     snac.type = 0x0004;
     snac.flags = 0x0000;
 
-    snac.data = malloc(strlen(roomname));
-    memcpy(snac.data, roomname, strlen(roomname));
+    snac.data = malloc(strlen(roomname)+1);
+    strcpy(snac.data, roomname);
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
-  return (aim_snac_nextid++);
+#endif
+  return (sess->snac_nextid++);
+}
+
+int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo)
+{
+  int namelen = 0;
+  int i = 0;
+
+  if (!buf || !outinfo)
+    return 0;
+
+  outinfo->exchange = aimutil_get16(buf+i);
+  i += 2;
+
+  namelen = aimutil_get8(buf+i);
+  i += 1;
+
+  outinfo->name = (char *)malloc(namelen+1);
+  memcpy(outinfo->name, buf+i, namelen);
+  outinfo->name[namelen] = '\0';
+  i += namelen;
+
+  outinfo->instance = aimutil_get16(buf+i);
+  i += 2;
+  
+  return i;
+};
+
+
+/*
+ * General room information.  Lots of stuff.
+ *
+ * Values I know are in here but I havent attached
+ * them to any of the 'Unknown's:
+ *	- Language (English)
+ *
+ */
+int aim_chat_parse_infoupdate(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  struct aim_userinfo_s *userinfo = NULL;
+  rxcallback_t userfunc=NULL;	
+  int ret = 1, i = 0;
+  int usercount = 0;
+  u_char detaillevel = 0;
+  char *roomname = NULL;
+  struct aim_chat_roominfo roominfo;
+  u_short tlvcount = 0;
+  struct aim_tlvlist_t *tlvlist;
+  char *roomdesc = NULL;
+
+  i = 10;
+  i += aim_chat_readroominfo(command->data+i, &roominfo);
+  
+  detaillevel = aimutil_get8(command->data+i);
+  i++;
+  
+  tlvcount = aimutil_get16(command->data+i);
+  i += 2;
+
+  /*
+   * Everything else are TLVs.
+   */ 
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+  
+  /*
+   * TLV type 0x006a is the room name in Human Readable Form.
+   */
+  if (aim_gettlv(tlvlist, 0x006a, 1))
+      roomname = aim_gettlv_str(tlvlist, 0x006a, 1);
+
+  /*
+   * Type 0x006f: Number of occupants.
+   */
+  if (aim_gettlv(tlvlist, 0x006f, 1))
+    {
+      struct aim_tlv_t *tmptlv;
+      tmptlv = aim_gettlv(tlvlist, 0x006f, 1);
+
+      usercount = aimutil_get16(tmptlv->value);
+    }
+
+  /*
+   * Type 0x0073:  Occupant list.
+   */
+  if (aim_gettlv(tlvlist, 0x0073, 1))
+    {	
+      int curoccupant = 0;
+      struct aim_tlv_t *tmptlv;
+
+      tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
+
+      /* Allocate enough userinfo structs for all occupants */
+      userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));
+
+      i = 0;
+      while (curoccupant < usercount)
+	i += aim_extractuserinfo(tmptlv->value+i, &userinfo[curoccupant++]);
+    }
+  
+  /* 
+   * Type 0x00c9: Unknown.
+   */
+  if (aim_gettlv(tlvlist, 0x00c9, 1))
+    ;
+  
+  /* 
+   * Type 0x00ca: Creation date
+   */
+  if (aim_gettlv(tlvlist, 0x00ca, 1))
+    ;
+
+  /* 
+   * Type 0x00d1: Maximum Message Length
+   */
+  if (aim_gettlv(tlvlist, 0x00d1, 1))
+    ;
+
+  /* 
+   * Type 0x00d2: Unknown.
+   */
+  if (aim_gettlv(tlvlist, 0x00d2, 1))
+    ;
+
+  /* 
+   * Type 0x00d3: Room Description
+   */
+  if (aim_gettlv(tlvlist, 0x00d3, 1))
+    roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
+
+  /* 
+   * Type 0x00d5: Unknown.
+   */
+  if (aim_gettlv(tlvlist, 0x00d5, 1))
+    ;
+
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE);
+  if (userfunc)
+    {
+      ret = userfunc(sess,
+		     command, 
+		     &roominfo,
+		     roomname,
+		     usercount,
+		     userinfo,	
+		     roomdesc);
+    }
+  free(roominfo.name);
+  free(userinfo);
+  free(roomname);
+  free(roomdesc);
+  aim_freetlvchain(&tlvlist);
+ 
+  return ret;
 }
+
+int aim_chat_parse_joined(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  struct aim_userinfo_s *userinfo = NULL;
+  rxcallback_t userfunc=NULL;	
+  int i = 10, curcount = 0, ret = 1;
+
+  while (i < command->commandlen)
+    {
+      curcount++;
+      userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
+      i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
+    }
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN);
+  if (userfunc)
+    {
+      ret = userfunc(sess,
+		     command, 
+		     curcount,
+		     userinfo);
+    }
+
+  free(userinfo);
+
+  return ret;
+}	      
+
+int aim_chat_parse_leave(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+
+  struct aim_userinfo_s *userinfo = NULL;
+  rxcallback_t userfunc=NULL;	
+  int i = 10, curcount = 0, ret = 1;
+
+  while (i < command->commandlen)
+    {
+      curcount++;
+      userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
+      i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
+    }
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE);
+  if (userfunc)
+    {
+      ret = userfunc(sess,
+		     command, 
+		     curcount,
+		     userinfo);
+    }
+
+  free(userinfo);
+
+  return ret;
+}	   
+
+/*
+ * We could probably include this in the normal ICBM parsing 
+ * code as channel 0x0003, however, since only the start
+ * would be the same, we might as well do it here.
+ */
+int aim_chat_parse_incoming(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  struct aim_userinfo_s userinfo;
+  rxcallback_t userfunc=NULL;	
+  int ret = 1, i = 0, z = 0;
+  u_char cookie[8];
+  int channel;
+  struct aim_tlvlist_t *outerlist;
+  char *msg = NULL;
+
+  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+
+  i = 10; /* skip snac */
+
+  /*
+   * ICBM Cookie.  Ignore it.
+   */ 
+  for (z=0; z<8; z++,i++)
+    cookie[z] = command->data[i];
+
+  /*
+   * Channel ID
+   *
+   * Channels 1 and 2 are implemented in the normal ICBM
+   * parser.
+   *
+   * We only do channel 3 here.
+   *
+   */
+  channel = aimutil_get16(command->data+i);
+  i += 2;
+
+  if (channel != 0x0003)
+    {
+      printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
+      return 1;
+    }
+
+  /*
+   * Start parsing TLVs right away. 
+   */
+  outerlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+  
+  /*
+   * Type 0x0003: Source User Information
+   */
+  if (aim_gettlv(outerlist, 0x0003, 1))
+    {
+      struct aim_tlv_t *userinfotlv;
+      
+      userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
+      aim_extractuserinfo(userinfotlv->value, &userinfo);
+    }
+
+  /*
+   * Type 0x0001: Unknown.
+   */
+  if (aim_gettlv(outerlist, 0x0001, 1))
+    ;
+
+  /*
+   * Type 0x0005: Message Block.  Conains more TLVs.
+   */
+  if (aim_gettlv(outerlist, 0x0005, 1))
+    {
+      struct aim_tlvlist_t *innerlist;
+      struct aim_tlv_t *msgblock;
+
+      msgblock = aim_gettlv(outerlist, 0x0005, 1);
+      innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
+      
+      /* 
+       * Type 0x0001: Message.
+       */	
+      if (aim_gettlv(innerlist, 0x0001, 1))
+	  msg = aim_gettlv_str(innerlist, 0x0001, 1);
+
+      aim_freetlvchain(&innerlist); 
+    }
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG);
+  if (userfunc)
+    {
+      ret = userfunc(sess,
+		     command, 
+		     &userinfo,
+		     msg);
+    }
+  free(msg);
+  aim_freetlvchain(&outerlist);
+
+  return ret;
+}	      
+
+u_long aim_chat_clientready(struct aim_session_t *sess,
+			    struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 0x20)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+
+  i+= aimutil_put16(newpacket->data+i, 0x000e);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0004);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0003);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0004);
+  i+= aimutil_put16(newpacket->data+i, 0x0686);
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
+{
+  int i;
+
+  for (i=0;i<AIM_CONN_MAX;i++)
+    {
+      if (sess->conns[i].type == AIM_CONN_TYPE_CHAT)
+	{
+	  if (sess->conns[i].priv)
+	    if (!strcmp((char *)sess->conns[i].priv, name))
+	      {
+		aim_conn_close(&sess->conns[i]);
+		return 0;
+	      }
+	}
+    }
+  return -1;
+}
+
+/*
+ * conn must be a BOS connection!
+ */
+u_long aim_chat_invite(struct aim_session_t *sess,
+		       struct aim_conn_t *conn,
+		       char *sn,
+		       char *msg,
+		       u_short exchange,
+		       char *roomname,
+		       u_short instance)
+{
+  struct command_tx_struct *newpacket;
+  int i,curbyte=0;
+
+  if (!sess || !conn || !sn || !msg || !roomname)
+    return 0;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /*
+   * Cookie
+   */
+  for (i=0;i<8;i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());
+
+  /*
+   * Channel (2)
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+
+  /*
+   * Dest sn
+   */
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
+
+  /*
+   * TLV t(0005)
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02);
+  
+  /* 
+   * Unknown info
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  
+  /*
+   * TLV t(000a) -- Unknown
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  
+  /*
+   * TLV t(000f) -- Unknown
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  
+  /*
+   * TLV t(000c) -- Invitation message
+   */
+  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg);
+
+  /*
+   * TLV t(2711) -- Container for room information 
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2);
+  curbyte += aimutil_put16(newpacket->data+curbyte, exchange);
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname));
+  curbyte += aimutil_put16(newpacket->data+curbyte, instance);
+
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
--- a/libfaim/aim_chatnav.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_chatnav.c	Sat May 20 00:30:53 2000 +0000
@@ -5,5 +5,319 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h>
+
+
+/*
+ * conn must be a chatnav connection!
+ */
+u_long aim_chatnav_reqrights(struct aim_session_t *sess,
+			  struct aim_conn_t *conn)
+{
+  struct aim_snac_t snac;
+
+  snac.id = aim_genericreq_n(sess, conn, 0x000d, 0x0002);
+
+  snac.family = 0x000d;
+  snac.type = 0x0002;
+  snac.flags = 0x0000;
+  snac.data = NULL;
+  
+  aim_newsnac(sess, &snac);
+
+  return (sess->snac_nextid); /* already incremented */
+}
+
+u_long aim_chatnav_clientready(struct aim_session_t *sess,
+			       struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket; 
+  int i;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 0x20)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+
+  i+= aimutil_put16(newpacket->data+i, 0x000d);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0004);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0003);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0004);
+  i+= aimutil_put16(newpacket->data+i, 0x0686);
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+/*
+ * Since multiple things can trigger this callback,
+ * we must lookup the snacid to determine the original
+ * snac subtype that was called.
+ */
+int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  struct aim_snac_t *snac;
+  u_long snacid;
+  rxcallback_t userfunc;
+  
+  snacid = aimutil_get32(command->data+6);
+  snac = aim_remsnac(sess, snacid);
+
+  if (!snac)
+    {
+      printf("faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snacid);
+      return 1;
+    }
+  
+  if (snac->family != 0x000d)
+    {
+      printf("faim: chatnav_parse_info: recieved response that maps to corrupt request! (fam=%04x)\n", snac->family);
+      return 1;
+    }
+
+  /*
+   * We now know what the original SNAC subtype was.
+   */
+  switch(snac->type)
+    {
+    case 0x0002: /* request chat rights */
+      {
+	  struct aim_tlvlist_t *tlvlist;
+	  struct aim_chat_exchangeinfo *exchanges = NULL;
+	  int curexchange = 0;
+	  struct aim_tlv_t *exchangetlv;
+	  u_char maxrooms = 0;
+	  int ret = 1;
+	  struct aim_tlvlist_t *innerlist;
+	 
+	  tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
+	  
+	  /* 
+	   * Type 0x0002: Maximum concurrent rooms.
+	   */ 
+	  if (aim_gettlv(tlvlist, 0x0002, 1))
+	    {
+	      struct aim_tlv_t *maxroomstlv;
+	      maxroomstlv = aim_gettlv(tlvlist, 0x0002, 1);
+	      maxrooms = aimutil_get8(maxroomstlv->value);
+	    }
+
+	  /* 
+	   * Type 0x0003: Exchange information
+	   *
+	   * There can be any number of these, each one
+	   * representing another exchange.  
+	   * 
+	   */
+	  curexchange = 0;
+	  while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1)))
+	    {	
+	      curexchange++;
+	      exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
+	      
+	      /* exchange number */
+	      exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value);
+	      innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2);
+	      
+	      /* 
+	       * Type 0x000d: Unknown.
+	       */
+	      if (aim_gettlv(innerlist, 0x000d, 1))
+		;
+	      
+	      /* 
+	       * Type 0x0004: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x0004, 1))
+		;
+
+	      /*
+	       * Type 0x00c9: Unknown
+	       */ 
+	      if (aim_gettlv(innerlist, 0x00c9, 1))
+		;
+	      
+	      /*
+	       * Type 0x00ca: Creation Date 
+	       */
+	      if (aim_gettlv(innerlist, 0x00ca, 1))
+		;
+	      
+	      /*
+	       * Type 0x00d0: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x00d0, 1))
+		;
 
+	      /*
+	       * Type 0x00d1: Maximum Message length
+	       */
+	      if (aim_gettlv(innerlist, 0x00d1, 1))
+		;
+
+	      /*
+	       * Type 0x00d2: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x00d2, 1))	
+		;	
+
+	      /*
+	       * Type 0x00d3: Exchange Name
+	       */
+	      if (aim_gettlv(innerlist, 0x00d3, 1))	
+		exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
+	      else
+		exchanges[curexchange-1].name = NULL;
+
+	      /*
+	       * Type 0x00d5: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x00d5, 1))
+		;
+
+	      /*
+	       * Type 0x00d6: Character Set (First Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d6, 1))	
+		exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
+	      else
+		exchanges[curexchange-1].charset1 = NULL;
+	      
+	      /*
+	       * Type 0x00d7: Language (First Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d7, 1))	
+		exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
+	      else
+		exchanges[curexchange-1].lang1 = NULL;
+
+	      /*
+	       * Type 0x00d8: Character Set (Second Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d8, 1))	
+		exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
+	      else
+		exchanges[curexchange-1].charset2 = NULL;
+
+	      /*
+	       * Type 0x00d9: Language (Second Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d9, 1))	
+		exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
+	      else
+		exchanges[curexchange-1].lang2 = NULL;
+
+	    }
+	  
+	  /*
+	   * Call client.
+	   */
+	  userfunc = aim_callhandler(command->conn, 0x000d, 0x0009);
+	  if (userfunc)
+	    ret = userfunc(sess, 
+			   command, 
+			   snac->type,
+			   maxrooms,
+			   curexchange, 
+			   exchanges);
+	  curexchange--;
+	  while(curexchange)
+	    {
+	      if (exchanges[curexchange].name)
+		free(exchanges[curexchange].name);
+	      if (exchanges[curexchange].charset1)
+		free(exchanges[curexchange].charset1);
+	      if (exchanges[curexchange].lang1)
+		free(exchanges[curexchange].lang1);
+	      if (exchanges[curexchange].charset2)
+		free(exchanges[curexchange].charset2);
+	      if (exchanges[curexchange].lang2)
+		free(exchanges[curexchange].lang2);
+	      curexchange--;
+	    }
+	  free(exchanges);
+	  aim_freetlvchain(&innerlist);
+	  aim_freetlvchain(&tlvlist);
+	  return ret;
+      }
+    case 0x0003: /* request exchange info */
+      printf("faim: chatnav_parse_info: resposne to exchange info\n");
+      return 1;
+    case 0x0004: /* request room info */
+      printf("faim: chatnav_parse_info: response to room info\n");
+      return 1;
+    case 0x0005: /* request more room info */
+      printf("faim: chatnav_parse_info: response to more room info\n");
+      return 1;
+    case 0x0006: /* request occupant list */
+      printf("faim: chatnav_parse_info: response to occupant info\n");
+      return 1;
+    case 0x0007: /* search for a room */
+      printf("faim: chatnav_parse_info: search results\n");
+      return 1;
+    case 0x0008: /* create room */
+      printf("faim: chatnav_parse_info: response to create room\n");
+      return 1;
+    default: /* unknown */
+      printf("faim: chatnav_parse_info: unknown request subtype (%04x)\n", snac->type);
+    }
+
+  return 1; /* shouldn't get here */
+}
+
+u_long aim_chatnav_createroom(struct aim_session_t *sess,
+			      struct aim_conn_t *conn,
+			      char *name, 
+			      u_short exchange)
+{
+  struct command_tx_struct *newpacket; 
+  int i;
+  struct aim_snac_t snac;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+12+strlen("invite")+strlen(name))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x000d, 0x0008, 0x0000, sess->snac_nextid);
+
+  /* exchange */
+  i+= aimutil_put16(newpacket->data+i, exchange);
+
+  /* room cookie */
+  i+= aimutil_put8(newpacket->data+i, strlen("invite"));
+  i+= aimutil_putstr(newpacket->data+i, "invite", strlen("invite"));
+
+  /* instance */
+  i+= aimutil_put16(newpacket->data+i, 0xffff);
+  
+  /* detail level */
+  i+= aimutil_put8(newpacket->data+i, 0x01);
+  
+  /* tlvcount */
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  /* room name */
+  i+= aim_puttlv_str(newpacket->data+i, 0x00d3, strlen(name), name);
+
+  snac.id = sess->snac_nextid;
+  snac.family = 0x000d;
+  snac.type = 0x0008;
+  snac.flags = 0x0000;
+  snac.data = NULL;
+  
+  aim_newsnac(sess, &snac);
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
--- a/libfaim/aim_conn.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_conn.c	Sat May 20 00:30:53 2000 +0000
@@ -6,26 +6,31 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h> 
 
-void aim_connrst(void)
+void aim_connrst(struct aim_session_t *sess)
 {
   int i;
   for (i = 0; i < AIM_CONN_MAX; i++)
     {
-      aim_conns[i].fd = -1;
-      aim_conns[i].type = -1;
-      aim_conns[i].status = 0;
+      sess->conns[i].fd = -1;
+      sess->conns[i].type = -1;
+      sess->conns[i].status = 0;
+      sess->conns[i].seqnum = 0;
+      sess->conns[i].lastactivity = 0;
+      sess->conns[i].forcedlatency = 0;
+      aim_clearhandlers(&(sess->conns[i]));
+      sess->conns[i].handlerlist = NULL;
     }
 
 }
 
-struct aim_conn_t *aim_conn_getnext(void)
+struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
 {
   int i;
   for (i=0;i<AIM_CONN_MAX;i++)
-    if (aim_conns[i].fd == -1)
-      return &(aim_conns[i]);
+    if (sess->conns[i].fd == -1)
+      return &(sess->conns[i]);
   return NULL;
 }
 
@@ -35,14 +40,23 @@
     close(deadconn->fd);
   deadconn->fd = -1;
   deadconn->type = -1;
+  deadconn->seqnum = 0;
+  deadconn->lastactivity = 0;
+  deadconn->forcedlatency = 0;
+  aim_clearhandlers(deadconn);
+  deadconn->handlerlist = NULL;
+  if (deadconn->priv)
+    free(deadconn->priv);
+  deadconn->priv = NULL;
 }
 
-struct aim_conn_t *aim_getconn_type(int type)
+struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
+				    int type)
 {
   int i;
   for (i=0; i<AIM_CONN_MAX; i++)
-    if (aim_conns[i].type == type)
-      return &(aim_conns[i]);
+    if (sess->conns[i].type == type)
+      return &(sess->conns[i]);
   return NULL;
 }
 
@@ -55,20 +69,28 @@
  * FIXME: Return errors in a more sane way.
  *
  */
-struct aim_conn_t *aim_newconn(int type, char *dest)
+struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
+			       int type, char *dest)
 {
   struct aim_conn_t *connstruct;
   int ret;
   struct sockaddr_in sa;
   struct hostent *hp;
-  int port = FAIM_LOGIN_PORT;
+  u_short port = FAIM_LOGIN_PORT;
+  char *host = NULL;
   int i=0;
   
-  if (!dest || ((connstruct=aim_conn_getnext())==NULL))
+  if ((connstruct=aim_conn_getnext(sess))==NULL)
     return NULL;
 
   connstruct->type = type;
 
+  if (!dest) { /* just allocate a struct */
+    connstruct->fd = -1;
+    connstruct->status = 0;
+    return connstruct;
+  }
+
   /* 
    * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
    * colon, in the BOS redirect.  This fatally breaks all previous 
@@ -77,15 +99,21 @@
    * We put this here to catch every case. 
    *
    */
-  for(i=0;(i<strlen(dest));i++)
-    if (dest[i] == ':') break; 
-  if (i<strlen(dest))
-      {
-	port = atoi(dest+i);
-	dest[i] = '\0';
+
+  for(i=0;i<strlen(dest);i++)
+    {
+      if (dest[i] == ':') {
+	port = atoi(&(dest[i+1]));
+	break;
       }
+    }
+  host = (char *)malloc(i+1);
+  strncpy(host, dest, i);
+  host[i] = '\0';
 
-  hp = gethostbyname2(dest, AF_INET);
+  hp = gethostbyname2(host, AF_INET);
+  free(host);
+
   if (hp == NULL)
     {
       connstruct->status = (h_errno | AIM_CONN_STATUS_RESOLVERR);
@@ -105,26 +133,26 @@
       connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
       return connstruct;
     }
-
+  
   return connstruct;
 }
 
-int aim_conngetmaxfd(void)
+int aim_conngetmaxfd(struct aim_session_t *sess)
 {
   int i,j;
   j=0;
   for (i=0;i<AIM_CONN_MAX;i++)
-    if(aim_conns[i].fd > j)
-      j = aim_conns[i].fd;
+    if(sess->conns[i].fd > j)
+      j = sess->conns[i].fd;
   return j;
 }
 
-int aim_countconn(void)
+int aim_countconn(struct aim_session_t *sess)
 {
   int i,cnt;
   cnt = 0;
   for (i=0;i<AIM_CONN_MAX;i++)
-    if (aim_conns[i].fd > -1)
+    if (sess->conns[i].fd > -1)
       cnt++;
   return cnt;
 }
@@ -135,46 +163,51 @@
  * Waits for a socket with data or for timeout, whichever comes first.
  * See select(2).
  * 
+ * Return codes in *status:
+ *   -1  error in select() (NULL returned)
+ *    0  no events pending (NULL returned)
+ *    1  outgoing data pending (NULL returned)
+ *    2  incoming data pending (connection with pending data returned)
+ *
  */ 
-struct aim_conn_t *aim_select(struct timeval *timeout)
+struct aim_conn_t *aim_select(struct aim_session_t *sess,
+			      struct timeval *timeout, int *status)
 {
   fd_set fds;
-  fd_set errfds;
   int i;
 
-  if (aim_countconn() <= 0)
+  if (aim_countconn(sess) <= 0)
     return 0;
-  
+
+  /* 
+   * If we have data waiting to be sent, return immediatly
+   */
+  if (sess->queue_outgoing != NULL) {
+    *status = 1;
+    return NULL;
+  } 
+
   FD_ZERO(&fds);
-  FD_ZERO(&errfds);
   
   for(i=0;i<AIM_CONN_MAX;i++)
-    if (aim_conns[i].fd>-1)
-      {
-	FD_SET(aim_conns[i].fd, &fds);
-	FD_SET(aim_conns[i].fd, &errfds);
-      }
+    if (sess->conns[i].fd>-1)
+      FD_SET(sess->conns[i].fd, &fds);
   
-  i = select(aim_conngetmaxfd()+1, &fds, NULL, &errfds, timeout);
-  if (i>=1)
-    {
-      int j;
-      for (j=0;j<AIM_CONN_MAX;j++)
-	{
-	  if ((FD_ISSET(aim_conns[j].fd, &errfds)))
-	    {
-	      /* got an exception; close whats left of it up */
-	      aim_conn_close(&(aim_conns[j]));
-	      return (struct aim_conn_t *)-1;
+  if ((i = select(aim_conngetmaxfd(sess)+1, &fds, NULL, NULL, timeout))>=1) {
+    int j;
+    for (j=0;j<AIM_CONN_MAX;j++) {
+	if (sess->conns[j].fd > -1) {
+	    if ((FD_ISSET(sess->conns[j].fd, &fds))) {
+	      *status = 2;
+	      return &(sess->conns[j]);  /* return the first waiting struct */
 	    }
-	  else if ((FD_ISSET(aim_conns[j].fd, &fds)))
-	    return &(aim_conns[j]);  /* return the first waiting struct */
-	}
-      /* should never get here */
-    }
-  else
-    return (struct aim_conn_t *)i;  /* no waiting or error, return -- FIXME: return type funnies */
-  return NULL; /* NO REACH */
+	  }	
+	}	
+    /* should never get here */
+  }
+
+  *status = i; /* may be 0 or -1 */
+  return NULL;  /* no waiting or error, return */
 }
 
 int aim_conn_isready(struct aim_conn_t *conn)
@@ -193,3 +226,47 @@
     return -1;
 }
 
+int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
+{
+  if (!conn)
+    return -1;
+  
+  conn->forcedlatency = newval;
+  conn->lastactivity = 0; /* reset this just to make sure */
+
+  return 0;
+}
+
+void aim_session_init(struct aim_session_t *sess)
+{
+  int i;
+
+  if (!sess)
+    return;
+
+  memset(sess->logininfo.screen_name, 0x00, MAXSNLEN);
+  sess->logininfo.BOSIP = NULL;
+  memset(sess->logininfo.cookie, 0x00, AIM_COOKIELEN);
+  sess->logininfo.email = NULL;
+  sess->logininfo.regstatus = 0x00;
+
+  for (i = 0; i < AIM_CONN_MAX; i++)
+    {
+      sess->conns[i].fd = -1;
+      sess->conns[i].type = -1;
+      sess->conns[i].status = 0;
+      sess->conns[i].seqnum = 0;
+      sess->conns[i].lastactivity = 0;
+      sess->conns[i].forcedlatency = 0;
+      sess->conns[i].handlerlist = NULL;
+      sess->conns[i].priv = NULL;
+    }
+  
+  sess->queue_outgoing = NULL;
+  sess->queue_incoming = NULL;
+  sess->pendingjoin = NULL;
+  sess->outstanding_snacs = NULL;
+  sess->snac_nextid = 0x00000001;
+
+  return;
+}
--- a/libfaim/aim_global.c	Thu May 18 18:20:18 2000 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-/*
-  aim_global.c
-
-  These are things that are globally required, but don't fit the
-  naming of the rest of the functions.  Namely, the queue ptrs and fds.
-
- */
-
-#include "aim.h" 
-
-/* the dreaded global variables... */
-
-struct login_phase1_struct aim_logininfo;
-
-/* queue (linked list) pointers */
-struct command_tx_struct *aim_queue_outgoing = NULL; /* incoming commands */
-struct command_rx_struct *aim_queue_incoming = NULL; /* outgoing commands */
--- a/libfaim/aim_im.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_im.c	Sat May 20 00:30:53 2000 +0000
@@ -5,7 +5,7 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h>
 
 /*
  * Send an ICBM (instant message).  
@@ -16,75 +16,103 @@
  *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
  *                        when the message is received (of type 0x0004/0x000c)
  *
- *
- * TODO: Update to new standard form 
- *
- *
  */
-
-u_long aim_send_im(struct aim_conn_t *conn, char *destsn, int flags, char *msg)
+u_long aim_send_im(struct aim_session_t *sess,
+		   struct aim_conn_t *conn, 
+		   char *destsn, u_int flags, char *msg)
 {   
 
-  int curbyte;
-  struct command_tx_struct newpacket;
+  int curbyte,i;
+  struct command_tx_struct *newpacket;
   
-  newpacket.lock = 1; /* lock struct */
-  newpacket.type = 0x02; /* IMs are always family 0x02 */
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 1152)))
+    return -1;
 
-  newpacket.commandlen = 20+1+strlen(destsn)+1+1+2+7+2+4+strlen(msg)+2;
-
-  if (flags & AIM_IMFLAGS_ACK)
-    newpacket.commandlen += 4;
-  if (flags & AIM_IMFLAGS_AWAY)
-    newpacket.commandlen += 4;
-
-  newpacket.data = (char *) calloc(1, newpacket.commandlen);
+  newpacket->lock = 1; /* lock struct */
 
   curbyte  = 0;
-  curbyte += aim_putsnac(newpacket.data+curbyte, 0x0004, 0x0006, 0x0000, aim_snac_nextid);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0001);
-  curbyte += aimutil_put8(newpacket.data+curbyte,strlen(destsn));
-  curbyte += aimutil_putstr(newpacket.data+curbyte, destsn, strlen(destsn));
+  curbyte += aim_putsnac(newpacket->data+curbyte, 
+			 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /* 
+   * 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, 
+   * the 8byte message cookie is returned as well as the 
+   * SNAC ID.
+   *
+   */
+  for (i=0;i<8;i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random());
 
-  if (flags & AIM_IMFLAGS_ACK)
-    {
-      curbyte += aimutil_put16(newpacket.data+curbyte,0x0003);
-      curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-    }
+  /*
+   * Channel ID
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte,0x0001);
+
+  /* 
+   * Destination SN (prepended with byte length)
+   */
+  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+
+  /*
+   * metaTLV start.
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x0d);
 
-  if (flags & AIM_IMFLAGS_AWAY)
-    {
-      curbyte += aimutil_put16(newpacket.data+curbyte,0x0004);
-      curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-    }
+  /*
+   * Flag data?
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0501);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101);
+  curbyte += aimutil_put8 (newpacket->data+curbyte, 0x01);
+
+  /* 
+   * Message block length.
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04);
+
+  /*
+   * Character set data? 
+   */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
 
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0002);
-  curbyte += aimutil_put16(newpacket.data+curbyte,strlen(msg)+0xf);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0501);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0101);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0101);
-  curbyte += aimutil_put8(newpacket.data+curbyte,0x01);
-  curbyte += aimutil_put16(newpacket.data+curbyte,strlen(msg)+4);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
-  curbyte += aimutil_putstr(newpacket.data+curbyte, msg, strlen(msg));
+  /*
+   * Message.  Not terminated.
+   */
+  curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg));
 
-  aim_tx_enqueue(&newpacket);
+  /*
+   * Set the Request Acknowledge flag.  
+   */
+  if (flags & AIM_IMFLAGS_ACK) {
+    curbyte += aimutil_put16(newpacket->data+curbyte,0x0003);
+    curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
+  }
+  
+  /*
+   * Set the Autoresponse flag.
+   */
+  if (flags & AIM_IMFLAGS_AWAY) {
+    curbyte += aimutil_put16(newpacket->data+curbyte,0x0004);
+    curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
+  }
+  
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
 
 #ifdef USE_SNAC_FOR_IMS
  {
     struct aim_snac_t snac;
 
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x0004;
     snac.type = 0x0006;
     snac.flags = 0x0000;
@@ -92,162 +120,428 @@
     snac.data = malloc(strlen(destsn)+1);
     memcpy(snac.data, destsn, strlen(destsn)+1);
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
- aim_cleansnacs(60); /* clean out all SNACs over 60sec old */
+ aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
 #endif
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
-int aim_parse_incoming_im_middle(struct command_rx_struct *command)
+/*
+ * It can easily be said that parsing ICBMs is THE single
+ * most difficult thing to do in the in AIM protocol.  In
+ * fact, I think I just did say that.
+ *
+ * Below is the best damned solution I've come up with
+ * over the past sixteen months of battling with it. This
+ * can parse both away and normal messages from every client
+ * I have access to.  Its not fast, its not clean.  But it works.
+ *
+ * We should also support at least minimal parsing of 
+ * Channel 2, so that we can at least know the name of the
+ * room we're invited to, but obviously can't attend...
+ *
+ */
+int aim_parse_incoming_im_middle(struct aim_session_t *sess,
+				 struct command_rx_struct *command)
 {
-  int i = 0;
-  char *srcsn = NULL;
-  char *msg = NULL;
-  unsigned int msglen = 0;
-  int warninglevel = 0;
-  int tlvcnt = 0;
-  int class = 0;
-  ulong membersince = 0;
-  ulong onsince = 0;
-  int idletime = 0;
-  int isautoreply = 0;
+  u_int i = 0,z;
+  rxcallback_t userfunc = NULL;
+  u_char cookie[8];
+  int channel;
+  struct aim_tlvlist_t *tlvlist;
+  struct aim_userinfo_s userinfo;
+  u_short wastebits;
+
+  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+ 
+  i = 10; /* Skip SNAC header */
 
-  i = 20;
+  /*
+   * Read ICBM Cookie.  And throw away.
+   */
+  for (z=0; z<8; z++,i++)
+    cookie[z] = command->data[i];
   
-  srcsn = malloc(command->data[i] + 1);
-  memcpy(srcsn, &(command->data[i+1]), command->data[i]);
-  srcsn[(int)command->data[i]] = '\0';
+  /*
+   * 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 0x0002 is the Rendevous channel, which
+   * is where Chat Invitiations and various client-client
+   * connection negotiations come from.
+   * 
+   */
+  channel = aimutil_get16(command->data+i);
+  i += 2;
   
-  i += (int) command->data[i] + 1; /* add SN len */
-  
-  /* warning level */
-  warninglevel = (command->data[i] << 8);
-  warninglevel += (command->data[i+1]);
+  /*
+   *
+   */
+  if ((channel != 0x01) && (channel != 0x02))
+    {
+      printf("faim: icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
+      return 1;
+    }
+
+  /*
+   * Source screen name.
+   */
+  memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]);
+  userinfo.sn[(int)command->data[i]] = '\0';
+  i += 1 + (int)command->data[i];
+
+  /*
+   * Warning Level
+   */
+  userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */
+  i += 2;
+
+  /*
+   * Number of TLVs that follow.  Not needed.
+   */
+  wastebits = aimutil_get16(command->data+i);
   i += 2;
   
-  tlvcnt = ((command->data[i++]) << 8) & 0xFF00;
-  tlvcnt += (command->data[i++]) & 0x00FF;
-  
-  /* a mini TLV parser */
-  {
-    int curtlv = 0;
-    int tlv1 = 0;
-
-    while (curtlv < tlvcnt)
-      {
-	if ((command->data[i] == 0x00) &&
-	    (command->data[i+1] == 0x01) )
-	  {
-	    if (tlv1)
-	      break;
-	    /* t(0001) = class */
-	    if (command->data[i+3] != 0x02)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(1)\n", command->data[i+3]);
-	    class = ((command->data[i+4]) << 8) & 0xFF00;
-	    class += (command->data[i+5]) & 0x00FF;
-	    i += (2 + 2 + command->data[i+3]);
-	    tlv1++;
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x02))
-	  {
-	    /* t(0002) = member since date  */
-	    if (command->data[i+3] != 0x04)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(2)\n", command->data[i+3]);
-
-	    membersince = ((command->data[i+4]) << 24) &  0xFF000000;
-	    membersince += ((command->data[i+5]) << 16) & 0x00FF0000;
-	    membersince += ((command->data[i+6]) << 8) &  0x0000FF00;
-	    membersince += ((command->data[i+7]) ) &      0x000000FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x03))
-	  {
-	    /* t(0003) = on since date  */
-	    if (command->data[i+3] != 0x04)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(3)\n", command->data[i+3]);
+  /*
+   * Read block of TLVs.  All further data is derived
+   * from what is parsed here.
+   */
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
 
-	    onsince = ((command->data[i+4]) << 24) &  0xFF000000;
-	    onsince += ((command->data[i+5]) << 16) & 0x00FF0000;
-	    onsince += ((command->data[i+6]) << 8) &  0x0000FF00;
-	    onsince += ((command->data[i+7]) ) &      0x000000FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x04) )
-	  {
-	    /* t(0004) = idle time */
-	    if (command->data[i+3] != 0x02)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(4)\n", command->data[i+3]);
-	    idletime = ((command->data[i+4]) << 8) & 0xFF00;
-	    idletime += (command->data[i+5]) & 0x00FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }  
-	else
-	  {
-	    printf("faim: userinfo: **warning: unexpected TLV t(%02x%02x) l(%02x%02x)\n", command->data[i], command->data[i+1], command->data[i+2], command->data[i+3]);
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	curtlv++;
-      }
-  }
+  /*
+   * From here on, its depends on what channel we're on.
+   */
+  if (channel == 1)
+    {
+     u_int j = 0, y = 0, z = 0;
+      char *msg = NULL;
+      u_int icbmflags = 0;
+      struct aim_tlv_t *msgblocktlv, *tmptlv;
+      u_char *msgblock;
+      u_short flag1,flag2;
+           
+      /*
+       * Check Autoresponse status.  If it is an autoresponse,
+       * it will contain a second type 0x0004 TLV, with zero length.
+       */
+      if (aim_gettlv(tlvlist, 0x0004, 2))
+	icbmflags |= AIM_IMFLAGS_AWAY;
+      
+      /*
+       * Check Ack Request status.
+       */
+      if (aim_gettlv(tlvlist, 0x0003, 2))
+	icbmflags |= AIM_IMFLAGS_ACK;
+      
+      /*
+       * Extract the various pieces of the userinfo struct.
+       */
+      /* Class. */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
+	userinfo.class = aimutil_get16(tmptlv->value);
+      /* Member-since date. */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1)))
+	{
+	  /* If this is larger than 4, its probably the message block, skip */
+	  if (tmptlv->length <= 4)
+	    userinfo.membersince = aimutil_get32(tmptlv->value);
+	}
+      /* On-since date */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
+	userinfo.onlinesince = aimutil_get32(tmptlv->value);
+      /* Idle-time */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
+	userinfo.idletime = aimutil_get16(tmptlv->value);
+      /* Session Length (AIM) */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
+	userinfo.sessionlen = aimutil_get16(tmptlv->value);
+      /* Session Length (AOL) */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
+	userinfo.sessionlen = aimutil_get16(tmptlv->value);
+      
+      /*
+       * Message block.
+       *
+       * XXX: Will the msgblock always be the second 0x0002? 
+       */
+      msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
+      if (!msgblocktlv)
+	{
+	  printf("faim: icbm: major error! no message block TLV found!\n");
+	  aim_freetlvchain(&tlvlist);
+	  return 1;
+	}
+      
+      /*
+       * Extracting the message from the unknown cruft.
+       * 
+       * This is a bit messy, and I'm not really qualified,
+       * even as the author, to comment on it.  At least
+       * its not as bad as a while loop shooting into infinity.
+       *
+       * "Do you believe in magic?"
+       *
+       */
+      msgblock = msgblocktlv->value;
+      j = 0;
+      
+      wastebits = aimutil_get8(msgblock+j++);
+      wastebits = aimutil_get8(msgblock+j++);
+      
+      y = aimutil_get16(msgblock+j);
+      j += 2;
+      for (z = 0; z < y; z++)
+	wastebits = aimutil_get8(msgblock+j++);
+      wastebits = aimutil_get8(msgblock+j++);
+      wastebits = aimutil_get8(msgblock+j++);
+      
+      /* 
+       * Message string length, including flag words.
+       */
+      i = aimutil_get16(msgblock+j);
+      j += 2;
+      
+      /*
+       * Flag words.
+       *
+       * Its rumored that these can kick in some funky
+       * 16bit-wide char stuff that used to really kill
+       * libfaim.  Hopefully the latter is no longer true.
+       *
+       * Though someone should investiagte the former.
+       *
+       */
+      flag1 = aimutil_get16(msgblock+j);
+      j += 2;
+      flag2 = aimutil_get16(msgblock+j);
+      j += 2;
+      
+      if (flag1 || flag2)
+	printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2);
+      
+      /* 
+       * Message string. 
+       */
+      i -= 4;
+      msg = (char *)malloc(i+1);
+      memcpy(msg, msgblock+j, i);
+      msg[i] = '\0';
+      
+      /*
+       * Call client.
+       */
+      userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
+      if (userfunc)
+	i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2);
+      else 
+	i = 0;
+      
+      free(msg);
+    }
+  else if (channel == 0x0002)
+    {	
+      int rendtype;
+      struct aim_tlv_t *block1;
+      struct aim_tlvlist_t *list2;
+      struct aim_tlv_t *tmptlv;
+      int a;
+      
+      /* Class. */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
+	userinfo.class = aimutil_get16(tmptlv->value);
+      /* On-since date */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
+	userinfo.onlinesince = aimutil_get32(tmptlv->value);
+      /* Idle-time */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
+	userinfo.idletime = aimutil_get16(tmptlv->value);
+      /* Session Length (AIM) */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
+	userinfo.sessionlen = aimutil_get16(tmptlv->value);
+      /* Session Length (AOL) */
+      if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
+	userinfo.sessionlen = aimutil_get16(tmptlv->value);
 
-  {
-    /* detect if this is an auto-response or not */
-    /*   auto-responses can be detected by the presence of a *second* TLV with
-	 t(0004), but of zero length (and therefore no value portion) */
-    struct aim_tlv_t *tsttlv = NULL;
-    tsttlv = aim_grabtlv((u_char *) &(command->data[i]));
-    if (tsttlv->type == 0x04)
-      isautoreply = 1;
-    aim_freetlv(&tsttlv);
-  }
-  
-  i += 2;
-  
-  i += 2; /* skip first msglen */
-  i += 7; /* skip garbage */
-  i -= 4;
+      /*
+       * There's another block of TLVs embedded in the type 5 here. 
+       */
+      block1 = aim_gettlv(tlvlist, 0x0005, 1);
+      if (!block1)
+	return 1; /* major problem */
+
+      a = 0x1a; /* skip -- not sure what this information is! */
+
+      /*
+       * XXX: Ignore if there's no data, only cookie information.
+       *
+       * Its probably just an accepted invitation or something.
+       *  
+       */
+      if (block1->length <= 0x1a)
+	{
+	  aim_freetlvchain(&tlvlist);
+	  return 1;
+	}
+
+      list2 = aim_readtlvchain(block1->value+a, block1->length-a);
+
+      if (aim_gettlv(list2, 0x0004, 1) /* start connection */ ||  
+	  aim_gettlv(list2, 0x000b, 1) /* close conncetion */)
+	{
+	  rendtype = 1; /* voice request */
 
-  /* oh boy is this terrible...  this comes from a specific of the spec */
-  while(1)
-    {
-      if ( ( (command->data[i] == 0x00) &&
-	     (command->data[i+1] == 0x00) &&
-	     (command->data[i+2] == 0x00) &&
-	     (command->data[i+3] == 0x00) ) &&
-	   (i < command->commandlen) ) /* prevent infinity */
-	break;
+	  /*
+	   * Call client.
+	   */
+	  userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
+	  if (userfunc)
+	    i = userfunc(sess, 
+			 command, 
+			 channel, 
+			 rendtype,
+			 &userinfo);
+	  else 
+	    i = 0;
+	}
       else
-	i++;
+	{
+	  struct aim_chat_roominfo roominfo;
+	  char *msg=NULL,*encoding=NULL,*lang=NULL;
+
+	  rendtype = 0; /* chat invite */
+	  if (aim_gettlv(list2, 0x2711, 1))
+	    {
+	      struct aim_tlv_t *nametlv;
+	      
+	      nametlv = aim_gettlv(list2, 0x2711, 1);
+	      aim_chat_readroominfo(nametlv->value, &roominfo);
+	    }
+	  
+	  if (aim_gettlv(list2, 0x000c, 1))
+	    msg = aim_gettlv_str(list2, 0x000c, 1);
+	  
+	  if (aim_gettlv(list2, 0x000d, 1))
+	    encoding = aim_gettlv_str(list2, 0x000d, 1);
+	  
+	  if (aim_gettlv(list2, 0x000e, 1))
+	    lang = aim_gettlv_str(list2, 0x000e, 1);
+      
+	  /*
+	   * Call client.
+	   */
+	  userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
+	  if (userfunc)
+	    i = userfunc(sess, 
+			 command, 
+			 channel, 
+			 rendtype,
+			 &userinfo, 
+			 &roominfo, 
+			 msg, 
+			 encoding?encoding+1:NULL, 
+			 lang?lang+1:NULL);
+	  else 
+	    i = 0;
+      
+	  free(roominfo.name);
+	  free(msg);
+	  free(encoding);
+	  free(lang);
+	}
+      aim_freetlvchain(&list2);
     }
 
-  i -= 2;
+  /*
+   * Free up the TLV chain.
+   */
+  aim_freetlvchain(&tlvlist);
   
-  if ( (command->data[i] == 0x00) &&
-       (command->data[i+1] == 0x00) )
-    i += 2;
-
-  msglen = ( (( (unsigned int) command->data[i]) & 0xFF ) << 8);
-  msglen += ( (unsigned int) command->data[i+1]) & 0xFF; /* mask off garbage */
-  i += 2;
-
-  msglen -= 4; /* skip four 0x00s */
-  i += 4;
-  
-  msg = malloc(msglen +1);
-  
-  memcpy(msg, &(command->data[i]), msglen);
-  msg[msglen] = '\0'; 
-
-  i = (aim_callbacks[AIM_CB_INCOMING_IM])(command, srcsn, msg, warninglevel, class, membersince, onsince, idletime, isautoreply);
-
-  free(srcsn);
-  free(msg);
 
   return i;
 }
+
+/*
+ * Not real sure what this does, nor does anyone I've talk to.
+ *
+ * Didn't use to send it.  But now I think it might be a good
+ * idea. 
+ *
+ */
+u_long aim_seticbmparam(struct aim_session_t *sess,
+			struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int curbyte;
+
+  if(!(newpacket = aim_tx_new(0x0002, conn, 10+16)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0x1f);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0x40);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0x03);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0xe7);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0x03);
+  curbyte += aimutil_put8(newpacket->data+curbyte,  0xe7);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+int aim_parse_msgerror_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  u_long snacid = 0x000000000;
+  struct aim_snac_t *snac = NULL;
+  int ret = 0;
+  rxcallback_t userfunc = NULL;
+
+  /*
+   * Get SNAC from packet and look it up 
+   * the list of unrepliedto/outstanding
+   * SNACs.
+   *
+   * After its looked up, the SN that the
+   * message should've gone to will be 
+   * in the ->data element of the snac struct.
+   *
+   */
+  snacid = aimutil_get32(command->data+6);
+  snac = aim_remsnac(sess, snacid);
+
+  if (!snac)
+    {
+      printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
+    }
+
+  /*
+   * Call client.
+   */
+  userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
+  if (userfunc)
+    ret =  userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)");
+  else
+    ret = 0;
+  
+  free(snac->data);
+  free(snac);
+
+  return ret;
+}
+
+
--- a/libfaim/aim_info.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_info.c	Sat May 20 00:30:53 2000 +0000
@@ -1,68 +1,350 @@
 /*
-  aim_info.c
-
-  The functions here are responsible for requesting and parsing information-
-  gathering SNACs.  
-  
+ * aim_info.c
+ *
+ * The functions here are responsible for requesting and parsing information-
+ * gathering SNACs.  
+ *
  */
 
 
-#include "aim.h" /* for most everything */
-
-u_long aim_getinfo(struct aim_conn_t *conn, const char *sn)
-{
-  struct command_tx_struct newpacket;
+#include <aim.h>
 
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
+u_long aim_getinfo(struct aim_session_t *sess,
+		   struct aim_conn_t *conn, 
+		   const char *sn)
+{
+  struct command_tx_struct *newpacket;
+  int i = 0;
 
-  newpacket.lock = 1;
-  newpacket.type = 0x0002;
-
-  newpacket.commandlen = 12 + 1 + strlen(sn);
-  newpacket.data = (char *) malloc(newpacket.commandlen);
+  if (!sess || !conn || !sn)
+    return 0;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x02;
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x05;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
+  if (!(newpacket = aim_tx_new(0x0002, conn, 12+1+strlen(sn))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);
 
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+  i += aimutil_put8(newpacket->data+i, strlen(sn));
+  i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
 
-  /* TLV: Screen Name */
-  /* t(0x0001) */
-  newpacket.data[10] = 0x00;
-  newpacket.data[11] = 0x01; 
-  /* l() */
-  newpacket.data[12] = strlen(sn);
-  /* v() */
-  memcpy(&(newpacket.data[13]), sn, strlen(sn));
-
-  aim_tx_enqueue(&newpacket);
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
 
   {
     struct aim_snac_t snac;
     
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x0002;
     snac.type = 0x0005;
     snac.flags = 0x0000;
 
     snac.data = malloc(strlen(sn)+1);
-    memcpy(snac.data, sn, strlen(sn)+1);
+    strcpy(snac.data, sn);
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
+}
+
+
+/*
+ * Capability blocks.  
+ */
+u_char aim_caps[6][16] = {
+  
+  /* Buddy icon */
+  {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+  
+  /* Voice */
+  {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+  
+  /* IM image */
+  {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+  
+  /* Chat */
+  {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+  
+  /* Get file */
+  {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+  
+  /* Send file */
+  {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, 
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
+};
+
+/*
+ * AIM is fairly regular about providing user info.  This
+ * is a generic routine to extract it in its standard form.
+ */
+int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo)
+{
+  int i = 0;
+  int tlvcnt = 0;
+  int curtlv = 0;
+  int tlv1 = 0;
+  u_short curtype;
+
+
+  if (!buf || !outinfo)
+    return -1;
+
+  /* Clear out old data first */
+  memset(outinfo, 0x00, sizeof(struct aim_userinfo_s));
+
+  /*
+   * Screen name.    Stored as an unterminated string prepended
+   *                 with an unsigned byte containing its length.
+   */
+  memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
+  outinfo->sn[(int)buf[i]] = '\0';
+  i = 1 + (int)buf[i];
+
+  /*
+   * Warning Level.  Stored as an unsigned short.
+   */
+  outinfo->warnlevel = aimutil_get16(&buf[i]);
+  i += 2;
+
+  /*
+   * TLV Count.      Unsigned short representing the number of 
+   *                 Type-Length-Value triples that follow.
+   */
+  tlvcnt = aimutil_get16(&buf[i]);
+  i += 2;
+
+  /* 
+   * Parse out the Type-Length-Value triples as they're found.
+   */
+  while (curtlv < tlvcnt)
+    {
+      curtype = aimutil_get16(&buf[i]);
+      switch (curtype)
+	{
+	  /*
+	   * Type = 0x0001: Member Class.   
+	   * 
+	   * Specified as any of the following bitwise ORed together:
+	   *      0x0001  Trial (user less than 60days)
+	   *      0x0002  Unknown bit 2
+	   *      0x0004  AOL Main Service user
+	   *      0x0008  Unknown bit 4
+	   *      0x0010  Free (AIM) user 
+	   *      0x0020  Away
+	   *
+	   * In some odd cases, we can end up with more
+	   * than one of these.  We only want the first,
+	   * as the others may not be something we want.
+	   *
+	   */
+	case 0x0001:
+	  if (tlv1) /* use only the first */
+	    break;
+	  outinfo->class = aimutil_get16(&buf[i+4]);
+	  tlv1++;
+	  break;
+	  
+	  /*
+	   * Type = 0x0002: Member-Since date. 
+	   *
+	   * The time/date that the user originally
+	   * registered for the service, stored in 
+	   * time_t format
+	   */
+	case 0x0002: 
+	  outinfo->membersince = aimutil_get32(&buf[i+4]);
+	  break;
+	  
+	  /*
+	   * Type = 0x0003: On-Since date.
+	   *
+	   * The time/date that the user started 
+	   * their current session, stored in time_t
+	   * format.
+	   */
+	case 0x0003:
+	  outinfo->onlinesince = aimutil_get32(&buf[i+4]);
+	  break;
+
+	  /*
+	   * Type = 0x0004: Idle time.
+	   *
+	   * Number of seconds since the user
+	   * actively used the service.
+	   */
+	case 0x0004:
+	  outinfo->idletime = aimutil_get16(&buf[i+4]);
+	  break;
+
+	  /*
+	   * Type = 0x000d
+	   *
+	   * Capability information.  Not real sure of
+	   * actual decoding.  See comment on aim_bos_setprofile()
+	   * in aim_misc.c about the capability block, its the same.
+	   *
+	   * Ignore.
+	   *
+	   */
+	case 0x000d:
+	  {
+	    int z,y; 
+	    int len;
+	    len = aimutil_get16(buf+i+2);
+	    if (!len)
+	      break;
+
+	    for (z = 0; z < len; z+=0x10) {
+	      for(y=0; y < 6; y++) {
+		if (memcmp(&aim_caps[y], buf+i+4+z, 0x10) == 0) {
+		  switch(y) {
+		  case 0: outinfo->capabilities |= AIM_CAPS_BUDDYICON; break;
+		  case 1: outinfo->capabilities |= AIM_CAPS_VOICE; break;
+		  case 2: outinfo->capabilities |= AIM_CAPS_IMIMAGE; break;
+		  case 3: outinfo->capabilities |= AIM_CAPS_CHAT; break;
+		  case 4: outinfo->capabilities |= AIM_CAPS_GETFILE; break;
+		  case 5: outinfo->capabilities |= AIM_CAPS_SENDFILE; break;
+		  default: outinfo->capabilities |= 0xff00; break;
+		  }
+		}
+	      }
+	    }
+	  }
+	  break;
+
+	  /*
+	   * Type = 0x000e
+	   *
+	   * Unknown.  Always of zero length, and always only
+	   * on AOL users.
+	   *
+	   * Ignore.
+	   *
+	   */
+	case 0x000e:
+	  break;
+	  
+	  /*
+	   * Type = 0x000f: Session Length. (AIM)
+	   * Type = 0x0010: Session Length. (AOL)
+	   *
+	   * The duration, in seconds, of the user's
+	   * current session.
+	   *
+	   * Which TLV type this comes in depends
+	   * on the service the user is using (AIM or AOL).
+	   *
+	   */
+	case 0x000f:
+	case 0x0010:
+	  outinfo->sessionlen = aimutil_get32(&buf[i+4]);
+	  break;
+
+	  /*
+	   * Reaching here indicates that either AOL has
+	   * added yet another TLV for us to deal with, 
+	   * or the parsing has gone Terribly Wrong.
+	   *
+	   * Either way, inform the owner and attempt
+	   * recovery.
+	   *
+	   */
+	default:
+	  {
+	    int len,z = 0, y = 0, x = 0;
+	    char tmpstr[80];
+	    printf("faim: userinfo: **warning: unexpected TLV:\n");
+	    printf("faim: userinfo:   sn    =%s\n", outinfo->sn);
+	    printf("faim: userinfo:   curtlv=0x%04x\n", curtlv);
+	    printf("faim: userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
+	    printf("faim: userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
+	    printf("faim: userinfo:   data: \n");
+	    while (z<len)
+	      {
+		x = sprintf(tmpstr, "faim: userinfo:      ");
+		for (y = 0; y < 8; y++)
+		  {
+		    if (z<len)
+		      {
+			sprintf(tmpstr+x, "%02x ", buf[i+4+z]);
+			z++;
+			x += 3;
+		      }
+		    else
+		      break;
+		  }
+		printf("%s\n", tmpstr);
+	      }
+	  }
+	  break;
+	}  
+      /*
+       * No matter what, TLV triplets should always look like this:
+       *
+       *   u_short type;
+       *   u_short length;
+       *   u_char  data[length];
+       *
+       */
+      i += (2 + 2 + aimutil_get16(&buf[i+2]));
+      
+      curtlv++;
+    }
+  
+  return i;
+}
+
+/*
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure.  Its close enough to run
+ * through aim_extractuserinfo() however.
+ *
+ */
+int aim_parse_oncoming_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  struct aim_userinfo_s userinfo;
+  u_int i = 0;
+  rxcallback_t userfunc=NULL;
+
+  i = 10;
+  i += aim_extractuserinfo(command->data+i, &userinfo);
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING);
+  if (userfunc)
+    i = userfunc(sess, command, &userinfo);
+
+  return 1;
+}
+
+/*
+ * Offgoing Buddy notifications contain no useful
+ * information other than the name it applies to.
+ *
+ */
+int aim_parse_offgoing_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
+{
+  char sn[MAXSNLEN+1];
+  u_int i = 0;
+  rxcallback_t userfunc=NULL;
+
+  strncpy(sn, command->data+11, (int)command->data[10]);
+  sn[(int)command->data[10]] = '\0';
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING);
+  if (userfunc)
+    i = userfunc(sess, command, sn);
+
+  return 1;
 }
 
 /*
@@ -70,151 +352,150 @@
  * the higher-level callback (in the user app).
  *
  */
-
-int aim_parse_userinfo_middle(struct command_rx_struct *command)
+int aim_parse_userinfo_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command)
 {
-  char *sn = NULL;
+  struct aim_userinfo_s userinfo;
   char *prof_encoding = NULL;
   char *prof = NULL;
-  u_short warnlevel = 0x0000;
-  u_short idletime = 0x0000;
-  u_short class = 0x0000;
-  u_long membersince = 0x00000000;
-  u_long onlinesince = 0x00000000;
-  int tlvcnt = 0;
-  int i = 0;
+  u_int i = 0;
+  rxcallback_t userfunc=NULL;
+  struct aim_tlvlist_t *tlvlist;
 
   {
     u_long snacid = 0x000000000;
     struct aim_snac_t *snac = NULL;
 
-    snacid = (command->data[6] << 24) & 0xFF000000;
-    snacid+= (command->data[7] << 16) & 0x00FF0000;
-    snacid+= (command->data[8] <<  8) & 0x0000FF00;
-    snacid+= (command->data[9])       & 0x000000FF;
+    snacid = aimutil_get32(&command->data[6]);
+    snac = aim_remsnac(sess, snacid);
 
-    snac = aim_remsnac(snacid);
-
-    free(snac->data);
-    free(snac);
+    if (snac)
+      {
+	if (snac->data)
+	  free(snac->data);
+	else
+	  printf("faim: parse_userinfo_middle: warning: no ->data in cached SNAC\n");
+	free(snac);
+      }
+    else
+      printf("faim: parseuserinfo_middle: warning: no SNAC cached with for this response (%08lx)\n", snacid);
 
   }
-
-  sn = (char *) malloc(command->data[10]+1);
-  memcpy(sn, &(command->data[11]), command->data[10]);
-  sn[(int)command->data[10]] = '\0';
   
-  i = 11 + command->data[10];
-  warnlevel = ((command->data[i++]) << 8) & 0xFF00;
-  warnlevel += (command->data[i++]) & 0x00FF;
-
-  tlvcnt = ((command->data[i++]) << 8) & 0xFF00;
-  tlvcnt += (command->data[i++]) & 0x00FF;
-
-  /* a mini TLV parser */
-  {
-    int curtlv = 0;
-    int tlv1 = 0;
-
-    while (curtlv < tlvcnt)
-      {
-	if ((command->data[i] == 0x00) &&
-	    (command->data[i+1] == 0x01) )
-	  {
-	    if (tlv1)
-	      break;
-	    /* t(0001) = class */
-	    class = ((command->data[i+4]) << 8) & 0xFF00;
-	    class += (command->data[i+5]) & 0x00FF;
-	    i += (2 + 2 + command->data[i+3]);
-	    tlv1++;
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x02))
-	  {
-	    /* t(0002) = member since date  */
-	    if (command->data[i+3] != 0x04)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(2)\n", command->data[i+3]);
-
-	    membersince = ((command->data[i+4]) << 24) &  0xFF000000;
-	    membersince += ((command->data[i+5]) << 16) & 0x00FF0000;
-	    membersince += ((command->data[i+6]) << 8) &  0x0000FF00;
-	    membersince += ((command->data[i+7]) ) &      0x000000FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x03))
-	  {
-	    /* t(0003) = on since date  */
-	    if (command->data[i+3] != 0x04)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(3)\n", command->data[i+3]);
+  i = 10;
 
-	    onlinesince = ((command->data[i+4]) << 24) &  0xFF000000;
-	    onlinesince += ((command->data[i+5]) << 16) & 0x00FF0000;
-	    onlinesince += ((command->data[i+6]) << 8) &  0x0000FF00;
-	    onlinesince += ((command->data[i+7]) ) &      0x000000FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	else if ((command->data[i] == 0x00) &&
-		 (command->data[i+1] == 0x04) )
-	  {
-	    /* t(0004) = idle time */
-	    if (command->data[i+3] != 0x02)
-	      printf("faim: userinfo: **warning: strange v(%x) for t(4)\n", command->data[i+3]);
-	    idletime = ((command->data[i+4]) << 8) & 0xFF00;
-	    idletime += (command->data[i+5]) & 0x00FF;
-	    i += (2 + 2 + command->data[i+3]);
-	  }  
-	else
-	  {
-	    printf("faim: userinfo: **warning: unexpected TLV t(%02x%02x) l(%02x%02x)\n", command->data[i], command->data[i+1], command->data[i+2], command->data[i+3]);
-	    i += (2 + 2 + command->data[i+3]);
-	  }
-	curtlv++;
-      }
-  }
-  if (i < command->commandlen)
+  /*
+   * extractuserinfo will give us the basic metaTLV information
+   */
+  i += aim_extractuserinfo(command->data+i, &userinfo);
+  
+  /*
+   * However, in this command, there's usually more TLVs following...
+   */ 
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+  prof_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
+  prof = aim_gettlv_str(tlvlist, 0x0002, 1);
+
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO);
+  if (userfunc)
     {
-      if ( (command->data[i] == 0x00) &&
-	   (command->data[i+1] == 0x01) )
-	{
-	  int len = 0;
-	  
-	  len = ((command->data[i+2] << 8) & 0xFF00);
-	  len += (command->data[i+3]) & 0x00FF;
-	  
-	  prof_encoding = (char *) malloc(len+1);
-	  memcpy(prof_encoding, &(command->data[i+4]), len);
-	  prof_encoding[len] = '\0';
-	  
-	  i += (2+2+len);
-	}
-      else
-	{
-	  printf("faim: userinfo: **warning: unexpected TLV after TLVblock t(%02x%02x) l(%02x%02x)\n", command->data[i], command->data[i+1], command->data[i+2], command->data[i+3]);
-	  i += 2 + 2 + command->data[i+3];
-	}
+      i = userfunc(sess,
+		   command, 
+		   &userinfo, 
+		   prof_encoding, 
+		   prof); 
     }
-
-  if (i < command->commandlen)
-    {
-      int len = 0;
-
-      len = ((command->data[i+2]) << 8) & 0xFF00;
-      len += (command->data[i+3]) & 0x00FF;
-
-      prof = (char *) malloc(len+1);
-      memcpy(prof, &(command->data[i+4]), len);
-      prof[len] = '\0';
-    }
-  else
-    printf("faim: userinfo: **early parse abort...no profile?\n");
-
-  i = (aim_callbacks[AIM_CB_USERINFO])(command, sn, prof_encoding, prof, warnlevel, idletime, class, membersince, onlinesince);
-
-  free(sn);
+  
   free(prof_encoding);
   free(prof);
+  aim_freetlvchain(&tlvlist);
 
+  return 1;
+}
+
+/*
+ * Inverse of aim_extractuserinfo()
+ */
+int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
+{
+  int i = 0;
+  struct aim_tlvlist_t *tlvlist = NULL;
+
+  if (!buf || !info)
+    return 0;
+
+  i += aimutil_put8(buf+i, strlen(info->sn));
+  i += aimutil_putstr(buf+i, info->sn, strlen(info->sn));
+
+  i += aimutil_put16(buf+i, info->warnlevel);
+
+  /* XXX: we only put down five */
+  i += aimutil_put16(buf+i, 5);
+  aim_addtlvtochain16(&tlvlist, 0x0001, info->class);
+  aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
+  aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
+  aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
+  /* XXX: should put caps here */
+  aim_addtlvtochain32(&tlvlist, (info->class)&AIM_CLASS_AOL?0x0010:0x000f, info->sessionlen);
+  
+  i += aim_writetlvchain(buf+i, buflen-i, &tlvlist);
+  aim_freetlvchain(&tlvlist);
+  
   return i;
 }
+
+int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info)
+{
+  struct command_tx_struct *tx;
+  int i = 0;
+
+  if (!sess || !conn || !info)
+    return 0;
+
+  if (!(tx = aim_tx_new(0x0002, conn, 1152)))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aimutil_put16(tx->data+i, 0x0003);
+  i += aimutil_put16(tx->data+i, 0x000b);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+
+  i += aim_putuserinfo(tx->data+i, tx->commandlen-i, info);
+
+  tx->commandlen = i;
+  tx->lock = 0;
+  aim_tx_enqueue(sess, tx);
+
+  return 0;
+}
+
+int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn)
+{
+  struct command_tx_struct *tx;
+  int i = 0;
+
+  if (!sess || !conn || !sn)
+    return 0;
+
+  if (!(tx = aim_tx_new(0x0002, conn, 10+1+strlen(sn))))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aimutil_put16(tx->data+i, 0x0003);
+  i += aimutil_put16(tx->data+i, 0x000c);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+
+  i += aimutil_put8(tx->data+i, strlen(sn));
+  i += aimutil_putstr(tx->data+i, sn, strlen(sn));
+  
+  tx->lock = 0;
+  aim_tx_enqueue(sess, tx);
+
+  return 0;
+}
--- a/libfaim/aim_login.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_login.c	Sat May 20 00:30:53 2000 +0000
@@ -5,7 +5,7 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h>
 
 
 /*
@@ -15,8 +15,57 @@
 #include "tis_telnet_proxy.h"
 #endif
 
+int aim_sendconnack(struct aim_session_t *sess,
+		      struct aim_conn_t *conn)
+{
+  int curbyte=0;
+  
+  struct command_tx_struct *newpacket;
+
+  if (!(newpacket = aim_tx_new(0x0001, conn, 4)))
+    return -1;
+
+  newpacket->lock = 1;
+  
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+  newpacket->lock = 0;
+  return aim_tx_enqueue(sess, newpacket);
+}
+
+#ifdef SNACLOGIN
 /*
- *  send_login(int socket, char *sn, char *password)
+ * In AIM 3.5 protocol, the first stage of login is to request
+ * login from the Authorizer, passing it the screen name
+ * for verification.  If the name is invalid, a 0017/0003 
+ * is spit back, with the standard error contents.  If valid,
+ * a 0017/0007 comes back, which is the signal to send
+ * it the main login command (0017/0002).  
+ */
+int aim_request_login(struct aim_session_t *sess,
+		      struct aim_conn_t *conn, 
+		      char *sn)
+{
+  int curbyte=0;
+  
+  struct command_tx_struct *newpacket;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+2+2+strlen(sn))))
+    return -1;
+
+  newpacket->lock = 1;
+  
+  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0017, 0x0006, 0x0000, 0x00010000);
+  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+
+  newpacket->lock = 0;
+  return aim_tx_enqueue(sess, newpacket);
+}
+#endif /* SNACLOGIN */
+
+/*
+ * send_login(int socket, char *sn, char *password)
  *  
  * This is the initial login request packet.
  *
@@ -24,84 +73,109 @@
  * encode_password().  See that function for their
  * stupid method of doing it.
  *
- *
- *
  */
-int aim_send_login (struct aim_conn_t *conn, char *sn, char *password, struct client_info_s *clientinfo)
+int aim_send_login (struct aim_session_t *sess,
+		    struct aim_conn_t *conn, 
+		    char *sn, char *password, struct client_info_s *clientinfo)
 {
-  char *password_encoded = NULL;  /* to store encoded password */
+  u_char *password_encoded = NULL;  /* to store encoded password */
   int curbyte=0;
 
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
+
+  if (!clientinfo || !sn || !password)
+    return -1;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 1152)))
+    return -1;
 
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_AUTH);
-
-  newpacket.commandlen = 6+2+strlen(sn)+1+1+2+strlen(password)+6;
+#ifdef SNACLOGIN 
+  newpacket->commandlen = 10;
+  newpacket->commandlen += 2 + 2 + strlen(sn);
+  newpacket->commandlen += 2 + 2 + strlen(password);
+  newpacket->commandlen += 2 + 2 + strlen(clientinfo->clientstring);
+  newpacket->commandlen += 56;
+  
+  newpacket->lock = 1;
 
-  if (clientinfo)
-    {
-      if (strlen(clientinfo->clientstring))
-	newpacket.commandlen += strlen(clientinfo->clientstring)+4;
-      newpacket.commandlen += 6+6+6; 
-      if (strlen(clientinfo->country))
-	newpacket.commandlen += strlen(clientinfo->country)+4;
-      if (strlen(clientinfo->lang))
-	newpacket.commandlen += strlen(clientinfo->lang)+4;
-    }
+  curbyte = aim_putsnac(newpacket->data+curbyte, 0x0017, 0x0002, 0x0000, 0x00010000);
+  curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+  password_encoded = (u_char *) malloc(strlen(password));
+  aim_encode_password(password, password_encoded);
+  curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded);
+  curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0003, 
+			   strlen(clientinfo->clientstring), 
+			   clientinfo->clientstring);
+  /* XXX: should use clientinfo provided version info */
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x0004);
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0003);
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x0005);
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0000);
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0686);
+  curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0001, 0x0002, clientinfo->country);
+  curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0001, 0x0002, clientinfo->lang);
+  curbyte+= aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x0000002a);
+  curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
+#else
+  
+  newpacket->commandlen = 4 + 4+strlen(sn) + 4+strlen(password) + 6;
+ 
+  if (clientinfo) {
+    if (strlen(clientinfo->clientstring))
+      newpacket->commandlen += 4+strlen(clientinfo->clientstring);
+    newpacket->commandlen += 6+6+6+6;
+    if (strlen(clientinfo->country))
+      newpacket->commandlen += 4+strlen(clientinfo->country);
+    if (strlen(clientinfo->lang))
+      newpacket->commandlen += 4+strlen(clientinfo->lang);
+  }
+  newpacket->commandlen += 6;
 
-  newpacket.data = (char *) calloc (1,  newpacket.commandlen );
-  newpacket.lock = 1;
-  newpacket.type = 0x01;
+  newpacket->lock = 1;
+  newpacket->type = 0x01;
 
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
-  curbyte += aimutil_put16(newpacket.data+curbyte, strlen(sn));
-  curbyte += aimutil_putstr(newpacket.data+curbyte, sn, strlen(sn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(sn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
 
-  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket.data+curbyte, strlen(password));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, strlen(password));
   password_encoded = (char *) malloc(strlen(password));
   aim_encode_password(password, password_encoded);
-  curbyte += aimutil_putstr(newpacket.data+curbyte, password_encoded, strlen(password));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, password_encoded, strlen(password));
   free(password_encoded);
   
-  curbyte += aim_puttlv_16(newpacket.data+curbyte, 0x0016, 0x0001);
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x0004);
+  
+  if (clientinfo) {
+    if (strlen(clientinfo->clientstring)) {
+      curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->clientstring));
+      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->clientstring, strlen(clientinfo->clientstring));
+    }
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, clientinfo->major /*0x0001*/);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, clientinfo->minor /*0x0001*/);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0000);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, clientinfo->build /*0x0013*/);
+    if (strlen(clientinfo->country)) {
+      curbyte += aimutil_put16(newpacket->data+curbyte, 0x000e);
+      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->country));
+      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->country, strlen(clientinfo->country));
+    }
+    if (strlen(clientinfo->lang)) {
+      curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->lang));
+      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->lang, strlen(clientinfo->lang));
+    }
+  }
 
-  if (clientinfo)
-    {
-      if (strlen(clientinfo->clientstring))
-	{
-	  curbyte += aimutil_put16(newpacket.data+curbyte, 0x0003);
-	  curbyte += aimutil_put16(newpacket.data+curbyte, strlen(clientinfo->clientstring));
-	  curbyte += aimutil_putstr(newpacket.data+curbyte, clientinfo->clientstring, strlen(clientinfo->clientstring));
-	}
-      curbyte += aim_puttlv_16(newpacket.data+curbyte, 0x0017, 0x0001);
-      curbyte += aim_puttlv_16(newpacket.data+curbyte, 0x0018, 0x0001);
-      curbyte += aim_puttlv_16(newpacket.data+curbyte, 0x001a, 0x0013);
-      if (strlen(clientinfo->country))
-	{
-	  curbyte += aimutil_put16(newpacket.data+curbyte, 0x000e);
-	  curbyte += aimutil_put16(newpacket.data+curbyte, strlen(clientinfo->country));
-	  curbyte += aimutil_putstr(newpacket.data+curbyte, clientinfo->country, strlen(clientinfo->country));
-	}
-       if (strlen(clientinfo->lang))
-	{
-	  curbyte += aimutil_put16(newpacket.data+curbyte, 0x000f);
-	  curbyte += aimutil_put16(newpacket.data+curbyte, strlen(clientinfo->lang));
-	  curbyte += aimutil_putstr(newpacket.data+curbyte, clientinfo->lang, strlen(clientinfo->lang));
-	}
-    }
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
+#endif
 
-  curbyte += aim_puttlv_16(newpacket.data+curbyte, 0x0009, 0x0015);
-
-  newpacket.lock = 0;
-  aim_tx_enqueue(&newpacket);
-
-  return 0;
+  newpacket->lock = 0;
+  return aim_tx_enqueue(sess, newpacket);
 }
 
 /*
@@ -120,9 +194,9 @@
  * hope it doesn't change over time!  
  *
  */
-int aim_encode_password(const char *password, char *encoded)
+int aim_encode_password(const char *password, u_char *encoded)
 {
-  char encoding_table[] = {
+  u_char encoding_table[] = {
     0xf3, 0xb3, 0x6c, 0x99,
     0x95, 0x3f, 0xac, 0xb6,
     0xc5, 0xfa, 0x6b, 0x63,
@@ -137,6 +211,224 @@
   return 0;
 }
 
+/*
+ * This is sent back as a general response to the login command.
+ * It can be either an error or a success, depending on the
+ * precense of certain TLVs.  
+ *
+ * The client should check the value of logininfo->errorcode. If
+ * its nonzero, there was an error.
+ *
+ */
+int aim_authparse(struct aim_session_t *sess, 
+		  struct command_rx_struct *command)
+{
+  struct aim_tlvlist_t *tlvlist;
+  int ret = 1;
+  char *sn;
+  rxcallback_t userfunc = NULL;
+
+  memset(&sess->logininfo, 0x00, sizeof(sess->logininfo));
+
+  /*
+   * Read block of TLVs.  All further data is derived
+   * from what is parsed here.
+   */
+#ifdef SNACLOGIN
+  tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
+#else
+  tlvlist = aim_readtlvchain(command->data, command->commandlen);
+#endif
+  /*
+   * No matter what, we should have a screen name.
+   */
+  sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+  memcpy(sess->logininfo.screen_name, sn, strlen(sn));
+  sn[(strlen(sn))] = '\0';
+  
+  /*
+   * Check for an error code.  If so, we should also
+   * have an error url.
+   */
+  if (aim_gettlv(tlvlist, 0x0008, 1))
+    {
+      struct aim_tlv_t *errtlv;
+      errtlv = aim_gettlv(tlvlist, 0x0008, 1);
+      sess->logininfo.errorcode = aimutil_get16(errtlv->value);
+      sess->logininfo.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+    }
+  /* 
+   * If we have both an IP number (0x0005) and a cookie (0x0006),
+   * then the login was successful.
+   */
+  else if (aim_gettlv(tlvlist, 0x0005, 1) && aim_gettlv(tlvlist, 0x0006, 1))
+    {
+      struct aim_tlv_t *tmptlv;
+
+      /*
+       * IP address of BOS server.
+       */
+      sess->logininfo.BOSIP = aim_gettlv_str(tlvlist, 0x0005, 1);
+
+      /*
+       * Authorization Cookie
+       */
+      tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+      memcpy(sess->logininfo.cookie, tmptlv->value, AIM_COOKIELEN);
+
+      /*
+       * The email address attached to this account
+       */
+      sess->logininfo.email = aim_gettlv_str(tlvlist, 0x0011, 1);
+
+      /*
+       * The registration status.  (Not real sure what it means.)
+       */
+      tmptlv = aim_gettlv(tlvlist, 0x0013, 1);
+      sess->logininfo.regstatus = aimutil_get16(tmptlv->value);
+      
+    }
+
+#ifdef SNACLOGIN
+  userfunc = aim_callhandler(command->conn, 0x0017, 0x0003);
+#else
+  userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_AUTHSUCCESS);
+#endif
+  if (userfunc)
+    ret = userfunc(sess, command);
+
+  aim_freetlvchain(&tlvlist);
+
+  /* These have been clobbered by the freetlvchain */
+  sess->logininfo.BOSIP = NULL;
+  sess->logininfo.email = NULL;
+  sess->logininfo.errorurl = NULL;
+
+  return ret;
+}
+
+/*
+ * Generate an authorization response.  
+ *
+ * You probably don't want this unless you're writing an AIM server.
+ *
+ */
+unsigned long aim_sendauthresp(struct aim_session_t *sess, 
+			       struct aim_conn_t *conn, 
+			       char *sn, char *bosip, 
+			       char *cookie, char *email, 
+			       int regstatus)
+{	
+  struct command_tx_struct *tx;
+  struct aim_tlvlist_t *tlvlist = NULL;
+
+  if (!(tx = aim_tx_new(0x0001 /*right??*/, conn, 1152)))
+    return -1;
+  
+  tx->lock = 1;
+
+  if (sn)
+    aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn));
+  else
+    aim_addtlvtochain_str(&tlvlist, 0x0001, sess->logininfo.screen_name, strlen(sess->logininfo.screen_name));
+
+  if (sess->logininfo.errorcode) {
+    aim_addtlvtochain16(&tlvlist, 0x0008, sess->logininfo.errorcode);
+    aim_addtlvtochain_str(&tlvlist, 0x0004, sess->logininfo.errorurl, strlen(sess->logininfo.errorurl));
+  } else {
+    aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip));
+    aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
+    aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email));
+    aim_addtlvtochain16(&tlvlist, 0x0013, regstatus);
+  }
+
+  tx->commandlen = aim_writetlvchain(tx->data, tx->commandlen, &tlvlist);
+  tx->lock = 0;
+  return aim_tx_enqueue(sess, tx);
+}
+
+/*
+ * Generate a random cookie.  (Non-client use only)
+ */
+int aim_gencookie(unsigned char *buf)
+{
+  int i;
+
+  srand(time(NULL));
+
+  for (i=0; i < AIM_COOKIELEN; i++)
+    buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0));
+
+  return i;
+}
+
+/*
+ * Send Server Ready.  (Non-client)
+ */
+int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  struct command_tx_struct *tx;
+  int i = 0;
+
+  if (!(tx = aim_tx_new(0x0002, conn, 10+0x20)))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aimutil_put16(tx->data+i, 0x0001);
+  i += aimutil_put16(tx->data+i, 0x0003);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  
+  i += aimutil_put16(tx->data+i, 0x0001);  
+  i += aimutil_put16(tx->data+i, 0x0002);
+  i += aimutil_put16(tx->data+i, 0x0003);
+  i += aimutil_put16(tx->data+i, 0x0004);
+  i += aimutil_put16(tx->data+i, 0x0006);
+  i += aimutil_put16(tx->data+i, 0x0008);
+  i += aimutil_put16(tx->data+i, 0x0009);
+  i += aimutil_put16(tx->data+i, 0x000a);
+  i += aimutil_put16(tx->data+i, 0x000b);
+  i += aimutil_put16(tx->data+i, 0x000c);
+
+  tx->lock = 0;
+
+  return aim_tx_enqueue(sess, tx);
+}
 
 
+/* 
+ * Send service redirect.  (Non-Client)
+ */
+unsigned long aim_sendredirect(struct aim_session_t *sess, 
+			       struct aim_conn_t *conn, 
+			       unsigned short servid, 
+			       char *ip,
+			       char *cookie)
+{	
+  struct command_tx_struct *tx;
+  struct aim_tlvlist_t *tlvlist = NULL;
+  int i = 0;
 
+  if (!(tx = aim_tx_new(0x0002, conn, 1152)))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aimutil_put16(tx->data+i, 0x0001);
+  i += aimutil_put16(tx->data+i, 0x0005);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  i += aimutil_put16(tx->data+i, 0x0000);
+  
+  aim_addtlvtochain16(&tlvlist, 0x000d, servid);
+  aim_addtlvtochain_str(&tlvlist, 0x0005, ip, strlen(ip));
+  aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
+
+  tx->commandlen = aim_writetlvchain(tx->data+i, tx->commandlen-i, &tlvlist)+i;
+  aim_freetlvchain(&tlvlist);
+
+  tx->lock = 0;
+  return aim_tx_enqueue(sess, tx);
+}
--- a/libfaim/aim_logoff.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_logoff.c	Sat May 20 00:30:53 2000 +0000
@@ -4,7 +4,7 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h> 
 
 /* 
  * aim_logoff()
@@ -12,16 +12,16 @@
  * Closes -ALL- open connections.
  *
  */
-int aim_logoff(void)
+int aim_logoff(struct aim_session_t *sess)
 {
   int i = AIM_CONN_MAX-1;
   while (i > -1)
     {
-      if (aim_conns[i].fd>-1)
-	aim_conn_close(&(aim_conns[i]));
+      if (sess->conns[i].fd>-1)
+	aim_conn_close(&(sess->conns[i]));
       i--;
     }
-  aim_connrst();  /* in case we want to connect again */
+  aim_connrst(sess);  /* in case we want to connect again */
 
   return 0;
 
--- a/libfaim/aim_misc.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_misc.c	Sat May 20 00:30:53 2000 +0000
@@ -11,7 +11,7 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h> 
 
 /*
  * aim_bos_setidle()
@@ -21,9 +21,11 @@
  *  time.  
  *
  */
-u_long aim_bos_setidle(struct aim_conn_t *conn, u_long idletime)
+u_long aim_bos_setidle(struct aim_session_t *sess,
+		       struct aim_conn_t *conn, 
+		       u_long idletime)
 {
-  return aim_genericreq_l(conn, 0x0001, 0x0011, &idletime);
+  return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
 }
 
 
@@ -55,91 +57,66 @@
  *
  *
  */
-u_long aim_bos_changevisibility(struct aim_conn_t *conn, int changetype, char *denylist)
+u_long aim_bos_changevisibility(struct aim_session_t *sess,
+				struct aim_conn_t *conn, 
+				int changetype, char *denylist)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
+  int packlen = 0;
+  u_short subtype;
 
   char *localcpy = NULL;
   char *tmpptr = NULL;
-  char *tmpptr2 = NULL;
   int i,j;
+  int listcount;
 
   if (!denylist)
     return 0;
 
-  newpacket.lock = 1;
-
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-
-  newpacket.type = 0x02;
-  newpacket.commandlen = 10;
-
   localcpy = (char *) malloc(strlen(denylist)+1);
   memcpy(localcpy, denylist, strlen(denylist)+1);
-  tmpptr2 = localcpy; /* save this for the free() */
+  
+  listcount = aimutil_itemcnt(localcpy, '&');
+  packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
 
-  i = 0;
-  tmpptr = strsep(&localcpy, "&");
-  while (strlen(tmpptr) && (i < 100))
-    {
-      newpacket.commandlen += strlen(tmpptr)+1;
-      i++;
-      tmpptr = strsep(&localcpy, "&");
-    }
-  free(tmpptr2);
-  tmpptr2 = NULL;
+  if (!(newpacket = aim_tx_new(0x0002, conn, packlen)))
+    return -1;
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  memset(newpacket.data, 0x00, newpacket.commandlen);
+  newpacket->lock = 1;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x09;
-  newpacket.data[2] = 0x00;
   switch(changetype)
     {
-    case AIM_VISIBILITYCHANGE_PERMITADD:
-      newpacket.data[3] = 0x05; break;
-    case AIM_VISIBILITYCHANGE_PERMITREMOVE:
-      newpacket.data[3] = 0x06; break;
-    case AIM_VISIBILITYCHANGE_DENYADD:
-      newpacket.data[3] = 0x07; break;
-    case AIM_VISIBILITYCHANGE_DENYREMOVE:
-      newpacket.data[4] = 0x08; break;
+    case AIM_VISIBILITYCHANGE_PERMITADD:    subtype = 0x05; break;
+    case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break;
+    case AIM_VISIBILITYCHANGE_DENYADD:      subtype = 0x07; break;
+    case AIM_VISIBILITYCHANGE_DENYREMOVE:   subtype = 0x08; break;
     default:
+      free(newpacket->data);
+      free(newpacket);
       return 0;
     }
-  /* SNAC reqid -- we actually DO NOT send a SNAC ID with this one! */
-  newpacket.data[6] = 0;
-  newpacket.data[7] = 0;
-  newpacket.data[8] = 0;
-  newpacket.data[9] = 0;
+
+  /* We actually DO NOT send a SNAC ID with this one! */
+  aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0);
  
   j = 10;  /* the next byte */
-
-  localcpy = (char *) malloc(strlen(denylist)+1);
-  memcpy(localcpy, denylist, strlen(denylist)+1);
-  tmpptr2 = localcpy; /* save this for the free() */
-
-  i = 0;
-  tmpptr = strsep(&localcpy, "&");
-  while (strlen(tmpptr) && (i < 100))
+  
+  for (i=0; (i < (listcount - 1)) && (i < 99); i++)
     {
-      newpacket.data[j] = strlen(tmpptr);
-      memcpy(&(newpacket.data[j+1]), tmpptr, strlen(tmpptr));
+      tmpptr = aimutil_itemidx(localcpy, i, '&');
+
+      newpacket->data[j] = strlen(tmpptr);
+      memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
       j += strlen(tmpptr)+1;
-      i++;
-      tmpptr = strsep(&localcpy, "&");
+      free(tmpptr);
     }
-  free(tmpptr2);
+  free(localcpy);
 
-  newpacket.lock = 0;
+  newpacket->lock = 0;
 
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
-  return (aim_snac_nextid); /* dont increment */
+  return (sess->snac_nextid); /* dont increment */
 
 }
 
@@ -152,14 +129,18 @@
  *
  * buddy_list = "Screen Name One&ScreenNameTwo&";
  *
- * TODO: Clean this up.
+ * TODO: Clean this up.  
+ *
+ * XXX: I can't stress the TODO enough.
  *
  */
-u_long aim_bos_setbuddylist(struct aim_conn_t *conn, char *buddy_list)
+u_long aim_bos_setbuddylist(struct aim_session_t *sess,
+			    struct aim_conn_t *conn, 
+			    char *buddy_list)
 {
   int i, j;
 
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
 
   int packet_login_phase3c_hi_b_len = 0;
 
@@ -169,15 +150,9 @@
   packet_login_phase3c_hi_b_len = 16; /* 16b for FLAP and SNAC headers */
 
   /* bail out if we can't make the packet */
-  if (buddy_list == NULL)
-    {
-      printf("\nNO BUDDIES!  ARE YOU THAT LONELY???\n");
-      return 0;
-    }
-#if debug > 0
-  printf("****buddy list: %s\n", buddy_list);
-  printf("****buddy list len: %d (%x)\n", strlen(buddy_list), strlen(buddy_list));
-#endif
+  if (!buddy_list) {
+    return -1;
+  }
 
   localcpy = (char *) malloc(strlen(buddy_list)+1);
   memcpy(localcpy, buddy_list, strlen(buddy_list)+1);
@@ -198,27 +173,12 @@
 #endif
   free(localcpy);
 
-  newpacket.type = 0x02;
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.commandlen = packet_login_phase3c_hi_b_len - 6;
-  newpacket.lock = 1;
-  
-  newpacket.data = (char *) malloc(newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, packet_login_phase3c_hi_b_len - 6)))
+    return -1;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x03;
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x04;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  newpacket->lock = 1;
+  
+  aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, sess->snac_nextid);
 
   j = 10;  /* the next byte */
 
@@ -229,18 +189,18 @@
 #if debug > 0
       printf("---adding %s (%d)\n", tmpptr, strlen(tmpptr));
 #endif
-      newpacket.data[j] = strlen(tmpptr);
-      memcpy(&(newpacket.data[j+1]), tmpptr, strlen(tmpptr));
+      newpacket->data[j] = strlen(tmpptr);
+      memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
       j += strlen(tmpptr)+1;
       i++;
       tmpptr = strtok(NULL, "&");
     }
 
-  newpacket.lock = 0;
+  newpacket->lock = 0;
 
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
 /* 
@@ -248,70 +208,70 @@
  *
  * Gives BOS your profile.
  *
+ * 
+ * The large data chunk given here is of unknown decoding.
+ * What I do know is that each 0x20 byte repetition 
+ * represents a capability.  People with only the 
+ * first two reptitions can support normal messaging
+ * and chat (client version 2.0 or 3.0).  People with 
+ * the third as well can also support voice chat (client
+ * version 3.5 or higher).  IOW, if we don't send this,
+ * we won't get chat invitations (get "software doesn't
+ * support chat" error).
+ *
+ * This data is broadcast along with your oncoming
+ * buddy command to everyone who has you on their
+ * buddy list, as a type 0x0002 TLV.
+ * 
  */
-u_long aim_bos_setprofile(struct aim_conn_t *conn, char *profile)
+u_long aim_bos_setprofile(struct aim_session_t *sess,
+			  struct aim_conn_t *conn, 
+			  char *profile,
+			  char *awaymsg,
+			  unsigned int caps)
 {
-  int packet_profile_len = 0;
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   int i = 0;
 
-  /* len: SNAC */
-  packet_profile_len = 10;
-  /* len: T+L (where t(0001)) */
-  packet_profile_len += 2 + 2;
-  /* len: V (where t(0001)) */
-  packet_profile_len += strlen("text/x-aolrtf");
-  /* len: T+L (where t(0002)) */
-  packet_profile_len += 2 + 2;
-  /* len: V (where t(0002)) */
-  packet_profile_len += strlen(profile);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 1152+strlen(profile)+1+(awaymsg?strlen(awaymsg):0))))
+    return -1;
 
-  newpacket.type = 0x02;
-  if (conn)
-    newpacket.conn = conn;
+  i += aim_putsnac(newpacket->data, 0x0002, 0x004, 0x0000, sess->snac_nextid);
+  i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\"");
+  i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(profile), profile);
+  /* why do we send this twice?  */
+  i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen("text/x-aolrtf; charset=\"us-ascii\""), "text/x-aolrtf; charset=\"us-ascii\"");
+  
+  /* Away message -- we send this no matter what, even if its blank */
+  if (awaymsg)
+    i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(awaymsg), awaymsg);
   else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.commandlen = packet_profile_len;
-  newpacket.data = (char *) malloc(packet_profile_len);
+    i += aim_puttlv_str(newpacket->data+i, 0x0004, 0x0000, NULL);
 
-  i = 0;
-  /* SNAC: family */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x02;
-  /* SNAC: subtype */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x04;
-  /* SNAC: flags */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x00;
-  /* SNAC: id */
-  /* SNAC reqid */
-  newpacket.data[i++] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[i++] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[i++] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[i++] = (aim_snac_nextid) & 0xFF;
-  /* TLV t(0001) */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x01;
-  /* TLV l(000d) */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x0d;
-  /* TLV v(text/x-aolrtf) */
-  memcpy(&(newpacket.data[i]), "text/x-aolrtf", 0x000d);
-  i += 0x000d;
+  /* Capability information. */
+  {
+    int isave;
+    i += aimutil_put16(newpacket->data+i, 0x0005);
+    isave = i;
+    i += aimutil_put16(newpacket->data+i, 0);
+    if (caps & AIM_CAPS_BUDDYICON)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[0], 0x10);
+    if (caps & AIM_CAPS_VOICE)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[1], 0x10);
+    if (caps & AIM_CAPS_IMIMAGE)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[2], 0x10);
+    if (caps & AIM_CAPS_CHAT)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[3], 0x10);
+    if (caps & AIM_CAPS_GETFILE)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[4], 0x10);
+    if (caps & AIM_CAPS_SENDFILE)
+      i += aimutil_putstr(newpacket->data+i, aim_caps[5], 0x10);
+    aimutil_put16(newpacket->data+isave, i-isave-2);
+  }
+  newpacket->commandlen = i;
+  aim_tx_enqueue(sess, newpacket);
   
-  /* TLV t(0002) */
-  newpacket.data[i++] = 0x00;
-  newpacket.data[i++] = 0x02;
-  /* TLV l() */
-  newpacket.data[i++] = (strlen(profile) >> 8) & 0xFF;
-  newpacket.data[i++] = (strlen(profile) & 0xFF);
-  /* TLV v(profile) */
-  memcpy(&(newpacket.data[i]), profile, strlen(profile));
-
-  aim_tx_enqueue(&newpacket);
-  
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
 /* 
@@ -320,9 +280,11 @@
  * Set group permisson mask.  Normally 0x1f.
  *
  */
-u_long aim_bos_setgroupperm(struct aim_conn_t *conn, u_long mask)
+u_long aim_bos_setgroupperm(struct aim_session_t *sess,
+			    struct aim_conn_t *conn, 
+			    u_long mask)
 {
-  return aim_genericreq_l(conn, 0x0009, 0x0004, &mask);
+  return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
 }
 
 /*
@@ -333,43 +295,71 @@
  * TODO: Dynamisize.
  *
  */
-u_long aim_bos_clientready(struct aim_conn_t *conn)
+u_long aim_bos_clientready(struct aim_session_t *sess,
+			   struct aim_conn_t *conn)
 {
-  char command_2[] = {
-     0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x7a, 0x8c,
-     0x11, 0xab, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01,
-     0x00, 0x13, 0x00, 0x09, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x01,
-     0x00, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x01,
+  u_char command_2[] = {
+     /* placeholders for dynamic data */
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 
+     /* real data */
+     0x00, 0x01,   
+     0x00, 0x03, 
+     0x00, 0x04, 
+     0x06, 0x86,  
+     0x00, 0x02, 
+     0x00, 0x01,  
+     0x00, 0x04, 
+     0x00, 0x01, 
+ 
+     0x00, 0x03, 
+     0x00, 0x01,  
+     0x00, 0x04, 
+     0x00, 0x01, 
+     0x00, 0x04, 
+     0x00, 0x01, 
+     0x00, 0x04,
+     0x00, 0x01,
+ 
+     0x00, 0x06, 
+     0x00, 0x01, 
+     0x00, 0x04,  
+     0x00, 0x01, 
+     0x00, 0x08, 
+     0x00, 0x01, 
+     0x00, 0x04,
+     0x00, 0x01,
+ 
+     0x00, 0x09, 
+     0x00, 0x01, 
+     0x00, 0x04,
+     0x00, 0x01, 
+     0x00, 0x0a, 
+     0x00, 0x01, 
+     0x00, 0x04,
+     0x00, 0x01,
+ 
+     0x00, 0x0b,
+     0x00, 0x01, 
+     0x00, 0x04,
      0x00, 0x01
   };
   int command_2_len = 0x52;
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   
-  newpacket.lock = 1;
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-  newpacket.commandlen = command_2_len;
-  newpacket.data = (char *) malloc (newpacket.commandlen);
-  memcpy(newpacket.data, command_2, newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, command_2_len)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  memcpy(newpacket->data, command_2, command_2_len);
   
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  /* This write over the dynamic parts of the byte block */
+  aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
 
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
 /* 
@@ -380,9 +370,10 @@
  *  TODO: Move to aim_conn.
  *  TODO: Move to SNAC interface.
  */
-u_long aim_bos_reqrate(struct aim_conn_t *conn)
+u_long aim_bos_reqrate(struct aim_session_t *sess,
+		       struct aim_conn_t *conn)
 {
-  return aim_genericreq_n(conn, 0x0001, 0x0006);
+  return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
 }
 
 /* 
@@ -391,43 +382,32 @@
  *  Rate Information Response Acknowledge.
  *
  */
-u_long aim_bos_ackrateresp(struct aim_conn_t *conn)
+u_long aim_bos_ackrateresp(struct aim_session_t *sess,
+			   struct aim_conn_t *conn)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
+  int packlen = 18, i=0;
 
-  newpacket.lock = 1;
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-  newpacket.commandlen = 18;
+  if (conn->type != AIM_CONN_TYPE_BOS)
+    packlen += 2;
+
+  if(!(newpacket = aim_tx_new(0x0002, conn, packlen)));
+  
+  newpacket->lock = 1;
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x01;
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x08;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, sess->snac_nextid);
+  i += aimutil_put16(newpacket->data+i, 0x0001); 
+  i += aimutil_put16(newpacket->data+i, 0x0002);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+  i += aimutil_put16(newpacket->data+i, 0x0004);
+  
+  if (conn->type != AIM_CONN_TYPE_BOS) {
+    i += aimutil_put16(newpacket->data+i, 0x0005);
+  }
 
-  newpacket.data[10] = 0x00;
-  newpacket.data[11] = 0x01;
-  newpacket.data[12] = 0x00;
-  newpacket.data[13] = 0x02;
-  newpacket.data[14] = 0x00;
-  newpacket.data[15] = 0x03;
-  newpacket.data[16] = 0x00;
-  newpacket.data[17] = 0x04;
+  aim_tx_enqueue(sess, newpacket);
 
-  aim_tx_enqueue(&newpacket);
-
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
 /* 
@@ -439,9 +419,11 @@
  *
  *
  */
-u_long aim_bos_setprivacyflags(struct aim_conn_t *conn, u_long flags)
+u_long aim_bos_setprivacyflags(struct aim_session_t *sess,
+			       struct aim_conn_t *conn, 
+			       u_long flags)
 {
-  return aim_genericreq_l(conn, 0x0001, 0x0014, &flags);
+  return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
 }
 
 /*
@@ -451,50 +433,97 @@
  * because aparently it uses SNAC flags.
  *
  */
-u_long aim_bos_reqpersonalinfo(struct aim_conn_t *conn)
+u_long aim_bos_reqpersonalinfo(struct aim_session_t *sess,
+			       struct aim_conn_t *conn)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   
-  newpacket.lock = 1;
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-  newpacket.commandlen = 12;
+  if (!(newpacket = aim_tx_new(0x0002, conn, 12)))
+    return -1;
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, 0x000a, 0x0001, 0x000e /* huh? */, sess->snac_nextid);
   
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x0a;
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x01;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x0e; /* huh? */
+  newpacket->data[10] = 0x0d;
+  newpacket->data[11] = 0xda;
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+u_long aim_setversions(struct aim_session_t *sess,
+                               struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10 + (4*11))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
+
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+
+  i += aimutil_put16(newpacket->data+i, 0x0002);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x0004);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
 
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  i += aimutil_put16(newpacket->data+i, 0x0006);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x0008);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x0009);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x000a);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x000b);
+  i += aimutil_put16(newpacket->data+i, 0x0002);
 
-  newpacket.data[10] = 0x0d;
-  newpacket.data[11] = 0xda;
+  i += aimutil_put16(newpacket->data+i, 0x000c);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x0015);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
 
-  aim_tx_enqueue(&newpacket);
+#if 0
+  for (j = 0; j < 0x10; j++) {
+    i += aimutil_put16(newpacket->data+i, j); /* family */
+    i += aimutil_put16(newpacket->data+i, 0x0003); /* version */
+  }
+#endif
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
+
 /*
  * aim_bos_reqservice(serviceid)
  *
  * Service request. 
  *
  */
-u_long aim_bos_reqservice(struct aim_conn_t *conn, u_short serviceid)
+u_long aim_bos_reqservice(struct aim_session_t *sess,
+			  struct aim_conn_t *conn, 
+			  u_short serviceid)
 {
-  return aim_genericreq_s(conn, 0x0001, 0x0004, &serviceid);
+  return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
 }
 
 /*
@@ -503,9 +532,10 @@
  * Request BOS rights.
  *
  */
-u_long aim_bos_reqrights(struct aim_conn_t *conn)
+u_long aim_bos_reqrights(struct aim_session_t *sess,
+			 struct aim_conn_t *conn)
 {
-  return aim_genericreq_n(conn, 0x0009, 0x0002);
+  return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
 }
 
 /*
@@ -514,9 +544,10 @@
  * Request Buddy List rights.
  *
  */
-u_long aim_bos_reqbuddyrights(struct aim_conn_t *conn)
+u_long aim_bos_reqbuddyrights(struct aim_session_t *sess,
+			      struct aim_conn_t *conn)
 {
-  return aim_genericreq_n(conn, 0x0003, 0x0002);
+  return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
 }
 
 /*
@@ -531,124 +562,77 @@
  * back to the single.  I don't see any advantage to doing it either way.
  *
  */
-u_long aim_genericreq_n(struct aim_conn_t *conn, u_short family, u_short subtype)
+u_long aim_genericreq_n(struct aim_session_t *sess,
+			struct aim_conn_t *conn, 
+			u_short family, u_short subtype)
 {
-  struct command_tx_struct newpacket;
-
-  newpacket.lock = 1;
-
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-
-  newpacket.commandlen = 10;
+  struct command_tx_struct *newpacket;
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  memset(newpacket.data, 0x00, newpacket.commandlen);
-  newpacket.data[0] = (family & 0xff00)>>8;
-  newpacket.data[1] = family & 0xff;
-  newpacket.data[2] = (subtype & 0xff00)>>8;
-  newpacket.data[3] = subtype & 0xff;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10)))
+    return 0;
+
+  newpacket->lock = 1;
 
-  aim_tx_enqueue(&newpacket);
-  return (aim_snac_nextid++);
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
+ 
+  aim_tx_enqueue(sess, newpacket);
+  return (sess->snac_nextid++);
 }
 
 /*
  *
  *
  */
-u_long aim_genericreq_l(struct aim_conn_t *conn, u_short family, u_short subtype, u_long *longdata)
+u_long aim_genericreq_l(struct aim_session_t *sess,
+			struct aim_conn_t *conn, 
+			u_short family, u_short subtype, u_long *longdata)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   u_long newlong;
 
   /* If we don't have data, there's no reason to use this function */
   if (!longdata)
-    return aim_genericreq_n(conn, family, subtype);
-
-  newpacket.lock = 1;
-
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-
-  newpacket.commandlen = 10+sizeof(u_long);
+    return aim_genericreq_n(sess, conn, family, subtype);
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  memset(newpacket.data, 0x00, newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+sizeof(u_long))))
+    return -1;
 
-  newpacket.data[0] = (family & 0xff00)>>8;
-  newpacket.data[1] = family & 0xff;
-  newpacket.data[2] = (subtype & 0xff00)>>8;
-  newpacket.data[3] = subtype & 0xff;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
 
   /* copy in data */
   newlong = htonl(*longdata);
-  memcpy(&(newpacket.data[10]), &newlong, sizeof(u_long));
+  memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long));
 
-  aim_tx_enqueue(&newpacket);
-  return (aim_snac_nextid++);
+  aim_tx_enqueue(sess, newpacket);
+  return (sess->snac_nextid++);
 }
 
-u_long aim_genericreq_s(struct aim_conn_t *conn, u_short family, u_short subtype, u_short *shortdata)
+u_long aim_genericreq_s(struct aim_session_t *sess,
+			struct aim_conn_t *conn, 
+			u_short family, u_short subtype, u_short *shortdata)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   u_short newshort;
 
   /* If we don't have data, there's no reason to use this function */
   if (!shortdata)
-    return aim_genericreq_n(conn, family, subtype);
-
-  newpacket.lock = 1;
-
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-  newpacket.type = 0x02;
-
-  newpacket.commandlen = 10+sizeof(u_short);
+    return aim_genericreq_n(sess, conn, family, subtype);
 
-  newpacket.data = (char *) malloc(newpacket.commandlen);
-  memset(newpacket.data, 0x00, newpacket.commandlen);
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+sizeof(u_short))))
+    return -1;
 
-  newpacket.data[0] = (family & 0xff00)>>8;
-  newpacket.data[1] = family & 0xff;
-  newpacket.data[2] = (subtype & 0xff00)>>8;
-  newpacket.data[3] = subtype & 0xff;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
 
   /* copy in data */
   newshort = htons(*shortdata);
-  memcpy(&(newpacket.data[10]), &newshort, sizeof(u_short));
+  memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short));
 
-  aim_tx_enqueue(&newpacket);
-  return (aim_snac_nextid++);
+  aim_tx_enqueue(sess, newpacket);
+  return (sess->snac_nextid++);
 }
 
 /*
@@ -657,9 +641,10 @@
  * Request Location services rights.
  *
  */
-u_long aim_bos_reqlocaterights(struct aim_conn_t *conn)
+u_long aim_bos_reqlocaterights(struct aim_session_t *sess,
+			       struct aim_conn_t *conn)
 {
-  return aim_genericreq_n(conn, 0x0002, 0x0002);
+  return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
 }
 
 /*
@@ -668,7 +653,8 @@
  * Request ICBM parameter information.
  *
  */
-u_long aim_bos_reqicbmparaminfo(struct aim_conn_t *conn)
+u_long aim_bos_reqicbmparaminfo(struct aim_session_t *sess,
+				struct aim_conn_t *conn)
 {
-  return aim_genericreq_n(conn, 0x0004, 0x0004);
+  return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
 }
--- a/libfaim/aim_rxhandlers.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_rxhandlers.c	Sat May 20 00:30:53 2000 +0000
@@ -1,80 +1,274 @@
+/*
+ * aim_rxhandlers.c
+ *
+ * This file contains most all of the incoming packet handlers, along
+ * with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
+ * actually done in aim_rxqueue.c.
+ *
+ */
+
+#include <aim.h>
 
 /*
-  aim_rxhandlers.c
-
-  This file contains most all of the incoming packet handlers, along
-  with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
-  actually done in aim_rxqueue.c.
-  
+ * Bleck functions get called when there's no non-bleck functions
+ * around to cleanup the mess...
  */
+int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
+{
+  u_short family;
+  u_short subtype;
 
+  u_short maxf;
+  u_short maxs;
 
-#include "aim.h" /* for most everything */
+  /* XXX: this is ugly. and big just for debugging. */
+  char *literals[14][25] = {
+    {"Invalid", 
+     NULL
+    },
+    {"General", 
+     "Invalid",
+     "Error",
+     "Client Ready",
+     "Server Ready",
+     "Service Request",
+     "Redirect",
+     "Rate Information Request",
+     "Rate Information",
+     "Rate Information Ack",
+     NULL,
+     "Rate Information Change",
+     "Server Pause",
+     NULL,
+     "Server Resume",
+     "Request Personal User Information",
+     "Personal User Information",
+     "Evil Notification",
+     NULL,
+     "Migration notice",
+     "Message of the Day",
+     "Set Privacy Flags",
+     "Well Known URL",
+     "NOP"
+    },
+    {"Location", 
+      "Invalid",
+      "Error",
+      "Request Rights",
+      "Rights Information", 
+      "Set user information", 
+      "Request User Information", 
+      "User Information", 
+      "Watcher Sub Request",
+      "Watcher Notification"
+    },
+    {"Buddy List Management", 
+      "Invalid", 
+      "Error", 
+      "Request Rights",
+      "Rights Information",
+      "Add Buddy", 
+      "Remove Buddy", 
+      "Watcher List Query", 
+      "Watcher List Response", 
+      "Watcher SubRequest", 
+      "Watcher Notification", 
+      "Reject Notification", 
+      "Oncoming Buddy", 
+      "Offgoing Buddy"
+    },
+    {"Messeging", 
+      "Invalid",
+      "Error", 
+      "Add ICBM Parameter",
+      "Remove ICBM Parameter", 
+      "Request Parameter Information",
+      "Parameter Information",
+      "Outgoing Message", 
+      "Incoming Message",
+      "Evil Request",
+      "Evil Reply", 
+      "Missed Calls",
+      "Message Error", 
+      "Host Ack"
+    },
+    {"Advertisements", 
+      "Invalid", 
+      "Error", 
+      "Request Ad",
+      "Ad Data (GIFs)"
+    },
+    {"Invitation / Client-to-Client", 
+     "Invalid",
+     "Error",
+     "Invite a Friend",
+     "Invitation Ack"
+    },
+    {"Administrative", 
+      "Invalid",
+      "Error",
+      "Information Request",
+      "Information Reply",
+      "Information Change Request",
+      "Information Chat Reply",
+      "Account Confirm Request",
+      "Account Confirm Reply",
+      "Account Delete Request",
+      "Account Delete Reply"
+    },
+    {"Popups", 
+      "Invalid",
+      "Error",
+      "Display Popup"
+    },
+    {"BOS", 
+      "Invalid",
+      "Error",
+      "Request Rights",
+      "Rights Response",
+      "Set group permission mask",
+      "Add permission list entries",
+      "Delete permission list entries",
+      "Add deny list entries",
+      "Delete deny list entries",
+      "Server Error"
+    },
+    {"User Lookup", 
+      "Invalid",
+      "Error",
+      "Search Request",
+      "Search Response"
+    },
+    {"Stats", 
+      "Invalid",
+      "Error",
+      "Set minimum report interval",
+      "Report Events"
+    },
+    {"Translate", 
+      "Invalid",
+      "Error",
+      "Translate Request",
+      "Translate Reply",
+    },
+    {"Chat Navigation", 
+      "Invalid",
+      "Error",
+      "Request rights",
+      "Request Exchange Information",
+      "Request Room Information",
+      "Request Occupant List",
+      "Search for Room",
+      "Outgoing Message", 
+      "Incoming Message",
+      "Evil Request", 
+      "Evil Reply", 
+      "Chat Error",
+    }
+  };
 
-int bleck(struct command_rx_struct *param, ...)
-{
+  maxf = sizeof(literals) / sizeof(literals[0]);
+  maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
+
+  family = aimutil_get16(workingPtr->data+0);
+  subtype= aimutil_get16(workingPtr->data+2);
+
+  if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
+    printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
+  else
+    printf("bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
+
   return 1;
 }
 
-/*
- * The callbacks.  Used to pass data up from the wire into the client.
- *
- * TODO: MASSIVE OVERHAUL.  This method of doing it (array of function 
- *       pointers) is ugly.  Overhaul may mean including chained callbacks
- *       for having client features such as run-time loadable modules.
- *
- */
-rxcallback_t aim_callbacks[] = {
-  bleck, /* incoming IM */
-  bleck, /* oncoming buddy */
-  bleck, /* offgoing buddy */
-  bleck, /* messaging error */
-  bleck, /* server missed call */
-  bleck, /* login phase 4 packet C command 1*/
-  bleck, /* login phase 4 packet C command 2 */
-  bleck, /* login phase 2, first resp */
-  bleck, /* login phase 2, second resp -- **REQUIRED** */
-  bleck, /* login phase 3 packet B */
-  bleck, /* login phase 3D packet A */
-  bleck, /* login phase 3D packet B */
-  bleck, /* login phase 3D packet C */
-  bleck, /* login phase 3D packet D */
-  bleck, /* login phase 3D packet E */
-  bleck, /* redirect -- **REQUIRED** */
-  bleck, /* server rate change */
-  bleck, /* user location error */
-  aim_parse_unknown, /* completely unknown command */
-  bleck, /* User Info Response */
-  bleck, /* User Search by Address response */
-  bleck, /* User Search by Name response */
-  bleck, /* user search fail */
-  bleck, /* auth error */
-  bleck, /* auth success */
-  bleck, /* auth server ready */
-  bleck, /* auth other */
-  bleck, /* info change reply */
-  bleck, /* ChatNAV: server ready */
-  0x00
-};
+int aim_conn_addhandler(struct aim_session_t *sess,
+			struct aim_conn_t *conn,
+			u_short family,
+			u_short type,
+			rxcallback_t newhandler,
+			u_short flags)
+{
+  struct aim_rxcblist_t *new,*cur;
+
+  if (!conn)
+    return -1;
+
+  faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
+
+  new = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
+  new->family = family;
+  new->type = type;
+  new->flags = flags;
+  if (!newhandler)
+    new->handler = &bleck;
+  else
+    new->handler = newhandler;
+  new->next = NULL;
+  
+  cur = conn->handlerlist;
+  if (!cur)
+    conn->handlerlist = new;
+  else 
+    {
+      while (cur->next)
+	cur = cur->next;
+      cur->next = new;
+    }
+
+  return 0;
+}
+
+int aim_clearhandlers(struct aim_conn_t *conn)
+{
+ struct aim_rxcblist_t *cur,*tmp;
+ if (!conn)
+   return -1;
 
+ cur = conn->handlerlist;
+ while(cur)
+   {
+     tmp = cur->next;
+     free(cur);
+     cur = tmp;
+   }
+ return 0;
+}
 
-int aim_register_callbacks(rxcallback_t *newcallbacks)
+rxcallback_t aim_callhandler(struct aim_conn_t *conn,
+		    u_short family,
+		    u_short type)
 {
-  int i = 0;
+  struct aim_rxcblist_t *cur;
+
+  if (!conn)
+    return NULL;
+
+  faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
   
-  for (i = 0; aim_callbacks[i] != 0x00; i++)
+  cur = conn->handlerlist;
+  while(cur)
     {
-      if ( (newcallbacks[i] != NULL) &&
-	   (newcallbacks[i] != 0x00) )
-	{
-#if debug > 3
-	  printf("aim_register_callbacks: changed handler %d\n", i);
-#endif
-	  aim_callbacks[i] = newcallbacks[i];
-	}
+      if ( (cur->family == family) && (cur->type == type) )
+	return cur->handler;
+      cur = cur->next;
     }
-  
-  return 0;
+
+  if (type==0xffff)
+    return NULL;
+  return aim_callhandler(conn, family, 0xffff);
+}
+
+int aim_callhandler_noparam(struct aim_session_t *sess,
+			    struct aim_conn_t *conn,
+			    u_short family,
+			    u_short type,
+			    struct command_rx_struct *ptr)
+{
+  rxcallback_t userfunc = NULL;
+  userfunc = aim_callhandler(conn, family, type);
+  if (userfunc)
+    return userfunc(sess, ptr);
+  return 1; /* XXX */
 }
 
 /*
@@ -100,349 +294,380 @@
   TODO: Allow for NULL handlers.
   
  */
-int aim_rxdispatch(void)
+int aim_rxdispatch(struct aim_session_t *sess)
 {
   int i = 0;
   struct command_rx_struct *workingPtr = NULL;
   
-  if (aim_queue_incoming == NULL)
-    /* this shouldn't really happen, unless the main loop's select is broke  */
-    printf("parse_generic: incoming packet queue empty.\n");
-  else
-    {
-      workingPtr = aim_queue_incoming;
-      for (i = 0; workingPtr != NULL; i++)
-	{
-	  switch(workingPtr->conn->type)
-	    {
-	    case AIM_CONN_TYPE_AUTH:
-	      if ( (workingPtr->data[0] == 0x00) && 
-		   (workingPtr->data[1] == 0x00) &&
-		   (workingPtr->data[2] == 0x00) &&
-		   (workingPtr->data[3] == 0x01) )
-		{
-#if debug > 0
-		  fprintf(stderr, "got connection ack on auth line\n");
+  if (sess->queue_incoming == NULL) {
+    faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
+    return 0;
+  } else {
+    workingPtr = sess->queue_incoming;
+    for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
+      /*
+       * XXX: This is still fairly ugly.
+       */
+      if (workingPtr->handled)
+	continue;
+
+      switch(workingPtr->conn->type) {
+      case -1:
+	/*
+	 * This can happen if we have a queued command
+	 * that was recieved after a connection has 
+	 * been terminated.  In which case, the handler
+	 * list has been cleared, and there's nothing we
+	 * can do for it.  We can only cancel it.
+	 */
+	workingPtr->handled = 1;
+	break;
+      case AIM_CONN_TYPE_AUTH: {
+	u_long head;
+	
+	head = aimutil_get32(workingPtr->data);
+	if (head == 0x00000001) {
+	  faimdprintf(1, "got connection ack on auth line\n");
+	  workingPtr->handled = 1;
+	} else {
+	  u_short family,subtype;
+	  
+	  family = aimutil_get16(workingPtr->data);
+	  subtype = aimutil_get16(workingPtr->data+2);
+	  
+	  switch (family) {
+	    /* New login protocol */
+#ifdef SNACLOGIN
+	  case 0x0017:
+	    if (subtype == 0x0001)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
+	    else if (subtype == 0x0003)
+	      workingPtr->handled = aim_authparse(sess, workingPtr);
+	    else if (subtype == 0x0007)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0007, workingPtr);
+	    else
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
+	    break;
+#else	
+	    /* XXX: this isnt foolproof */
+	  case 0x0001:
+	    if (subtype == 0x0003)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, workingPtr);
+	    else
+	      workingPtr->handled = aim_authparse(sess, workingPtr);
+	    break;
+	  case 0x0007:
+	    if (subtype == 0x0005)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
+	    break;
+	  default:
+	    /* Old login protocol */
+	    /* any user callbacks will be called from here */
+	    workingPtr->handled = aim_authparse(sess, workingPtr);
 #endif
-		  workingPtr->handled = 1;
-		}
-	      else
-		{
-		  /* any user callbacks will be called from here */
-		  workingPtr->handled = aim_authparse(workingPtr);
-		}
-	      break;
-	    case AIM_CONN_TYPE_BOS:
-	      {
-		u_short family;
-		u_short subtype;
-		family = (workingPtr->data[0] << 8) + workingPtr->data[1];
-		subtype = (workingPtr->data[2] << 8) + workingPtr->data[3];
-		switch (family)
-		  {
-		  case 0x0000: /* not really a family, but it works */
-		    if (subtype == 0x0001)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P2_1])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  case 0x0001: /* Family: General */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = aim_parse_generalerrs(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P2_2])(workingPtr);
-			break;
-		      case 0x0005:
-			workingPtr->handled = aim_handleredirect_middle(workingPtr);
-			break;
-		      case 0x0007:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3_B])(workingPtr);
-			break;
-		      case 0x000a:
-			workingPtr->handled = (aim_callbacks[AIM_CB_RATECHANGE])(workingPtr);
-			break;
-		      case 0x000f:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_A])(workingPtr);
-			break;
-		      case 0x0013:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P4_C2])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0002: /* Family: Location */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_MISSED_IM])(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_D])(workingPtr);
-			break;
-		      case 0x0006:
-			workingPtr->handled = aim_parse_userinfo_middle(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0003: /* Family: Buddy List */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = aim_parse_generalerrs(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_C])(workingPtr);
-			break;
-		      case 0x000b:
-			workingPtr->handled = (aim_callbacks[AIM_CB_ONCOMING_BUDDY])(workingPtr);
-			break;
-		      case 0x000c:
-			workingPtr->handled = (aim_callbacks[AIM_CB_OFFGOING_BUDDY])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0004: /* Family: Messeging */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_USERERROR])(workingPtr);
-			break;
-		      case 0x0005:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_E])(workingPtr);
-			break;
-		      case 0x0007:
-			workingPtr->handled = aim_parse_incoming_im_middle(workingPtr);
-			break;
-		      case 0x000a:
-			workingPtr->handled = (aim_callbacks[AIM_CB_MISSED_CALL])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0009:
-		    if (subtype == 0x0001)
-		      workingPtr->handled = aim_parse_generalerrs(workingPtr);
-		    else if (subtype == 0x0003)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_B])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  case 0x000a:  /* Family: User lookup */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_SEARCH_FAIL])(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_SEARCH_ADDRESS])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x000b:
-		    if (subtype == 0x0001)
-		      workingPtr->handled = aim_parse_generalerrs(workingPtr);
-		    else if (subtype == 0x0002)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P4_C1])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  default:
-		    workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  }
-	      }
-	      break;
-	    case AIM_CONN_TYPE_CHATNAV:
-	      if ( (workingPtr->data[0] == 0x00) &&
-		   (workingPtr->data[1] == 0x02) &&
-		   (workingPtr->data[2] == 0x00) &&
-		   (workingPtr->data[3] == 0x06) )
-		{
-		  workingPtr->handled = 1;
-		  aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
-		}
-	      else
-		workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    case AIM_CONN_TYPE_CHAT:
-	      fprintf(stderr, "\nAHH! Dont know what to do with CHAT stuff yet!\n");
-	      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    default:
-	      fprintf(stderr, "\nAHHHHH! UNKNOWN CONNECTION TYPE!\n\n");
-	      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    }
-	      /* move to next command */
-	  workingPtr = workingPtr->next;
+	  }
 	}
+	break;
+      }
+      case AIM_CONN_TYPE_BOS: {
+	u_short family;
+	u_short subtype;
+	
+	family = aimutil_get16(workingPtr->data);
+	subtype = aimutil_get16(workingPtr->data+2);
+	
+	switch (family) {
+	case 0x0000: /* not really a family, but it works */
+	  if (subtype == 0x0001)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
+	  else
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	  break;
+	case 0x0001: /* Family: General */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
+	    break;
+	  case 0x0005:
+	    workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
+	    break;
+	  case 0x0007:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
+	    break;
+	  case 0x000a:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000a, workingPtr);
+	    break;
+	  case 0x000f:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
+	    break;
+	  case 0x0013:
+	    workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
+	    break;
+	  }
+	case 0x0002: /* Family: Location */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0001, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
+	    break;
+	  case 0x0006:
+	    workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
+	    break;
+	  }
+	case 0x0003: /* Family: Buddy List */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0003, 0x0003, workingPtr);
+	    break;
+	  case 0x000b: /* oncoming buddy */
+	    workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
+	    break;
+	  case 0x000c: /* offgoing buddy */
+	    workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
+	  }
+	  break;
+	case 0x0004: /* Family: Messeging */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
+	    break;
+	  case 0x0005:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
+	    break;
+	  case 0x0007:
+	    workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
+	    break;
+	  case 0x000a:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x000a, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
+	  }
+	  break;
+	case 0x0009:
+	  if (subtype == 0x0001)
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	  else if (subtype == 0x0003)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0009, 0x0003, workingPtr);
+	  else
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
+	  break;
+	case 0x000a:  /* Family: User lookup */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
+	  }
+	  break;
+	case 0x000b:
+	  if (subtype == 0x0001)
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	  else if (subtype == 0x0002)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
+	  else
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
+	  break;
+	default:
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	  break;
+	}
+	break;
+      }
+      case AIM_CONN_TYPE_CHATNAV: {
+	u_short family;
+	u_short subtype;
+	family = aimutil_get16(workingPtr->data);
+	subtype= aimutil_get16(workingPtr->data+2);
+	
+	if ((family == 0x0002) && (subtype == 0x0006)) {
+	  workingPtr->handled = 1;
+	  aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
+	} else if ((family == 0x000d) && (subtype == 0x0009)) {
+	  workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
+	} else {
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+	}
+	break;
+      }
+      case AIM_CONN_TYPE_CHAT: {
+	u_short family, subtype;
+	
+	family = aimutil_get16(workingPtr->data);
+	subtype= aimutil_get16(workingPtr->data+2);
+	
+	if ((family == 0x0000) && (subtype == 0x00001))
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
+	else if (family == 0x0001) {
+	  if (subtype == 0x0001)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
+	  else if (subtype == 0x0003)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
+	  else if (subtype == 0x0007)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
+	  else
+	    printf("Chat: unknown snac %04x/%04x\n", family, subtype);
+	} else if (family == 0x000e) {
+	  if (subtype == 0x0002)
+	    workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
+	  else if (subtype == 0x0003)
+	    workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);	
+	  else if (subtype == 0x0004)
+	    workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);	
+	  else if (subtype == 0x0006)
+	    workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
+	  else	
+	    printf("Chat: unknown snac %04x/%04x\n", family, subtype); 
+	} else {
+	  printf("Chat: unknown snac %04x/%04x\n", family, subtype);
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
+	}
+	break;
+      }
+      default:
+	printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, channel = %02x, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->type, workingPtr->commandlen);
+	workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	break;
+      }	
     }
+  }
 
-  aim_queue_incoming = aim_purge_rxqueue(aim_queue_incoming);
+  /* 
+   * This doesn't have to be called here.  It could easily be done
+   * by a seperate thread or something. It's an administrative operation,
+   * and can take a while. Though the less you call it the less memory
+   * you'll have :)
+   */
+  aim_purge_rxqueue(sess);
   
   return 0;
 }
 
-/*
- * TODO: check and cure memory leakage in this function.
- */
-int aim_authparse(struct command_rx_struct *command)
+int aim_parsemotd_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command, ...)
 {
-  int iserror = 0;
-  struct aim_tlv_t *tlv = NULL;
-  char *errorurl = NULL;
-  short errorcode;
-  int z = 0;
+  rxcallback_t userfunc = NULL;
+  char *msg;
+  int ret=1;
+  struct aim_tlvlist_t *tlvlist;
+  u_short id;
+
+  /*
+   * Dunno.
+   */
+  id = aimutil_get16(command->data+10);
+
+  /* 
+   * TLVs follow 
+   */
+  tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12);
+  
+  msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+  
+  userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
+  if (userfunc)
+    ret =  userfunc(sess, command, id, msg);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;
+  
+}
 
-  if ( (command->data[0] == 0x00) &&
-       (command->data[1] == 0x01) &&
-       (command->data[2] == 0x00) &&
-       (command->data[3] == 0x03) )
+int aim_handleredirect_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command, ...)
+{
+  struct aim_tlv_t *tmptlv = NULL;
+  int serviceid = 0x00;
+  char cookie[AIM_COOKIELEN];
+  char *ip = NULL;
+  rxcallback_t userfunc = NULL;
+  struct aim_tlvlist_t *tlvlist;
+  int ret = 1;
+  
+  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
     {
-      /* "server ready"  -- can be ignored */
-      return (aim_callbacks[AIM_CB_AUTH_SVRREADY])(command);
+      printf("libfaim: major bug: unable to read tlvchain from redirect\n");
+      return ret;
+    }
+  
+  if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1))) 
+    {
+      printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
+      aim_freetlvchain(&tlvlist);
+      return ret;
     }
-  else if ( (command->data[0] == 0x00) &&
-	    (command->data[1] == 0x07) &&
-	    (command->data[2] == 0x00) &&
-	    (command->data[3] == 0x05) )
+  serviceid = aimutil_get16(tmptlv->value);
+
+  if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1))) 
+    {
+      printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
+      aim_freetlvchain(&tlvlist);
+      return ret;
+    }
+  
+  if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
     {
-      /* "information change reply" */
-      return (aim_callbacks[AIM_CB_AUTH_INFOCHNG_REPLY])(command);
+      printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
+      aim_freetlvchain(&tlvlist);
+      return ret;
+    }
+  memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
+
+  if (serviceid == AIM_CONN_TYPE_CHAT)
+    {
+      /*
+       * Chat hack.
+       *
+       */
+      userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
+      if (userfunc)
+	ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
+      free(sess->pendingjoin);
+      sess->pendingjoin = NULL;
     }
   else
     {
-      /* anything else -- usually used for login; just parse as pure TLVs */
-
+      userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
+      if (userfunc)
+	ret =  userfunc(sess, command, serviceid, ip, cookie);
+    }
 
-      /* all this block does is figure out if it's an
-	 error or a success, nothing more */
-      while (z < command->commandlen)
-	{
-	  tlv = aim_grabtlvstr(&(command->data[z]));
-	  switch(tlv->type) 
-	    {
-	    case 0x0001: /* screen name */
-	      aim_logininfo.screen_name = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0004: /* error URL */
-	      errorurl = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0005: /* BOS IP */
-	      aim_logininfo.BOSIP = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0006: /* auth cookie */
-	      aim_logininfo.cookie = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv=NULL;
-	      break;
-	    case 0x0011: /* email addy */
-	      aim_logininfo.email = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0013: /* registration status */
-	      aim_logininfo.regstatus = *(tlv->value);
-	      z += 2 + 2 + tlv->length;
-	      aim_freetlv(&tlv);
-	      break;
-	    case 0x0008: /* error code */
-	      errorcode = *(tlv->value);
-	      z += 2 + 2 + tlv->length;
-	      aim_freetlv(&tlv);
-	      iserror = 1;
-	      break;
-	    default:
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	  /* dunno */
-	    }
-	}
+  /*
+   * XXX: Is there a leak here?  Where does IP get freed?
+   */
+  aim_freetlvchain(&tlvlist);
 
-      if (iserror && 
-	  errorurl && 
-	  errorcode)
-	return (aim_callbacks[AIM_CB_AUTH_ERROR])(command, &aim_logininfo, errorurl, errorcode);
-      else if (aim_logininfo.screen_name && 
-	       aim_logininfo.cookie && aim_logininfo.BOSIP)
-	return (aim_callbacks[AIM_CB_AUTH_SUCCESS])(command, &aim_logininfo);
-      else
-	return (aim_callbacks[AIM_CB_AUTH_OTHER])(command);
-    }
+  return ret;
 }
 
-/*
- * TODO: check for and cure any memory leaks here.
- */
-int aim_handleredirect_middle(struct command_rx_struct *command, ...)
+int aim_parse_unknown(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...)
 {
-  struct aim_tlv_t *tlv = NULL;
-  int z = 10;
-  int serviceid;
-  char *cookie;
-  char *ip;
+  u_int i = 0;
 
-  while (z < command->commandlen)
-    {
-      tlv = aim_grabtlvstr(&(command->data[z]));
-      switch(tlv->type)
-	{
-	case 0x000d:  /* service id */
-	  aim_freetlv(&tlv);
-	  /* regrab as an int */
-	  tlv = aim_grabtlv(&(command->data[z]));
-	  serviceid = (tlv->value[0] << 8) + tlv->value[1]; /* hehe */
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	  break;
-	case 0x0005:  /* service server IP */
-	  ip = tlv->value;
-	  z += 2 + 2 + tlv->length;
-	  free(tlv);
-	  tlv = NULL;
-	  break;
-	case 0x0006: /* auth cookie */
-	  cookie = tlv->value;
-	  z += 2 + 2 + tlv->length;
-	  free(tlv);
-	  tlv = NULL;
-	  break;
-	default:
-	  /* dunno */
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	}
-    }
-  return (aim_callbacks[AIM_CB_LOGIN_P3D_F])(command, serviceid, ip, cookie);
-}
-
-int aim_parse_unknown(struct command_rx_struct *command, ...)
-{
-  int i = 0;
-
-  printf("\nRecieved unknown packet:");
+  faimdprintf(1, "\nRecieved unknown packet:");
 
   for (i = 0; i < command->commandlen; i++)
     {
@@ -464,18 +689,20 @@
  * Middle handler for 0x0001 snac of each family.
  *
  */
-int aim_parse_generalerrs(struct command_rx_struct *command, ...)
+int aim_parse_generalerrs(struct aim_session_t *sess,
+			  struct command_rx_struct *command, ...)
 {
   u_short family;
   u_short subtype;
-  family = (command->data[0] << 8) + command->data[1];
-  subtype = (command->data[2] << 8) + command->data[3];
+  
+  family = aimutil_get16(command->data+0);
+  subtype= aimutil_get16(command->data+2);
   
   switch(family)
     {
     default:
       /* Unknown family */
-      return (aim_callbacks[AIM_CB_UNKNOWN])(command);
+      return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);
     }
 
   return 1;
--- a/libfaim/aim_rxqueue.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_rxqueue.c	Sat May 20 00:30:53 2000 +0000
@@ -1,247 +1,165 @@
 /*
-  aim_rxqueue.c
-
-  This file contains the management routines for the receive
-  (incoming packet) queue.  The actual packet handlers are in
-  aim_rxhandlers.c.
-
+ *  aim_rxqueue.c
+ *
+ * This file contains the management routines for the receive
+ * (incoming packet) queue.  The actual packet handlers are in
+ * aim_rxhandlers.c.
  */
 
-#include "aim.h"
-
-/*
-  This is a modified read() to make SURE we get the number
-  of bytes we are told to, otherwise block.
- */
-int Read(int fd, u_char *buf, int len)
-{
-  int i = 0;
-  int j = 0;
-
-  while ((i < len) && (!(i < 0)))
-    {
-      j = read(fd, &(buf[i]), len-i);
-      if ( (j < 0) && (errno != EAGAIN))
-	return -errno; /* fail */
-      else
-	i += j; /* success, continue */
-    }
-#if 0
-  printf("\nRead Block: (%d/%04x)\n", len, len);
-  printf("\t");
-  for (j = 0; j < len; j++)
-    {
-      if (j % 8 == 0)
-	printf("\n\t");
-      if (buf[j] >= ' ' && buf[j] < 127)
-	 printf("%c=%02x ",buf[j], buf[j]);
-      else
-	 printf("0x%02x ", buf[j]);
-    }
-  printf("\n\n");
-#endif
-  return i;
-}
+#include <aim.h> 
 
 /*
-  struct command_struct *
-                         get_generic(
-                                     struct connection_info struct *,
-				     struct command_struct * 
-				     )
-  
-  Grab as many command sequences as we can off the socket, and enqueue
-  each command in the incoming event queue in a seperate struct.
-
-*/
-int aim_get_command(void)
+ * Grab a single command sequence off the socket, and enqueue
+ * it in the incoming event queue in a seperate struct.
+ */
+int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn)
 {
-  int i, readgood, j, isav, err;
-  int s;
-  fd_set fds;
-  struct timeval tv;
-  char generic[6]; 
-  struct command_rx_struct *workingStruct = NULL;
-  struct command_rx_struct *workingPtr = NULL;
-  struct aim_conn_t *conn = NULL;
-#if debug > 0
-  printf("Reading generic/unknown response...");
-#endif
-  
-  
-  /* dont wait at all (ie, never call this unless something is there) */
-  tv.tv_sec = 0; 
-  tv.tv_usec = 0;
-  conn = aim_select(&tv);
+  u_char generic[6]; 
+  struct command_rx_struct *newrx = NULL;
 
-  if (conn==NULL)
-    return 0;  /* nothing waiting */
-
-  s = conn->fd;
+  if (!sess || !conn)
+    return 0;
 
-  FD_ZERO(&fds);
-  FD_SET(s, &fds);
-  tv.tv_sec = 0;  /* wait, but only for 10us */
-  tv.tv_usec = 10;
-  
-  generic[0] = 0x00;  
+  if (conn->fd < 3)  /* can happen when people abuse the interface */
+    return 0;
 
-  readgood = 0;
-  i = 0;
-  j = 0;
-  /* read first 6 bytes (the FLAP header only) off the socket */
-  while ( (select(s+1, &fds, NULL, NULL, &tv) == 1) && (i < 6))
-    {
-      if ((err = Read(s, &(generic[i]), 1)) < 0)
-	{
-	  /* error is probably not recoverable...(must be a pessimistic day) */
-	  aim_conn_close(conn);
-	  return err;
-   	}
+  /*
+   * Read FLAP header.  Six bytes:
+   *    
+   *   0 char  -- Always 0x2a
+   *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
+   *   2 short -- Sequence number 
+   *   4 short -- Number of data bytes that follow.
+   */
+  if (read(conn->fd, generic, 6) < 6){
+    aim_conn_close(conn);
+    return -1;
+  }
 
-      if (readgood == 0)
-	{
-	  if (generic[i] == 0x2a)
-	  {
-	    readgood = 1;
-#if debug > 1
-	    printf("%x ", generic[i]);
-	    fflush(stdout);
-#endif
-	    i++;
-	  }
-	  else
-	    {
-#if debug > 1
-	      printf("skipping 0x%d ", generic[i]);
-	      fflush(stdout);
-#endif
-	      j++;
-	    }
-	}
-      else
-	{
-#if debug > 1
-	  printf("%x ", generic[i]);
-#endif
-	  i++;
-	}
-      FD_ZERO(&fds);
-      FD_SET(s, &fds);
-      tv.tv_sec= 2;
-      tv.tv_usec= 2;
-    }
-
-  if (generic[0] != 0x2a)
-    {
-      /* this really shouldn't happen, since the main loop
-	 select() should protect us from entering this function
-	 without data waiting  */
-      printf("Bad incoming data!");
-      return -1;
-    }
-
-  isav = i;
+  /*
+   * This shouldn't happen unless the socket breaks, the server breaks,
+   * or we break.  We must handle it just in case.
+   */
+  if (generic[0] != 0x2a) {
+    faimdprintf(1, "Bad incoming data!");
+    return -1;
+  }	
 
   /* allocate a new struct */
-  workingStruct = (struct command_rx_struct *) malloc(sizeof(struct command_rx_struct));
-  workingStruct->lock = 1;  /* lock the struct */
+  newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct));
+  if (!newrx)
+    return -1;
+  memset(newrx, 0x00, sizeof(struct command_rx_struct));
 
-  /* store type -- byte 2 */
-  workingStruct->type = (char) generic[1];
+  newrx->lock = 1;  /* lock the struct */
+
+  /* store channel -- byte 2 */
+  newrx->type = (char) generic[1];
 
   /* store seqnum -- bytes 3 and 4 */
-  workingStruct->seqnum = ( (( (unsigned int) generic[2]) & 0xFF) << 8);
-  workingStruct->seqnum += ( (unsigned int) generic[3]) & 0xFF;
+  newrx->seqnum = aimutil_get16(generic+2);
 
   /* store commandlen -- bytes 5 and 6 */
-  workingStruct->commandlen = ( (( (unsigned int) generic[4]) & 0xFF ) << 8);
-  workingStruct->commandlen += ( (unsigned int) generic[5]) & 0xFF;
-  
-  printf("%d\n", workingStruct->commandlen);
+  newrx->commandlen = aimutil_get16(generic+4);
+
+  newrx->nofree = 0; /* free by default */
 
   /* malloc for data portion */
-  workingStruct->data = (char *) malloc(workingStruct->commandlen);
+  newrx->data = (u_char *) malloc(newrx->commandlen);
+  if (!newrx->data) {
+    free(newrx);
+    return -1;
+  }
 
   /* read the data portion of the packet */
-  i = Read(s, workingStruct->data, workingStruct->commandlen);
-  if (i < 0)
-    {
-      aim_conn_close(conn);
-      return i;
-    }
+  if (read(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){
+    free(newrx->data);
+    free(newrx);
+    aim_conn_close(conn);
+    return -1;
+  }
 
-#if debug > 0
-  printf(" done. (%db+%db read, %db skipped)\n", isav, i, j);
-#endif
+  newrx->conn = conn;
 
-  workingStruct->conn = conn;
-
-  workingStruct->next = NULL;  /* this will always be at the bottom */
-  workingStruct->lock = 0; /* unlock */
+  newrx->next = NULL;  /* this will always be at the bottom */
+  newrx->lock = 0; /* unlock */
 
   /* enqueue this packet */
-  if (aim_queue_incoming == NULL)
-    aim_queue_incoming = workingStruct;
-  else
-    {
-      workingPtr = aim_queue_incoming;
-      while (workingPtr->next != NULL)
-	workingPtr = workingPtr->next;
-      workingPtr->next = workingStruct;
-    }
+  if (sess->queue_incoming == NULL) {
+    sess->queue_incoming = newrx;
+  } else {
+    struct command_rx_struct *cur;
+
+    /*
+     * This append operation takes a while.  It might be faster
+     * if we maintain a pointer to the last entry in the queue
+     * and just update that.  Need to determine if the overhead
+     * to maintain that is lower than the overhead for this loop.
+     */
+    for (cur = sess->queue_incoming; cur->next; cur = cur->next)
+      ;
+    cur->next = newrx;
+  }
+  
+  newrx->conn->lastactivity = time(NULL);
 
   return 0;  
 }
 
 /*
-  purge_rxqueue()
-
-  This is just what it sounds.  It purges the receive (rx) queue of
-  all handled commands.  This is normally called from inside 
-  aim_rxdispatch() after it's processed all the commands in the queue.
-  
+ * Purge recieve queue of all handled commands (->handled==1).  Also
+ * allows for selective freeing using ->nofree so that the client can
+ * keep the data for various purposes.  
+ *
+ * If ->nofree is nonzero, the frame will be delinked from the global list, 
+ * but will not be free'ed.  The client _must_ keep a pointer to the
+ * data -- libfaim will not!  If the client marks ->nofree but
+ * does not keep a pointer, it's lost forever.
+ *
  */
-struct command_rx_struct *aim_purge_rxqueue(struct command_rx_struct *queue)
+void aim_purge_rxqueue(struct aim_session_t *sess)
 {
-  int i = 0;
-  struct command_rx_struct *workingPtr = NULL;
-  struct command_rx_struct *workingPtr2 = NULL;
+  struct command_rx_struct *cur = NULL;
+  struct command_rx_struct *tmp;
 
-  workingPtr = queue;
-  if (queue == NULL)
-    {
-      return queue;
+  if (sess->queue_incoming == NULL)
+    return;
+  
+  if (sess->queue_incoming->next == NULL) {
+    if (sess->queue_incoming->handled) {
+      tmp = sess->queue_incoming;
+      sess->queue_incoming = NULL;
+
+      if (!tmp->nofree) {
+	free(tmp->data);
+	free(tmp);
+      } else
+	tmp->next = NULL;
     }
-  else if (queue->next == NULL)
-    {
-      if (queue->handled == 1)
-	{
-	  workingPtr2 = queue;
-	  queue = NULL;
-	  free(workingPtr2->data);
-	  free(workingPtr2);
-	}
-      return queue;
-    }
-  else
-    {
-      for (i = 0; workingPtr != NULL; i++)
-	{
-	  if (workingPtr->next->handled == 1)
-	    {
-	      /* save struct */
-	      workingPtr2 = workingPtr->next;
-	      /* dequeue */
-	      workingPtr->next = workingPtr2->next;
-	      /* free */
-	      free(workingPtr2->data);
-	      free(workingPtr2);
-	    }
+    return;
+  }
 
-	  workingPtr = workingPtr->next;  
-	}
-    }
+  for(cur = sess->queue_incoming; cur->next != NULL; ) {
+    if (cur->next->handled) {
+      tmp = cur->next;
+      cur->next = tmp->next;
+      if (!tmp->nofree) {
+	free(tmp->data);
+	free(tmp);
+      } else
+	tmp->next = NULL;
+    }	
+    cur = cur->next;
 
-  return queue;
+    /* 
+     * Be careful here.  Because of the way we just
+     * manipulated the pointer, cur may be NULL and 
+     * the for() will segfault doing the check unless
+     * we find this case first.
+     */
+    if (cur == NULL)	
+      break;
+  }
+
+  return;
 }
--- a/libfaim/aim_search.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_search.c	Sat May 20 00:30:53 2000 +0000
@@ -8,46 +8,30 @@
 
 #include <aim.h>
 
-u_long aim_usersearch_address(struct aim_conn_t *conn, char *address)
+u_long aim_usersearch_address(struct aim_session_t *sess,
+			      struct aim_conn_t *conn, 
+			      char *address)
 {
-  struct command_tx_struct newpacket;
+  struct command_tx_struct *newpacket;
   
   if (!address)
     return -1;
 
-  newpacket.lock = 1;
+  if (!(newpacket = aim_tx_new(0x0002, conn, 10+strlen(address))))
+    return -1;
 
-  if (conn)
-    newpacket.conn = conn;
-  else
-    newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-
-  newpacket.type = 0x0002;
-  
-  newpacket.commandlen = 10 + strlen(address);
-  newpacket.data = (char *) malloc(newpacket.commandlen);
+  newpacket->lock = 1;
 
-  newpacket.data[0] = 0x00;
-  newpacket.data[1] = 0x0a;
-  newpacket.data[2] = 0x00;
-  newpacket.data[3] = 0x02;
-  newpacket.data[4] = 0x00;
-  newpacket.data[5] = 0x00;
+  aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid);
 
-  /* SNAC reqid */
-  newpacket.data[6] = (aim_snac_nextid >> 24) & 0xFF;
-  newpacket.data[7] = (aim_snac_nextid >> 16) & 0xFF;
-  newpacket.data[8] = (aim_snac_nextid >>  8) & 0xFF;
-  newpacket.data[9] = (aim_snac_nextid) & 0xFF;
+  aimutil_putstr(newpacket->data+10, address, strlen(address));
 
-  memcpy(&(newpacket.data[10]), address, strlen(address));
-
-  aim_tx_enqueue(&newpacket);
+  aim_tx_enqueue(sess, newpacket);
 
   {
     struct aim_snac_t snac;
     
-    snac.id = aim_snac_nextid;
+    snac.id = sess->snac_nextid;
     snac.family = 0x000a;
     snac.type = 0x0002;
     snac.flags = 0x0000;
@@ -55,9 +39,9 @@
     snac.data = malloc(strlen(address)+1);
     memcpy(snac.data, address, strlen(address)+1);
 
-    aim_newsnac(&snac);
+    aim_newsnac(sess, &snac);
   }
 
-  return (aim_snac_nextid++);
+  return (sess->snac_nextid++);
 }
 
--- a/libfaim/aim_snac.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_snac.c	Sat May 20 00:30:53 2000 +0000
@@ -13,51 +13,60 @@
  */
 
 #include <aim.h>
-#include <assert.h>
 
-struct aim_snac_t	*aim_outstanding_snacs = NULL;
-u_long	aim_snac_nextid = 0x00000001;
-
-u_long	aim_newsnac(struct aim_snac_t *newsnac) {
-	struct aim_snac_t	*snac = NULL, *cur = aim_outstanding_snacs;
+u_long aim_newsnac(struct aim_session_t *sess,
+		   struct aim_snac_t *newsnac) 
+{
+  struct aim_snac_t *snac = NULL, *cur = NULL;
   
-	assert(newsnac != NULL);
-	snac = calloc(1, sizeof(struct aim_snac_t));
-	assert(snac != NULL);
-	memcpy(snac, newsnac, sizeof(struct aim_snac_t));
-	snac->issuetime = time(&snac->issuetime);
-	snac->next = NULL;
+  if (!newsnac)
+    return 0;
+
+  cur = sess->outstanding_snacs;
 
-	if (cur == NULL) {
-		aim_outstanding_snacs = snac;
-		return(snac->id);
-	}
-	while (cur->next != NULL)
-		cur = cur->next;
-	cur->next = snac;
-	return(snac->id);
+  snac = calloc(1, sizeof(struct aim_snac_t));
+  if (!snac)
+    return 0;
+  memcpy(snac, newsnac, sizeof(struct aim_snac_t));
+  snac->issuetime = time(&snac->issuetime);
+  snac->next = NULL;
+  
+  if (cur == NULL) {
+    sess->outstanding_snacs = snac;
+    return(snac->id);
+  }
+  while (cur->next != NULL)
+    cur = cur->next;
+  cur->next = snac;
+
+  return(snac->id);
 }
 
-struct aim_snac_t	*aim_remsnac(u_long id) {
-	struct aim_snac_t	*cur = aim_outstanding_snacs;
+struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, 
+			       u_long id) 
+{
+  struct aim_snac_t *cur;
+
+  cur = sess->outstanding_snacs;
+
+  if (cur == NULL)
+    return(NULL);
 
-	if (cur == NULL)
-		return(NULL);
-	if (cur->id == id) {
-		aim_outstanding_snacs = cur->next;
-		return(cur);
-	}
-	while (cur->next != NULL) {
-		if (cur->next->id == id) {
-			struct aim_snac_t	*tmp = NULL;
-
-			tmp = cur->next;
-			cur->next = cur->next->next;
-			return(tmp);
-		}
-		cur = cur->next;
-	}
-	return(NULL);
+  if (cur->id == id) {
+    sess->outstanding_snacs = cur->next;
+    return(cur);
+  }
+  while (cur->next != NULL) {
+    if (cur->next->id == id) {
+      struct aim_snac_t	*tmp = NULL;
+      
+      tmp = cur->next;
+      cur->next = cur->next->next;
+      return(tmp);
+    }
+    cur = cur->next;
+  }
+  return(NULL);
 }
 
 /*
@@ -68,22 +77,25 @@
  * why its called _max_age).
  *
  */
-int aim_cleansnacs(int maxage)
+int aim_cleansnacs(struct aim_session_t *sess,
+		   int maxage)
 {
-  struct aim_snac_t *cur = aim_outstanding_snacs;
+  struct aim_snac_t *cur;
   struct aim_snac_t *remed = NULL;
   time_t curtime;
+ 
+  cur = sess->outstanding_snacs;
   
   curtime = time(&curtime);
-
+ 
   while (cur)
     {
       if ( (cur) && (((cur->issuetime) + maxage) < curtime))
 	{
 #if DEBUG > 1
-	  printf("aimsnac: WARNING purged obsolete snac %ul\n", cur->id);
+	  printf("aimsnac: WARNING purged obsolete snac %08lx\n", cur->id);
 #endif
-	  remed = aim_remsnac(cur->id);
+	  remed = aim_remsnac(sess, cur->id);
 	  if (remed)
 	    {
 	      if (remed->data)
@@ -93,16 +105,16 @@
 	}
       cur = cur->next;
     }
-
+  
   return 0;
 }
 
 int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
 {
   int curbyte = 0;
-  curbyte += aimutil_put16(buf+curbyte,family&0xffff);
-  curbyte += aimutil_put16(buf+curbyte,subtype&0xffff);
-  curbyte += aimutil_put16(buf+curbyte,flags&0xffff);
-  curbyte += aimutil_put32(buf+curbyte,snacid);
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff));
+  curbyte += aimutil_put32(buf+curbyte, snacid);
   return curbyte;
 }
--- a/libfaim/aim_tlv.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_tlv.c	Sat May 20 00:30:53 2000 +0000
@@ -1,5 +1,246 @@
 #include <aim.h>
 
+struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen)
+{
+  int pos;
+  struct aim_tlvlist_t *list;
+  struct aim_tlvlist_t *cur;
+  
+  u_short type;
+  u_short length;
+
+  if (!buf)
+    return NULL;
+
+  list = NULL;
+  
+  pos = 0;
+
+  while (pos < maxlen)
+    {
+      type = aimutil_get16(buf+pos);
+      pos += 2;
+
+      if (pos < maxlen)
+	{
+	  length = aimutil_get16(buf+pos);
+	  pos += 2;
+	  
+	  if ((pos+length) <= maxlen)
+	    {
+	      cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+	      memset(cur, 0x00, sizeof(struct aim_tlvlist_t));
+
+	      cur->tlv = aim_createtlv();	
+	      cur->tlv->type = type;
+	      cur->tlv->length = length;
+	      cur->tlv->value = (u_char *)malloc(length*sizeof(u_char));
+	      memcpy(cur->tlv->value, buf+pos, length);
+	      
+	      cur->next = list;
+	      list = cur;
+	      
+	      pos += length;
+	    }
+	}
+    }
+
+  return list;
+}
+
+void aim_freetlvchain(struct aim_tlvlist_t **list)
+{
+  struct aim_tlvlist_t *cur, *cur2;
+
+  if (!list || !(*list))
+    return;
+
+  cur = *list;
+  while (cur)
+    {
+      aim_freetlv(&cur->tlv);
+      cur2 = cur->next;
+      free(cur);
+      cur = cur2;
+    }
+  list = NULL;
+  return;
+}
+
+int aim_counttlvchain(struct aim_tlvlist_t **list)
+{
+  struct aim_tlvlist_t *cur;
+  int count = 0;
+
+  if (!list || !(*list))
+    return 0;
+
+  for (cur = *list; cur; cur = cur->next)
+    count++;
+ 
+  return count;
+}
+
+int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len)
+{
+  struct aim_tlvlist_t *new;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  new = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(new, 0x00, sizeof(struct aim_tlvlist_t));
+
+  new->tlv = aim_createtlv();	
+  new->tlv->type = type;
+  new->tlv->length = len;
+  new->tlv->value = (u_char *)malloc(new->tlv->length*sizeof(u_char));
+  memcpy(new->tlv->value, str, new->tlv->length);
+
+  new->next = NULL;
+
+  if (*list == NULL) {
+    *list = new;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = new;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = new;
+  }
+  return new->tlv->length;
+}
+
+int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val)
+{
+  struct aim_tlvlist_t *new;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  new = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(new, 0x00, sizeof(struct aim_tlvlist_t));
+
+  new->tlv = aim_createtlv();	
+  new->tlv->type = type;
+  new->tlv->length = 2;
+  new->tlv->value = (u_char *)malloc(new->tlv->length*sizeof(u_char));
+  aimutil_put16(new->tlv->value, val);
+
+  new->next = NULL;
+
+  if (*list == NULL) {
+    *list = new;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = new;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = new;
+  }
+  return 2;
+}
+
+int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val)
+{
+  struct aim_tlvlist_t *new;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  new = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(new, 0x00, sizeof(struct aim_tlvlist_t));
+
+  new->tlv = aim_createtlv();	
+  new->tlv->type = type;
+  new->tlv->length = 4;
+  new->tlv->value = (u_char *)malloc(new->tlv->length*sizeof(u_char));
+  aimutil_put32(new->tlv->value, val);
+
+  new->next = NULL;
+
+  if (*list == NULL) {
+    *list = new;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = new;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = new;
+  }
+  return 4;
+}
+
+int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list)
+{
+  int goodbuflen = 0;
+  int i = 0;
+  struct aim_tlvlist_t *cur;
+
+  if (!list || !buf || !buflen)
+    return 0;
+
+  /* do an initial run to test total length */
+  for (cur = *list; cur; cur = cur->next) {
+    goodbuflen += 2 + 2; /* type + len */
+    goodbuflen += cur->tlv->length;
+  }
+
+  if (goodbuflen > buflen)
+    return 0; /* not enough buffer */
+
+  /* do the real write-out */
+  for (cur = *list; cur; cur = cur->next) {
+    i += aimutil_put16(buf+i, cur->tlv->type);
+    i += aimutil_put16(buf+i, cur->tlv->length);
+    memcpy(buf+i, cur->tlv->value, cur->tlv->length);
+    i += cur->tlv->length;
+  }
+
+  return i;
+}
+
+
+/*
+ * Grab the Nth TLV of type type in the TLV list list.
+ */
+struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, u_short type, int nth)
+{
+  int i;
+  struct aim_tlvlist_t *cur;
+  
+  i = 0;
+  for (cur = list; cur != NULL; cur = cur->next)
+    {
+      if (cur && cur->tlv)
+	{
+	  if (cur->tlv->type == type)
+	    i++;
+	  if (i >= nth)
+	    return cur->tlv;
+	}
+    }
+  return NULL;
+}
+
+char *aim_gettlv_str(struct aim_tlvlist_t *list, u_short type, int nth)
+{
+  struct aim_tlv_t *tlv;
+  char *newstr;
+
+  if (!(tlv = aim_gettlv(list, type, nth)))
+    return NULL;
+  
+  newstr = (char *) malloc(tlv->length + 1);
+  memcpy(newstr, tlv->value, tlv->length);
+  *(newstr + tlv->length) = '\0';
+
+  return newstr;
+}
+
 struct aim_tlv_t *aim_grabtlv(u_char *src)
 {
   struct aim_tlv_t *dest = NULL;
@@ -79,8 +320,30 @@
 int aim_puttlv_16(u_char *buf, u_short t, u_short v)
 {
   int curbyte=0;
-  curbyte += aimutil_put16(buf+curbyte, t&0xffff);
-  curbyte += aimutil_put16(buf+curbyte, 0x0002);
-  curbyte += aimutil_put16(buf+curbyte, v&0xffff);
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)0x0002);
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(v&0xffff));
+  return curbyte;
+}
+
+int aim_puttlv_32(u_char *buf, u_short t, u_long v)
+{
+  int curbyte=0;
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)0x0004);
+  curbyte += aimutil_put32(buf+curbyte, (u_long)(v&0xffffffff));
   return curbyte;
 }
+
+int aim_puttlv_str(u_char *buf, u_short t, u_short l, u_char *v)
+{
+  int curbyte;
+  
+  curbyte  = 0;
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(l&0xffff));
+  if (v)
+    memcpy(buf+curbyte, v, l);
+  curbyte += l;
+  return curbyte;
+}
--- a/libfaim/aim_txqueue.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_txqueue.c	Sat May 20 00:30:53 2000 +0000
@@ -1,307 +1,290 @@
 /*
-  aim_txqueue.c
-
-  Herein lies all the mangement routines for the transmit (Tx) queue.
-
- */
-
-#include "aim.h"
-
-/*
-  aim_tx_enqeue()
-
-  The overall purpose here is to enqueue the passed in command struct
-  into the outgoing (tx) queue.  Basically...
-    1) Make a scope-irrelevent copy of the struct
-    2) Lock the struct
-    3) Mark as not-sent-yet
-    4) Enqueue the struct into the list
-    5) Unlock the struct once it's linked in
-    6) Return
-
+ *  aim_txqueue.c
+ *
+ * Herein lies all the mangement routines for the transmit (Tx) queue.
+ *
  */
 
-int aim_tx_enqueue(struct command_tx_struct *newpacket)
+#include <aim.h>
+
+/*
+ * Allocate a new tx frame.
+ *
+ * This is more for looks than anything else.
+ *
+ * Right now, that is.  If/when we implement a pool of transmit
+ * frames, this will become the request-an-unused-frame part.
+ */
+struct command_tx_struct *aim_tx_new(int chan, struct aim_conn_t *conn, int datalen)
 {
-  struct command_tx_struct *workingPtr = NULL;
-  struct command_tx_struct *newpacket_copy = NULL;
+  struct command_tx_struct *new;
 
-  if (newpacket->conn == NULL)
-    {
-      printf("aim_tx_enqueue: WARNING: enqueueing packet with no connecetion,  defaulting to BOS\n");
-      newpacket->conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
-    }
- 
-  newpacket_copy = (struct command_tx_struct *) malloc (sizeof(struct command_tx_struct));
-  memcpy(newpacket_copy, newpacket, sizeof(struct command_tx_struct));
+  if (!conn)
+    return NULL;
+
+  new = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
+  if (!new)
+    return NULL;
+  memset(new, 0, sizeof(struct command_tx_struct));
 
-  /* assign seqnum */
-  newpacket_copy->seqnum = aim_get_next_txseqnum(newpacket_copy->conn);
-  /* set some more fields */
-  newpacket_copy->lock = 1; /* lock */
-  newpacket_copy->sent = 0; /* not sent yet */
-  newpacket_copy->next = NULL; /* always last */
+  new->conn = conn; 
+  new->type = chan;
+
+  if(datalen) {
+    new->data = (u_char *)malloc(datalen);
+    new->commandlen = datalen;
+  }
+
+  return new;
+}
 
-  if (aim_queue_outgoing == NULL)
-    {
-      aim_queue_outgoing = newpacket_copy;
-    }
-  else
-    {
-      workingPtr = aim_queue_outgoing;
-      while (workingPtr->next != NULL)
-	workingPtr = workingPtr->next;
-      workingPtr->next = newpacket_copy;
-    }
-
-  newpacket_copy->lock = 0; /* unlock so it can be sent */
+/*
+ * aim_tx_enqeue()
+ *
+ * The overall purpose here is to enqueue the passed in command struct
+ * into the outgoing (tx) queue.  Basically...
+ *   1) Make a scope-irrelevent copy of the struct
+ *   2) Lock the struct
+ *   3) Mark as not-sent-yet
+ *   4) Enqueue the struct into the list
+ *   5) Unlock the struct once it's linked in
+ *   6) Return
+ *
+ */
+int aim_tx_enqueue(struct aim_session_t *sess,
+		   struct command_tx_struct *newpacket)
+{
+  struct command_tx_struct *cur;
 
-#if debug > 2
-  printf("calling aim_tx_printqueue()\n");
-  aim_tx_printqueue();
-  printf("back from aim_tx_printqueue()\n");
-#endif
+  if (newpacket->conn == NULL) {
+      faimdprintf(1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
+      newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+  }
+ 
+  /* assign seqnum */
+  newpacket->seqnum = aim_get_next_txseqnum(newpacket->conn);
+  /* set some more fields */
+  newpacket->lock = 1; /* lock */
+  newpacket->sent = 0; /* not sent yet */
+  newpacket->next = NULL; /* always last */
 
-  /* we'll force a flush for now -- this behavior probably will change */
-#if debug > 1
-  printf("calling aim_tx_flushqueue()\n");
-#endif
-  aim_tx_flushqueue();
-#if debug > 1
-  printf("back from aim_tx_flushqueue()\n");
+  /* see overhead note in aim_rxqueue counterpart */
+  if (sess->queue_outgoing == NULL) {
+    sess->queue_outgoing = newpacket;
+  } else {
+    for (cur = sess->queue_outgoing;
+	 cur->next;
+	 cur = cur->next)
+      ;
+    cur->next = newpacket;
+  }
+
+  newpacket->lock = 0; /* unlock so it can be sent */
+
+#if debug == 2
+  faimdprintf(2, "calling aim_tx_printqueue()\n");
+  aim_tx_printqueue(sess);
+  faimdprintf(2, "back from aim_tx_printqueue()\n");
 #endif
 
   return 0;
 }
 
 /* 
-   aim_get_next_txseqnum()
-
-   This increments the tx command count, and returns the seqnum
-   that should be stamped on the next FLAP packet sent.  This is
-   normally called during the final step of packet preparation
-   before enqueuement (in aim_tx_enqueue()).
-
+ *  aim_get_next_txseqnum()
+ *
+ *   This increments the tx command count, and returns the seqnum
+ *   that should be stamped on the next FLAP packet sent.  This is
+ *   normally called during the final step of packet preparation
+ *   before enqueuement (in aim_tx_enqueue()).
+ *
  */
-unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn)
+u_int aim_get_next_txseqnum(struct aim_conn_t *conn)
 {
   return ( ++conn->seqnum );
 }
 
 /*
-  aim_tx_printqueue()
-
-  This is basically for debuging purposes only.  It dumps all the
-  records in the tx queue and their current status.  Very helpful
-  if the queue isn't working quite right.
-
+ *  aim_tx_printqueue()
+ *
+ *  This is basically for debuging purposes only.  It dumps all the
+ *  records in the tx queue and their current status.  Very helpful
+ *  if the queue isn't working quite right.
+ *
  */
-#if debug > 2
-int aim_tx_printqueue(void)
+#if debug == 2
+int aim_tx_printqueue(struct aim_session_t *sess)
 {
-  struct command_tx_struct *workingPtr = NULL;
+  struct command_tx_struct *cur;
 
-  workingPtr = aim_queue_outgoing;
-#if debug > 2
-  printf("\ncurrent aim_queue_outgoing...\n");
-  printf("\ttype seqnum  len  lock sent\n");  
-#endif
-  if (workingPtr == NULL)
-    printf("aim_tx_flushqueue(): queue empty");
-  else
-    {
-      while (workingPtr != NULL)
-	{
-	  printf("\t  %2x   %4x %4x   %1d    %1d\n", workingPtr->type, workingPtr->seqnum, workingPtr->commandlen, workingPtr->lock, workingPtr->sent);
-	  
-	  workingPtr = workingPtr->next;
-	}
-    }
+  faimdprintf(2, "\ncurrent aim_queue_outgoing...\n");
+  faimdprintf(2, "\ttype seqnum  len  lock sent\n");  
 
-  printf("\n(done printing queue)\n");
+  if (sess->queue_outgoing == NULL)
+    faimdprintf(2, "aim_tx_flushqueue(): queue empty");
+  else {
+      for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+	  faimdprintf(2, "\t  %2x   %4x %4x   %1d    %1d\n", 
+		      cur->type, cur->seqnum, 
+		      cur->commandlen, cur->lock, 
+		      cur->sent);
+      }
+  }
+
+  faimdprintf(2, "\n(done printing queue)\n");
   
   return 0;
 }
 #endif
 
 /*
-  aim_tx_flushqueue()
-
-  This the function is responsable for putting the queued commands
-  onto the wire.  This function is critical to the operation of 
-  the queue and therefore is the most prone to brokenness.  It
-  seems to be working quite well at this point.
-
-  Procedure:
-    1) Traverse the list, only operate on commands that are unlocked
-       and haven't been sent yet.
-    2) Lock the struct
-    3) Allocate a temporary buffer to store the finished, fully
-       processed packet in.
-    4) Build the packet from the command_tx_struct data.
-    5) Write the packet to the socket.
-    6) If success, mark the packet sent, if fail report failure, do NOT
-       mark the packet sent (so it will not get purged and therefore
-       be attempted again on next call).
-    7) Unlock the struct.
-    8) Free the temp buffer
-    9) Step to next struct in list and go back to 1.
-
+ *  aim_tx_flushqueue()
+ *
+ *  This the function is responsable for putting the queued commands
+ *  onto the wire.  This function is critical to the operation of 
+ *  the queue and therefore is the most prone to brokenness.  It
+ *  seems to be working quite well at this point.
+ *
+ *  Procedure:
+ *    1) Traverse the list, only operate on commands that are unlocked
+ *       and haven't been sent yet.
+ *    2) Lock the struct
+ *    3) Allocate a temporary buffer to store the finished, fully
+ *       processed packet in.
+ *    4) Build the packet from the command_tx_struct data.
+ *    5) Write the packet to the socket.
+ *    6) If success, mark the packet sent, if fail report failure, do NOT
+ *       mark the packet sent (so it will not get purged and therefore
+ *       be attempted again on next call).
+ *    7) Unlock the struct.
+ *    8) Free the temp buffer
+ *    9) Step to next struct in list and go back to 1.
+ *
  */
-int aim_tx_flushqueue(void)
+int aim_tx_flushqueue(struct aim_session_t *sess)
 {
-  struct command_tx_struct *workingPtr = NULL;
-  unsigned char *curPacket = NULL;
+  struct command_tx_struct *cur;
+  u_char *curPacket = NULL;
 #if debug > 1
   int i = 0;
 #endif
 
-  workingPtr = aim_queue_outgoing;
-#if debug > 1
-  printf("beginning txflush...\n");
-#endif
-  while (workingPtr != NULL)
-    {
-      /* only process if its unlocked and unsent */
-      if ( (workingPtr->lock == 0) &&
-	   (workingPtr->sent == 0) )
-	{
-	  workingPtr->lock = 1; /* lock the struct */
-	  
-	  /* allocate full-packet buffer */
-	  curPacket = (char *) malloc(workingPtr->commandlen + 6);
-	  
-	  /* command byte */
-	  curPacket[0] = 0x2a;
-	  /* type/family byte */
-	  curPacket[1] = workingPtr->type;
-	  /* bytes 3+4: word: FLAP sequence number */
-	  curPacket[2] = (char) ( (workingPtr->seqnum) >> 8);
-	  curPacket[3] = (char) ( (workingPtr->seqnum) & 0xFF);
-	  /* bytes 5+6: word: SNAC len */
-	  curPacket[4] = (char) ( (workingPtr->commandlen) >> 8);
-	  curPacket[5] = (char) ( (workingPtr->commandlen) & 0xFF);
-	  /* bytes 7 and on: raw: SNAC data */
-	  memcpy(&(curPacket[6]), workingPtr->data, workingPtr->commandlen);
-	  
-	  /* full image of raw packet data now in curPacket */
+  if (sess->queue_outgoing == NULL)
+    return 0;
+
+  faimdprintf(2, "beginning txflush...\n");
+  for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+    /* only process if its unlocked and unsent */
+    if (!cur->lock && !cur->sent) {
+
+      /*
+       * And now for the meager attempt to force transmit
+       * latency and avoid missed messages.
+       */
+      if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
+	/* FIXME FIXME -- should be a break! we dont want to block the upper layers */
+	sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
+      }
+      
+      cur->lock = 1; /* lock the struct */
+      
+      /* allocate full-packet buffer */
+      curPacket = (char *) malloc(cur->commandlen + 6);
+      
+      /* command byte */
+      curPacket[0] = 0x2a;
+      
+      /* type/family byte */
+      curPacket[1] = cur->type;
+      
+      /* bytes 3+4: word: FLAP sequence number */
+      aimutil_put16(curPacket+2, cur->seqnum);
 
-	  if ( write(workingPtr->conn->fd, curPacket, (workingPtr->commandlen + 6)) != (workingPtr->commandlen + 6))
-	    {
-	      perror("write");
-	      printf("\nWARNING: Error in sending packet 0x%4x -- will try again next time\n\n", workingPtr->seqnum);
-	      workingPtr->sent = 0; /* mark it unsent */
-	      return -1;  /* bail out */
-	    }
-	  else
-	    {
-#if debug > 2
-	      printf("\nSENT 0x%4x\n\n", workingPtr->seqnum);
-#endif
-	      workingPtr->sent = 1; /* mark the struct as sent */
-	    }
+      /* bytes 5+6: word: SNAC len */
+      aimutil_put16(curPacket+4, cur->commandlen);
+      
+      /* bytes 7 and on: raw: SNAC data */
+      memcpy(&(curPacket[6]), cur->data, cur->commandlen);
+      
+      /* full image of raw packet data now in curPacket */
+      if ( (u_int)write(cur->conn->fd, curPacket, (cur->commandlen + 6)) != (cur->commandlen + 6)) {
+	printf("\nWARNING: Error in sending packet 0x%4x -- will try again next time\n\n", cur->seqnum);
+	cur->sent = 0; /* mark it unsent */
+	continue; /* bail out */
+      } else {
+	faimdprintf(2, "\nSENT 0x%4x\n\n", cur->seqnum);
+
+	cur->sent = 1; /* mark the struct as sent */
+	cur->conn->lastactivity = time(NULL);
+      }
 #if debug > 2
-	  printf("\nPacket:");
-	  for (i = 0; i < (workingPtr->commandlen + 6); i++)
-	    {
-	      if ((i % 8) == 0)
-		printf("\n\t");
-	      if (curPacket[i] >= ' ' && curPacket[i]<127)
-		 printf("%c=%02x ",curPacket[i], curPacket[i]);
-	      else
-		 printf("0x%2x ", curPacket[i]);
-	    }
-	  printf("\n");
+      faimdprintf(2, "\nPacket:");
+      for (i = 0; i < (cur->commandlen + 6); i++) {
+	if ((i % 8) == 0) {
+	  faimdprintf(2, "\n\t");
+	}
+	if (curPacket[i] >= ' ' && curPacket[i]<127) {
+	  faimdprintf(2, "%c=%02x ", curPacket[i], curPacket[i]);
+	} else {
+	  faimdprintf(2, "0x%2x ", curPacket[i]);
+	}
+      }
+      faimdprintf(2, "\n");
 #endif
-	  workingPtr->lock = 0; /* unlock the struct */
-	  free(curPacket); /* free up full-packet buffer */
-	}
-      workingPtr = workingPtr->next;
+      cur->lock = 0; /* unlock the struct */
+      free(curPacket); /* free up full-packet buffer */
     }
+  }
 
   /* purge sent commands from queue */
-  /*   this may not always occur explicitly--i may put this on a timer later */
-#if debug > 1
-  printf("calling aim_tx_purgequeue()\n");
-#endif
-  aim_tx_purgequeue();
-#if debug > 1
-  printf("back from aim_tx_purgequeu() [you must be a lucky one]\n");
-#endif
+  aim_tx_purgequeue(sess);
 
   return 0;
 }
 
 /*
-  aim_tx_purgequeue()
-  
-  This is responsable for removing sent commands from the transmit 
-  queue. This is not a required operation, but it of course helps
-  reduce memory footprint at run time!  
-
+ *  aim_tx_purgequeue()
+ *  
+ *  This is responsable for removing sent commands from the transmit 
+ *  queue. This is not a required operation, but it of course helps
+ *  reduce memory footprint at run time!  
+ *
  */
-int aim_tx_purgequeue(void)
+void aim_tx_purgequeue(struct aim_session_t *sess)
 {
-  struct command_tx_struct *workingPtr = NULL;
-  struct command_tx_struct *workingPtr2 = NULL;
-#if debug > 1
-  printf("purgequeue(): starting purge\n");
-#endif
-  /* Empty queue: nothing to do */
-  if (aim_queue_outgoing == NULL)
-    {
-#if debug > 1
-      printf("purgequeue(): purge done (len=0)\n");
-#endif
-      return 0;
+  struct command_tx_struct *cur = NULL;
+  struct command_tx_struct *tmp;
+
+  if (sess->queue_outgoing == NULL)
+    return;
+  
+  if (sess->queue_outgoing->next == NULL) {
+    if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) {
+      tmp = sess->queue_outgoing;
+      sess->queue_outgoing = NULL;
+      free(tmp->data);
+      free(tmp);
     }
-  /* One Node queue: free node and return */
-  else if (aim_queue_outgoing->next == NULL)
-    {
-#if debug > 1
-      printf("purgequeue(): entered case len=1\n");
-#endif
-      /* only free if sent AND unlocked -- dont assume sent structs are done */
-      if ( (aim_queue_outgoing->lock == 0) &&
-	   (aim_queue_outgoing->sent == 1) )
-	{
-#if debug > 1
-	  printf("purgequeue(): purging seqnum 0x%04x\n", aim_queue_outgoing->seqnum);
-#endif
-	  workingPtr2 = aim_queue_outgoing;
-	  aim_queue_outgoing = NULL;
-	  free(workingPtr2->data);
-	  free(workingPtr2);
-	}
-#if debug > 1
-      printf("purgequeue(): purge done (len=1)\n");
-#endif
-      return 0;
-    }
-  else
-    {
-#if debug > 1
-      printf("purgequeue(): entering case len>1\n");
-#endif
-      while(workingPtr->next != NULL)
-	{
-	  if ( (workingPtr->next->lock == 0) &&
-	       (workingPtr->next->sent == 1) )
-	    {
-#if debug > 1
-	      printf("purgequeue(): purging seqnum 0x%04x\n", workingPtr->next->seqnum);
-#endif
-	      workingPtr2 = workingPtr->next;
-	      workingPtr->next = workingPtr2->next;
-	      free(workingPtr2->data);
-	      free(workingPtr2);
-	    }
-	}
-#if debug > 1
-      printf("purgequeue(): purge done (len>1)\n");
-#endif
-      return 0;
-    }
+    return;
+  }
+
+  for(cur = sess->queue_outgoing; cur->next != NULL; ) {
+    if (!cur->next->lock && cur->next->sent) {
+      tmp = cur->next;
+      cur->next = tmp->next;
+      free(tmp->data);
+      free(tmp);
+    }	
+    cur = cur->next;
 
-  /* no reach */
+    /* 
+     * Be careful here.  Because of the way we just
+     * manipulated the pointer, cur may be NULL and 
+     * the for() will segfault doing the check unless
+     * we find this case first.
+     */
+    if (cur == NULL)	
+      break;
+  }
+  return;
 }
--- a/libfaim/aim_util.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_util.c	Sat May 20 00:30:53 2000 +0000
@@ -4,35 +4,226 @@
  *
  */
 
-#include "aim.h"
+#include <aim.h>
+#include <ctype.h>
+
+#define AIMUTIL_USEMACROS
 
-int aimutil_put8(u_char *buf, u_short data)
+#ifdef AIMUTIL_USEMACROS
+/* macros in aim.h */
+#else
+inline int aimutil_put8(u_char *buf, u_char data)
 {
-  buf[0] = data&0xff;
+  buf[0] = (u_char)data&0xff;
   return 1;
 }
 
+inline u_char aimutil_get8(u_char *buf)
+{
+  return buf[0];
+}
+
 /*
  * Endian-ness issues here?
  */
-int aimutil_put16(u_char *buf, u_short data)
+inline int aimutil_put16(u_char *buf, u_short data)
 {
-  buf[0] = (data>>8)&0xff;
-  buf[1] = (data)&0xff;
+  buf[0] = (u_char)(data>>8)&0xff;
+  buf[1] = (u_char)(data)&0xff;
   return 2;
 }
 
-int aimutil_put32(u_char *buf, u_long data)
+inline u_short aimutil_get16(u_char *buf)
 {
-  buf[0] = (data>>24)&0xff;
-  buf[1] = (data>>16)&0xff;
-  buf[2] = (data>>8)&0xff;
-  buf[3] = (data)&0xff;
+  u_short val;
+  val = (buf[0] << 8) & 0xff00;
+  val+= (buf[1]) & 0xff;
+  return val;
+}
+
+inline int aimutil_put32(u_char *buf, u_long data)
+{
+  buf[0] = (u_char)(data>>24)&0xff;
+  buf[1] = (u_char)(data>>16)&0xff;
+  buf[2] = (u_char)(data>>8)&0xff;
+  buf[3] = (u_char)(data)&0xff;
   return 4;
 }
 
-int aimutil_putstr(u_char *dest, u_char *src, int len)
+inline u_long aimutil_get32(u_char *buf)
+{
+  u_long val;
+  val = (buf[0] << 24) & 0xff000000;
+  val+= (buf[1] << 16) & 0x00ff0000;
+  val+= (buf[2] <<  8) & 0x0000ff00;
+  val+= (buf[3]      ) & 0x000000ff;
+  return val;
+}
+#endif /* AIMUTIL_USEMACROS */
+
+inline int aimutil_putstr(u_char *dest, const u_char *src, int len)
 {
   memcpy(dest, src, len);
   return len;
 }
+
+/*
+ * Tokenizing functions.  Used to portably replace strtok/sep.
+ *   -- DMP.
+ *
+ */
+int aimutil_tokslen(char *toSearch, int index, char dl)
+{
+  int curCount = 1;
+  char *next;
+  char *last;
+  int toReturn;
+
+  last = toSearch;
+  next = strchr(toSearch, dl);
+  
+  while(curCount < index && next != NULL)
+    {
+      curCount++;
+      last = next + 1;
+      next = strchr(last, dl);
+    }
+  
+  if ((curCount < index) || (next == NULL))
+    toReturn = strlen(toSearch) - (curCount - 1);
+  else
+    toReturn = next - toSearch - (curCount - 1);
+
+  return toReturn;
+}
+
+int aimutil_itemcnt(char *toSearch, char dl)
+{
+  int curCount;
+  char *next;
+  
+  curCount = 1;
+  
+  next = strchr(toSearch, dl);
+  
+  while(next != NULL)
+    {
+      curCount++;
+      next = strchr(next + 1, dl);
+    }
+  
+  return curCount;
+}
+
+char *aimutil_itemidx(char *toSearch, int index, char dl)
+{
+  int curCount;
+  char *next;
+  char *last;
+  char *toReturn;
+  
+  curCount = 0;
+  
+  last = toSearch;
+  next = strchr(toSearch, dl);
+  
+  while(curCount < index && next != NULL)
+    {
+      curCount++;
+      last = next + 1;
+      next = strchr(last, dl);
+    }
+  
+  if (curCount < index)
+    {
+      toReturn = malloc(sizeof(char));
+      *toReturn = '\0';
+    }
+  next = strchr(last, dl);
+  
+  if (curCount < index)
+    {
+      toReturn = malloc(sizeof(char));
+      *toReturn = '\0';
+    }
+  else
+    {
+      if (next == NULL)
+	{
+	  toReturn = malloc((strlen(last) + 1) * sizeof(char));
+	  strcpy(toReturn, last);
+	}
+      else
+	{
+	  toReturn = malloc((next - last + 1) * sizeof(char));
+	  memcpy(toReturn, last, (next - last));
+	  toReturn[next - last] = '\0';
+	}
+    }
+  return toReturn;
+}
+
+/*
+ * int snlen(const char *)
+ * 
+ * This takes a screen name and returns its length without
+ * spaces.  If there are no spaces in the SN, then the 
+ * return is equal to that of strlen().
+ *
+ */
+int aim_snlen(const char *sn)
+{
+  int i = 0;
+  const char *curPtr = NULL;
+
+  if (!sn)
+    return 0;
+
+  curPtr = sn;
+  while ( (*curPtr) != (char) NULL) {
+      if ((*curPtr) != ' ')
+	i++;
+      curPtr++;
+    }
+
+  return i;
+}
+
+/*
+ * int sncmp(const char *, const char *)
+ *
+ * This takes two screen names and compares them using the rules
+ * on screen names for AIM/AOL.  Mainly, this means case and space
+ * insensitivity (all case differences and spacing differences are
+ * ignored).
+ *
+ * Return: 0 if equal
+ *     non-0 if different
+ *
+ */
+
+int aim_sncmp(const char *sn1, const char *sn2)
+{
+  const char *curPtr1 = NULL, *curPtr2 = NULL;
+
+  if (aim_snlen(sn1) != aim_snlen(sn2))
+    return 1;
+
+  curPtr1 = sn1;
+  curPtr2 = sn2;
+  while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) {
+    if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) {
+      if (*curPtr1 == ' ')
+	curPtr1++;
+      if (*curPtr2 == ' ')
+	curPtr2++;
+    } else {
+      if ( toupper(*curPtr1) != toupper(*curPtr2))
+	return 1;
+      curPtr1++;
+      curPtr2++;
+    }
+  }
+
+  return 0;
+}
--- a/libfaim/faimconfig.h	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/faimconfig.h	Sat May 20 00:30:53 2000 +0000
@@ -1,42 +1,106 @@
+/*
+ *  faimconfig.h
+ *
+ * Contains various compile-time options that apply _only_ to libfaim.
+ * Note that setting any of these options in a frontend header does not imply
+ * that they'll get set here.  Notably, the 'debug' of this file is _not_ 
+ * the same as the frontend 'debug'.  They can be different values.
+ *
+ */
+
 #ifndef __FAIMCONFIG_H__
 #define __FAIMCONFIG_H__
 
-/*
-  faimconfig.h
+/* 
+ * set debug to be > 0 if you want debugging information spewing
+ * on the attached tty.  set to 0 for daily use.  this value
+ * is _not_ inherited by the frontend, only this backend.
+ *
+ * Default: 0  
+*/
+#define debug 0
 
-  Contains various compile-time options that apply _only to the faim backend_.
-  Note that setting any of these options in a frontend header does not imply
-  that they'll get set here.  Notably, the 'debug' of this file is _not_ 
-  the same as the frontend 'debug'.  They can be different values.
-
+/*
+ * Maximum number of connections the library can simultaneously
+ * handle per session structure.  Five is fairly arbitrary.  
+ * 
+ * Normally, only one connection gets used at a time.  However, if
+ * chat is used, its easily possible for several connections to be
+ * open simultaneously.
+ *
+ * Normal connection list looks like this:
+ *   1 -- used for authentication at login (closed after login)
+ *   1 -- used for BOS (general messaging) (stays open for entire session)
+ *   1 -- used for chatnav (room creation, etc) (opened at random)
+ *  1n -- used for n connected chat rooms (AOL limits to three)
+ *
+ * Default: 7
+ *
  */
+#define AIM_CONN_MAX 7
 
-/* 
-   set debug to be > 0 if you want debugging information spewing
-   on the attached tty.  set to 0 for daily use.  this value
-   is _not_ inherited by the frontend, only this backend.
-
-   Default: 0  
-*/
-#define debug 10
+/*
+ * USE_SNAC_FOR_IMS is an old feature that allowed better
+ * tracking of error messages by caching SNAC IDs of outgoing
+ * ICBMs and comparing them to incoming errors.  However,
+ * its a helluvalot of overhead for something that should
+ * rarely happen.  
+ *
+ * Default: defined.  This is now defined by default
+ * because it should be stable and its not too bad.  
+ * And Josh wanted it.
+ *
+ */
+#define USE_SNAC_FOR_IMS
 
 /*
-  define TIS_TELNET_PROXY if you have a TIS firewall (Gauntlet) and
-  you want to use FAIM through the firewall
-
-  Default: undefined
+ * As of AIM 3.5 or so, AOL as added a better way of
+ * logging in.  Define this to use it instead of the 
+ * old Version 1.0 way.  
+ *
+ * The largest caveat here is that I have no idea
+ * how to encode passwords using the new 3.5 way.
+ * Until someone figures that out the...
+ *
+ * Default: Undefined.
+ *
  */
-/* #define TIS_TELNET_PROXY "proxy.mydomain.com" */
-
+#undef SNACLOGIN
 
-/* #define USE_SNAC_FOR_IMS */
-
-/* ---- these shouldn't need any changes ---- */
-
-/* authentication server of OSCAR */
+/*
+ * Default Authorizer server name and TCP port for the OSCAR farm.  
+ *
+ * You shouldn't need to change this unless you're writing
+ * your own server. 
+ *
+ * Note that only one server is needed to start the whole
+ * AIM process.  The later server addresses come from
+ * the authorizer service.
+ *
+ * This is only here for convenience.  Its still up to
+ * the client to connect to it.
+ *
+ */
 #define FAIM_LOGIN_SERVER "login.oscar.aol.com"
-/* port on OSCAR authenticator to connect to */
 #define FAIM_LOGIN_PORT 5190
 
+/*
+ * MAX_READ_ERROR can be decreased if you find dead connections
+ * lingering around, and not getting detected, for too long.
+ *
+ * Default: 100
+ *
+ */
+#define MAX_READ_ERROR 100
+
+/*
+ * The integer extraction/copying functions in aim_util.c have
+ * both a function version and a macro version.  The macro 
+ * version is suggested.  Since the function version is more
+ * readable, I leave both around for reference.
+ *
+ * Default: defined.
+ */
+#define AIMUTIL_USEMACROS
 
 #endif /* __FAIMCONFIG_H__ */
--- a/src/dialogs.c	Thu May 18 18:20:18 2000 +0000
+++ b/src/dialogs.c	Sat May 20 00:30:53 2000 +0000
@@ -1085,10 +1085,7 @@
 		return;
 	}
 
-	buf = g_malloc(BUF_LONG);
-	g_snprintf(buf, BUF_LONG, "toc_change_passwd %s %s", orig, new1);
-	sflap_send(buf, strlen(buf), TYPE_DATA);
-	g_free(buf);
+	serv_change_passwd(orig, new1);
 	
 	destroy_dialog(NULL, b->window);
 	g_free(b);
@@ -2098,7 +2095,7 @@
 			return;
 	}
         if ((f = fopen(path,"w"))) {
-                toc_build_config(buf, 8192 - 1);
+                serv_build_config(buf, 8192 - 1);
                 fprintf(f, "%s\n", buf);
                 fclose(f);
                 chmod(buf, S_IRUSR | S_IWUSR);
--- a/src/gaim.h	Thu May 18 18:20:18 2000 +0000
+++ b/src/gaim.h	Sat May 20 00:30:53 2000 +0000
@@ -337,7 +337,7 @@
 #define TYPE_SIGNOFF   4
 #define TYPE_KEEPALIVE 5
 
-#define REVISION "gaim:$Revision: 244 $"
+#define REVISION "gaim:$Revision: 247 $"
 #define FLAPON "FLAPON\r\n\r\n"
 
 #define ROAST "Tic/Toc"
@@ -357,7 +357,10 @@
 #endif /* USE_APPLET */
 
 /* Globals in oscar.c */
-extern struct aim_conn_t *gaim_conn;
+#ifdef USE_OSCAR
+extern struct aim_session_t *gaim_sess;
+extern struct aim_conn_t    *gaim_conn;
+#endif
 
 /* Globals in server.c */
 extern int correction_time;
@@ -505,12 +508,14 @@
 extern void serv_set_idle(int);
 extern void serv_set_info(char *);
 extern void serv_set_away(char *);
+extern void serv_change_passwd(char *, char *);
 extern void serv_add_buddy(char *);
 extern void serv_add_buddies(GList *);
 extern void serv_remove_buddy(char *);
 extern void serv_add_permit(char *);
 extern void serv_add_deny(char *);
 extern void serv_set_permit_deny();
+extern void serv_build_config(char *, int);
 extern void serv_save_config();
 extern void serv_warn(char *, int);
 extern void serv_set_dir(char *, char *, char *, char *, char *, char *, char *, int);
@@ -553,8 +558,8 @@
 extern int connect_address(unsigned int, unsigned short);
 
 /* Functions in oscar.c */
-extern void oscar_close();
 extern int oscar_login(char *, char *);
+extern int oscar_send_im(char *, char *, int);
 
 /* Functions in toc.c */
 extern void toc_close();
--- a/src/oscar.c	Thu May 18 18:20:18 2000 +0000
+++ b/src/oscar.c	Sat May 20 00:30:53 2000 +0000
@@ -35,367 +35,382 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include "gaim.h"
-#include <aim.h>
+#include "aim.h"
 #include "gnome_applet_mgr.h"
 
-struct aim_conn_t *gaim_conn = NULL;
 static int inpa = -1;
+static struct aim_session_t *gaim_sess;
+static struct aim_conn_t    *gaim_conn;
 
-int gaim_auth_failure(struct command_rx_struct *command, ...);
-int gaim_auth_success(struct command_rx_struct *command, ...);
-int gaim_serverready_handle(struct command_rx_struct *command, ...);
-int gaim_redirect_handle(struct command_rx_struct *command, ...);
-int gaim_im_handle(struct command_rx_struct *command, ...);
+static int gaim_parse_auth_resp  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_auth_server_ready(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_server_ready     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_handle_redirect  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_oncoming   (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_offgoing   (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_misses     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_user_info  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_unknown    (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_motd       (struct aim_session_t *, struct command_rx_struct *, ...);
+
+static void oscar_callback(gpointer data, gint source,
+				GdkInputCondition condition) {
+	struct aim_session_t *sess = (struct aim_session_t *)data;
+	struct aim_conn_t *conn = NULL;
+	struct timeval tv;
+	int selstat;
 
-rxcallback_t gaim_callbacks[] = {
-        gaim_im_handle,                 /* incoming IM */
-        NULL,/*gaim_buddy_coming,               oncoming buddy */
-        NULL,/*gaim_buddy_going,                offgoing buddy */
-        NULL,                           /* last IM was missed 1 */
-        NULL,                           /* last IM was missed 2 */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        gaim_serverready_handle,        /* server ready */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        NULL,                   /* UNUSED */
-        gaim_redirect_handle,           /* redirect */
-        NULL,                           /* last command bad */
-        NULL,                           /* missed some messages */
-        NULL,                           /* completely unknown command */
-        NULL, /*gaim_userinfo_handler,           User Info Response */
-        NULL,                           /* Address search response */
-        NULL,                           /* Name search response */
-        NULL,                           /* User Search fail */
-        gaim_auth_failure,              /* auth error */
-        gaim_auth_success,              /* auth success */
-        NULL,                           /* auth server ready */
-        NULL,                           /* ? */
-        NULL,                           /* password change done */
-        gaim_serverready_handle,        /* server ready */
-        0x00
-};
+	/* FIXME : There's gotta be a better way of getting this to not
+	 * either hang until a message is received or only update every
+	 * 2 seconds. If someone can tell me what it is, let me know. */
+	tv.tv_sec = 2;
+	tv.tv_usec = 0;
+	conn = aim_select(sess, &tv, &selstat);
 
-struct client_info_s cinfo;
-
-
-void oscar_close()
-{
-#ifdef USE_APPLET
-	setUserState(offline);
-#endif /* USE_APPLET */
-        set_state(STATE_OFFLINE);
-        aim_conn_close(gaim_conn);
-        if (inpa > 0)
-                gdk_input_remove(inpa);
-	inpa=-1;
+	switch(selstat) {
+		case -1: /* error */
+			signoff();
+			hide_login_progress("Disconnected.");
+			aim_logoff(sess);
+			gdk_input_remove(inpa);
+			break;
+		case 0: /* this should never happen because of the gdk_input */
+			gdk_input_remove(inpa);
+			while (gtk_events_pending())
+				gtk_main_iteration();
+			inpa = gdk_input_add(gaim_conn->fd,
+					GDK_INPUT_READ | GDK_INPUT_WRITE |
+					GDK_INPUT_EXCEPTION,
+					oscar_callback, sess);
+			break;
+		case 1: /* outgoing data pending */
+			aim_tx_flushqueue(sess);
+			break;
+		case 2: /* incoming data pending */
+			if (aim_get_command(sess, conn) < 0)
+				debug_print("connection error!\n");
+			else
+				aim_rxdispatch(sess);
+			break;
+	}
 }
 
+int oscar_login(char *username, char *password) {
+	struct aim_session_t *sess;
+	struct aim_conn_t *conn;
+	struct client_info_s info = {"Gaim/Faim", 3, 5, 1670, "us", "en"};
+	char buf[256];
 
-void oscar_callback(gpointer data, gint source, GdkInputCondition condition)
-{
-        if (aim_get_command() < 0) {
-                signoff();
-                hide_login_progress("Connection Closed");
-                return;
-        } else
-                aim_rxdispatch();
+	sess = g_new0(struct aim_session_t, 1);
+	aim_session_init(sess);
+
+	sprintf(buf, "Looking up %s", FAIM_LOGIN_SERVER);
+	set_login_progress(1, buf);
+	conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, FAIM_LOGIN_SERVER);
 
+	if (conn == NULL) {
+		debug_print("internal connection error\n");
+#ifdef USE_APPLET
+		setUserState(offline);
+#endif
+		set_state(STATE_OFFLINE);
+		hide_login_progress("Unable to login to AIM");
+		return -1;
+	} else if (conn->fd == -1) {
+#ifdef USE_APPLET
+		setUserState(offline);
+#endif
+		set_state(STATE_OFFLINE);
+		if (conn->status & AIM_CONN_STATUS_RESOLVERR) {
+			sprintf(debug_buff, "couldn't resolve host\n");
+			debug_print(debug_buff);
+			hide_login_progress(debug_buff);
+		} else if (conn->status & AIM_CONN_STATUS_CONNERR) {
+			sprintf(debug_buff, "couldn't connect to host\n");
+			debug_print(debug_buff);
+			hide_login_progress(debug_buff);
+		}
+		return -1;
+	}
+	g_snprintf(buf, sizeof(buf), "Signon: %s", username);
+	set_login_progress(2, buf);
+
+	aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL,
+				AIM_CB_SPECIAL_AUTHSUCCESS,
+				gaim_parse_auth_resp, 0);
+	aim_conn_addhandler(sess, conn, AIM_CB_FAM_GEN,
+				AIM_CB_GEN_SERVERREADY,
+				gaim_auth_server_ready, 0);
+	aim_send_login(sess, conn, username, password, &info);
+
+	inpa = gdk_input_add(conn->fd,
+				GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+				oscar_callback, sess);
+
+	if (!current_user) {
+		current_user = g_new0(struct aim_user, 1);
+		g_snprintf(current_user->username, sizeof(current_user->username),
+				DEFAULT_INFO);
+		aim_users = g_list_append(aim_users, current_user);
+	}
+	g_snprintf(current_user->username, sizeof(current_user->username),
+				"%s", username);
+	g_snprintf(current_user->password, sizeof(current_user->password),
+				"%s", password);
+	save_prefs();
+
+	return 0;
 }
 
-int oscar_login(char *username, char *password)
-{
-        char buf[256];
-        struct timeval timeout;
-        time_t lastcycle=0;
-        
-        aim_connrst();
-        aim_register_callbacks(gaim_callbacks);
-
-        aim_conn_getnext()->fd = STDIN_FILENO;
-
-	spintf(buf, "Looking up %s", login_host);
-        set_login_progress(1, buf);
-
-        gaim_conn = aim_newconn(AIM_CONN_TYPE_AUTH, login_host);
-
-        if (!gaim_conn) {
-#ifdef USE_APPLET
-                setUserState(offline);
-#endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
-                hide_login_progress("Unable to login to AIM");
-                return -1;
-        } else if (gaim_conn->fd == -1) {
-#ifdef USE_APPLET
-                setUserState(offline);
-#endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
-                
-                if (gaim_conn->status & AIM_CONN_STATUS_RESOLVERR) {
-			sprintf(buf, "Unable to lookup %s", login_host);
-                        hide_login_progress(buf);
-                } else if (gaim_conn->status & AIM_CONN_STATUS_CONNERR) {
-			sprintf(buf, "Unable to connect to %s", login_host);
-                        hide_login_progress(buf);
-                }
-                return -1;
-        }
-
-        g_snprintf(buf, sizeof(buf), "Signon: %s",username);
-	
-        set_login_progress(2, buf);
-
-        strcpy(cinfo.clientstring, "libfaim/GAIM, jimduchek@ou.edu, see at http://www.marko.net/gaim");
-        cinfo.major = 0;
-        cinfo.minor = 9;
-        cinfo.build = 7;
-        strcpy(cinfo.country, "us");
-        strcpy(cinfo.lang, "en");
-
-        aim_send_login(gaim_conn, username, password, &cinfo);
-
-        if (!current_user) {
-                current_user = g_new0(struct aim_user, 1);
-                g_snprintf(current_user->username, sizeof(current_user->username), DEFAULT_INFO);
-                aim_users = g_list_append(aim_users, current_user);
-        }
-
-        g_snprintf(current_user->username, sizeof(current_user->username), "%s", username);
-        g_snprintf(current_user->password, sizeof(current_user->password), "%s", password);
-
-        save_prefs();
-
-        inpa = gdk_input_add(gaim_conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, oscar_callback, NULL);
-
-        return 0;
+int oscar_send_im(char *name, char *msg, int away) {
+	if (away)
+		aim_send_im(gaim_sess, gaim_conn, name, AIM_IMFLAGS_AWAY, msg);
+	else
+		aim_send_im(gaim_sess, gaim_conn, name, 0, msg);
 }
 
-int gaim_auth_success(struct command_rx_struct *command, ...)
-{
-        va_list ap;
-        struct login_phase1_struct *logininfo;
-        struct aim_conn_t *bosconn = NULL;
-        char buf[128];
-
-        va_start(ap, command);
-        logininfo = va_arg(ap, struct login_phase1_struct *);
-        va_end(ap);
+int gaim_parse_auth_resp(struct aim_session_t *sess,
+			 struct command_rx_struct *command, ...) {
+	struct aim_conn_t *bosconn = NULL;
+	sprintf(debug_buff, "inside auth_resp (Screen name: %s)\n",
+			sess->logininfo.screen_name);
+	debug_print(debug_buff);
 
-        g_snprintf(buf, sizeof(buf), "Auth successful, logging in to %s:", logininfo->BOSIP);
-        set_login_progress(3, buf);
-        
-        printf("          Screen name: %s\n", logininfo->screen_name);
-        printf("       Email addresss: %s\n", logininfo->email);
-        printf("  Registration status: %02i\n", logininfo->regstatus);
-        printf("Connecting to %s, closing auth connection.\n",
-                logininfo->BOSIP);
+	if (sess->logininfo.errorcode) {
+		sprintf(debug_buff, "Login Error Code 0x%04x\n",
+				sess->logininfo.errorcode);
+		debug_print(debug_buff);
+		sprintf(debug_buff, "Error URL: %s\n",
+				sess->logininfo.errorurl);
+		debug_print(debug_buff);
+#ifdef USE_APPLET
+		setUserState(offline);
+#endif
+		set_state(STATE_OFFLINE);
+		hide_login_progress("Authentication Failed");
+		gdk_input_remove(inpa);
+		aim_conn_close(command->conn);
+		return 0;
+	}
 
-        aim_conn_close(command->conn);
 
-        gdk_input_remove(inpa);
+	sprintf(debug_buff, "Email: %s\n", sess->logininfo.email);
+	debug_print(debug_buff);
+	sprintf(debug_buff, "Closing auth connection...\n");
+	debug_print(debug_buff);
+	gdk_input_remove(inpa);
+	aim_conn_close(command->conn);
 
-        if ((bosconn = aim_newconn(AIM_CONN_TYPE_BOS, logininfo->BOSIP))
-            == NULL) {
+	bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, sess->logininfo.BOSIP);
+	if (bosconn == NULL) {
 #ifdef USE_APPLET
-                setUserState(offline);
-#endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
-
-                hide_login_progress("Could not connect to BOS: internal error");
-                return(-1);
-        } else if (bosconn->status != 0) {
+		setUserState(offline);
+#endif
+		set_state(STATE_OFFLINE);
+		hide_login_progress("Internal Error");
+		return -1;
+	} else if (bosconn->status != 0) {
 #ifdef USE_APPLET
-                setUserState(offline);
-#endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
+		setUserState(offline);
+#endif
+		set_state(STATE_OFFLINE);
+		hide_login_progress("Could Not Connect");
+		return -1;
+	}
 
-                hide_login_progress("Could not connect to BOS");
-                return(-1);
-        } else {
-                aim_auth_sendcookie(bosconn, logininfo->cookie);
-                inpa = gdk_input_add(bosconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, oscar_callback, NULL);
-                set_login_progress(4, "BOS connection established, cookie sent.");
-                return(1);
-        }
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, gaim_server_ready, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_CTN, AIM_CB_CTN_DEFAULT, gaim_parse_unknown, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEFAULT, gaim_parse_unknown, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
+
+	aim_auth_sendcookie(sess, bosconn, sess->logininfo.cookie);
+	inpa = gdk_input_add(bosconn->fd,
+			GDK_INPUT_READ | GDK_INPUT_WRITE | GDK_INPUT_EXCEPTION,
+			oscar_callback, sess);
+	set_login_progress(4, "Connection established, cookie sent");
+	return 1;
 }
 
-int gaim_auth_failure(struct command_rx_struct *command, ...)
-{
-        va_list ap;
-        struct login_phase1_struct      *logininfo;
-        char    *errorurl;
-        short   errorcode;
-
-        va_start(ap, command);
-        logininfo = va_arg(ap, struct login_phase1_struct *);
-        printf("Screen name: %s\n", logininfo->screen_name);
-        errorurl = va_arg(ap, char *);
-        printf("Error URL: %s\n", errorurl);
-        errorcode = va_arg(ap, short);
-        printf("Error code: 0x%02x\n", errorcode);
-        va_end(ap);
-#ifdef USE_APPLET
-        setUserState(offline);
-#endif /* USE_APPLET */
-        set_state(STATE_OFFLINE);
-        hide_login_progress("Authentication Failed");
-
-        aim_conn_close(aim_getconn_type(AIM_CONN_TYPE_AUTH));
-
-        return 1;
+int gaim_auth_server_ready(struct aim_session_t *sess,
+			   struct command_rx_struct *command, ...) {
+	aim_auth_clientready(sess, command->conn);
+	return 1;
 }
 
-int gaim_serverready_handle(struct command_rx_struct *command, ...)
-{
-        switch (command->conn->type) {
-        case AIM_CONN_TYPE_BOS:
-                aim_bos_reqrate(command->conn);
-                aim_bos_ackrateresp(command->conn);
-                aim_bos_setprivacyflags(command->conn, 0x00000003);
-                aim_bos_reqservice(command->conn, AIM_CONN_TYPE_ADS);
-                aim_bos_setgroupperm(NULL, 0x1f);
-                break;
-        case AIM_CONN_TYPE_CHATNAV:
+int gaim_server_ready(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	switch (command->conn->type) {
+	case AIM_CONN_TYPE_BOS:
+		aim_bos_reqrate(sess, command->conn);
+		aim_bos_ackrateresp(sess, command->conn);
+		aim_bos_setprivacyflags(sess, command->conn, 0x00000003);
+		aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_ADS);
+		aim_setversions(sess, command->conn);
+		aim_bos_setgroupperm(sess, command->conn, 0x1f);
+		debug_print("done with BOS ServerReady\n");
 		break;
-        default:
-		printf("Unknown connection type on serverready\n");
+	case AIM_CONN_TYPE_CHATNAV:
+		break;
+	case AIM_CONN_TYPE_CHAT:
 		break;
-        }
-        return(1);
-
+	default: /* huh? */
+		break;
+	}
+	return 1;
 }
 
-int gaim_redirect_handle(struct command_rx_struct *command, ...)
-{
-        va_list ap;
-        int     serviceid;
-        char    *ip, *cookie;
+int gaim_handle_redirect(struct aim_session_t *sess,
+			 struct command_rx_struct *command, ...) {
+	va_list ap;
+	int serviceid;
+	char *ip;
+	char *cookie;
 
-        va_start(ap, command);
-        serviceid = va_arg(ap, int);
-        ip = va_arg(ap, char *);
-        cookie = va_arg(ap, char *);
-        va_end(ap);
+	/* FIXME */
+	char buddies[] = "EWarmenhoven&RobFlynn&Zilding&FlynOrange&";
+	char profile[] = "Hello";
 
-        switch(serviceid) {
-        case 0x0005: {
-                char *buf;
-                char *buf2;
-                char file[1024];
-                FILE *f;
+	va_start(ap, command);
+	serviceid = va_arg(ap, int);
+	ip        = va_arg(ap, char *);
+	cookie    = va_arg(ap, char *);
 
-                g_snprintf(file, sizeof(file), "%s/.gaimbuddy", getenv("HOME"));
-        
-                if (!(f = fopen(file,"r"))) {
-                } else {
-                        buf = g_malloc(BUF_LONG);
-                        fread(buf, BUF_LONG, 1, f);
+	switch(serviceid) {
+	case 0x0005: /* Ads */
+		aim_bos_setbuddylist(sess, command->conn, buddies);
+		aim_bos_setprofile(sess, command->conn, profile, NULL, AIM_CAPS_CHAT);
 
-                        parse_toc_buddy_list(buf);
+		aim_bos_clientready(sess, command->conn);
 
-                        build_edit_tree();
-                        build_permit_tree();
-        
+		gaim_sess = sess;
+		gaim_conn = command->conn;
 
-                        g_free(buf);
-                }
-                
-
-
-                aim_bos_clientready(command->conn);
-
-		set_login_progress(5, "Logged in.\n");
+		debug_print("Roger that, all systems go\n");
 #ifdef USE_APPLET
+		make_buddy();
 		if (general_options & OPT_GEN_APP_BUDDY_SHOW) {
-			show_buddy_list();
-			refresh_buddy_window();
+			gnome_buddy_show();
+			createOnlinePopup();
+			set_applet_draw_open();
 		} else {
+			gnome_buddy_hide();
+			set_applet_draw_closed();
 		}
-
-		set_applet_draw_closed();
 		setUserState(online);
+		gtk_widget_hide(mainwindow);
 #else
 		gtk_widget_hide(mainwindow);
 		show_buddy_list();
 		refresh_buddy_window();
 #endif
-		serv_finish_login();
-		gaim_conn = command->conn;
-
-                break;
-        }
-	case 0x0007: {
-		struct aim_conn_t       *tstconn;
-
-		tstconn = aim_newconn(AIM_CONN_TYPE_AUTH, ip);
-		if ((tstconn == NULL) ||
-		    (tstconn->status >= AIM_CONN_STATUS_RESOLVERR)) {
-#ifdef USE_APPLET
-			setUserState(offline);
-#endif /* USE_APPLET */
-			set_state(STATE_OFFLINE);
-			hide_login_progress("Unable to reconnect to authorizer");
-		} else
-			aim_auth_sendcookie(tstconn, cookie);
 		break;
-	}
-	case 0x000d: {
-		struct aim_conn_t       *tstconn;
-
-		tstconn = aim_newconn(AIM_CONN_TYPE_CHATNAV, ip);
-		if ((tstconn == NULL) ||
-		    (tstconn->status >= AIM_CONN_STATUS_RESOLVERR))
-			printf("Unable to connect to chatnav server\n");
+	case 0x7: /* Authorizer */
+		{
+		struct aim_conn_t *tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
+		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR)
+			debug_print("unable to reconnect with authorizer\n");
 		else
-			aim_auth_sendcookie(
-					    aim_getconn_type(AIM_CONN_TYPE_CHATNAV),
-					    cookie);
+			aim_auth_sendcookie(sess, tstconn, cookie);
+		}
+		break;
+	case 0xd: /* ChatNav */
+		break;
+	case 0xe: /* Chat */
+		break;
+	default: /* huh? */
+		sprintf(debug_buff, "got redirect for unknown service 0x%04x\n",
+				serviceid);
+		debug_print(debug_buff);
 		break;
 	}
-	case 0x000e:
-		printf("CHAT is not yet supported :(\n");
-		break;
-	default:
-		printf("Unknown redirect %#04X\n", serviceid);
-		break;
-	}
-	return(1);
-		
+
+	va_end(ap);
+
+	return 1;
+}
+
+int gaim_parse_oncoming(struct aim_session_t *sess,
+			struct command_rx_struct *command, ...) {
+	return 1;
+}
+
+int gaim_parse_offgoing(struct aim_session_t *sess,
+			struct command_rx_struct *command, ...) {
+	return 1;
 }
 
-
+int gaim_parse_incoming_im(struct aim_session_t *sess,
+			   struct command_rx_struct *command, ...) {
+	int channel;
+	va_list ap;
 
-int gaim_im_handle(struct command_rx_struct *command, ...)
-{
-        time_t  t = 0;
-        char    *screenname, *msg;
-        int     warninglevel, class, idletime, isautoreply;
-        ulong   membersince, onsince;
-        va_list ap;
+	va_start(ap, command);
+	channel = va_arg(ap, int);
 
-        va_start(ap, command);
-	screenname = va_arg(ap, char *);
-	msg = va_arg(ap, char *);
-	warninglevel = va_arg(ap, int);
-	class = va_arg(ap, int);
-        membersince = va_arg(ap, ulong);
-        onsince = va_arg(ap, ulong);
-        idletime = va_arg(ap, int);
-        isautoreply = va_arg(ap, int);
-        va_end(ap);
+	/* channel 1: standard message */
+	if (channel == 1) {
+		struct aim_userinfo_s *userinfo;
+		char *msg = NULL;
+		u_int icbmflags = 0;
+		char *tmpstr = NULL;
+		u_short flag1, flag2;
 
-        printf("'%s'\n", msg);
-        
-        serv_got_im(screenname, msg, isautoreply);
+		userinfo  = va_arg(ap, struct aim_userinfo_s *);
+		msg       = va_arg(ap, char *);
+		icbmflags = va_arg(ap, u_int);
+		flag1     = va_arg(ap, u_short);
+		flag2     = va_arg(ap, u_short);
+		va_end(ap);
 
-        return(1);
+		serv_got_im(userinfo->sn, msg, icbmflags & AIM_IMFLAGS_AWAY);
+	}
 
-
+	return 1;
 }
 
-#endif
+int gaim_parse_misses(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	return 1;
+}
+
+int gaim_parse_user_info(struct aim_session_t *sess,
+			 struct command_rx_struct *command, ...) {
+	return 1;
+}
+
+int gaim_parse_unknown(struct aim_session_t *sess,
+		       struct command_rx_struct *command, ...) {
+	return 1;
+}
+
+int gaim_parse_motd(struct aim_session_t *sess,
+		    struct command_rx_struct *command, ...) {
+	char *msg;
+	u_short id;
+	va_list ap;
+
+	va_start(ap, command);
+	id  = va_arg(ap, u_short);
+	msg = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(debug_buff, "MOTD: %s\n", msg);
+	debug_print(debug_buff);
+
+	return 1;
+}
+
+#endif /* USE_OSCAR */
--- a/src/rvous.c	Thu May 18 18:20:18 2000 +0000
+++ b/src/rvous.c	Sat May 20 00:30:53 2000 +0000
@@ -19,6 +19,8 @@
  *
  */
 
+#ifndef USE_OSCAR
+
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -699,3 +701,5 @@
 		*/
 	}
 }
+
+#endif /* USE_OSCAR */
--- a/src/server.c	Thu May 18 18:20:18 2000 +0000
+++ b/src/server.c	Sat May 20 00:30:53 2000 +0000
@@ -53,8 +53,6 @@
 {
 #ifndef USE_OSCAR
 	toc_close();
-#else
-        oscar_close();
 #endif
         gtk_timeout_remove(idle_timer);
         idle_timer = -1;
@@ -159,7 +157,7 @@
                    message, ((away) ? " auto" : ""));
 	sflap_send(buf, strlen(buf), TYPE_DATA);
 #else
-	aim_send_im(NULL, normalize(name), ((away) ? 0 : AIM_IMFLAGS_AWAY), message);
+	oscar_send_im(name, message, away);
 #endif
         if (!away)
                 serv_touch_idle();
@@ -167,8 +165,8 @@
 
 void serv_get_info(char *name)
 {
+        char buf[MSG_LEN];
 #ifndef USE_OSCAR
-        char buf[MSG_LEN];
         g_snprintf(buf, MSG_LEN, "toc_get_info %s", normalize(name));
         sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -176,8 +174,8 @@
 
 void serv_get_dir(char *name)
 {
+        char buf[MSG_LEN];
 #ifndef USE_OSCAR
-        char buf[MSG_LEN];
         g_snprintf(buf, MSG_LEN, "toc_get_dir %s", normalize(name));
         sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -186,8 +184,8 @@
 void serv_set_dir(char *first, char *middle, char *last, char *maiden,
 		  char *city, char *state, char *country, int web)
 {
+	char buf2[BUF_LEN*4], buf[BUF_LEN];
 #ifndef USE_OSCAR
-	char buf2[BUF_LEN*4], buf[BUF_LEN];
 	g_snprintf(buf2, sizeof(buf2), "%s:%s:%s:%s:%s:%s:%s:%s", first,
 		   middle, last, maiden, city, state, country,
 		   (web == 1) ? "Y" : "");
@@ -200,8 +198,8 @@
 void serv_dir_search(char *first, char *middle, char *last, char *maiden,
 		     char *city, char *state, char *country, char *email)
 {
+	char buf[BUF_LONG];
 #ifndef USE_OSCAR
-	char buf[BUF_LONG];
 	g_snprintf(buf, sizeof(buf)/2, "toc_dir_search %s:%s:%s:%s:%s:%s:%s:%s", first, middle, last, maiden, city, state, country, email);
 	sprintf(debug_buff,"Searching for: %s,%s,%s,%s,%s,%s,%s\n", first, middle, last, maiden, city, state, country);
 	debug_print(debug_buff);
@@ -212,8 +210,8 @@
 
 void serv_set_away(char *message)
 {
+        char buf[MSG_LEN];
 #ifndef USE_OSCAR
-        char buf[MSG_LEN];
         if (message)
                 g_snprintf(buf, MSG_LEN, "toc_set_away \"%s\"", message);
         else
@@ -228,16 +226,22 @@
 #ifndef USE_OSCAR
 	g_snprintf(buf, sizeof(buf), "toc_set_info \"%s\n\"", info);
 	sflap_send(buf, -1, TYPE_DATA);
-#else
-	g_snprintf(buf, sizeof(buf), "%s\n", info);
-	aim_bos_setprofile(gaim_conn, buf);
+#endif
+}
+
+void serv_change_passwd(char *orig, char *new) {
+#ifndef USE_OSCAR
+	char *buf = g_malloc(BUF_LONG); 
+	g_snprintf(buf, BUF_LONG, "toc_change_passwd %s %s", orig, new);
+	sflap_send(buf, strlen(buf), TYPE_DATA);
+	g_free(buf);
 #endif
 }
 
 void serv_add_buddy(char *name)
 {
+	char buf[1024];
 #ifndef USE_OSCAR
-	char buf[1024];
 	g_snprintf(buf, sizeof(buf), "toc_add_buddy %s", normalize(name));
 	sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -247,8 +251,8 @@
 {
 	char buf[MSG_LEN];
         int n, num = 0;
+
 #ifndef USE_OSCAR
-	
         n = g_snprintf(buf, sizeof(buf), "toc_add_buddy");
         while(buddies) {
                 if (num == 20) {
@@ -261,25 +265,14 @@
                 buddies = buddies->next;
         }
 	sflap_send(buf, -1, TYPE_DATA);
-#else
-	while(buddies) {
-		if (num == 20) {
-                        aim_bos_setbuddylist(gaim_conn, buf);
-                        num = 0;
-                }
-                ++num;
-                n += g_snprintf(buf + n, sizeof(buf) - n, "%s&", normalize(buddies->data));
-                buddies = buddies->next;
-	}
-	aim_bos_setbuddylist(gaim_conn, buf);
 #endif
 }
 
 
 void serv_remove_buddy(char *name)
 {
+	char buf[1024];
 #ifndef USE_OSCAR
-	char buf[1024];
 	g_snprintf(buf, sizeof(buf), "toc_remove_buddy %s", normalize(name));
 	sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -287,8 +280,8 @@
 
 void serv_add_permit(char *name)
 {
+	char buf[1024];
 #ifndef USE_OSCAR
-	char buf[1024];
 	g_snprintf(buf, sizeof(buf), "toc_add_permit %s", normalize(name));
 	sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -298,8 +291,8 @@
 
 void serv_add_deny(char *name)
 {
+	char buf[1024];
 #ifndef USE_OSCAR
-	char buf[1024];
 	g_snprintf(buf, sizeof(buf), "toc_add_deny %s", normalize(name));
 	sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -309,10 +302,11 @@
 
 void serv_set_permit_deny()
 {
-#ifndef USE_OSCAR
 	char buf[MSG_LEN];
 	int at;
 	GList *list;
+
+#ifndef USE_OSCAR
         /* FIXME!  We flash here. */
         if (permdeny == 1 || permdeny == 3) {
         	g_snprintf(buf, sizeof(buf), "toc_add_permit");
@@ -340,16 +334,13 @@
 	}
 	buf[at] = 0;
 	sflap_send(buf, -1, TYPE_DATA);
-
-
-
 #endif
 }
 
 void serv_set_idle(int time)
 {
+	char buf[256];
 #ifndef USE_OSCAR
-	char buf[256];
 	g_snprintf(buf, sizeof(buf), "toc_set_idle %d", time);
 	sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -367,38 +358,24 @@
 #endif
 }
 
+void serv_build_config(char *buf, int len) {
+#ifndef USE_OSCAR
+	toc_build_config(buf, len);
+#endif
+}
+
 
 void serv_save_config()
 {
 #ifndef USE_OSCAR
 	char *buf = g_malloc(BUF_LONG);
 	char *buf2 = g_malloc(MSG_LEN);
-	toc_build_config(buf, BUF_LONG / 2);
+	serv_build_config(buf, BUF_LONG / 2);
 	g_snprintf(buf2, MSG_LEN, "toc_set_config {%s}", buf);
         sflap_send(buf2, -1, TYPE_DATA);
 	g_free(buf2);
 	g_free(buf);
-#else
-	FILE *f;
-	char *buf = g_malloc(BUF_LONG);
-	char file[1024];
-
-	g_snprintf(file, sizeof(file), "%s/.gaimbuddy", getenv("HOME"));
-
-        if ((f = fopen(file,"w"))) {
-                build_config(buf, BUF_LONG - 1);
-                fprintf(f, "%s\n", buf);
-                fclose(f);
-                chmod(buf, S_IRUSR | S_IWUSR);
-        } else {
-                g_snprintf(buf, BUF_LONG / 2, "Error writing file %s", file);
-                do_error_dialog(buf, "Error");
-        }
-        
-        g_free(buf);
-
 #endif
-	
 }
 
 
@@ -414,8 +391,8 @@
 
 void serv_join_chat(int exchange, char *name)
 {
+        char buf[BUF_LONG];
 #ifndef USE_OSCAR
-        char buf[BUF_LONG];
         g_snprintf(buf, sizeof(buf)/2, "toc_chat_join %d \"%s\"", exchange, name);
         sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -423,8 +400,8 @@
 
 void serv_chat_invite(int id, char *message, char *name)
 {
+        char buf[BUF_LONG];
 #ifndef USE_OSCAR
-        char buf[BUF_LONG];
         g_snprintf(buf, sizeof(buf)/2, "toc_chat_invite %d \"%s\" %s", id, message, normalize(name));
         sflap_send(buf, -1, TYPE_DATA);
 #endif
@@ -442,8 +419,8 @@
 
 void serv_chat_whisper(int id, char *who, char *message)
 {
+        char buf2[MSG_LEN];
 #ifndef USE_OSCAR
-        char buf2[MSG_LEN];
         g_snprintf(buf2, sizeof(buf2), "toc_chat_whisper %d %s \"%s\"", id, who, message);
         sflap_send(buf2, -1, TYPE_DATA);
 #endif
@@ -451,8 +428,8 @@
 
 void serv_chat_send(int id, char *message)
 {
+        char buf[MSG_LEN];
 #ifndef USE_OSCAR
-        char buf[MSG_LEN];
         g_snprintf(buf, sizeof(buf), "toc_chat_send %d \"%s\"",id, message);
         sflap_send(buf, -1, TYPE_DATA);
 #endif
--- a/src/toc.c	Thu May 18 18:20:18 2000 +0000
+++ b/src/toc.c	Sat May 20 00:30:53 2000 +0000
@@ -35,6 +35,7 @@
 #include "gaim.h"
 #include "gnome_applet_mgr.h"
 
+#ifndef USE_OSCAR
 
 
 /* descriptor for talking to TOC */
@@ -843,6 +844,8 @@
         }
 }
 
+#endif /* USE_OSCAR */
+
 void parse_toc_buddy_list(char *config)
 {
 	char *c;