changeset 1535:1e2cc8c8bf3c

[gaim-migrate @ 1545] libfaim updates. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Mon, 05 Mar 2001 03:59:32 +0000
parents d98b92e3d9ff
children 3a9f11c9278b
files ChangeLog TODO libfaim/CHANGES libfaim/Makefile.am libfaim/adverts.c libfaim/aim.h libfaim/aim_ads.c 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_ft.c libfaim/aim_im.c libfaim/aim_info.c libfaim/aim_login.c libfaim/aim_logoff.c libfaim/aim_meta.c libfaim/aim_misc.c libfaim/aim_msgcookie.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/auth.c libfaim/buddylist.c libfaim/chat.c libfaim/chatnav.c libfaim/conn.c libfaim/faim/aim.h libfaim/faim/aim_cbtypes.h libfaim/faim/faimconfig.h libfaim/faimconfig.h libfaim/ft.c libfaim/im.c libfaim/info.c libfaim/libfaim_config.h libfaim/login.c libfaim/meta.c libfaim/misc.c libfaim/msgcookie.c libfaim/rxhandlers.c libfaim/rxqueue.c libfaim/search.c libfaim/snac.c libfaim/tlv.c libfaim/txqueue.c libfaim/util.c src/dialogs.c src/gaim.h src/oscar.c
diffstat 55 files changed, 12271 insertions(+), 11410 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Mar 04 22:37:18 2001 +0000
+++ b/ChangeLog	Mon Mar 05 03:59:32 2001 +0000
@@ -5,6 +5,7 @@
 	* Napster plugin is included again
 	  (Thanks for pointing out that it was missing,
 	  (Yan V. Bulgak)
+	* Oscar can: search by email, request confirmation, change password
 
 version 0.11.0-pre5 (02/26/2001):
 	* Minor GUI changes
--- a/TODO	Sun Mar 04 22:37:18 2001 +0000
+++ b/TODO	Mon Mar 05 03:59:32 2001 +0000
@@ -15,12 +15,6 @@
 	Sounds on buddy pounce
 	Implement system logging
 
-	I have an updated copy of libfaim. It adds a few new features
-		(change password, confirm, req. email, change email,
-		search by email). Unfortunately it breaks direct im.
-		Once direct im is fixed (which should also cause get
-		file to work) I'll add all these things to gaim.
-
 	ICQ through Oscar plugin (requires hacking libfaim :-/)
 
 	Syd is cool and gave all of these ideas:
--- a/libfaim/CHANGES	Sun Mar 04 22:37:18 2001 +0000
+++ b/libfaim/CHANGES	Mon Mar 05 03:59:32 2001 +0000
@@ -1,6 +1,61 @@
 
 No release numbers
 ------------------
+ - Mon Mar  5 01:19:48 UTC 2001
+  - Fix typo in last CHANGES entry.
+  - Add fix from Eric to keep efence from complaining about malloc(0)
+  - Add .cvsignore files (also from Eric)
+  - Throw in josh's latest OFT/ODC kludges
+  - Completly remove all calls to printf and (unbounded) sprintf
+  - Add aim_setdebuggingcb()
+  - Remove preprocessor-based debug semantics (faimconfig.h)
+  - faimdprintf() now calls a user-specified callback to do printing
+  - Rearrange aim_tx_new() parameters to be more sensical, add sess
+  - Do some sanity checking in aim_tx_new()
+  - Add #defines to keep printf from compiling inside libfaim
+  - Who knows what else. (in total, this patch is nearly 5k lines)
+
+ - Fri Mar  2 02:17:22 UTC 2001
+  - Add aim_fingerprintclient(). (AB will trust this, but you shouldn't.)
+  - Add some EXTRA_DIST to Makefile.am
+
+ - Mon Feb 26 01:46:34 UTC 2001
+  - Fix swapped bytes in aim_caps[]. Thanks to 
+      Bruce Miller <bmiller@ics.uci.edu> for pointing this out.
+
+ - Sun Feb 11 01:07:36 UTC 2001
+  - Make secondary connections to the authorizer work again
+  - Make aim_auth_changepasswd() work again.
+  - Add aim_auth_setversions(), aim_auth_getinfo(), aim_auth_setemail(),
+    aim_auth_reqconfirm()
+  - Add middle handlers for responses to all of above.
+  - Add examples of above to faimtest (start with 'reqauth')
+ 
+ - Fri Feb  9 22:31:22 UTC 2001
+  - Add aim_tx_setenqueue().
+
+ - Thu Feb  8 20:12:39 UTC 2001
+  - Let TLV functions out of aim.h if FAIM_NEED_TLV is defined.
+
+ - Thu Feb  8 02:31:25 UTC 2001
+  - Remove aim_purgecookies() -- never used anyway
+  - Clean up a few bits of aim_msgcookie.c
+  - Remove AIM_CONN_MAX constant (hasn't been relevent in ages)
+  - Change commentary on synchro in aim.h
+  - Add NOPLOCKS and make them default
+  - Make faim/aim.h more of a mess; try to keep interfaces hiddenish.
+  - Add #define FAIM_INTERNAL to top of every file
+  - Remove aim_countconn (no one uses it anyway)
+  - Add aim_cloneconn() (cringe.)
+  - Throw in josh's getfile patch (and then munge it enough to break it)
+  - Add getopt() to faimtest.
+  - Remove aim_buildcode.h/mkbuildinfo.sh crap.
+  - Clean up aim_cleansnacs()
+  - There may have been other stuff too.  As always, the diff is the 
+    most accurate changelog there is.
+  - *** FT is currently more broken than it was before.  Also, this patch
+    introduces even more memory leaks.  You've been warned.
+
  - Sun Dec 17 07:19:04 UTC 2000
   - Update the capability block list
 
--- a/libfaim/Makefile.am	Sun Mar 04 22:37:18 2001 +0000
+++ b/libfaim/Makefile.am	Mon Mar 05 03:59:32 2001 +0000
@@ -1,30 +1,29 @@
 noinst_LIBRARIES = libfaim.a
 
-EXTRA_DIST = faim/aim.h faim/aim_cbtypes.h faim/faimconfig.h md5.h README \
+EXTRA_DIST = aim.h aim_cbtypes.h faimconfig.h md5.h README \
 		README.gaim CHANGES CHANGES.gaim COPYING BUGS AUTHORS
 
-libfaim_a_SOURCES = 	aim_ads.c		\
-			aim_auth.c 		\
-			aim_buddylist.c		\
-			aim_chat.c		\
-			aim_chatnav.c		\
-			aim_conn.c		\
-			aim_ft.c		\
-			aim_im.c		\
-			aim_info.c		\
-			aim_login.c		\
-			aim_logoff.c		\
-			aim_meta.c		\
-			aim_misc.c		\
-			aim_msgcookie.c		\
-			aim_rxhandlers.c	\
-			aim_rxqueue.c		\
-			aim_search.c		\
-			aim_snac.c		\
-			aim_tlv.c		\
-			aim_txqueue.c		\
-			aim_util.c		\
-			md5.c
+libfaim_a_SOURCES = 	adverts.c	\
+			auth.c 		\
+			buddylist.c	\
+			chat.c		\
+			chatnav.c	\
+			conn.c		\
+			ft.c		\
+			im.c		\
+			info.c		\
+			login.c		\
+			md5.c		\
+			meta.c		\
+			misc.c		\
+			msgcookie.c	\
+			rxhandlers.c	\
+			rxqueue.c	\
+			search.c	\
+			snac.c		\
+			tlv.c		\
+			txqueue.c	\
+			util.c
 
 CFLAGS += -DAIM_BUILDDATE=\"`date +%Y%m%d`\" -DAIM_BUILDTIME=\"`date +%H%M%S`\"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/adverts.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,43 @@
+/*
+ *
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess,
+					      struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 0x1a)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0002);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0013);
+
+  i+= aimutil_put16(newpacket->data+i, 0x0005);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess,
+					     struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/aim.h	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,918 @@
+/* 
+ * Main libfaim header.  Must be included in client for prototypes/macros.
+ *
+ * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
+ *                                                -- Josh Meyer
+ *
+ */
+
+#ifndef __AIM_H__
+#define __AIM_H__
+
+#define FAIM_VERSION_MAJOR 0
+#define FAIM_VERSION_MINOR 99
+#define FAIM_VERSION_MINORMINOR 1
+
+#include <faimconfig.h>
+#include <aim_cbtypes.h>
+
+#if !defined(FAIM_USEPTHREADS) && !defined(FAIM_USEFAKELOCKS) && !defined(FAIM_USENOPLOCKS)
+#error pthreads, fakelocks, or noplocks are currently required.
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#else
+#include <sys/time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef FAIM_USEPTHREADS
+#include <pthread.h>
+#define faim_mutex_t pthread_mutex_t 
+#define faim_mutex_init(x) pthread_mutex_init(x, NULL)
+#define faim_mutex_lock(x) pthread_mutex_lock(x)
+#define faim_mutex_unlock(x) pthread_mutex_unlock(x)
+#define faim_mutex_destroy(x) pthread_mutex_destroy(x)
+#elif defined(FAIM_USEFAKELOCKS)
+/*
+ * For platforms without pthreads, we also assume
+ * we're not linking against a threaded app.  Which
+ * means we don't have to do real locking.  The 
+ * macros below do nothing really.  They're a joke.
+ * But they get it to compile.
+ */
+#define faim_mutex_t char
+#define faim_mutex_init(x) *x = 0
+#define faim_mutex_lock(x) while(*x != 0) {/* spin */}; *x = 1;
+#define faim_mutex_unlock(x) while(*x != 0) {/* spin spin spin */}; *x = 0;
+#define faim_mutex_destroy(x) while(*x != 0) {/* spiiiinnn */}; *x = 0;
+#elif defined(FAIM_USENOPLOCKS)
+#define faim_mutex_t char
+#define faim_mutex_init(x)
+#define faim_mutex_lock(x)
+#define faim_mutex_unlock(x)
+#define faim_mutex_destroy(x)
+#endif
+
+/* Portability stuff (DMP) */
+
+#ifdef _WIN32
+#define sleep Sleep
+#define socklen_t int /* this must be a POSIXy thing */
+#define snprintf _snprintf /* I'm not sure whats wrong with Microsoft here */
+#define close(x) closesocket(x) /* no comment */
+#endif
+
+#if defined(mach) && defined(__APPLE__)
+#define gethostbyname(x) gethostbyname2(x, AF_INET) 
+#endif
+
+#if defined(_WIN32) || defined(STRICT_ANSI)
+#define faim_shortfunc
+#else
+#define faim_shortfunc inline
+#endif
+
+#if defined(_WIN32) && !defined(WIN32_STATIC)
+/*
+ * For a win32 DLL, we define WIN32_INDLL if this file
+ * is included while compiling the DLL. If its not 
+ * defined (its included in a client app), the symbols
+ * will be imported instead of exported.
+ */
+#ifdef WIN32_INDLL
+#define faim_export __declspec(dllexport)
+#else 
+#define faim_export __declspec(dllimport)
+#endif /* WIN32_INDLL */
+#define faim_internal
+#else
+/*
+ * Nothing normally needed for unix...
+ */
+#define faim_export
+#define faim_internal
+#endif
+
+/* 
+ * Current Maximum Length for Screen Names (not including NULL) 
+ *
+ * Currently only names up to 16 characters can be registered
+ * however it is aparently legal for them to be larger.
+ */
+#define MAXSNLEN 32
+
+/*
+ * Current Maximum Length for Instant Messages
+ *
+ * This was found basically by experiment, but not wholly
+ * accurate experiment.  It should not be regarded
+ * as completely correct.  But its a decent approximation.
+ *
+ * Note that although we can send this much, its impossible
+ * for WinAIM clients (up through the latest (4.0.1957)) to
+ * send any more than 1kb.  Amaze all your windows friends
+ * with utterly oversized instant messages!
+ *
+ * XXX: the real limit is the total SNAC size at 8192. Fix this.
+ * 
+ */
+#define MAXMSGLEN 7987
+
+/*
+ * Current Maximum Length for Chat Room Messages
+ *
+ * This is actually defined by the protocol to be
+ * dynamic, but I have yet to see due cause to 
+ * define it dynamically here.  Maybe later.
+ *
+ */
+#define MAXCHATMSGLEN 512
+
+/*
+ * Standard size of an AIM authorization cookie
+ */
+#define AIM_COOKIELEN            0x100
+
+#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
+
+/*
+ * 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 size */
+  int major;
+  int minor;
+  int build;
+  char country[3];
+  char lang[3];
+  int major2;
+  int minor2;
+  long unknown;
+};
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* 
+ * 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
+
+/* they start getting arbitrary in rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak OSCAR! */
+#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
+
+/*
+ * Subtypes, we need these for OFT stuff.
+ */
+#define AIM_CONN_SUBTYPE_OFT_DIRECTIM  0x0001
+#define AIM_CONN_SUBTYPE_OFT_GETFILE   0x0002
+#define AIM_CONN_SUBTYPE_OFT_SENDFILE  0x0003
+#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004
+#define AIM_CONN_SUBTYPE_OFT_VOICE     0x0005
+
+/*
+ * 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   0x0040
+#define AIM_CONN_STATUS_CONNERR     0x0080
+#define AIM_CONN_STATUS_INPROGRESS  0x0100
+
+#define AIM_FRAMETYPE_OSCAR 0x0000
+#define AIM_FRAMETYPE_OFT 0x0001
+
+struct aim_conn_t {
+  int fd;
+  unsigned short type;
+  unsigned short subtype;
+  int seqnum;
+  int status;
+  void *priv; /* misc data the client may want to store */
+  time_t lastactivity; /* time of last transmit */
+  int forcedlatency; 
+  struct aim_rxcblist_t *handlerlist;
+  faim_mutex_t active; /* lock around read/writes */
+  faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */
+  struct aim_conn_t *next;
+};
+
+/* struct for incoming commands */
+struct command_rx_struct {
+  unsigned char hdrtype; /* defines which piece of the union to use */
+  union {
+    struct { 
+      char type;
+      unsigned short seqnum;     
+    } oscar;
+    struct {
+      unsigned short type;
+      unsigned char magic[4]; /* ODC2 OFT2 */
+      unsigned short hdr2len;
+      unsigned char *hdr2; /* rest of bloated header */
+    } oft;
+  } hdr;
+  unsigned short commandlen;         /* total payload length */
+  unsigned char *data;             /* packet data (from 7 byte on) */
+  unsigned char lock;               /* 0 = open, !0 = locked */
+  unsigned char handled;            /* 0 = new, !0 = been handled */
+  unsigned char nofree;		    /* 0 = free data on purge, 1 = only unlink */
+  struct aim_conn_t *conn;  /* the connection it came in on... */
+  struct command_rx_struct *next; /* ptr to next struct in list */
+};
+
+/* struct for outgoing commands */
+struct command_tx_struct {
+  unsigned char hdrtype; /* defines which piece of the union to use */
+  union {
+    struct {
+      unsigned char type;
+      unsigned short seqnum;
+    } oscar;
+    struct {
+      unsigned short type;
+      unsigned char magic[4]; /* ODC2 OFT2 */
+      unsigned short hdr2len;
+      unsigned char *hdr2;
+    } oft;
+  } hdr;
+  u_int commandlen;         
+  u_char *data;      
+  u_int lock;               /* 0 = open, !0 = locked */
+  u_int sent;               /* 0 = pending, !0 = has been sent */
+  struct aim_conn_t *conn; 
+  struct command_tx_struct *next; /* ptr to next struct in list */
+};
+
+/*
+ * AIM Session: The main client-data interface.  
+ *
+ */
+struct aim_session_t {
+
+  /* ---- Client Accessible ------------------------ */
+  /* 
+   * Our screen name.
+   *
+   */
+  char sn[MAXSNLEN+1];
+  
+  /*
+   * Pointer to anything the client wants to 
+   * explicitly associate with this session.
+   *
+   * This is for use in the callbacks mainly. In any
+   * callback, you can access this with sess->aux_data.
+   *
+   */
+  void *aux_data;
+
+  /* ---- Internal Use Only ------------------------ */
+  /* 
+   * Connection information
+   */
+  struct aim_conn_t *connlist;
+  faim_mutex_t connlistlock;
+  
+  /* 
+   * TX/RX queues 
+   */
+  struct command_tx_struct *queue_outgoing;   
+  struct command_rx_struct *queue_incoming; 
+  
+  /*
+   * Tx Enqueuing function
+   */
+  int (*tx_enqueue)(struct aim_session_t *, struct command_tx_struct *);
+
+  /*
+   * This is a dreadful solution to the what-room-are-we-joining
+   * problem.  (There's no connection between the service
+   * request and the resulting redirect.)
+   */ 
+  char *pendingjoin;
+  unsigned short pendingjoinexchange;
+
+  /*
+   * Outstanding snac handling 
+   *
+   * XXX: Should these be per-connection? -mid
+   */
+  struct aim_snac_t *snac_hash[FAIM_SNAC_HASH_SIZE];
+  faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE];
+  unsigned long snac_nextid;
+
+  struct {
+    char server[128];
+    char username[128];
+    char password[128];
+  } socksproxy;
+
+  unsigned long flags;
+
+  int debug;
+  void (*debugcb)(struct aim_session_t *sess, int level, const char *format, va_list va); /* same as faim_debugging_callback_t */
+
+  struct aim_msgcookie_t *msgcookies;
+};
+
+/* Values for sess->flags */
+#define AIM_SESS_FLAGS_SNACLOGIN       0x00000001
+#define AIM_SESS_FLAGS_XORLOGIN        0x00000002
+#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004
+
+/*
+ * AIM User Info, Standard Form.
+ */
+struct aim_userinfo_s {
+  char sn[MAXSNLEN+1];
+  u_short warnlevel;
+  u_short idletime;
+  u_short flags;
+  u_long membersince;
+  u_long onlinesince;
+  u_long sessionlen;  
+  u_short capabilities;
+  struct {
+    unsigned short status;
+    unsigned int ipaddr;
+    char crap[0x25]; /* until we figure it out... */
+  } icqinfo;
+};
+
+#define AIM_FLAG_UNCONFIRMED 	0x0001 /* "damned transients" */
+#define AIM_FLAG_ADMINISTRATOR	0x0002
+#define AIM_FLAG_AOL		0x0004
+#define AIM_FLAG_OSCAR_PAY	0x0008
+#define AIM_FLAG_FREE 		0x0010
+#define AIM_FLAG_AWAY		0x0020
+#define AIM_FLAG_UNKNOWN40	0x0040
+#define AIM_FLAG_UNKNOWN80	0x0080
+
+#define AIM_FLAG_ALLUSERS	0x001f
+
+
+#if defined(FAIM_INTERNAL) || defined(FAIM_NEED_TLV)
+/*
+ * 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 */
+faim_internal struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen);
+faim_internal void aim_freetlvchain(struct aim_tlvlist_t **list);
+faim_internal struct aim_tlv_t *aim_grabtlv(u_char *src);
+faim_internal struct aim_tlv_t *aim_grabtlvstr(u_char *src);
+faim_internal struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, u_short, int);
+faim_internal char *aim_gettlv_str(struct aim_tlvlist_t *, u_short, int);
+faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num);
+faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num);
+faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num);
+faim_internal int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv);
+faim_internal struct aim_tlv_t *aim_createtlv(void);
+faim_internal int aim_freetlv(struct aim_tlv_t **oldtlv);
+faim_internal int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char  v);
+faim_internal int aim_puttlv_16(u_char *, u_short, u_short);
+faim_internal int aim_puttlv_32(u_char *, u_short, u_long);
+faim_internal int aim_puttlv_str(u_char *buf, u_short t, int l, char *v);
+faim_internal int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list);
+faim_internal int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val);
+faim_internal int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val);
+faim_internal int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len);
+faim_internal int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps);
+faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type);
+faim_internal int aim_counttlvchain(struct aim_tlvlist_t **list);
+#endif /* FAIM_INTERNAL */
+
+/*
+ * Get command from connections / Dispatch commands
+ * already in queue.
+ */
+faim_export int aim_get_command(struct aim_session_t *, struct aim_conn_t *);
+int aim_rxdispatch(struct aim_session_t *);
+
+faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+faim_export int aim_logoff(struct aim_session_t *);
+
+#ifndef FAIM_INTERNAL
+/* the library should never call aim_conn_kill */
+faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn);
+#endif /* ndef FAIM_INTERNAL */
+
+typedef int (*rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...);
+
+#ifdef FAIM_INTERNAL
+faim_internal unsigned long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype);
+faim_internal unsigned long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *);
+faim_internal unsigned long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *);
+#endif
+
+/* aim_login.c */
+faim_export int aim_sendconnack(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
+faim_export int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *, char *key);
+faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, int errorcode, char *errorurl, char *bosip, char *cookie, char *email, int regstatus);
+faim_export int aim_gencookie(unsigned char *buf);
+faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie);
+faim_export void aim_purge_rxqueue(struct aim_session_t *);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_internal int aim_recv(int fd, void *buf, size_t count);
+
+faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *command, ...);
+faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur);
+faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *);
+faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned char framing, int chan, int datalen);
+faim_internal int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *);
+faim_internal int aim_tx_printqueue(struct aim_session_t *);
+faim_internal int aim_parse_hostonline(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+faim_internal int aim_parse_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_parse_infochange(struct aim_session_t *sess, struct command_rx_struct *command);
+#endif /* FAIM_INTERNAL */
+
+#define AIM_TX_QUEUED    0 /* default */
+#define AIM_TX_IMMEDIATE 1
+#define AIM_TX_USER      2
+faim_export int aim_tx_setenqueue(struct aim_session_t *sess, int what,  int (*func)(struct aim_session_t *, struct command_tx_struct *));
+
+faim_export int aim_tx_flushqueue(struct aim_session_t *);
+faim_export void aim_tx_purgequeue(struct aim_session_t *);
+
+struct aim_rxcblist_t {
+  u_short family;
+  u_short type;
+  rxcallback_t handler;
+  u_short flags;
+  struct aim_rxcblist_t *next;
+};
+
+faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval);
+
+faim_export int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, rxcallback_t newhandler, u_short flags);
+faim_export int aim_clearhandlers(struct aim_conn_t *conn);
+
+#ifdef FAIM_INTERNAL
+faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess, struct aim_conn_t *conn, u_short family, u_short type);
+#endif
+
+#ifdef FAIM_INTERNAL
+/*
+ * Generic SNAC structure.  Rarely if ever used.
+ */
+struct aim_snac_t {
+  u_long id;
+  u_short family;
+  u_short type;
+  u_short flags;
+  void *data;
+  time_t issuetime;
+  struct aim_snac_t *next;
+};
+faim_internal void aim_initsnachash(struct aim_session_t *sess);
+faim_internal unsigned long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac);
+faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, const unsigned short family, const unsigned short type, const unsigned short flags, const void *data, const int datalen);
+faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id);
+faim_internal int aim_cleansnacs(struct aim_session_t *, int maxage);
+faim_internal int aim_putsnac(u_char *, int, int, int, u_long);
+#endif /* FAIM_INTERNAL */
+
+#ifdef FAIM_INTERNAL
+faim_internal void aim_connrst(struct aim_session_t *);
+faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *);
+faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess, struct aim_conn_t *src);
+#endif /* FAIM_INTERNAL */
+
+faim_export void aim_conn_close(struct aim_conn_t *deadconn);
+faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest);
+faim_export int aim_conngetmaxfd(struct aim_session_t *);
+faim_export struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *);
+faim_export int aim_conn_isready(struct aim_conn_t *);
+faim_export int aim_conn_setstatus(struct aim_conn_t *, int);
+faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_conn_isconnecting(struct aim_conn_t *conn);
+
+typedef void (*faim_debugging_callback_t)(struct aim_session_t *sess, int level, const char *format, va_list va);
+faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t);
+faim_export void aim_session_init(struct aim_session_t *, unsigned long flags, int debuglevel);
+faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password);
+faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type);
+
+/* aim_misc.c */
+
+#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
+#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
+#define AIM_VISIBILITYCHANGE_DENYADD      0x07
+#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
+
+#define AIM_PRIVFLAGS_ALLOWIDLE           0x01
+#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE    0x02
+
+#define AIM_WARN_ANON                     0x01
+
+faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon);
+faim_export unsigned long aim_bos_nop(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long);
+faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *);
+faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *);
+faim_export unsigned long aim_bos_setprofile(struct aim_session_t *, struct aim_conn_t *, char *, char *, unsigned short);
+faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long);
+faim_export unsigned long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long);
+faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short);
+faim_export unsigned long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,struct aim_conn_t *conn);
+faim_export unsigned long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_auth_reqconfirm(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short info);
+faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess, struct aim_conn_t *conn, char *newemail);
+faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy);
+faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy);
+faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long status);
+
+faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *);
+faim_internal int aim_listenestablish(u_short);
+faim_internal int aim_tx_destroy(struct command_tx_struct *);
+#endif /* FAIM_INTERNAL */
+
+/* aim_rxhandlers.c */
+faim_export int aim_rxdispatch(struct aim_session_t *);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_authparse(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_handleredirect_middle(struct aim_session_t *, struct command_rx_struct *, ...);
+faim_internal int aim_parse_unknown(struct aim_session_t *, struct command_rx_struct *, ...);
+faim_internal int aim_parse_generalerrs(struct aim_session_t *, struct command_rx_struct *command, ...);
+faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+
+/* these are used by aim_*_clientready */
+#define AIM_TOOL_JAVA   0x0001
+#define AIM_TOOL_MAC    0x0002
+#define AIM_TOOL_WIN16  0x0003
+#define AIM_TOOL_WIN32  0x0004
+#define AIM_TOOL_MAC68K 0x0005
+#define AIM_TOOL_MACPPC 0x0006
+#define AIM_TOOL_NEWWIN 0x0010
+struct aim_tool_version {
+  unsigned short group;
+  unsigned short version;
+  unsigned short tool;
+  unsigned short toolversion;
+};
+
+#endif /* FAIM_INTERNAL */
+
+#define AIM_CLIENTTYPE_UNKNOWN  0x0000
+#define AIM_CLIENTTYPE_MC       0x0001
+#define AIM_CLIENTTYPE_WINAIM   0x0002
+#define AIM_CLIENTTYPE_WINAIM41 0x0003
+#define AIM_CLIENTTYPE_AOL_TOC  0x0004
+faim_export unsigned short aim_fingerprintclient(unsigned char *msghdr, int len);
+
+#define AIM_RATE_CODE_CHANGE     0x0001
+#define AIM_RATE_CODE_WARNING    0x0002
+#define AIM_RATE_CODE_LIMIT      0x0003
+#define AIM_RATE_CODE_CLEARLIMIT 0x0004
+faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+
+faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+#endif /* FAIM_INTERNAL */
+
+/* aim_im.c */
+struct aim_directim_priv {
+  unsigned char cookie[8];
+  char sn[MAXSNLEN+1];
+  char ip[30];
+};
+
+struct aim_fileheader_t {
+#if 0
+  char  magic[4];            /* 0 */
+  short hdrlen;           /* 4 */
+  short hdrtype;             /* 6 */
+#endif
+  char  bcookie[8];       /* 8 */
+  short encrypt;          /* 16 */
+  short compress;         /* 18 */
+  short totfiles;         /* 20 */
+  short filesleft;        /* 22 */
+  short totparts;         /* 24 */
+  short partsleft;        /* 26 */
+  long  totsize;          /* 28 */
+  long  size;             /* 32 */
+  long  modtime;          /* 36 */
+  long  checksum;         /* 40 */
+  long  rfrcsum;          /* 44 */
+  long  rfsize;           /* 48 */
+  long  cretime;          /* 52 */
+  long  rfcsum;           /* 56 */
+  long  nrecvd;           /* 60 */
+  long  recvcsum;         /* 64 */
+  char  idstring[32];     /* 68 */
+  char  flags;            /* 100 */
+  char  lnameoffset;      /* 101 */
+  char  lsizeoffset;      /* 102 */
+  char  dummy[69];        /* 103 */
+  char  macfileinfo[16];  /* 172 */
+  short nencode;          /* 188 */
+  short nlanguage;        /* 190 */
+  char  name[64];         /* 192 */
+                          /* 256 */
+};
+
+struct aim_filetransfer_priv {
+  char sn[MAXSNLEN];
+  char cookie[8];
+  char ip[30];
+  int state;
+  struct aim_fileheader_t fh;
+};
+
+
+#define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */
+#define AIM_IMFLAGS_ACK  0x02 /* request a receipt notice */
+
+faim_export unsigned long aim_send_im(struct aim_session_t *, struct aim_conn_t *, char *, u_int, char *);
+faim_export int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *);
+faim_export struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *destsn);
+faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *);
+faim_export unsigned long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_parse_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+faim_internal int aim_parse_missedcall(struct aim_session_t *sess, struct command_rx_struct *command);
+#endif /* FAIM_INTERNAL */
+
+faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn);
+faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size);
+faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+/* aim_info.c */
+#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
+#define AIM_CAPS_GAMES 0x40
+#define AIM_CAPS_SAVESTOCKS 0x80
+
+#ifdef FAIM_INTERNAL
+extern u_char aim_caps[8][16];
+faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen);
+faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps);
+#endif /* FAIM_INTERNAL */
+
+
+#define AIM_GETINFO_GENERALINFO 0x00001
+#define AIM_GETINFO_AWAYMESSAGE 0x00003
+
+struct aim_msgcookie_t {
+  unsigned char cookie[8];
+  int type;
+  void *data;
+  time_t addtime;
+  struct aim_msgcookie_t *next;
+};
+
+struct aim_invite_priv {
+  char *sn;
+  char *roomname;
+  int exchange;
+  int instance;
+};
+
+#define AIM_COOKIETYPE_UNKNOWN  0x00
+#define AIM_COOKIETYPE_ICBM     0x01
+#define AIM_COOKIETYPE_ADS      0x02
+#define AIM_COOKIETYPE_BOS      0x03
+#define AIM_COOKIETYPE_IM       0x04
+#define AIM_COOKIETYPE_CHAT     0x05
+#define AIM_COOKIETYPE_CHATNAV  0x06
+#define AIM_COOKIETYPE_INVITE   0x07
+/* we'll move OFT up a bit to give breathing room.  not like it really
+ * matters. */
+#define AIM_COOKIETYPE_OFTIM    0x10
+#define AIM_COOKIETYPE_OFTGET   0x11
+#define AIM_COOKIETYPE_OFTSEND  0x12
+#define AIM_COOKIETYPE_OFTVOICE 0x13
+#define AIM_COOKIETYPE_OFTIMAGE 0x14
+#define AIM_COOKIETYPE_OFTICON  0x15
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_cachecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
+faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type);
+faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *, int, void *);
+faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *, const unsigned char *, const int);
+faim_internal int aim_freecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
+faim_internal int aim_msgcookie_gettype(int reqclass);
+faim_internal int aim_cookie_free(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
+#endif /* FAIM_INTERNAL */
+
+faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur);
+
+#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
+#define AIM_TRANSFER_DENY_DECLINE 0x0001
+#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
+faim_export unsigned long aim_denytransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sender, char *cookie, unsigned short code);
+faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *cookie, char *ip, unsigned short listingfiles, unsigned short listingtotsize, unsigned short listingsize, unsigned int listingchecksum, unsigned short rendid);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *, struct aim_userinfo_s *);
+faim_internal int aim_parse_userinfo_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_parse_oncoming_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_parse_offgoing_middle(struct aim_session_t *, struct command_rx_struct *);
+faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info);
+faim_internal int aim_parse_locateerr(struct aim_session_t *sess, struct command_rx_struct *command);
+#endif /* FAIM_INTERNAL */
+
+faim_export unsigned long aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *, unsigned short);
+faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info);
+faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
+
+
+/* aim_auth.c */
+faim_export int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *);
+faim_export u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *);
+faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *);
+
+/* aim_buddylist.c */
+faim_export unsigned long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
+faim_export unsigned long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+#endif /* FAIM_INTERNAL */
+
+/* aim_search.c */
+faim_export u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *);
+
+#ifdef FAIM_INTERNAL
+faim_internal unsigned long aim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *);
+faim_internal unsigned long aim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *);
+#endif
+
+struct aim_chat_roominfo {
+  u_short exchange;
+  char *name;
+  u_short instance;
+};
+
+struct aim_chat_exchangeinfo {
+  u_short number;
+  char *name;
+  char *charset1;
+  char *lang1;
+  char *charset2;
+  char *lang2;
+};
+
+faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg);
+faim_export unsigned long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname);
+faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname);
+faim_export char *aim_chat_getname(struct aim_conn_t *conn);
+faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name);
+
+faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn);
+faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
+
+faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance);
+
+faim_export u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange);
+faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name);
+
+#ifdef FAIM_INTERNAL
+faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo);
+faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, struct command_rx_struct *command);
+faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command);
+#endif /* FAIM_INTERNAL */
+
+/* aim_util.c */
+#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);
+u_long aimutil_get32(u_char *);
+#endif
+
+faim_export int aimutil_putstr(u_char *, const char *, int);
+faim_export int aimutil_tokslen(char *toSearch, int index, char dl);
+faim_export int aimutil_itemcnt(char *toSearch, char dl);
+faim_export char *aimutil_itemidx(char *toSearch, int index, char dl);
+
+faim_export int aim_snlen(const char *sn);
+faim_export int aim_sncmp(const char *sn1, const char *sn2);
+
+/* for libc's that dont have it */
+faim_export char *aim_strsep(char **pp, const char *delim);
+
+/* aim_meta.c */
+faim_export char *aim_getbuilddate(void);
+faim_export char *aim_getbuildtime(void);
+faim_export char *aim_getbuildstring(void);
+
+#ifdef FAIM_INTERNAL
+faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...);
+
+/* why the hell wont cpp let you use #error inside #define's? */
+#define printf() printf called inside libfaim
+#define sprintf() unbounded sprintf used inside libfaim
+
+#endif /* FAIM_INTERNAL */
+
+#endif /* __AIM_H__ */
+
--- a/libfaim/aim_ads.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- *
- *
- */
-
-#include <faim/aim.h>
-
-faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
-{
-  struct command_tx_struct *newpacket;
-  int i;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x1a)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
-
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0002);
-
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0013);
-
-  i+= aimutil_put16(newpacket->data+i, 0x0005);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid++);
-}
-
-faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess,
-					     struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0005, 0x0002);
-}
--- a/libfaim/aim_auth.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
-  aim_auth.c
-
-  Deals with the authorizer.
-
- */
-
-#include <faim/aim.h> 
-
-/* this just pushes the passed cookie onto the passed connection -- NO SNAC! */
-faim_export int aim_auth_sendcookie(struct aim_session_t *sess, 
-				    struct aim_conn_t *conn, 
-				    unsigned char *chipsahoy)
-{
-  struct command_tx_struct *newpacket;
-  int curbyte=0;
-  
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0001, conn, 4+2+2+AIM_COOKIELEN)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  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);
-
-  return aim_tx_enqueue(sess, newpacket);
-}
-
-faim_export unsigned long aim_auth_clientready(struct aim_session_t *sess,
-					       struct aim_conn_t *conn)
-{
-  struct command_tx_struct *newpacket;
-  int curbyte = 0;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 26)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  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(sess, newpacket);
-
-  aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, NULL, 0);
-
-  return sess->snac_nextid;
-}
-
-faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *sess,
-						struct aim_conn_t *conn, 
-						char *new, char *current)
-{
-  struct command_tx_struct *newpacket;
-  int i;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+4+strlen(current)+4+strlen(new))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
-
-  /* current password TLV t(0002) */
-  i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(current), current);
-
-  /* new password TLV t(0012) */
-  i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(new), new);
-
-  aim_tx_enqueue(sess, newpacket);
-
-  aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, NULL, 0);
-
-  return sess->snac_nextid;
-}
--- a/libfaim/aim_buddylist.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-
-#include <faim/aim.h>
-
-/*
- * aim_add_buddy()
- *
- * Adds a single buddy to your buddy list after login.
- *
- */
-faim_export unsigned long aim_add_buddy(struct aim_session_t *sess,
-					struct aim_conn_t *conn, 
-					char *sn )
-{
-   struct command_tx_struct *newpacket;
-   int i;
-
-   if(!sn)
-     return -1;
-
-   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn))))
-     return -1;
-
-   newpacket->lock = 1;
-
-   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));
-
-   aim_tx_enqueue(sess, newpacket );
-
-   aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
-
-   return sess->snac_nextid;
-}
-
-faim_export unsigned long aim_remove_buddy(struct aim_session_t *sess,
-					   struct aim_conn_t *conn, 
-					   char *sn )
-{
-   struct command_tx_struct *newpacket;
-   int i;
-
-   if(!sn)
-     return -1;
-
-   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+1+strlen(sn))))
-     return -1;
-
-   newpacket->lock = 1;
-
-   i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid);
-
-   i += aimutil_put8(newpacket->data+i, strlen(sn));
-   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
-
-   aim_tx_enqueue(sess, newpacket);
-
-   aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
-
-   return sess->snac_nextid;
-}
-
-faim_internal int aim_parse_buddyrights(struct aim_session_t *sess,
-					struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  int ret=1;
-  struct aim_tlvlist_t *tlvlist;
-  unsigned short maxbuddies = 0, maxwatchers = 0;
-
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
-    return ret;
-
-  /*
-   * TLV type 0x0001: Maximum number of buddies.
-   */
-  if (aim_gettlv(tlvlist, 0x0001, 1))
-    maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1);
-
-  /*
-   * TLV type 0x0002: Maximum number of watchers.
-   *
-   * XXX: what the hell is a watcher? 
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0002, 1))
-    maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
-  
-  if ((userfunc = aim_callhandler(command->conn, 0x0003, 0x0003)))
-    ret =  userfunc(sess, command, maxbuddies, maxwatchers);
-
-  aim_freetlvchain(&tlvlist);
-
-  return ret;  
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/aim_cbtypes.h	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,231 @@
+/*
+ * 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_OFT 0xfffe /* OFT/Rvous */
+#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_RIGHTSQUERY 0x0002
+#define AIM_CB_BOS_RIGHTS 0x0003
+#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
+
+/*
+ * OFT Services
+ *
+ * See non-SNAC note below.
+ */
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
+#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+
+#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */
+#define AIM_CB_OFT_GETFILEFILEREQ 0x0008    /* received file request */
+#define AIM_CB_OFT_GETFILEFILESEND 0x0009   /* received file request confirm -- send data */
+#define AIM_CB_OFT_GETFILECOMPLETE 0x000a   /* received file send complete*/
+#define AIM_CB_OFT_GETFILEINITIATE 0x000b   /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c   /* OFT connection disconnected.*/
+#define AIM_CB_OFT_GETFILELISTING 0x000d   /* OFT listing.txt received.*/
+#define AIM_CB_OFT_GETFILERECEIVE 0x000e   /* OFT file incoming.*/
+#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f
+#define AIM_CB_OFT_GETFILESTATE4 0x0010
+
+#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020   /* OFT connection disconnected.*/
+
+
+
+/*
+ * 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_CONNERR 0x0003
+#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
+#define AIM_CB_SPECIAL_FLAPVER 0x0005
+#define AIM_CB_SPECIAL_DEBUGCONN_CONNECT 0xe001
+#define AIM_CB_SPECIAL_UNKNOWN 0xffff
+#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
+
+
+#endif/*__AIM_CBTYPES_H__ */
--- a/libfaim/aim_chat.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,647 +0,0 @@
-/*
- * aim_chat.c
- *
- * Routines for the Chat service.
- *
- */
-
-#include <faim/aim.h> 
-
-faim_export 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 ! */
-}
-
-faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
-{
-  struct aim_conn_t *cur;
-  
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->type != AIM_CONN_TYPE_CHAT)
-      continue;
-    if (!cur->priv) {
-      printf("faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
-      continue;
-    }
-    if (strcmp((char *)cur->priv, name) == 0)
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return cur;
-}
-
-faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
-{
-  if (!conn || !roomname)
-    return -1;
-
-  if (conn->priv)
-    free(conn->priv);
-
-  conn->priv = strdup(roomname);
-
-  return 0;
-}
-
-faim_export unsigned 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(AIM_FRAMETYPE_OSCAR, 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) rand());
-
-  aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL));
-
-  /*
-   * 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++);
-}
-
-/*
- * Join a room of name roomname.  This is the first
- * step to joining an already created room.  It's 
- * basically a Service Request for family 0x000e, 
- * with a little added on to specify the exchange
- * and room name.
- *
- */
-faim_export unsigned long aim_chat_join(struct aim_session_t *sess,
-					struct aim_conn_t *conn, 
-					u_short exchange,
-					const char *roomname)
-{
-  struct command_tx_struct *newpacket;
-  int i;
-
-  if (!sess || !conn || !roomname)
-    return 0;
-  
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+9+strlen(roomname)+2)))
-    return -1;
-
-  newpacket->lock = 1;
-  
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);
-
-  i+= aimutil_put16(newpacket->data+i, 0x000e);
-
-  /* 
-   * this is techinally a TLV, but we can't use normal functions
-   * because we need the extraneous nulls and other weird things.
-   */
-  i+= aimutil_put16(newpacket->data+i, 0x0001);
-  i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
-  i+= aimutil_put16(newpacket->data+i, exchange);
-  i+= aimutil_put8(newpacket->data+i, strlen(roomname));
-  i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
-  i+= aimutil_put16(newpacket->data+i, 0x0000); /* instance? */
-
-  /*
-   * Chat hack.
-   *
-   * XXX: A problem occurs here if we request a channel
-   *      join but it fails....pendingjoin will be nonnull
-   *      even though the channel is never going to get a
-   *      redirect!
-   *
-   */
-  sess->pendingjoin = strdup(roomname);
-  sess->pendingjoinexchange = exchange;
-
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1);
-
-  return sess->snac_nextid;
-}
-
-faim_internal 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)
- *
- * SNAC 000e/0002
- */
-faim_internal 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;
-  unsigned short unknown_c9 = 0;
-  unsigned long creationtime = 0;
-  unsigned short maxmsglen = 0;
-  unsigned short unknown_d2 = 0, unknown_d5 = 0;
-
-  i = 10;
-  i += aim_chat_readroominfo(command->data+i, &roominfo);
-  
-  detaillevel = aimutil_get8(command->data+i);
-  i++;
-
-  if (detaillevel != 0x02) {
-    if (detaillevel == 0x01)
-      printf("faim: chat_roomupdateinfo: detail level 1 not supported\n");
-    else
-      printf("faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
-    return 1;
-  }
-  
-  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))
-    usercount = aim_gettlv16(tlvlist, 0x006f, 1);
-
-  /*
-   * 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. (2 bytes)
-   */
-  if (aim_gettlv(tlvlist, 0x00c9, 1))
-    unknown_c9 = aim_gettlv16(tlvlist, 0x00c9, 1);
-  
-  /* 
-   * Type 0x00ca: Creation time (4 bytes)
-   */
-  if (aim_gettlv(tlvlist, 0x00ca, 1))
-    creationtime = aim_gettlv32(tlvlist, 0x00ca, 1);
-
-  /* 
-   * Type 0x00d1: Maximum Message Length
-   */
-  if (aim_gettlv(tlvlist, 0x00d1, 1))
-    maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1);
-
-  /* 
-   * Type 0x00d2: Unknown. (2 bytes)
-   */
-  if (aim_gettlv(tlvlist, 0x00d2, 1))
-    unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1);
-
-  /* 
-   * Type 0x00d3: Room Description
-   */
-  if (aim_gettlv(tlvlist, 0x00d3, 1))
-    roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
-
-  /* 
-   * Type 0x00d5: Unknown. (1 byte)
-   */
-  if (aim_gettlv(tlvlist, 0x00d5, 1))
-    unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1);
-
-
-  if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE))) {
-    ret = userfunc(sess,
-		   command, 
-		   &roominfo,
-		   roomname,
-		   usercount,
-		   userinfo,	
-		   roomdesc,
-		   unknown_c9,
-		   creationtime,
-		   maxmsglen,
-		   unknown_d2,
-		   unknown_d5);
-  }	
-  free(roominfo.name);
-  free(userinfo);
-  free(roomname);
-  free(roomdesc);
-  aim_freetlvchain(&tlvlist);
- 
-  return ret;
-}
-
-faim_internal 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]);
-  }
-
-  if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN))) {
-    ret = userfunc(sess,
-		   command, 
-		   curcount,
-		   userinfo);
-  }
-
-  free(userinfo);
-
-  return ret;
-}	      
-
-faim_internal 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]);
-  }
-
-  if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE))) {
-    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.
- */
-faim_internal 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;
-  unsigned char cookie[8];
-  int channel;
-  struct aim_tlvlist_t *outerlist;
-  char *msg = NULL;
-  struct aim_msgcookie_t *ck;
-
-  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
-
-  i = 10; /* skip snac */
-
-  /*
-   * ICBM Cookie.  Cache it.
-   */ 
-  for (z=0; z<8; z++,i++)
-    cookie[z] = command->data[i];
-
-  if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
-    if (ck->data)
-      free(ck->data);
-    free(ck);
-  }
-
-  /*
-   * Channel ID
-   *
-   * Channels 1 and 2 are implemented in the normal ICBM
-   * parser.
-   *
-   * We only do channel 3 here.
-   *
-   */
-  channel = aimutil_get16(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;
-}	      
-
-faim_export unsigned 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(AIM_FRAMETYPE_OSCAR, 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++);
-}
-
-faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
-{
-  struct aim_conn_t *conn;
-
-  if ((conn = aim_chat_getconn(sess, name)))
-    aim_conn_close(conn);
-
-  if (!conn)
-    return -1;
-  return 0;
-}
-
-/*
- * conn must be a BOS connection!
- */
-faim_export unsigned long aim_chat_invite(struct aim_session_t *sess,
-					  struct aim_conn_t *conn,
-					  char *sn,
-					  char *msg,
-					  u_short exchange,
-					  char *roomname,
-					  u_short instance)
-{
-  struct command_tx_struct *newpacket;
-  int i,curbyte=0;
-
-  if (!sess || !conn || !sn || !msg || !roomname)
-    return -1;
-
-  if (conn->type != AIM_CONN_TYPE_BOS)
-    return -1;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 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());
-
-  /* XXX this should get uncached by the unwritten 'invite accept' handler */
-  aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL));
-
-  /*
-   * 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	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,434 +0,0 @@
-/*
- *
- *
- *
- *
- */
-
-#include <faim/aim.h>
-
-
-/*
- * conn must be a chatnav connection!
- */
-faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess,
-						struct aim_conn_t *conn)
-{
-  aim_genericreq_n(sess, conn, 0x000d, 0x0002);
-
-  return sess->snac_nextid;
-}
-
-faim_export unsigned 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(AIM_FRAMETYPE_OSCAR, 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.
- */
-faim_internal 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;
-  int ret=1;
-  
-  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;
-	  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))
-	    maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
-
-	  /* 
-	   * Type 0x0003: Exchange information
-	   *
-	   * There can be any number of these, each one
-	   * representing another exchange.  
-	   * 
-	   */
-	  curexchange = 0;
-	  while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1)))
-	    {	
-	      curexchange++;
-	      exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
-	      
-
-	      /* exchange number */
-	      exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value);
-	      innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2);
-	      
-	      /* 
-	       * Type 0x000d: Unknown.
-	       */
-	      if (aim_gettlv(innerlist, 0x000d, 1))
-		;
-	      
-	      /* 
-	       * Type 0x0004: Unknown
-	       */
-	      if (aim_gettlv(innerlist, 0x0004, 1))
-		;
-
-	      /* 
-	       * Type 0x0002: Unknown
-	       */
-	      if (aim_gettlv(innerlist, 0x0002, 1)) {
-		unsigned short classperms;
-
-		classperms = aim_gettlv16(innerlist, 0x0002, 1);
-		
-		printf("faim: class permissions %x\n", classperms);
-	      }
-
-	      /*
-	       * Type 0x00c9: Unknown
-	       */ 
-	      if (aim_gettlv(innerlist, 0x00c9, 1))
-		;
-	      
-	      /*
-	       * Type 0x00ca: Creation Date 
-	       */
-	      if (aim_gettlv(innerlist, 0x00ca, 1))
-		;
-	      
-	      /*
-	       * Type 0x00d0: Mandatory Channels?
-	       */
-	      if (aim_gettlv(innerlist, 0x00d0, 1))
-		;
-
-	      /*
-	       * Type 0x00d1: Maximum Message length
-	       */
-	      if (aim_gettlv(innerlist, 0x00d1, 1))
-		;
-
-	      /*
-	       * Type 0x00d2: Maximum Occupancy?
-	       */
-	      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: Creation Permissions
-	       *
-	       * 0  Creation not allowed
-	       * 1  Room creation allowed
-	       * 2  Exchange creation allowed
-	       * 
-	       */
-	      if (aim_gettlv(innerlist, 0x00d5, 1)) {
-		unsigned char createperms;
-
-		createperms = aim_gettlv8(innerlist, 0x00d5, 1);
-	      }
-
-	      /*
-	       * Type 0x00d6: Character Set (First Time)
-	       */	      
-	      if (aim_gettlv(innerlist, 0x00d6, 1))	
-		exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
-	      else
-		exchanges[curexchange-1].charset1 = NULL;
-	      
-	      /*
-	       * Type 0x00d7: Language (First Time)
-	       */	      
-	      if (aim_gettlv(innerlist, 0x00d7, 1))	
-		exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
-	      else
-		exchanges[curexchange-1].lang1 = NULL;
-
-	      /*
-	       * Type 0x00d8: Character Set (Second Time)
-	       */	      
-	      if (aim_gettlv(innerlist, 0x00d8, 1))	
-		exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
-	      else
-		exchanges[curexchange-1].charset2 = NULL;
-
-	      /*
-	       * Type 0x00d9: Language (Second Time)
-	       */	      
-	      if (aim_gettlv(innerlist, 0x00d9, 1))	
-		exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
-	      else
-		exchanges[curexchange-1].lang2 = NULL;
-	      
-	      aim_freetlvchain(&innerlist);
-	    }
-	  
-	  /*
-	   * Call client.
-	   */
-	  userfunc = aim_callhandler(command->conn, 0x000d, 0x0009);
-	  if (userfunc)
-	    ret = userfunc(sess, 
-			   command, 
-			   snac->type,
-			   maxrooms,
-			   curexchange, 
-			   exchanges);
-	  curexchange--;
-	  while(curexchange >= 0)
-	    {
-	      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(&tlvlist);
-	  
-	  break;
-      }
-    case 0x0003: /* request exchange info */
-      printf("faim: chatnav_parse_info: resposne to exchange info\n");
-      break;
-    case 0x0004: /* request room info */
-      printf("faim: chatnav_parse_info: response to room info\n");
-      break;
-    case 0x0005: /* request more room info */
-      printf("faim: chatnav_parse_info: response to more room info\n");
-      break;
-    case 0x0006: /* request occupant list */
-      printf("faim: chatnav_parse_info: response to occupant info\n");
-      break;
-    case 0x0007: /* search for a room */
-      printf("faim: chatnav_parse_info: search results\n");
-      break;
-    case 0x0008: { /* create room */
-      /*
-	000d 0009 0000 0000 0010 
-	
-	0004 0053 
-	     0004 -- exchange
-	     0c 7a 6f6f 6f6d 7a6f 6f6f 6d34 32 cookie/name
-	     0000 -- instance
-	     02 -- detail level
-	     0007 -- unknown!
-	     006a 000c 7a 6f 6f6f 6d7a 6f6f 6f6d 3432 -- fully qualified name 
-	     00c9 0002 0011 -- flags
-	     00ca 0004 39c0 0883 -- create time
-	     00d1 0002 0200 -- max msg len 
-	     00d2 0002 0018 -- max occupants
-	     00d3 000c -- name
-	          7a6f 6f6f 6d7a 6f6f 6f6d 3432 
-	     00d5 0001 02 -- creation permission
-       */
-      struct aim_tlvlist_t *tlvlist, *innerlist;
-      char *ck = NULL, *fqcn = NULL, *name = NULL;
-      unsigned short exchange, instance, unknown, flags, maxmsglen, maxoccupancy;
-      unsigned long createtime = 0;
-      unsigned char createperms;
-      int i, cklen;
-      struct aim_tlv_t *bigblock;
-
-      i = 10;
-      if (!(tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i))) {
-	printf("faim: unable to read top tlv in create room response\n");
-	break;
-      }
-
-      if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
-	printf("faim: no bigblock in top tlv in create room response\n");
-	aim_freetlvchain(&tlvlist);
-	break;
-      }
-      i = 0;
-
-      exchange = aimutil_get16(bigblock->value+i);
-      i += 2;
-
-      cklen = aimutil_get8(bigblock->value+i);
-      i++;
-
-      ck = malloc(cklen+1);
-      memcpy(ck, bigblock->value+i, cklen);
-      ck[cklen] = '\0';
-      i += cklen;
-
-      instance = aimutil_get16(bigblock->value+i);
-      i += 2;
-
-      if (aimutil_get8(bigblock->value+i) != 0x02) {
-	printf("faim: unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i));
-	aim_freetlvchain(&tlvlist);
-	free(ck);	
-	break;
-      }
-      i += 1;
-      
-      unknown = aimutil_get16(bigblock->value+i);
-      i += 2;
-
-      if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) {
-	printf("faim: unable to read inner tlv chain in create room response\n");
-	aim_freetlvchain(&tlvlist);
-	free(ck);
-	break;
-      }
-
-      if (aim_gettlv(innerlist, 0x006a, 1))
-	fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
-
-      if (aim_gettlv(innerlist, 0x00c9, 1))
-	flags = aim_gettlv16(innerlist, 0x00c9, 1);
-
-      if (aim_gettlv(innerlist, 0x00ca, 1))
-	createtime = aim_gettlv32(innerlist, 0x00ca, 1);
-
-      if (aim_gettlv(innerlist, 0x00d1, 1))
-	maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
-
-      if (aim_gettlv(innerlist, 0x00d2, 1))
-	maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
-
-      if (aim_gettlv(innerlist, 0x00d3, 1))
-	name = aim_gettlv_str(innerlist, 0x00d3, 1);
-
-      if (aim_gettlv(innerlist, 0x00d5, 1))
-	createperms = aim_gettlv8(innerlist, 0x00d5, 1);
-
-      if ((userfunc = aim_callhandler(command->conn, 0x000d, 0x0009))) {
-	ret = userfunc(sess, command, snac->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
-      }
-     
-      if (ck)
-	free(ck);
-      if (name)
-	free(name);
-      if (fqcn)
-	free(fqcn);
-      aim_freetlvchain(&innerlist);
-      aim_freetlvchain(&tlvlist);
-
-      break;
-    }
-    default: /* unknown */
-      printf("faim: chatnav_parse_info: unknown request subtype (%04x)\n", snac->type);
-    }
-
-  if (snac && snac->data)
-    free(snac->data);
-  if (snac)
-    free(snac);
-  
-  return ret;
-}
-
-faim_export unsigned 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;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 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);
-
-  aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
-}
--- a/libfaim/aim_conn.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,805 +0,0 @@
-
-/*
- *  aim_conn.c
- *
- * Does all this gloriously nifty connection handling stuff...
- *
- */
-
-#include <faim/aim.h> 
-
-#ifndef _WIN32
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#endif
-
-/**
- * aim_connrst - Clears out connection list, killing remaining connections.
- * @sess: Session to be cleared
- *
- * Clears out the connection list and kills any connections left.
- *
- */
-faim_internal void aim_connrst(struct aim_session_t *sess)
-{
-  faim_mutex_init(&sess->connlistlock);
-  if (sess->connlist) {
-    struct aim_conn_t *cur = sess->connlist, *tmp;
-
-    while(cur) {
-      tmp = cur->next;
-      aim_conn_close(cur);
-      free(cur);
-      cur = tmp;
-    }
-  }
-  sess->connlist = NULL;
-  return;
-}
-
-/**
- * aim_conn_init - Reset a connection to default values.
- * @deadconn: Connection to be reset
- *
- * Initializes and/or resets a connection structure.
- *
- */
-static void aim_conn_init(struct aim_conn_t *deadconn)
-{
-  if (!deadconn)
-    return;
-
-  deadconn->fd = -1;
-  deadconn->subtype = -1;
-  deadconn->type = -1;
-  deadconn->seqnum = 0;
-  deadconn->lastactivity = 0;
-  deadconn->forcedlatency = 0;
-  deadconn->handlerlist = NULL;
-  deadconn->priv = NULL;
-  faim_mutex_init(&deadconn->active);
-  faim_mutex_init(&deadconn->seqnum_lock);
-  
-  return;
-}
-
-/**
- * aim_conn_getnext - Gets a new connection structure.
- * @sess: Session
- *
- * Allocate a new empty connection structure.
- *
- */
-faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
-{
-  struct aim_conn_t *newconn, *cur;
-
-  if (!(newconn = malloc(sizeof(struct aim_conn_t)))) 	
-    return NULL;
-
-  memset(newconn, 0, sizeof(struct aim_conn_t));
-  aim_conn_init(newconn);
-  newconn->next = NULL;
-
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL)
-    sess->connlist = newconn;
-  else {
-    for (cur = sess->connlist; cur->next; cur = cur->next)
-      ;
-    cur->next = newconn;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return newconn;
-}
-
-/**
- * aim_conn_kill - Close and free a connection.
- * @sess: Session for the connection
- * @deadconn: Connection to be freed
- *
- * Close, clear, and free a connection structure.
- *
- */
-faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
-{
-  struct aim_conn_t *cur;
-
-  if (!deadconn || !*deadconn)	
-    return;
-
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL)
-    ;
-  else if (sess->connlist->next == NULL) {
-    if (sess->connlist == *deadconn)
-      sess->connlist = NULL;
-  } else {
-    cur = sess->connlist;
-    while (cur->next) {
-      if (cur->next == *deadconn) {
-	cur->next = cur->next->next;
-	break;
-      }
-      cur = cur->next;
-    }
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  /* XXX: do we need this for txqueue too? */
-  aim_rxqueue_cleanbyconn(sess, *deadconn);
-
-  if ((*deadconn)->fd != -1) 
-    aim_conn_close(*deadconn);
-  if ((*deadconn)->priv)
-    free((*deadconn)->priv);
-  free(*deadconn);
-  deadconn = NULL;
-
-  return;
-}
-
-/**
- * aim_conn_close - Close a connection
- * @deadconn: Connection to close
- *
- * Close (but not free) a connection.
- *
- * This leaves everything untouched except for clearing the 
- * handler list and setting the fd to -1 (used to recognize
- * dead connections).
- *
- */
-faim_export void aim_conn_close(struct aim_conn_t *deadconn)
-{
-
-  faim_mutex_destroy(&deadconn->active);
-  faim_mutex_destroy(&deadconn->seqnum_lock);
-  if (deadconn->fd >= 3)
-    close(deadconn->fd);
-  deadconn->fd = -1;
-  if (deadconn->handlerlist)
-    aim_clearhandlers(deadconn);
-
-  return;
-}
-
-/**
- * aim_getconn_type - Find a connection of a specific type
- * @sess: Session to search
- * @type: Type of connection to look for
- *
- * Searches for a connection of the specified type in the 
- * specified session.  Returns the first connection of that
- * type found.
- *
- */
-faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
-						  int type)
-{
-  struct aim_conn_t *cur;
-
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-  return cur;
-}
-
-/**
- * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. 
- * @sess: Session to connect
- * @host: Host to connect to
- * @port: Port to connect to
- * @statusret: Return value of the connection
- *
- * Attempts to connect to the specified host via the configured
- * proxy settings, if present.  If no proxy is configured for
- * this session, the connection is done directly.
- *
- */
-static int aim_proxyconnect(struct aim_session_t *sess, 
-			    char *host, unsigned short port,
-			    int *statusret)
-{
-  int fd = -1;
-
-  if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
-    int i;
-    unsigned char buf[512];
-    struct sockaddr_in sa;
-    struct hostent *hp;
-    char *proxy;
-    unsigned short proxyport = 1080;
-
-    for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
-      if (sess->socksproxy.server[i] == ':') {
-	proxyport = atoi(&(sess->socksproxy.server[i+1]));
-	break;
-      }
-    }
-    proxy = (char *)malloc(i+1);
-    strncpy(proxy, sess->socksproxy.server, i);
-    proxy[i] = '\0';
-
-    if (!(hp = gethostbyname(proxy))) {
-      printf("proxyconnect: unable to resolve proxy name\n");
-      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
-      return -1;
-    }
-    free(proxy);
-
-    memset(&sa.sin_zero, 0, 8);
-    sa.sin_port = htons(proxyport);
-    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
-    sa.sin_family = hp->h_addrtype;
-  
-    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
-    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
-      printf("proxyconnect: unable to connect to proxy\n");
-      close(fd);
-      return -1;
-    }
-
-    i = 0;
-    buf[0] = 0x05; /* SOCKS version 5 */
-    if (strlen(sess->socksproxy.username)) {
-      buf[1] = 0x02; /* two methods */
-      buf[2] = 0x00; /* no authentication */
-      buf[3] = 0x02; /* username/password authentication */
-      i = 4;
-    } else {
-      buf[1] = 0x01;
-      buf[2] = 0x00;
-      i = 3;
-    }
-
-    if (write(fd, buf, i) < i) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-
-    if (read(fd, buf, 2) < 2) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-
-    if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
-      *statusret = EINVAL;
-      close(fd);
-      return -1;
-    }
-
-    /* check if we're doing username authentication */
-    if (buf[1] == 0x02) {
-      i  = aimutil_put8(buf, 0x01); /* version 1 */
-      i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
-      i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
-      i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
-      i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
-      if (write(fd, buf, i) < i) {
-	*statusret = errno;
-	close(fd);
-	return -1;
-      }
-      if (read(fd, buf, 2) < 2) {
-	*statusret = errno;
-	close(fd);
-	return -1;
-      }
-      if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
-	*statusret = EINVAL;
-	close(fd);
-	return -1;
-      }
-    }
-
-    i  = aimutil_put8(buf, 0x05);
-    i += aimutil_put8(buf+i, 0x01); /* CONNECT */
-    i += aimutil_put8(buf+i, 0x00); /* reserved */
-    i += aimutil_put8(buf+i, 0x03); /* address type: host name */
-    i += aimutil_put8(buf+i, strlen(host));
-    i += aimutil_putstr(buf+i, host, strlen(host));
-    i += aimutil_put16(buf+i, port);
-
-    if (write(fd, buf, i) < i) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-    if (read(fd, buf, 10) < 10) {
-      *statusret = errno;
-      close(fd);
-      return -1;
-    }
-    if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
-      *statusret = EINVAL;
-      close(fd);
-      return -1;
-    }
-
-  } else { /* connecting directly */
-    struct sockaddr_in sa;
-    struct hostent *hp;
-
-    if (!(hp = gethostbyname(host))) {
-      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
-      return -1;
-    }
-
-    memset(&sa, 0, sizeof(struct sockaddr_in));
-    sa.sin_port = htons(port);
-    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
-    sa.sin_family = hp->h_addrtype;
-  
-    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
-
-    if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
-      fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
-
-    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
-      if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
-	if ((errno == EINPROGRESS) || (errno == EINTR)) {
-	  if (statusret)
-	    *statusret |= AIM_CONN_STATUS_INPROGRESS;
-	  return fd;
-	}
-      }
-      close(fd);
-      fd = -1;
-    }
-  }
-  return fd;
-}
-
-/**
- * aim_newconn - Open a new connection
- * @sess: Session to create connection in
- * @type: Type of connection to create
- * @dest: Host to connect to (in "host:port" syntax)
- *
- * Opens a new connection to the specified dest host of specified
- * type, using the proxy settings if available.  If @host is %NULL,
- * the connection is allocated and returned, but no connection 
- * is made.
- *
- * FIXME: Return errors in a more sane way.
- *
- */
-faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
-					   int type, char *dest)
-{
-  struct aim_conn_t *connstruct;
-  int ret;
-  u_short port = FAIM_LOGIN_PORT;
-  char *host = NULL;
-  int i=0;
-
-  if ((connstruct=aim_conn_getnext(sess))==NULL)
-    return NULL;
-
-  faim_mutex_lock(&connstruct->active);
-  
-  connstruct->type = type;
-
-  if (!dest) { /* just allocate a struct */
-    connstruct->fd = -1;
-    connstruct->status = 0;
-    faim_mutex_unlock(&connstruct->active);
-    return connstruct;
-  }
-
-  /* 
-   * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
-   * colon, in the BOS redirect.  This fatally breaks all previous 
-   * libfaims.  Bad, bad AOL.
-   *
-   * We put this here to catch every case. 
-   *
-   */
-
-  for(i=0;i<(int)strlen(dest);i++) {
-    if (dest[i] == ':') {
-      port = atoi(&(dest[i+1]));
-      break;
-    }
-  }
-  host = (char *)malloc(i+1);
-  strncpy(host, dest, i);
-  host[i] = '\0';
-
-  if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
-    connstruct->fd = -1;
-    connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
-    free(host);
-    faim_mutex_unlock(&connstruct->active);
-    return connstruct;
-  } else
-    connstruct->fd = ret;
-  
-  faim_mutex_unlock(&connstruct->active);
-
-  free(host);
-
-  return connstruct;
-}
-
-/**
- * aim_conngetmaxfd - Return the highest valued file discriptor in session
- * @sess: Session to search
- *
- * Returns the highest valued filed descriptor of all open 
- * connections in @sess.
- *
- */
-faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
-{
-  int j = 0;
-  struct aim_conn_t *cur;
-
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd > j)
-      j = cur->fd;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return j;
-}
-
-/**
- * aim_countconn - Return the number of open connections in the session
- * @sess: Session to look at
- *
- * Returns the number of number connections in @sess.
- *
- */
-static int aim_countconn(struct aim_session_t *sess)
-{
-  int cnt = 0;
-  struct aim_conn_t *cur;
-
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next)
-    cnt++;
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return cnt;
-}
-
-/**
- * aim_conn_in_sess - Predicate to test the precense of a connection in a sess
- * @sess: Session to look in
- * @conn: Connection to look for
- *
- * Searches @sess for the passed connection.  Returns 1 if its present,
- * zero otherwise.
- *
- */
-faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
-{
-  struct aim_conn_t *cur;
-
-  faim_mutex_lock(&sess->connlistlock);
-  for(cur = sess->connlist; cur; cur = cur->next)
-    if(cur == conn) {
-      faim_mutex_unlock(&sess->connlistlock);
-      return 1;
-    }
-  faim_mutex_unlock(&sess->connlistlock);
-  return 0;
-}
-
-/**
- * aim_select - Wait for a socket with data or timeout
- * @sess: Session to wait on
- * @timeout: How long to wait
- * @status: Return status
- *
- * Waits for a socket with data or for timeout, whichever comes first.
- * See select().
- * 
- * 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)
- *
- * XXX: we could probably stand to do a little courser locking here.
- *
- */ 
-faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
-					  struct timeval *timeout, int *status)
-{
-  struct aim_conn_t *cur;
-  fd_set fds, wfds;
-  int maxfd = 0;
-  int i, haveconnecting = 0;
-
-  faim_mutex_lock(&sess->connlistlock);
-  if (sess->connlist == NULL) {
-    faim_mutex_unlock(&sess->connlistlock);
-    *status = -1;
-    return NULL;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  FD_ZERO(&fds);
-  FD_ZERO(&wfds);
-  maxfd = 0;
-
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->fd == -1) {
-      /* don't let invalid/dead connections sit around */
-      *status = 2;
-      faim_mutex_unlock(&sess->connlistlock);
-      return cur;
-    } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
-      FD_SET(cur->fd, &wfds);
-      haveconnecting++;
-    }
-    FD_SET(cur->fd, &fds);
-    if (cur->fd > maxfd)
-      maxfd = cur->fd;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  /* 
-   * If we have data waiting to be sent, return
-   *
-   * We have to not do this if theres at least one
-   * connection thats still connecting, since that connection
-   * may have queued data and this return would prevent
-   * the connection from ever completing!  This is a major
-   * inadequacy of the libfaim way of doing things.  It means
-   * that nothing can transmit as long as there's connecting
-   * sockets. Evil.
-   *
-   * But its still better than having blocking connects.
-   *
-   */
-  if (!haveconnecting && (sess->queue_outgoing != NULL)) {
-    *status = 1;
-    return NULL;
-  } 
-
-  if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
-    faim_mutex_lock(&sess->connlistlock);
-    for (cur = sess->connlist; cur; cur = cur->next) {
-      if ((FD_ISSET(cur->fd, &fds)) || 
-	  ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
-	   FD_ISSET(cur->fd, &wfds))) {
-	*status = 2;
-	faim_mutex_unlock(&sess->connlistlock);
-	return cur; /* XXX race condition here -- shouldnt unlock connlist */
-      }
-    }
-    *status = 0; /* shouldn't happen */
-  } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
-    *status = 0;
-  else
-    *status = i; /* can be 0 or -1 */
-
-  faim_mutex_unlock(&sess->connlistlock);
-  return NULL;  /* no waiting or error, return */
-}
-
-/**
- * aim_conn_isready - Test if a connection is marked ready
- * @conn: Connection to test
- *
- * Returns true if the connection is ready, false otherwise.
- * Returns -1 if the connection is invalid.
- *
- * XXX: This is deprecated.
- *
- */
-faim_export int aim_conn_isready(struct aim_conn_t *conn)
-{
-  if (conn)
-    return (conn->status & 0x0001);
-  return -1;
-}
-
-/**
- * aim_conn_setstatus - Set the status of a connection
- * @conn: Connection
- * @status: New status
- *
- * @newstatus is %XOR'd with the previous value of the connection
- * status and returned.  Returns -1 if the connection is invalid.
- *
- * This isn't real useful.
- *
- */
-faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
-{
-  int val;
-
-  if (!conn)
-    return -1;
-  
-  faim_mutex_lock(&conn->active);
-  val = conn->status ^= status;
-  faim_mutex_unlock(&conn->active);
-  return val;
-}
-
-/**
- * aim_conn_setlatency - Set a forced latency value for connection
- * @conn: Conn to set latency for
- * @newval: Number of seconds to force between transmits
- *
- * Causes @newval seconds to be spent between transmits on a connection.
- *
- * This is my lame attempt at overcoming not understanding the rate
- * limiting. 
- *
- * XXX: This should really be replaced with something that scales and
- * backs off like the real rate limiting does.
- *
- */
-faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
-{
-  if (!conn)
-    return -1;
-
-  faim_mutex_lock(&conn->active);
-  conn->forcedlatency = newval;
-  conn->lastactivity = 0; /* reset this just to make sure */
-  faim_mutex_unlock(&conn->active);
-
-  return 0;
-}
-
-/**
- * aim_setupproxy - Configure a proxy for this session
- * @sess: Session to set proxy for
- * @server: SOCKS server
- * @username: SOCKS username
- * @password: SOCKS password
- *
- * Call this with your SOCKS5 proxy server parameters before
- * the first call to aim_newconn().  If called with all %NULL
- * args, it will clear out a previously set proxy.  
- *
- * Set username and password to %NULL if not applicable.
- *
- */
-faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
-{
-  /* clear out the proxy info */
-  if (!server || !strlen(server)) {
-    memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
-    memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
-    memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
-    return;
-  }
-
-  strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
-  if (username && strlen(username)) 
-    strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
-  if (password && strlen(password))
-    strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
-  return;
-}
-
-/**
- * aim_session_init - Initializes a session structure
- * @sess: Session to initialize
- * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
- *
- * Sets up the initial values for a session.
- *
- */
-faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags)
-{
-  if (!sess)
-    return;
-
-  memset(sess, 0, sizeof(struct aim_session_t));
-  aim_connrst(sess);
-  sess->queue_outgoing = NULL;
-  sess->queue_incoming = NULL;
-  sess->pendingjoin = NULL;
-  sess->pendingjoinexchange = 0;
-  aim_initsnachash(sess);
-  sess->msgcookies = NULL;
-  sess->snac_nextid = 0x00000001;
-
-  sess->flags = 0;
-
-  /*
-   * Default to SNAC login unless XORLOGIN is explicitly set.
-   */
-  if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
-    sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
-  sess->flags |= flags;
-
-  /*
-   * This must always be set.  Default to the queue-based
-   * version for back-compatibility.  
-   */
-  sess->tx_enqueue = &aim_tx_enqueue__queuebased;
-
-  return;
-}
-
-/**
- * aim_conn_isconnecting - Determine if a connection is connecting
- * @conn: Connection to examine
- *
- * Returns nonzero if the connection is in the process of
- * connecting (or if it just completed and aim_conn_completeconnect()
- * has yet to be called on it).
- *
- */
-faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
-{
-  if (!conn)
-    return 0;
-  return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
-}
-
-faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
-{
-  fd_set fds, wfds;
-  struct timeval tv;
-  int res, error = ETIMEDOUT;
-  rxcallback_t userfunc;
-
-  if (!conn || (conn->fd == -1))
-    return -1;
-
-  if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
-    return -1;
-
-  FD_ZERO(&fds);
-  FD_SET(conn->fd, &fds);
-  FD_ZERO(&wfds);
-  FD_SET(conn->fd, &wfds);
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
-    error = errno;
-    aim_conn_close(conn);
-    errno = error;
-    return -1;
-  } else if (res == 0) {
-    printf("faim: aim_conn_completeconnect: false alarm on %d\n", conn->fd);
-    return 0; /* hasn't really completed yet... */
-  } 
-
-  if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
-    int len = sizeof(error);
-
-    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
-      error = errno;
-  }
-
-  if (error) {
-    aim_conn_close(conn);
-    errno = error;
-    return -1;
-  }
-
-  fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
-
-  conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
-
-  if ((userfunc = aim_callhandler(conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
-    userfunc(sess, NULL, conn);
-
-  /* Flush out the queues if there was something waiting for this conn  */
-  aim_tx_flushqueue(sess);
-
-  return 0;
-}
--- a/libfaim/aim_ft.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1847 +0,0 @@
-#include <faim/aim.h>
-
-#ifndef _WIN32
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/utsname.h> /* for aim_directim_initiate */
-#include <arpa/inet.h> /* for inet_ntoa */
-#endif
-
-/* aim_msgcookies.c is mostly new. just look at the diff and replace yours, easiest. */
-
-/* 
-   function name       where i had it
-   aim_send_im_direct aim_im.c
-   aim_directim_initiate aim_im.c
-   aim_filetransfer_accept aim_im.c
-   aim_getlisting aim_misc.c (?!) -- prototype function. can be ignored.
-   establish aim_misc.c
-   aim_get_command_rendezvous aim_r
-   oft_getfh aim_rxqueue.c
-*/
-
-faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur)
-{
-  int acceptfd = 0;
-  rxcallback_t userfunc;
-  struct sockaddr cliaddr;
-  size_t clilen = sizeof(cliaddr);
-  int ret = 0;
-
-  /*
-   * Listener sockets only have incoming connections. No data.
-   */
-  if( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
-    return -1;
-
-  if (cliaddr.sa_family != AF_INET) /* just in case IPv6 really is happening */
-    return -1;
-
-  switch(cur->subtype) {
-  case AIM_CONN_SUBTYPE_OFT_DIRECTIM: {
-    struct aim_directim_priv *priv;
-    
-    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
-
-    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-    if(!cur->priv)
-      cur->priv = priv; /* what happens if there is one?! -- mid */
-
-    cur->type = AIM_CONN_TYPE_RENDEZVOUS;
-    close(cur->fd); /* should we really do this? seems like the client should decide. maybe clone the connection and keep the listener open. -- mid */
-    cur->fd = acceptfd;
-
-    if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
-      ret = userfunc(sess, NULL, cur);
-				   
-    break;
-  }
-  case AIM_CONN_SUBTYPE_OFT_GETFILE: {
-    struct aim_filetransfer_priv *priv;
-
-    priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
-
-    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
-
-    if(!cur->priv)
-      cur->priv = priv;
-
-    if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
-      ret = userfunc(sess, NULL, cur);
-    break;
-  } 
-  default: {
-    /* XXX */
-  }
-  }
-  return ret;
-}
-
-
-/*
- * aim_send_im_direct:
- * sess - session
- * conn - directim connection
- * msg  - null-terminated string to send
- */
-
-faim_export int aim_send_im_direct(struct aim_session_t *sess, 
-				   struct aim_conn_t *conn,
-				   char *msg)
-{
-  struct command_tx_struct *newpacket , *newpacket2; 
-
-  /* newpacket contains a real header with data, newpacket2 is just a
-     null packet, with a cookie and a lot of 0x00s. newpacket is the
-     "i'm sending", newpacket2 is the "i'm typing".*/
-
-  /* uhm. the client should send those as two seperate things -- mid */
-
-  struct aim_directim_priv *priv = NULL;
-  int i;
-
-  if (!sess || !conn || !(conn->type) || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv || !msg) {
-    printf("faim: directim: invalid arguments\n");
-    return -1;
-  };
-
-  if (strlen(msg) >= MAXMSGLEN)
-    return -1;
-
-  priv = (struct aim_directim_priv *)conn->priv;
-
-  /* NULLish Header */
-
-  if (!(newpacket2 = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, 0))) {
-    printf("faim: directim: tx_new2 failed\n");
-    return -1;
-  }                                                                           
-
-  newpacket2->lock = 1; /* lock struct */
-
-  memcpy(newpacket2->hdr.oft.magic, "ODC2", 4);
-  newpacket2->hdr.oft.hdr2len = 0x44;
-
-  if (!(newpacket2->hdr.oft.hdr2 = calloc(1,newpacket2->hdr.oft.hdr2len))) {
-    free(newpacket2);
-    return -1;
-  }
-
-  i = 0;
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0006);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
-
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_put32(newpacket2->hdr.oft.hdr2+i, 0x00000000);
-
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x000e);
-
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
-  
-  i = 52; /* 0x34 */
-  i += aimutil_put8(newpacket2->hdr.oft.hdr2+i, 0x00); /* 53 */
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); /* 55 */
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 61 */
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 65 */
-  i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */
-
-  newpacket2->lock = 0;
-  newpacket2->data = NULL;
-  
-  aim_tx_enqueue(sess, newpacket2);
-
-  /* Header packet */
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, strlen(msg)))) {
-    printf("faim: directim: tx_new failed\n");
-    return -1;
-  }
-
-  newpacket->lock = 1; /* lock struct */
-
-  memcpy(newpacket->hdr.oft.magic, "ODC2", 4);
-  newpacket->hdr.oft.hdr2len = 0x54;
-
-  if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) {
-    free(newpacket);
-    return -1;
-  }
-
-  i = 0;
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
-
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg));
-
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-
-  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
-  
-  i = 52; /* 0x34 */
-  i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00); /* 53 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); /* 55 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 61 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 65 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */
-
-  /* values grabbed from a dump */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); /* 69 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 71 */
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);/* 73 */ 
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);/* 73 */ 
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
-  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
-
-  memcpy(newpacket->data, msg, strlen(msg));
-
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return 0;
-}
-
-/*
- * aim_directim_intitiate:
- * For those times when we want to open up the directim channel ourselves.
- * sess is your session,
- * conn is the BOS conn,
- * priv is a dummy priv value (we'll let it get filled in later) (if
- * you pass a NULL, we alloc one) 
- * destsn is the SN to connect to.  
- */
-
-
-faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess,
-						     struct aim_conn_t *conn,
-						     struct aim_directim_priv *priv,
-						     char *destsn)
-{
-  struct command_tx_struct *newpacket;
-  struct aim_conn_t *newconn;
-
-  struct aim_msgcookie_t *cookie;
-
-  int curbyte, i, listenfd;
-  short port = 4443;
-
-  struct hostent *hptr;
-  char localhost[129];
-
-  unsigned char cap[16];
-  char d[4]; /* XXX: IPv6. *cough* */
-
-  /*
-   * Open our socket
-   */
-
-  if( (listenfd = aim_listenestablish(port)) == -1)
-    return NULL;
-
-  /*
-   * get our local IP
-   */
-
-  if(gethostname(localhost, 128) < 0)
-    return NULL;
-
-  if( (hptr = gethostbyname(localhost)) == NULL)
-    return NULL;
-
-  memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */
-
-  aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
-
-  /*
-   * create the OSCAR packet
-   */
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32)))
-    return NULL;
-
-  newpacket->lock = 1; /* lock struct */
-
-  curbyte  = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte, 
-			 0x0004, 0x0006, 0x0000, sess->snac_nextid);
-
-  /* 
-   * Generate a random message cookie 
-   * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible.
-   */
-  for (i=0;i<7;i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20));
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /*
-   * grab all the data for cookie caching.
-   */
-  cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
-
-  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
-  cookie->type = AIM_COOKIETYPE_OFTIM;
-  
-  if(!priv)
-    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
-
-  memcpy(priv->cookie, cookie, 8);
-  memcpy(priv->sn, destsn, sizeof(priv->sn));
- 
-  cookie->data = priv;
-
-  aim_cachecookie(sess, cookie);  /* cache da cookie */
-
-  /*
-   * Channel ID
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
-
-  /* 
-   * Destination SN (prepended with byte length)
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-
-  /* 
-   * enTLV start
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
-
-  /*
-   * Flag data / ICBM Parameters?
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /*
-   * Cookie 
-   */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
-
-  /*
-   * Capability String
-   */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
-
-  /*
-   * 000a/0002 : 0001
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-
-  /*
-   * 0003/0004: IP address
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
-
-  for(i = 0; i < 4; i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */
-
-  /*
-   * 0005/0002: Port
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, port);
-
-  /*
-   * 000f/0000: umm.. dunno. Zigamorph[1]?
-   * [1]: see esr's TNHD.
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-
-  printf("curbyte: 0x%x\n",curbyte);
-
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  /*
-   * allocate and set up our connection
-   */
-
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
-  if (!newconn) { 
-    perror("aim_newconn");
-    aim_conn_kill(sess, &newconn);
-    return NULL;
-  } 
-
-  newconn->fd = listenfd;
-  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-  newconn->priv = priv;
-  printf("faim: listening (fd = %d, unconnected)\n", newconn->fd);
-
-  /*
-   * XXX We need some way of closing the listener socket after
-   * n seconds of no connection. -- mid
-   */
-
-  return (newconn);
-} 
-
-/*
- * struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv)
- * sess is the session to append the conn to,
- * conn is the BOS connection,
- * priv is the filled-in priv data structure for the connection
- * returns conn if connected, NULL on error
- */
-
-
-faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess,
-						    struct aim_conn_t *conn,
-						    struct aim_directim_priv *priv )
-{
-  struct aim_conn_t *newconn = NULL;;
-
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip);
-  if (!newconn || (newconn->fd == -1)) { 
-    printf("could not connect to %s\n", priv->ip);
-    perror("aim_newconn");
-    aim_conn_kill(sess, &newconn);
-    return NULL;
-  } else {    
-    newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-    newconn->priv = priv;
-    printf("faim: connected to peer (fd = %d)\n", newconn->fd);
-    return newconn;
-  }
-  return newconn;
-}
-
-/*
- * struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
- * sess is your session, 
- * name is the name to get,
- * returns conn for directim with name, NULL if none found.
- */
-
-faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
-{
-  struct aim_conn_t *cur;
-  struct aim_directim_priv *priv;
-  
-  faim_mutex_lock(&sess->connlistlock);
-  for (cur = sess->connlist; cur; cur = cur->next) {
-    if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)
-      continue;
-    
-    priv = cur->priv;
-    if (aim_sncmp(priv->sn, name) == 0)
-      break;
-  }
-  faim_mutex_unlock(&sess->connlistlock);
-
-  return cur;
-}
-
-/*
- * aim_accepttransfer:
- * accept a file transfer request.
- * sess is the session,
- * conn is the BOS conn for the CAP reply
- * sn is the screenname to send it to,
- * cookie is the cookie used
- * ip is the ip to connect to
- * file is the listing file to use
- * rendid is CAP type
- *
- * returns connection
- */
-
-faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess,
-			  struct aim_conn_t *conn, 
-			  char *sn,
-			  char *cookie,
-			  char *ip,
-			  FILE *file,
-			  unsigned short rendid)
-{
-  struct command_tx_struct *newpacket, *newoft;
-
-  struct aim_conn_t *newconn;
-  struct aim_fileheader_t *listingfh;
-  struct aim_filetransfer_priv *priv;
-  struct aim_msgcookie_t *cachedcook;
-
-  int curbyte, i;
-
-  if(rendid == AIM_CAPS_GETFILE) {
-
-    newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
-    
-    newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
-    
-    if (!newconn || (newconn->fd == -1)) {
-      perror("aim_newconn");
-      faimdprintf(2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
-      aim_conn_kill(sess, &newconn);
-      return NULL;
-    } else {
-      priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
-      memcpy(priv->cookie, cookie, 8);
-      strncpy(priv->sn, sn, MAXSNLEN);
-      strncpy(priv->ip, ip, sizeof(priv->ip));
-      newconn->priv = (void *)priv;
-      faimdprintf(2, "faim: connected to peer (fd = %d)\n", newconn->fd);
-    }
- 
-    /* cache da cookie. COOOOOKIES! */
-
-    if(!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) {
-      faimdprintf(1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
-      /* XXX die here, or go on? search for cachedcook for future references */
-    }
-
-    if(cachedcook)
-      memcpy(cachedcook->cookie, cookie, 8);
-  
-    /*  strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name)); */
-
-    if(cachedcook) { /* see above calloc of cachedcook */
-      cachedcook->type = AIM_COOKIETYPE_OFTGET;
-      cachedcook->data = (void *)priv;
-    
-      if (aim_cachecookie(sess, cachedcook) != 0)
-	faimdprintf(1, "faim: ERROR caching message cookie\n");
-    }
-  
-    if(rendid == AIM_CAPS_GETFILE) {
-      faimdprintf(2, "faim: getfile request accept\n");
-      if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1108, newconn, 0))) {
-	faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n");
-	/* XXX: what else do we need to clean up here? */
-	return NULL;
-      }
-    
-      newoft->lock = 1;
-    
-      memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-      newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */
-    
-      if(!(listingfh = aim_getlisting(file))) {
-	return NULL;
-      }      
-
-      if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
-	free(newoft);
-	return NULL;
-      }  
-
-      memcpy(listingfh->bcookie, cookie, 8);
-
-      aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh);
-
-      free(listingfh);
-
-      newoft->lock = 0;
-      aim_tx_enqueue(sess, newoft);
-      printf("faim: getfile: OFT listing enqueued.\n");
-    
-    }
-
-    /* OSCAR CAP accept packet */
-
-    if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sn)+4+2+8+16)))
-      return NULL;
-  
-    newpacket->lock = 1;
-  
-    curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
-    for (i = 0; i < 8; i++)
-      curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-    curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
-    curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept */);
-    for (i = 0; i < 8; i++)
-      curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
-    curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid);
-
-    newpacket->lock = 0;
-    aim_tx_enqueue(sess, newpacket);
-
-    return newconn;
-  }
-  return NULL;
-}
-
-/*
- * aim_getlisting(FILE *file)
- * 
- * file is an opened listing file
- * returns a pointer to the filled-in fileheader_t
- *
- * currently omits checksum. we'll fix this when AOL breaks us, i
- * guess.
- *
- */
-
-faim_internal struct aim_fileheader_t *aim_getlisting(FILE *file) 
-{
-  struct aim_fileheader_t *fh;
-  u_long totsize = 0, size = 0, checksum = 0xffff0000;
-  short totfiles = 0;
-  char *linebuf, sizebuf[9];
-  
-  int linelength = 1024;
-
-  /* XXX: if we have a line longer than 1024chars, God help us. */
-  if( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
-    faimdprintf(2, "linebuf calloc failed\n");
-    return NULL;
-  }  
-
-  if(fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
-    perror("getlisting END1 fseek:");
-    faimdprintf(2, "getlising fseek END1 error\n");
-  }
-
-  if(fgetpos(file, &size) == -1) {
-    perror("getlisting END1 getpos:");
-    faimdprintf(2, "getlising getpos END1 error\n");
-  }
-
-  if(fseek(file, 0, SEEK_SET) != 0) {
-    perror("getlesting fseek(SET):");
-    faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n");
-  }
-
-  bzero(linebuf, linelength);
-
-  size = 0;
-
-  while(fgets(linebuf,  linelength, file)) {
-    totfiles++;
-    bzero(sizebuf, 9);
-
-    size += strlen(linebuf);
-    
-    if(strlen(linebuf) < 23) {
-      faimdprintf(2, "line \"%s\" too short. skipping\n", linebuf);
-      continue;
-    }
-    if(linebuf[strlen(linebuf)-1] != '\n') {
-      faimdprintf(2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
-    }
-
-    memcpy(sizebuf, linebuf+17, 8);
-
-    totsize += strtol(sizebuf, NULL, 10);
-    bzero(linebuf, linelength);
-  }   
-
-  /*  if(size != 0) {
-    faimdprintf(2, "faim: getlisting: size != 0 after while.. %i\n", size);
-    }*/
-
-  if(fseek(file, 0, SEEK_SET) == -1) {
-    perror("getlisting END2 fseek:");
-    faimdprintf(2, "getlising fseek END2 error\n");
-  }  
-
-  free(linebuf);
-
-  /* we're going to ignore checksumming the data for now -- that
-   * requires walking the whole listing.txt. it should probably be
-   * done at register time and cached, but, eh. */  
-
-  if(!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
-    return NULL;
-
-  printf( "faim: OFT: getlisting: totfiles: %u, totsize: %lu, size: %lu\n", totfiles, totsize, size);
-
-  fh->encrypt     = 0x0000;
-  fh->compress    = 0x0000; 
-  fh->totfiles    = totfiles;
-  fh->filesleft   = totfiles; /* is this right ?*/
-  fh->totparts    = 0x0001;
-  fh->partsleft   = 0x0001;
-  fh->totsize     = totsize;
-  fh->size        = size; /* ls -l listing.txt */
-  fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
-  fh->checksum    = checksum; /* XXX: checksum ! */
-  fh->rfcsum      = 0x00000000;
-  fh->rfsize      = 0x00000000;
-  fh->cretime     = 0x00000000;
-  fh->rfcsum      = 0x00000000;
-  fh->nrecvd      = 0x00000000;
-  fh->recvcsum    = 0x00000000;
-
-  /*  memset(fh->idstring, 0, sizeof(fh->idstring)); */
-  memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
-  memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
-
-  fh->flags       = 0x02;
-  fh->lnameoffset = 0x1a;
-  fh->lsizeoffset = 0x10;
-
-  /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
-  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
-
-  fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
-  fh->nlanguage   = 0x0000;
-
-  /*  memset(fh->name, 0, sizeof(fh->name)); */
-  memcpy(fh->name, "listing.txt", sizeof(fh->name));
-  memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
-
-  faimdprintf(2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
-  return fh;
-}
-
-/*
- * establish: create a listening socket on a port. you need to call
- * accept() when it's connected.
- * portnum is the port number to bind to.
- * returns your fd
- */
-
-faim_internal int aim_listenestablish(u_short portnum)
-{
-#if defined(__linux__) /* XXX what other OS's support getaddrinfo? */
-  int listenfd;
-  const int on = 1;
-  struct addrinfo hints, *res, *ressave;
-  char serv[5];
-  sprintf(serv, "%d", portnum);
-  memset(&hints, 0, sizeof(struct addrinfo));
-  hints.ai_flags = AI_PASSIVE;
-  hints.ai_family = AF_UNSPEC;
-  hints.ai_socktype = SOCK_STREAM;
-  if (getaddrinfo(NULL/*any IP*/, serv, &hints, &res) != 0) {
-    perror("getaddrinfo");
-    return -1;
-  }
-  ressave = res;
-  do {
-    listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-    if (listenfd < 0)
-      continue;
-    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-    if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
-      break; /* success */
-    close(listenfd);
-  } while ( (res = res->ai_next) );
-  if (!res)
-    return -1;
-  if (listen(listenfd, 1024)!=0) {
-    perror("listen");
-    return -1;
-  }
-  freeaddrinfo(ressave);
-  return listenfd;
-#else 
-  int listenfd;
-  const int on = 1;
-  struct sockaddr_in sockin;
-  
-  if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-    perror("socket(listenfd)");
-    return -1;
-  } 
-  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) {
-    perror("setsockopt(listenfd)");
-    close(listenfd);
-    return -1;
-  }
-  memset(&sockin, 0, sizeof(struct sockaddr_in));
-  sockin.sin_family = AF_INET;
-  sockin.sin_port = htons(portnum);
-  if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
-    perror("bind(listenfd)");
-    close(listenfd);
-    return -1;
-  }
-  if (listen(listenfd, 4) != 0) {
-    perror("listen(listenfd)");
-    close(listenfd);
-    return -1;
-  }
-
-  return listenfd;
-#endif
-}
-
-faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn)
-{
-  unsigned char hdrbuf1[6];
-  unsigned char *hdr = NULL;
-  int hdrlen, hdrtype;
-  int flags = 0;
-  rxcallback_t userfunc = NULL;
-
-
-  memset(hdrbuf1, 0, sizeof(hdrbuf1));
-
-  faim_mutex_lock(&conn->active); /* gets locked down for the entirety */
-
-  if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) {    
- 
-    faimdprintf(2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrlen);
-    faim_mutex_unlock(&conn->active);
-    
-    if(hdrlen < 0)
-      perror("read");
-    else { /* disconnected */
-      switch(conn->subtype) {
-      case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { /* XXX: clean up cookies here ? */
-	struct aim_directim_priv *priv = NULL;
-	if(!(priv = (struct aim_directim_priv *)conn->priv) ) 
-	  return -1; /* not much we can do */
-	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM);
-
-	
-	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) {
-	  aim_conn_close(conn);
-	  return  userfunc(sess, NULL, conn, priv->sn);
-	}
-
-	break;
-      }
-
-      case AIM_CONN_SUBTYPE_OFT_GETFILE: {
-	struct aim_filetransfer_priv *priv;
-	if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
-	  return -1;
-
-	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
-
-	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) {
-	  aim_conn_close(conn);
-	  return userfunc(sess, NULL, conn, priv->sn);
-	}
-
-	break;
-      }
-
-      case AIM_CONN_SUBTYPE_OFT_SENDFILE: {
-	struct aim_filetransfer_priv *priv;
-	if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
-	  return -1;
-
-	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
-
-	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) {
-	  aim_conn_close(conn);
-	  return userfunc(sess, NULL, conn, priv->sn);
-	}
-
-	break;
-      }
-      }
-
-      aim_conn_close(conn);
-      aim_conn_kill(sess, &conn);
-      
-      return -1;
-    }
-  }
-
-  hdrlen = aimutil_get16(hdrbuf1+4);
-
-  hdrlen -= 6;
-  if (!(hdr = malloc(hdrlen))) {
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-
-  if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
-    perror("read");
-    faimdprintf(2,"faim: rend: read2 error\n");
-    free(hdr);
-    faim_mutex_unlock(&conn->active);
-    aim_conn_close(conn);
-    return -1;
-  }
-
-  hdrtype = aimutil_get16(hdr);  
-
-  switch (hdrtype) {
-  case 0x0001: { /* directim */
-    int payloadlength = 0;
-    char *snptr = NULL;
-    struct aim_directim_priv *priv;
-    int i;
-
-    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
-
-    payloadlength = aimutil_get32(hdr+22);
-    flags = aimutil_get16(hdr+32);
-    snptr = (char *)hdr+38;
-
-    strncpy(priv->sn, snptr, MAXSNLEN);
-
-    faimdprintf(2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); 
-
-    if (flags == 0x000e) {
-      faim_mutex_unlock(&conn->active);
-      if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) )
-	return userfunc(sess, NULL, snptr);
-    } else if ((flags == 0x0000) && payloadlength) {
-      unsigned char *msg;
-
-      if(! (msg = calloc(1, payloadlength+1)) ) {
-	faim_mutex_unlock(&conn->active);
-	return -1;
-      }
-      
-      if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
-	perror("read");
-	printf("faim: rend: read3 error\n");
-	free(msg);
-	faim_mutex_unlock(&conn->active);
-	aim_conn_close(conn);
-	return -1;
-      }
-      faim_mutex_unlock(&conn->active);
-      msg[payloadlength] = '\0';
-      faimdprintf(2, "faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg);
-
-
-      if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
-	i = userfunc(sess, NULL, conn, snptr, msg);
-      
-      free(msg);
-      return i;
-    }
-    break;
-  }
-#if 0
-  /* currently experimental to a non-compiling degree */
-  case 0x1108: { /* getfile listing.txt incoming tx->rx */
-    struct aim_filetransfer_priv *ft;
-    struct aim_fileheader_t *fh;
-    struct aim_msgcookie_t *cook;
-    struct aim_conn_type *newoft;
-
-    int commandlen;
-    char *data;
-
-    faimdprintf(2,"faim: rend: fileget 0x1108\n");
-    
-    if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
-      faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-    ft->state = 1; /* we're waaaaiiiting.. */
-
-    fh = aim_oft_getfh(hdr);
-
-    memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-    
-    if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-    if(cook->data)
-      free(cook->data);
-
-    cook->data = ft;
-
-    aim_cachecookie(sess, cook);
-
-    if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1209, conn, 0))) {
-      /* XXX: what else do we need to clean up here? */
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-
-    aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, ft->fh); /* no change */
-    newoft->lock = 0;
-    aim_tx_enqueue(sess, newoft);
-    break;
-  }
-#endif 
-  case 0x1209: { /* get file listing ack rx->tx */
-    struct aim_filetransfer_priv *ft;
-    struct aim_fileheader_t *fh;
-    struct aim_msgcookie_t *cook;
-
-    int commandlen;
-    char *data;
-
-    faimdprintf(2,"faim: rend: fileget 0x1209\n");
-
-    if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
-      faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-    fh = aim_oft_getfh(hdr);
-
-    memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-    
-    cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET);
-
-    /* we currently trust the other client to be giving us Valid
-     *  Enough input, else this gets to be a messy function (and we
-     *  won't break like winaim does when it gets bad input =) */
-
-    if(cook->data)
-      free(cook->data); /* XXX */
-  
-    cook->data = ft;
-    
-    aim_cachecookie(sess, cook);
-
-    /* XXX: have this send chunks of the file instead of the whole
-     * file. requires rethinking some code. */
-
-    if(fseek(sess->oft.listing, 0, SEEK_SET) != 0) {
-      perror("get_command_rendezvous 1209 fseek(SET):");
-      faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n");
-    }
-    commandlen = ft->fh.size;
-
-    if((data = (char *)calloc(1, commandlen)) == NULL) {
-      faimdprintf(2, "faim: get_command_rendezvous 1209: couldn't malloc data.\n");
-      faim_mutex_unlock(&conn->active);
-      return -1;
-
-    }
-
-    for(commandlen = 0; commandlen < ft->fh.size; commandlen++)
-      if( (data[commandlen] = (unsigned char)fgetc(sess->oft.listing)) == EOF)
-	faimdprintf(2, "faim: get_command_rendezvous 1209: got early EOF (error?)\n");
-
-    commandlen = ft->fh.size;
-
-    if (write(conn->fd, data, commandlen) != commandlen) {
-      perror("listing write error");
-    }
-    faim_mutex_unlock(&conn->active);
-
-    faimdprintf(2, "faim: get_command_rendezvous: hit end of 1209\n");
-
-    free(data);
-
-    break;
-  }
-  case 0x120b: { /* get file second */
-    struct aim_filetransfer_priv *ft;
-    struct aim_msgcookie_t *cook;
-
-    struct aim_fileheader_t *fh;
-
-    faimdprintf(2, "faim: rend: fileget 120b\n");
-
-    if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
-      faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-    fh = aim_oft_getfh(hdr);
-
-    memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
- 
-    if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-  
-    if(cook->data)
-      free(cook->data); /* XXX: integrate cookie caching */
-
-    cook->data = ft;
-    
-    aim_cachecookie(sess, cook);
-
-    faim_mutex_unlock(&conn->active);
-
-    /* call listing.txt rx confirm */    
-
-    break;
-  }
-  case 0x120c: { /* yet more get file */
-    struct aim_filetransfer_priv *ft;
-    struct aim_msgcookie_t *cook;
-    struct aim_fileheader_t *listingfh;
-    struct command_tx_struct *newoft;
-
-    int i;
-
-    rxcallback_t userfunc;
-    
-    printf("faim: rend: fileget 120c\n");
-
-    if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
-      printf("faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-
-    if(hdrlen != 0x100)
-      printf("faim: fileget_command(120c): um. hdrlen != 0x100..\n");
-
-    listingfh = aim_oft_getfh((char *)hdr);
-
-    memcpy(&(ft->fh), listingfh, sizeof(struct aim_fileheader_t));
-    
-    if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
-      faim_mutex_unlock(&conn->active);
-      return -1;
-    }
-  
-    if(cook->data)
-      free(cook->data); /* XXX */
-
-    cook->data = ft;
-    
-    aim_cachecookie(sess, cook);
-
-    faim_mutex_unlock(&conn->active);
-
-    faimdprintf(2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
-
-    if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
-      i = userfunc(sess, NULL, conn, &ft->fh, cook->cookie);
-    
-    if(i < 0)
-      return -1;
-      
-    if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0101, conn, 0))) {
-      faimdprintf(2, "faim: send_final_transfer: tx_new OFT failed\n");
-      return -1;
-    }
-    
-    newoft->lock = 1;
-    
-    memcpy(newoft->hdr.oft.magic, "OFT2", 4);
-    newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */
-    
-    if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
-      newoft->lock = 0;
-      aim_tx_destroy(newoft);
-      return -1;
-    }  
-    
-    /*  memcpy(listingfh->bcookie, ft->fh.bcookie, 8); */
-    
-    listingfh->nrecvd = 0; /* these need reset for protocol-correctness */
-    listingfh->recvcsum = 0;
-
-    aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh);
-    
-    newoft->lock = 0;
-    aim_tx_enqueue(sess, newoft);
-    faimdprintf(2, "faim: OFT: OFT file enqueued.\n");
-    
-    if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
-      return 1;
-    
-    i = userfunc(sess, NULL, conn, listingfh, listingfh->bcookie);
-
-    free(listingfh);
-
-    return i;
-
-    break;
-  }
-  case 0x0202: { /* get file: ready to recieve data */
-    struct aim_fileheader_t *fh;    
-    fh = aim_oft_getfh((char *)hdr);
-
-    faimdprintf(2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
-
-    faim_mutex_unlock(&conn->active);
-    
-    if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) == NULL)
-      return 1;
-
-    return userfunc(sess, NULL, conn, fh);       
-
-    free(fh);
-
-    break;
-  }
-  case 0x0204: { /* get file: finished. close it up */
-    int i;
-
-    struct aim_fileheader_t *fh;    
-    fh = aim_oft_getfh((char *)hdr);
-
-    faimdprintf(2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
-
-    faim_mutex_unlock(&conn->active);
-    
-    if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
-      i = userfunc(sess, NULL, conn, fh);
-    else
-      i = 1;
-
-    /*
-	  free(fh); */
-    /* not sure where to do this yet, as we need to keep it to allow multiple file sends... bleh */
-
-    return i;
-    break;
-  }
-  default: {
-    printf("OFT frame: type %04x\n", hdrtype);  
-    /* data connection may be unreliable here */
-    faim_mutex_unlock(&conn->active);
-    break;
-  }
-  } /* switch */
-
-  free(hdr);
-  
-  return 0;
-}
-
-/*
- * aim_oft_registerlisting()
- * sess: aim session
- * file: opened FILE *
- * listingdir: the path to listing.txt
- * returns -1 on error, 0 on success.
- *
- * it's not my problem if the listing fd is already set.
- */
-
-faim_export int aim_oft_registerlisting(struct aim_session_t *sess, FILE *file, char* listingdir) 
-{
-  if(!sess)
-    return -1;
-
-  /* XXX: checksum each file in the listing */
-
-#if 0
-   if(sess->oft.listing) {
-     faimdprintf(1, "We already have a file pointer. Closing and overwriting.\n");
-    fclose(sess->oft.listing);
-  }
-#endif
-  sess->oft.listing = file;
-#if 0
-  if(sess->oft.listingdir) {
-    faimdprintf(1, "We already have a directory string. Freeing and overwriting\n");
-    free(sess->oft.listingdir);
-  }
-#endif
-  
-  if( (sess->oft.listingdir = (char *)calloc(1, strlen(listingdir)+1)) )
-    memcpy(sess->oft.listingdir, listingdir, strlen(listingdir));
-  else
-    return -1;
-  return 0;
-}
-
-faim_internal struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) 
-{
-  struct aim_fileheader_t *fh;
-  int i, j;
-
-  if(!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
-    return NULL;
-
-  /* [0] and [1] are the type. we can ignore those here. */
-
-  i = 2;
-
-  for(j = 0; j < 8; j++, i++)
-    fh->bcookie[j] = hdr[i];
-  fh->encrypt = aimutil_get16(hdr+i);
-  i += 2;
-  fh->compress = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totfiles = aimutil_get16(hdr+i);
-  i += 2;
-  fh->filesleft = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totparts = aimutil_get16(hdr+i);
-  i += 2;
-  fh->partsleft = aimutil_get16(hdr+i);
-  i += 2;
-  fh->totsize = aimutil_get32(hdr+i);
-  i += 4;
-  fh->size = aimutil_get32(hdr+i);
-  i += 4;
-  fh->modtime = aimutil_get32(hdr+i);
-  i += 4;
-  fh->checksum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfrcsum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfsize = aimutil_get32(hdr+i);
-  i += 4;
-  fh->cretime = aimutil_get32(hdr+i);
-  i += 4;
-  fh->rfcsum = aimutil_get32(hdr+i);
-  i += 4;
-  fh->nrecvd = aimutil_get32(hdr+i);
-  i += 4;
-  fh->recvcsum = aimutil_get32(hdr+i);
-  i += 4;
-
-  memcpy(fh->idstring, hdr+i, 32);
-  i += 32;
-
-  fh->flags = aimutil_get8(hdr+i);
-  i += 1;
-  fh->lnameoffset = aimutil_get8(hdr+i);
-  i += 1;
-  fh->lsizeoffset = aimutil_get8(hdr+i);
-  i += 1;
-
-  memcpy(fh->dummy, hdr+i, 69);
-  i += 69;
-
-  memcpy(fh->macfileinfo, hdr+i, 16);
-  i += 16;
-
-  fh->nencode = aimutil_get16(hdr+i);
-  i += 2;
-  fh->nlanguage = aimutil_get16(hdr+i);
-  i += 2;
-
-  memcpy(fh->name, hdr+i, 64);
-  i += 64;
-
-  return fh;
-}
-
-faim_export int aim_oft_checksum(char *buffer, int bufsize, int *checksum)
-{    
-  short check0, check1;
-  int i;
-  
-  check0 = ((*checksum & 0xFF000000) >> 16);
-  check1 = ((*checksum & 0x00ff0000) >> 16);
-
-  for(i = 0; i < bufsize; i++) {
-
-    if(i % 2)  { /* use check1 -- second byte */
-      if ( (short)buffer[i] > check1 ) { /* wrapping */
-	
-	check1 += 0x100; /* this is a cheap way to wrap */
-
-	/* if we're wrapping, decrement the other one */
-	if(check0 == 0) /* XXX: check this corner case */
-	  check0 = 0x00ff;
-	else
-	  check0--;			
-      }
-
-      check1 -= buffer[i];
-    } else { /* use check0 -- first byte */
-      if ( (short)buffer[i] > check0 ) { /* wrapping */
-	
-	check0 += 0x100; /* this is a cheap way to wrap */
-
-	/* if we're wrapping, decrement the other one */
-	if(check1 == 0) /* XXX: check this corner case */
-	  check1 = 0x00ff;
-	else
-	  check1--;			
-      }
-
-      check0 -= buffer[i];
-    } 
-  }
-
-  if(check0 > 0xff || check1 > 0xff) { /* they shouldn't be able to do this. error! */
-    faimdprintf(2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
-    return -1;
-  }
-  
-  check0 &= 0xff; /* grab just the lowest byte */
-  check1 &= 0xff; /* this should be clean, but just in case */
-
-  *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
-
-  return *checksum;
-}
-
-
-/*
- * aim_oft_buildheader:
- * fills a buffer with network-order fh data.
- * returns length written; -1 on error.
- * dest: buffer to fill -- pre-alloced
- * listingfh: fh to get data from
- *
- * DOES NOT DO BOUNDS CHECKING!
- */
-
-
-faim_internal int aim_oft_buildheader(char *dest,struct aim_fileheader_t *listingfh) 
-{
-  int i, curbyte;
-
-  if(!dest || !listingfh)
-    return -1;
-  
-  curbyte = 0;    
-  for(i = 0; i < 8; i++)
-    curbyte += aimutil_put8(dest+curbyte, listingfh->bcookie[i]);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->encrypt);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->compress);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->totfiles);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->filesleft);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->totparts);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->partsleft);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->totsize);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->size);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->modtime);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->checksum);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->rfrcsum);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->rfsize);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->cretime);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->rfcsum);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->nrecvd);
-  curbyte += aimutil_put32(dest+curbyte, listingfh->recvcsum);
-
-  memcpy(dest+curbyte, listingfh->idstring, 32);
-  curbyte += 32;
-
-  curbyte += aimutil_put8(dest+curbyte, listingfh->flags);
-  curbyte += aimutil_put8(dest+curbyte, listingfh->lnameoffset);
-  curbyte += aimutil_put8(dest+curbyte, listingfh->lsizeoffset);
-
-  memcpy(dest+curbyte, listingfh->dummy, 69);
-  curbyte += 69;
-    
-  memcpy(dest+curbyte, listingfh->macfileinfo, 16);
-  curbyte += 16;
-
-  curbyte += aimutil_put16(dest+curbyte, listingfh->nencode);
-  curbyte += aimutil_put16(dest+curbyte, listingfh->nlanguage);
-
-  memcpy(dest+curbyte, listingfh->name, 64); /* XXX: Filenames longer than 64B */
-  curbyte += 64;
-
-  return curbyte;
-}
-
-/*
- * int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
- * conn is the OFT connection to shove the data down,
- * tosend is the FILE * for the file to send
- * fh is the filled-in fh value
- * returns -1 on error, 1 on success.
- */
-
-faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
-{
-  int  pos, bufpos, i; 
-  const int bufsize = 4096;
-  char *buf;
-
-  /* sanity checks */
-  if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->subtype != AIM_CONN_SUBTYPE_OFT_GETFILE) {
-    faimdprintf(1, "getfile_send: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
-    return -1;
-  }
-  
-  if(!tosend) {
-    faimdprintf(1, "getfile_send: file pointer isn't valid\n");
-    return -1;
-  }
-
-  if(!fh) {
-    faimdprintf(1, "getfile_send: fh isn't valid\n");
-    return -1;
-  }
-
-  /* real code */
-
-  if(!(buf = (char *)calloc(1, bufsize))) {
-    perror("faim: getfile_send: calloc:");
-    faimdprintf(2, "getfile_send calloc error\n");
-    return -1;
-  }
-
-  pos = 0;
-
-  if( fseek(tosend, 0, SEEK_SET) == -1) {
-    perror("faim: getfile_send:  fseek:");
-    faimdprintf(2, "getfile_send fseek error\n");
-  }  
-
-  faimdprintf(2, "faim: getfile_send: using %i byte blocks\n", bufsize);
-
-  for(pos = 0; pos < fh->size; pos++) {
-    bufpos = pos % bufsize;
-
-    if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
-      if ( (i = write(conn->fd, buf, bufsize)) != bufsize ) {
-	perror("faim: getfile_send: write1: ");
-	faimdprintf(1, "faim: getfile_send: whoopsy, didn't write it all...\n");
-	free(buf);   
-	return -1;
-      }
-    }
-    if( (buf[bufpos] = fgetc(tosend)) == EOF) {
-      if(pos != fh->size) {
-	printf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
-	faimdprintf(1, "faim: getfile_send: hrm... apparent early EOF at pos 0x%lx of 0x%lx\n", pos, fh->size);
-	free(buf);   
-	return -1;
-      }
-    }
-      
-      
-  }
-
-  if( (i = write(conn->fd, buf, bufpos+1)) != (bufpos+1)) {
-    perror("faim: getfile_send: write2: ");
-    faimdprintf(1, "faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
-    free(buf);   
-    return -1;
-  }
-
-  free(buf);   
-  
-  fclose(tosend);
-  return 1; 
-}
-
-/*
- * int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
- * conn is the OFT connection to shove the data down,
- * tosend is the FILE * for the file to send
- * fh is the filled-in fh value
- * pos is the position to start at (at beginning should be 0, after 5
- *  bytes are sent should be 5); -1 for "don't seek"
- * bufsize is the size of the chunk to send
- *
- * returns -1 on error, new pos on success.
- *
- * 
- * Notes on usage: 
- * You don't really have to keep track of pos if you don't want
- *  to. just always call with -1 for pos, and it'll use the one
- *  contained within the FILE *.
- *
- * if (pos + chunksize > fh->size), we only send as much data as we
- *  can get (ie: up to fh->size.  
- */
-faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
-{
-  int bufpos; 
-  char *buf;
-
-  /* sanity checks */
-  if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->type != AIM_CONN_SUBTYPE_OFT_GETFILE) {
-    faimdprintf(1, "faim: getfile_send_chunk: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
-    return -1;
-  }
-  
-  if(!tosend) {
-    faimdprintf(1, "faim: getfile_send_chunk: file pointer isn't valid\n");
-    return -1;
-  }
-
-  if(!fh) {
-    faimdprintf(1, "faim: getfile_send_chunk: fh isn't valid\n");
-    return -1;
-  }
-
-  /* real code */
-
-  if(!(buf = (char *)calloc(1, bufsize))) {
-    perror("faim: getfile_send_chunk: calloc:");
-    faimdprintf(2, "faim: getfile_send_chunk calloc error\n");
-    return -1;
-  }
-  
-  if(pos != -1) {
-    if( fseek(tosend, pos, SEEK_SET) == -1) {
-      perror("faim: getfile_send_chunk:  fseek:");
-      faimdprintf(2, "faim: getfile_send_chunk fseek error\n");
-    }  
-  }
-
-  faimdprintf(2, "faim: getfile_send_chunk: using %i byte blocks\n", bufsize);
-
-  for(bufpos = 0; pos < fh->size; bufpos++, pos++) {
-    if( (buf[bufpos] = fgetc(tosend)) == EOF) {
-      if(pos != fh->size) {
-	faimdprintf(1, "faim: getfile_send_chunk: hrm... apparent early EOF at pos 0x%x of 0x%x\n", pos, fh->size);
-	free(buf);   
-	return -1;
-      }
-    }      
-  }
-
-  if( write(conn->fd, buf, bufpos+1) != (bufpos+1)) {
-    faimdprintf(1, "faim: getfile_send_chunk cleanup: whoopsy, didn't write it all...\n");
-    free(buf);   
-    return -1;
-  }
-
-  free(buf);   
-  
-  return (pos + bufpos);
-}
-
-/*
- * aim_tx_destroy:
- * free's tx_command_t's. if command is locked, doesn't free.
- * returns -1 on error (locked struct); 0 on success.
- * command is the command to free 
- */
-
-faim_internal int aim_tx_destroy(struct command_tx_struct *command)
-{
-  if(command->lock)
-    return -1;
-  if(command->data)
-    free(command->data);
-  free(command);
-
-  return 0;
-}
-
-#if 0
-/*
- * aim_getfile_intitiate:
- * For those times when we want to open up the directim channel ourselves.
- * sess is your session,
- * conn is the BOS conn,
- * priv is a dummy priv value (we'll let it get filled in later) (if
- * you pass a NULL, we alloc one) 
- * destsn is the SN to connect to.  
- */
-
-
-faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess,
-			  struct aim_conn_t *conn,
-			  struct aim_getfile_priv *priv,
-			  char *destsn)
-{
-  struct command_tx_struct *newpacket;
-  struct aim_conn_t *newconn;
-
-  struct aim_msgcookie_t *cookie;
-
-  int curbyte, i, listenfd;
-  short port = 4443;
-
-  struct hostent *hptr;
-  struct utsname myname;
-
-  char cap[16];
-  char d[4]; /* XXX: IPv6. *cough* */
-
-  /*
-   * Open our socket
-   */
-
-  if( (listenfd = aim_listenestablish(port)) == -1)
-    return NULL;
-
-  /*
-   * get our local IP
-   */
-
-  if(uname(&myname) < 0)
-    return NULL;
-
-  if( (hptr = gethostbyname(myname.nodename)) == NULL)
-    return NULL;
-
-  memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */
-
-  aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
-
-  /*
-   * create the OSCAR packet
-   */
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32)))
-    return NULL;
-
-  newpacket->lock = 1; /* lock struct */
-
-  curbyte  = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte, 
-			 0x0004, 0x0006, 0x0000, sess->snac_nextid);
-
-  /* 
-   * Generate a random message cookie 
-   * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible.
-   */
-  for (i=0;i<7;i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 20));
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /*
-   * grab all the data for cookie caching.
-   */
-  cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
-
-  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
-  cookie->type = AIM_COOKIETYPE_OFTIM;
-  
-  if(!priv)
-    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
-
-  memcpy(priv->cookie, cookie, 8);
-  memcpy(priv->sn, destsn, sizeof(priv->sn));
- 
-  cookie->data = priv;
-
-  aim_cachecookie(sess, cookie);  /* cache da cookie */
-
-  /*
-   * Channel ID
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
-
-  /* 
-   * Destination SN (prepended with byte length)
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-
-  /* 
-   * enTLV start
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
-
-  /*
-   * Flag data / ICBM Parameters?
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
-
-  /*
-   * Cookie 
-   */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
-
-  /*
-   * Capability String
-   */
-  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
-
-  /*
-   * 000a/0002 : 0001
-   */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-
-  /*
-   * 0003/0004: IP address
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
-
-  for(i = 0; i < 4; i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */
-
-  /*
-   * 0005/0002: Port
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put16(newpacket->data+curbyte, port);
-
-  /*
-   * 000f/0000: umm.. dunno. Zigamorph[1]?
-   * [1]: see esr's TNHD.
-   */
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
-
-  printf("curbyte: 0x%x\n",curbyte);
-
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  /*
-   * allocate and set up our connection
-   */
-
-  i = fcntl(listenfd, F_GETFL, 0);
-  fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
-
-  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
-  if (!newconn) { 
-    perror("aim_newconn");
-    aim_conn_kill(sess, &newconn);
-    return NULL;
-  } 
-
-  newconn->fd = listenfd;
-  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
-  newconn->priv = priv;
-  printf("faim: listening (fd = %d, unconnected)\n", newconn->fd);
-
-  /*
-   * XXX We need some way of closing the listener socket after
-   * n seconds of no connection. -- mid
-   */
-
-  return newconn;
-}
-
-#endif
--- a/libfaim/aim_im.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,896 +0,0 @@
-/*
- *  aim_im.c
- *
- *  The routines for sending/receiving Instant Messages.
- *
- */
-
-#include <faim/aim.h>
-
-/*
- * Send an ICBM (instant message).  
- *
- *
- * Possible flags:
- *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
- *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
- *                        when the message is received (of type 0x0004/0x000c)
- *
- */
-faim_export unsigned long aim_send_im(struct aim_session_t *sess,
-				      struct aim_conn_t *conn, 
-				      char *destsn, u_int flags, char *msg)
-{   
-
-  int curbyte,i;
-  struct command_tx_struct *newpacket;
-  
-  if (strlen(msg) >= MAXMSGLEN)
-    return -1;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, strlen(msg)+256)))
-    return -1;
-
-  newpacket->lock = 1; /* lock struct */
-
-  curbyte  = 0;
-  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) rand());
-
-  /*
-   * 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) + 0x10);
-
-  /*
-   * Flag data / ICBM Parameters?
-   */
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x05);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-
-  /* number of bytes to follow */
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
-  curbyte += aimutil_put8(newpacket->data+curbyte, 0x02);
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101);
-
-  /* 
-   * 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);
-
-  /*
-   * Message.  Not terminated.
-   */
-  curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg));
-
-  /*
-   * 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);
-
-  aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, destsn, strlen(destsn)+1);
-  aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
-
-  return sess->snac_nextid;
-}
-
-faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *sess,
-					       struct command_rx_struct *command)
-{
-  unsigned int i = 0, z;
-  rxcallback_t userfunc = NULL;
-  unsigned char cookie[8];
-  int channel;
-  struct aim_tlvlist_t *tlvlist;
-  char sn[MAXSNLEN];
-  unsigned short icbmflags = 0;
-  unsigned char flag1 = 0, flag2 = 0;
-  unsigned char *msgblock = NULL, *msg = NULL;
-
-  i = 10;
-  
-  /* ICBM Cookie. */
-  for (z=0; z<8; z++,i++)
-    cookie[z] = command->data[i];
-
-  /* Channel ID */
-  channel = aimutil_get16(command->data+i);
-  i += 2;
-
-  if (channel != 0x01) {
-    printf("faim: icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
-    return 1;
-  }
-
-  strncpy(sn, (char *) command->data+i+1, (int) *(command->data+i));
-  i += 1 + (int) *(command->data+i);
-
-  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
-
-  if (aim_gettlv(tlvlist, 0x0003, 1))
-    icbmflags |= AIM_IMFLAGS_ACK;
-  if (aim_gettlv(tlvlist, 0x0004, 1))
-    icbmflags |= AIM_IMFLAGS_AWAY;
-
-  if (aim_gettlv(tlvlist, 0x0002, 1)) {
-    int j = 0;
-
-    msgblock = (unsigned char *)aim_gettlv_str(tlvlist, 0x0002, 1);
-
-    /* no, this really is correct.  I'm not high or anything either. */
-    j += 2;
-    j += 2 + aimutil_get16(msgblock+j);
-    j += 2;
-    
-    j += 2; /* final block length */
-
-    flag1 = aimutil_get16(msgblock);
-    j += 2;
-    flag2 = aimutil_get16(msgblock);
-    j += 2;
-    
-    msg = msgblock+j;
-  }
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x0006)) || (i = 0))
-    i = userfunc(sess, command, channel, sn, msg, icbmflags, flag1, flag2);
-  
-  if (msgblock)
-    free(msgblock);
-  aim_freetlvchain(&tlvlist);
-
-  return 0;
-}
-
-/*
- * 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...
- *
- */
-faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *sess,
-					       struct command_rx_struct *command)
-{
-  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 */
-
-  /*
-   * Read ICBM Cookie.  And throw away.
-   */
-  for (z=0; z<8; z++,i++)
-    cookie[z] = command->data[i];
-  
-  /*
-   * 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;
-  
-  /*
-   *
-   */
-  if ((channel != 0x01) && (channel != 0x02))
-    {
-      printf("faim: icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
-      return 1;
-    }
-
-  /*
-   * Extract the standard user info block.
-   *
-   * Note that although this contains TLVs that appear contiguous
-   * with the TLVs read below, they are two different pieces.  The
-   * userinfo block contains the number of TLVs that contain user
-   * information, the rest are not even though there is no seperation.
-   * aim_extractuserinfo() returns the number of bytes used by the
-   * userinfo tlvs, so you can start reading the rest of them right
-   * afterward.  
-   *
-   * That also means that TLV types can be duplicated between the
-   * userinfo block and the rest of the message, however there should
-   * never be two TLVs of the same type in one block.
-   * 
-   */
-  i += aim_extractuserinfo(command->data+i, &userinfo);
-  
-  /*
-   * Read block of TLVs (not including the userinfo data).  All 
-   * further data is derived from what is parsed here.
-   */
-  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
-
-  /*
-   * 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;
-      u_char *msgblock;
-      u_short flag1,flag2;
-           
-      /*
-       * Check Autoresponse status.  If it is an autoresponse,
-       * it will contain a type 0x0004 TLV, with zero length.
-       */
-      if (aim_gettlv(tlvlist, 0x0004, 1))
-	icbmflags |= AIM_IMFLAGS_AWAY;
-      
-      /*
-       * Check Ack Request status.
-       */
-      if (aim_gettlv(tlvlist, 0x0003, 1))
-	icbmflags |= AIM_IMFLAGS_ACK;
-      
-      /*
-       * Message block.
-       */
-      msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
-      if (!msgblocktlv || !msgblocktlv->value) {
-	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)
-    {	
-      struct aim_tlv_t *block1;
-      struct aim_tlvlist_t *list2;
-      unsigned short reqclass = 0;
-      unsigned short status = 0;
-      
-      /*
-       * There's another block of TLVs embedded in the type 5 here. 
-       */
-      block1 = aim_gettlv(tlvlist, 0x0005, 1);
-      if (!block1) {
-	printf("faim: no tlv 0x0005 in rendezvous transaction!\n");
-	aim_freetlvchain(&tlvlist);
-	return 1; /* major problem */
-      }
-
-      /*
-       * First two bytes represent the status of the connection.
-       *
-       * 0 is a request, 2 is an accept
-       */ 
-      status = aimutil_get16(block1->value+0);
-      
-      /*
-       * Next comes the cookie.  Should match the ICBM cookie.
-       */
-      if (memcmp(block1->value+2, cookie, 8) != 0) 
-	printf("faim: rend: warning cookies don't match!\n");
-
-      /*
-       * The next 16bytes are a capability block so we can
-       * identify what type of rendezvous this is.
-       *
-       * Thanks to Eric Warmenhoven <warmenhoven@linux.com> (of GAIM)
-       * for pointing some of this out to me.  In fact, a lot of 
-       * the client-to-client info comes from the work of the GAIM 
-       * developers. Thanks!
-       *
-       * Read off one capability string and we should have it ID'd.
-       * 
-       */
-      reqclass = aim_getcap(block1->value+2+8, 0x10);
-      if (reqclass == 0x0000) {
-	printf("faim: rend: no ID block\n");
-	aim_freetlvchain(&tlvlist);
-	return 1;
-      }
-
-      /* 
-       * What follows may be TLVs or nothing, depending on the
-       * purpose of the message.
-       *
-       * Ack packets for instance have nothing more to them.
-       */
-      list2 = aim_readtlvchain(block1->value+2+8+16, block1->length-2-8-16);
-      
-      if (!list2 || ((reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) {
-	struct aim_msgcookie_t *cook;
-	int type;
-	
-	type = aim_msgcookie_gettype(reqclass); /* XXX: fix this shitty code */
-
-	if ((cook = aim_uncachecookie(sess, cookie, type)) == NULL) {
-	  printf("faim: non-data rendezvous thats not in cache!\n");
-	  aim_freetlvchain(&list2);
-	  aim_freetlvchain(&tlvlist);
-	  return 1;
-	}
-
-	if (cook->type == AIM_CAPS_SENDFILE) {
-	  struct aim_filetransfer_priv *ft;
-
-	  if (cook->data) {
-	    int errorcode = -1; /* XXX shouldnt this be 0? */
-
-	    ft = (struct aim_filetransfer_priv *)cook->data;
-
-	    if (aim_gettlv(list2, 0x000b, 1))
-	      errorcode = aim_gettlv16(list2, 0x000b, 1);
-
-	    if (errorcode) {
-	      printf("faim: transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode);
-	    } else if (status == 0x0002) { /* connection accepted */
-	      printf("faim: transfer from %s (%s) for %s accepted\n", ft->sn, ft->ip, ft->fh.name);
-	    }
-	    free(cook->data);
-	  } else {
-	    printf("faim: not data attached to file transfer\n");
-	  }
-	} else if (cook->type == AIM_CAPS_VOICE) {
-	  printf("faim: voice request cancelled\n");
-	} else {
-	  printf("faim: unknown cookie cache type %d\n", cook->type);
-	}
-
-	free(cook);
-	if (list2)
-	  aim_freetlvchain(&list2);
-	aim_freetlvchain(&tlvlist);
-	return 1;
-      }
-
-      /*
-       * The rest of the handling depends on what type it is.
-       */
-      if (reqclass & AIM_CAPS_BUDDYICON) {
-
-	/*
-	 * Call client.
-	 */
-#if 0
-	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
-	if (userfunc || (i = 0))
-	  i = userfunc(sess, 
-		       command, 
-		       channel, 
-		       reqclass,
-		       &userinfo,
-		       ip,
-		       cookie);
-#endif
-
-      } else if (reqclass & AIM_CAPS_VOICE) {
-	struct aim_msgcookie_t *cachedcook;
-
-	printf("faim: rend: voice!\n");
-
-	if(!(cachedcook = (struct aim_msgcookie_t*)calloc(1, sizeof(struct aim_msgcookie_t))))
-	  return 1;
-
-	memcpy(cachedcook->cookie, cookie, 8);
-	cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
-	cachedcook->data = NULL;
-
-	if (aim_cachecookie(sess, cachedcook) != 0)
-	  printf("faim: ERROR caching message cookie\n");
-
-	/* XXX: implement all this */
-
-	/*
-	 * Call client.
-	 */
-	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
-	if (userfunc || (i = 0)) {
-	  i = userfunc(sess, command, channel, reqclass, &userinfo);
-	}
-      } else if ((reqclass & AIM_CAPS_IMIMAGE) || (reqclass & AIM_CAPS_BUDDYICON)) {
-	char ip[30];
-	struct aim_directim_priv *priv;
-
-	memset(ip, 0, 30);
-	
-	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
-	  struct aim_tlv_t *iptlv, *porttlv;
-	  
-	  iptlv = aim_gettlv(list2, 0x0003, 1);
-	  porttlv = aim_gettlv(list2, 0x0005, 1);
-
-	  snprintf(ip, 30, "%d.%d.%d.%d:%d", 
-		  aimutil_get8(iptlv->value+0),
-		  aimutil_get8(iptlv->value+1),
-		  aimutil_get8(iptlv->value+2),
-		  aimutil_get8(iptlv->value+3),
-		  4443 /*aimutil_get16(porttlv->value)*/);
-	}
-
-	printf("faim: rend: directIM request from %s (%s)\n",
-	       userinfo.sn,
-	       ip);
-
-       /* XXX: there are a couple of different request packets for
-	*          different things */
-	
-	priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
-	memcpy(priv->ip, ip, sizeof(priv->ip));
-	memcpy(priv->sn, userinfo.sn, sizeof(priv->sn));
-	memcpy(priv->cookie, cookie, sizeof(priv->cookie));
-
-	/*
-	 * Call client.
-	 */
-	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
-	if (userfunc || (i = 0))
-	  i = userfunc(sess, 
-		       command, 
-		       channel, 
-		       reqclass,
-		       &userinfo, priv);
-
-      } else if (reqclass & AIM_CAPS_CHAT) {
-	struct aim_tlv_t *miscinfo;
-	struct aim_chat_roominfo roominfo;
-	char *msg=NULL,*encoding=NULL,*lang=NULL;
-
-	miscinfo = aim_gettlv(list2, 0x2711, 1);
-	aim_chat_readroominfo(miscinfo->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 = 0))
-	  i = userfunc(sess, 
-		       command, 
-		       channel, 
-		       reqclass,
-		       &userinfo, 
-		       &roominfo, 
-		       msg, 
-		       encoding?encoding+1:NULL, 
-		       lang?lang+1:NULL);
-	  free(roominfo.name);
-	  free(msg);
-	  free(encoding);
-	  free(lang);
-      } else if (reqclass & AIM_CAPS_GETFILE) {
-	char ip[30];
-	struct aim_msgcookie_t *cachedcook;
-	struct aim_tlv_t *miscinfo;
-
-	if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t))))
-	  return 0;
-
-	memset(ip, 0, 30);
-
-	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) {
-	  free(cachedcook);
-	  return 0;
-	}
-
-	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
-	  struct aim_tlv_t *iptlv, *porttlv;
-
-	  if (!(iptlv = aim_gettlv(list2, 0x0003, 1)) || !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
-	    free(cachedcook);
-	    return 0;
-	  }
-
-	  snprintf(ip, 30, "%d.%d.%d.%d:%d",
-		   aimutil_get8(iptlv->value+0),
-		   aimutil_get8(iptlv->value+1),
-		   aimutil_get8(iptlv->value+2),
-		   aimutil_get8(iptlv->value+3),
-		   aimutil_get16(porttlv->value));
-	}
-
-	printf("faim: rend: file get request from %s (%s)\n", userinfo.sn, ip);
-
-	/*
-	 * Call client.
-	 */
-	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
-	if (userfunc || (i = 0))
-	  i = userfunc(sess, 
-		       command, 
-		       channel, 
-		       reqclass,
-		       &userinfo,
-		       ip,
-		       cookie);
-
-      } else if (reqclass & AIM_CAPS_SENDFILE) {
-#if 0
-       char ip[30];
-       char *desc = NULL;
-       struct aim_msgcookie_t *cachedcook;
-       struct aim_filetransfer_priv *ft;
-       struct aim_tlv_t *miscinfo;
-
-	memset(ip, 0, sizeof(ip));
-	
-	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)))
-	  return 0;
-
-	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) {
-	  struct aim_tlv_t *iptlv, *porttlv;
-	  
-	  iptlv = aim_gettlv(list2, 0x0003, 1);
-	  porttlv = aim_gettlv(list2, 0x0005, 1);
-
-	  snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", 
-		  aimutil_get8(iptlv->value+0),
-		  aimutil_get8(iptlv->value+1),
-		  aimutil_get8(iptlv->value+2),
-		  aimutil_get8(iptlv->value+3),
-		  aimutil_get16(porttlv->value));
-	}
-
-	if (aim_gettlv(list2, 0x000c, 1)) {
-	  desc = aim_gettlv_str(list2, 0x000c, 1);
-	}
-
-	printf("faim: rend: file transfer request from %s for %s: %s (%s)\n",
-	       userinfo.sn,
-	       miscinfo->value+8,
-	       desc, 
-	       ip);
-	
-	memcpy(cachedcook->cookie, cookie, 8);
-	
-	ft = malloc(sizeof(struct aim_filetransfer_priv));
-	strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
-	strncpy(ft->ip, ip, sizeof(ft->ip));
-	strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
-	cachedcook->type = AIM_COOKIETYPE_OFTSEND;
-	cachedcook->data = ft;
-
-	if (aim_cachecookie(sess, cachedcook) != 0)
-	  printf("faim: ERROR caching message cookie\n");
-	
-	
-	aim_accepttransfer(sess, command->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
-	
-	if (desc)
-	  free(desc);
-#endif	
-	/*
-	 * Call client.
-	 */
-	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
-	if (userfunc || (i = 0))
-	  i = userfunc(sess, 
-		       command, 
-		       channel, 
-		       reqclass,
-		       &userinfo);
-      } else {
-	printf("faim: rend: unknown rendezvous 0x%04x\n", reqclass);
-      }
-
-      aim_freetlvchain(&list2);
-    }
-
-  /*
-   * Free up the TLV chain.
-   */
-  aim_freetlvchain(&tlvlist);
-  
-
-  return i;
-}
-
-/*
- * Possible codes:
- *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
- *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
- *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
- * 
- */
-faim_export unsigned long aim_denytransfer(struct aim_session_t *sess,
-					   struct aim_conn_t *conn, 
-					   char *sender,
-					   char *cookie, 
-					   unsigned short code)
-{
-  struct command_tx_struct *newpacket;
-  int curbyte, i;
-
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sender)+6)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid);
-  for (i = 0; i < 8; i++)
-    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
-  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
-  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender));
-  curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender));
-  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code);
-
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid++);
-}
-
-/*
- * 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. 
- *
- */
-faim_export unsigned 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(AIM_FRAMETYPE_OSCAR, 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++);
-}
-
-faim_internal 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;
-  char *dest;
-  unsigned short reason = 0;
-
-  /*
-   * 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);
-    dest = NULL;
-  } else
-    dest = snac->data;
-
-  reason = aimutil_get16(command->data+10);
-
-  /*
-   * Call client.
-   */
-  userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
-  if (userfunc)
-    ret =  userfunc(sess, command, dest, reason);
-  else
-    ret = 0;
-  
-  if (snac) {
-    free(snac->data);
-    free(snac);
-  }
-
-  return ret;
-}
-
-
-faim_internal int aim_parse_missedcall(struct aim_session_t *sess,
-				       struct command_rx_struct *command)
-{
-  int i, ret = 1;
-  rxcallback_t userfunc = NULL;
-  unsigned short channel, nummissed, reason;
-  struct aim_userinfo_s userinfo;
- 
-  i = 10; /* Skip SNAC header */
-
-
-  /*
-   * XXX: supposedly, this entire packet can repeat as many times
-   * as necessary. Should implement that.
-   */
-
-  /*
-   * Channel ID.
-   */
-  channel = aimutil_get16(command->data+i);
-  i += 2;
-  
-  /*
-   * Extract the standard user info block.
-   */
-  i += aim_extractuserinfo(command->data+i, &userinfo);
-  
-  nummissed = aimutil_get16(command->data+i);
-  i += 2;
-  
-  reason = aimutil_get16(command->data+i);
-  i += 2;
-
-  /*
-   * Call client.
-   */
-  userfunc = aim_callhandler(command->conn, 0x0004, 0x000a);
-  if (userfunc)
-    ret =  userfunc(sess, command, channel, &userinfo, nummissed, reason);
-  else
-    ret = 0;
-  
-  return ret;
-}
--- a/libfaim/aim_info.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,702 +0,0 @@
-/*
- * aim_info.c
- *
- * The functions here are responsible for requesting and parsing information-
- * gathering SNACs.  
- *
- */
-
-
-#include <faim/aim.h>
-
-struct aim_priv_inforeq {
-  char sn[MAXSNLEN+1];
-  unsigned short infotype;
-};
-
-faim_export unsigned long aim_getinfo(struct aim_session_t *sess,
-				      struct aim_conn_t *conn, 
-				      const char *sn,
-				      unsigned short infotype)
-{
-  struct command_tx_struct *newpacket;
-  struct aim_priv_inforeq privdata;
-  int i = 0;
-
-  if (!sess || !conn || !sn)
-    return 0;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 12+1+strlen(sn))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);
-
-  i += aimutil_put16(newpacket->data+i, infotype);
-  i += aimutil_put8(newpacket->data+i, strlen(sn));
-  i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
-
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  strncpy(privdata.sn, sn, sizeof(privdata.sn));
-  privdata.infotype = infotype;
-  aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
-
-  return sess->snac_nextid;
-}
-
-faim_internal int aim_parse_locateerr(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;
-  char *dest;
-  unsigned short reason = 0;
-
-  /*
-   * Get SNAC from packet and look it up 
-   * the list of unrepliedto/outstanding
-   * SNACs.
-   *
-   */
-  snacid = aimutil_get32(command->data+6);
-  snac = aim_remsnac(sess, snacid);
-
-  if (!snac) {
-    printf("faim: locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
-    dest = NULL;
-  } else
-    dest = snac->data;
-
-  reason = aimutil_get16(command->data+10);
-
-  /*
-   * Call client.
-   */
-  userfunc = aim_callhandler(command->conn, 0x0002, 0x0001);
-  if (userfunc)
-    ret =  userfunc(sess, command, dest, reason);
-  else
-    ret = 0;
-  
-  if (snac) {
-    free(snac->data);
-    free(snac);
-  }
-
-  return ret;
-}
-
-/*
- * Capability blocks.  
- */
-u_char aim_caps[8][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},
-
-  /* Saves stock portfolios */
-  {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
-   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
-
-  /* Games */
-  {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
-   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
-};
-
-faim_internal unsigned short aim_getcap(unsigned char *capblock, int buflen)
-{
-  u_short ret = 0;
-  int y;
-  int offset = 0;
-  int identified;
-
-  while (offset < buflen) {
-    identified = 0;
-    for(y=0; y < (sizeof(aim_caps)/0x10); y++) {
-      if (memcmp(&aim_caps[y], capblock+offset, 0x10) == 0) {
-	switch(y) {
-	case 0: ret |= AIM_CAPS_BUDDYICON; identified++; break;
-	case 1: ret |= AIM_CAPS_VOICE; identified++; break;
-	case 2: ret |= AIM_CAPS_IMIMAGE; identified++; break;
-	case 3: ret |= AIM_CAPS_CHAT; identified++; break;
-	case 4: ret |= AIM_CAPS_GETFILE; identified++; break;
-	case 5: ret |= AIM_CAPS_SENDFILE; identified++; break;
-	case 6: ret |= AIM_CAPS_GAMES; identified++; break;
-	case 7: ret |= AIM_CAPS_SAVESTOCKS; identified++; break;
-	}
-      }
-    }
-    if (!identified) {
-      faimdprintf(1, "faim: unknown capability ");
-      for (y = 0; y < 0x10; y++)
-        faimdprintf(2, "%02x ", capblock[offset+y]);
-      faimdprintf(1, "\n");
-      ret |= 0xff00;
-    }
-
-    offset += 0x10;
-  } 
-  return ret;
-}
-
-faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps)
-{
-  int offset = 0;
-
-  if (!capblock)
-    return -1;
-
-  if ((caps & AIM_CAPS_BUDDYICON) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[0], sizeof(aim_caps[0]));
-    offset += sizeof(aim_caps[1]);
-  }
-  if ((caps & AIM_CAPS_VOICE) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[1], sizeof(aim_caps[1]));
-    offset += sizeof(aim_caps[1]);
-  }
-  if ((caps & AIM_CAPS_IMIMAGE) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[2], sizeof(aim_caps[2]));
-    offset += sizeof(aim_caps[2]);
-  }
-  if ((caps & AIM_CAPS_CHAT) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[3], sizeof(aim_caps[3]));
-    offset += sizeof(aim_caps[3]);
-  }
-  if ((caps & AIM_CAPS_GETFILE) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[4], sizeof(aim_caps[4]));
-    offset += sizeof(aim_caps[4]);
-  }
-  if ((caps & AIM_CAPS_SENDFILE) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[5], sizeof(aim_caps[5]));
-    offset += sizeof(aim_caps[5]);
-  }
-  if ((caps & AIM_CAPS_GAMES) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[6], sizeof(aim_caps[6]));
-    offset += sizeof(aim_caps[6]);
-  }
-  if ((caps & AIM_CAPS_SAVESTOCKS) && (offset < buflen)) {
-    memcpy(capblock+offset, aim_caps[7], sizeof(aim_caps[7]));
-    offset += sizeof(aim_caps[7]);
-  }
-
-  return offset;
-}
-
-/*
- * AIM is fairly regular about providing user info.  This
- * is a generic routine to extract it in its standard form.
- */
-faim_internal int aim_extractuserinfo(u_char *buf, struct aim_userinfo_s *outinfo)
-{
-  int i = 0;
-  int tlvcnt = 0;
-  int curtlv = 0;
-  int tlv1 = 0;
-  u_short curtype;
-  int lastvalid;
-
-
-  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.
-   */
-  if (buf[i] < MAXSNLEN) {
-    memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
-    outinfo->sn[(int)buf[i]] = '\0';
-  } else {
-    memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1);
-    outinfo->sn[MAXSNLEN] = '\0';
-  }
-  i = 1 + (int)buf[i];
-
-  /*
-   * Warning Level.  Stored as an unsigned short.
-   */
-  outinfo->warnlevel = 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) {
-    lastvalid = 1;
-    curtype = aimutil_get16(&buf[i]);
-    switch (curtype) {
-      /*
-       * Type = 0x0000: Invalid
-       *
-       * AOL has been trying to throw these in just to break us.
-       * They're real nice guys over there at AOL.  
-       *
-       * Just skip the two zero bytes and continue on. (This doesn't
-       * count towards tlvcnt!)
-       */
-    case 0x0000:
-      lastvalid = 0;
-      i += 2;
-      break;
-
-      /*
-       * Type = 0x0001: User flags
-       * 
-       * Specified as any of the following bitwise ORed together:
-       *      0x0001  Trial (user less than 60days)
-       *      0x0002  Unknown bit 2
-       *      0x0004  AOL Main Service user
-       *      0x0008  Unknown bit 4
-       *      0x0010  Free (AIM) user 
-       *      0x0020  Away
-       *
-       * In some odd cases, we can end up with more
-       * than one of these.  We only want the first,
-       * as the others may not be something we want.
-       *
-       */
-    case 0x0001:
-      if (tlv1) /* use only the first */
-	break;
-      outinfo->flags = aimutil_get16(&buf[i+4]);
-      tlv1++;
-      break;
-      
-      /*
-       * Type = 0x0002: Member-Since date. 
-       *
-       * The time/date that the user originally
-       * registered for the service, stored in 
-       * time_t format
-       */
-    case 0x0002: 
-      outinfo->membersince = aimutil_get32(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0003: On-Since date.
-       *
-       * The time/date that the user started 
-       * their current session, stored in time_t
-       * format.
-       */
-    case 0x0003:
-      outinfo->onlinesince = aimutil_get32(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0004: Idle time.
-       *
-       * Number of seconds since the user
-       * actively used the service.
-       */
-    case 0x0004:
-      outinfo->idletime = aimutil_get16(&buf[i+4]);
-      break;
-      
-      /*
-       * Type = 0x0006: ICQ Online Status
-       *
-       * ICQ's Away/DND/etc "enriched" status
-       * Some decoding of values done by Scott <darkagl@pcnet.com>
-       */
-    case 0x0006:
-      outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2);
-      break;
-
-
-      /*
-       * Type = 0x000a
-       *
-       * ICQ User IP Address.
-       * Ahh, the joy of ICQ security.
-       */
-    case 0x000a:
-      outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]);
-      break;
-
-      /* Type = 0x000c
-       *
-       * random crap containing the IP address,
-       * apparently a port number, and some Other Stuff.
-       *
-       */
-    case 0x000c:
-      memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25);
-      break;
-
-      /*
-       * Type = 0x000d
-       *
-       * Capability information.  Not real sure of
-       * actual decoding.  See comment on aim_bos_setprofile()
-       * in aim_misc.c about the capability block, its the same.
-       *
-       */
-    case 0x000d:
-      {
-	int len;
-	len = aimutil_get16(buf+i+2);
-	if (!len)
-	  break;
-	
-	outinfo->capabilities = aim_getcap(buf+i+4, len);
-	if (outinfo->capabilities & 0xff00)
-          faimdprintf(2, "%s\n", outinfo->sn);
-      }
-      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];
-     *
-     */
-    if (lastvalid) {
-      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.
- *
- */
-faim_internal 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.
- *
- */
-faim_internal 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, (char *)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;
-}
-
-/*
- * This parses the user info stuff out all nice and pretty then calls 
- * the higher-level callback (in the user app).
- *
- */
-faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess,
-					    struct command_rx_struct *command)
-{
-  struct aim_userinfo_s userinfo;
-  char *text_encoding = NULL;
-  char *text = NULL;
-  u_int i = 0;
-  rxcallback_t userfunc=NULL;
-  struct aim_tlvlist_t *tlvlist;
-  struct aim_snac_t *origsnac = NULL;
-  u_long snacid;
-  struct aim_priv_inforeq *inforeq;
-  
-  snacid = aimutil_get32(&command->data[6]);
-  origsnac = aim_remsnac(sess, snacid);
-
-  if (!origsnac || !origsnac->data) {
-    printf("faim: parse_userinfo_middle: major problem: no snac stored!\n");
-    return 1;
-  }
-
-  inforeq = (struct aim_priv_inforeq *)origsnac->data;
-
-  switch (inforeq->infotype) {
-  case AIM_GETINFO_GENERALINFO:
-  case AIM_GETINFO_AWAYMESSAGE:
-    i = 10;
-
-    /*
-     * 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);
-
-    /* 
-     * Depending on what informational text was requested, different
-     * TLVs will appear here.
-     *
-     * Profile will be 1 and 2, away message will be 3 and 4.
-     */
-    if (aim_gettlv(tlvlist, 0x0001, 1)) {
-      text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
-      text = aim_gettlv_str(tlvlist, 0x0002, 1);
-    } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
-      text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
-      text = aim_gettlv_str(tlvlist, 0x0004, 1);
-    }
-
-    userfunc = aim_callhandler(command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO);
-    if (userfunc) {
-      i = userfunc(sess,
-		   command, 
-		   &userinfo, 
-		   text_encoding, 
-		   text,
-		   inforeq->infotype); 
-    }
-  
-    free(text_encoding);
-    free(text);
-    aim_freetlvchain(&tlvlist);
-    break;
-  default:
-    printf("faim: parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
-    break;
-  }
-
-  if (origsnac) {
-    if (origsnac->data)
-      free(origsnac->data);
-    free(origsnac);
-  }
-
-  return 1;
-}
-
-/*
- * Inverse of aim_extractuserinfo()
- */
-faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
-{
-  int i = 0, numtlv = 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);
-
-
-  aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
-  numtlv++;
-
-  aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
-  numtlv++;
-
-  aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
-  numtlv++;
-
-  aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
-  numtlv++;
-
-#if ICQ_OSCAR_SUPPORT
-  if(atoi(info->sn) != 0) {
-    aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
-    aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
-  }
-#endif
-
-  aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
-  numtlv++;
-
-  aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen);
-  numtlv++;
-
-  i += aimutil_put16(buf+i, numtlv); /* tlvcount */
-  i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */
-  aim_freetlvchain(&tlvlist);
-
-  return i;
-}
-
-faim_export 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(AIM_FRAMETYPE_OSCAR, 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;
-}
-
-faim_export 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(AIM_FRAMETYPE_OSCAR, 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	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,579 +0,0 @@
-/*
- *  aim_login.c
- *
- *  This contains all the functions needed to actually login.
- *
- */
-
-#include <faim/aim.h>
-
-#include "md5.h"
-
-static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest);
-static int aim_encode_password(const char *password, unsigned char *encoded);
-
-/*
- * FIXME: Reimplement the TIS stuff.
- */
-#ifdef TIS_TELNET_PROXY
-#include "tis_telnet_proxy.h"
-#endif
-
-faim_export 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(AIM_FRAMETYPE_OSCAR, 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);
-}
-
-/*
- * 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).  
- */
-faim_export int aim_request_login(struct aim_session_t *sess,
-				  struct aim_conn_t *conn, 
-				  char *sn)
-{
-  int curbyte;
-  struct command_tx_struct *newpacket;
-
-  if (!sess || !conn || !sn)
-    return -1;
-
-  /*
-   * For ICQ, we enable the ancient horrible login and stuff
-   * a key packet into the queue to make it look like we got
-   * a reply back. This is so the client doesn't know we're
-   * really not doing MD5 login.
-   *
-   * This may sound stupid, but I'm not in the best of moods and 
-   * I don't plan to keep support for this crap around much longer.
-   * Its all AOL's fault anyway, really. I hate AOL.  Really.  They
-   * always seem to be able to piss me off by doing the dumbest little
-   * things.  Like disabling MD5 logins for ICQ UINs, or adding purposefully
-   * wrong TLV lengths, or adding superfluous information to host strings,
-   * or... I'll stop.
-   *
-   */
-  if ((sn[0] >= '0') && (sn[0] <= '9')) {
-    struct command_rx_struct *newrx;
-    int i;
-
-    if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct))))
-      return -1;
-    memset(newrx, 0x00, sizeof(struct command_rx_struct));
-    newrx->lock = 1; 
-    newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
-    newrx->hdr.oscar.type = 0x02;
-    newrx->hdr.oscar.seqnum = 0;
-    newrx->commandlen = 10+2+1;
-    newrx->nofree = 0; 
-    if (!(newrx->data = malloc(newrx->commandlen))) {
-      free(newrx);
-      return -1;
-    }
-
-    i = aim_putsnac(newrx->data, 0x0017, 0x0007, 0x0000, 0x0000);
-    i += aimutil_put16(newrx->data+i, 0x01);
-    i += aimutil_putstr(newrx->data+i, "0", 1);
-
-    newrx->conn = conn;
-
-    newrx->next = sess->queue_incoming;
-    sess->queue_incoming = newrx;
-
-    newrx->lock = 0;
-
-    sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
-
-    return 0;
-  } 
-
-  sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
-
-  aim_sendconnack(sess, conn);
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+2+2+strlen(sn))))
-    return -1;
-
-  newpacket->lock = 1;
-  
-  curbyte  = aim_putsnac(newpacket->data, 0x0017, 0x0006, 0x0000, 0x00010000);
-  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
-
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-
-  return aim_tx_enqueue(sess, newpacket);
-}
-
-/*
- * send_login(int socket, char *sn, char *password)
- *  
- * This is the initial login request packet.
- *
- * The password is encoded before transmition, as per
- * encode_password().  See that function for their
- * stupid method of doing it.
- *
- * Latest WinAIM:
- *   clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32"
- *   major2 = 0x0109
- *   major = 0x0400
- *   minor = 0x0003
- *   minor2 = 0x0000
- *   build = 0x088c
- *   unknown = 0x00000086
- *   lang = "en"
- *   country = "us"
- *   unknown4a = 0x01
- */
-faim_export int aim_send_login (struct aim_session_t *sess,
-				struct aim_conn_t *conn, 
-				char *sn, char *password, 
-				struct client_info_s *clientinfo,
-				char *key)
-{
-  int curbyte=0;
-  struct command_tx_struct *newpacket;
-
-  if (!clientinfo || !sn || !password)
-    return -1;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  newpacket->hdr.oscar.type = (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)?0x02:0x01;
-  
-  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
-    curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0002, 0x0000, 0x00010000);
-  else {
-    curbyte  = aimutil_put16(newpacket->data, 0x0000);
-    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
-  }
-
-  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
-  
-  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
-    md5_byte_t digest[16];
-
-    aim_encode_password_md5(password, key, digest);
-    curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0025, 16, (char *)digest);
-  } else { 
-    char *password_encoded;
-
-    password_encoded = (char *) malloc(strlen(password));
-    aim_encode_password(password, password_encoded);
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded);
-    free(password_encoded);
-  }
-
-  /* XXX is clientstring required by oscar? */
-  if (strlen(clientinfo->clientstring))
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring);
-
-  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, (unsigned short)clientinfo->major2);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, (unsigned short)clientinfo->major);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, (unsigned short)clientinfo->minor);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, (unsigned short)clientinfo->minor2);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, (unsigned short)clientinfo->build);
-  
-    curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, clientinfo->unknown);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
-    curbyte += aim_puttlv_8(newpacket->data+curbyte, 0x004a, 0x00);
-  } else {
-    /* Use very specific version numbers, to further indicate the hack. */
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0004);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x003c);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0cce);
-    curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055);
-  }
-
-  if (strlen(clientinfo->country))
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, strlen(clientinfo->country), clientinfo->country);
-  else
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, 2, "us");
-
-  if (strlen(clientinfo->lang))
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, strlen(clientinfo->lang), clientinfo->lang);
-  else
-    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, 2, "en");
-  
-  newpacket->commandlen = curbyte;
-
-  newpacket->lock = 0;
-  return aim_tx_enqueue(sess, newpacket);
-}
-
-static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest)
-{
-  md5_state_t state;
-
-  md5_init(&state);	
-  md5_append(&state, (const md5_byte_t *)key, strlen(key));
-  md5_append(&state, (const md5_byte_t *)password, strlen(password));
-  md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
-  md5_finish(&state, (md5_byte_t *)digest);
-
-  return 0;
-}
-
-/**
- * aim_encode_password - Encode a password using old XOR method
- * @password: incoming password
- * @encoded: buffer to put encoded password
- *
- * This takes a const pointer to a (null terminated) string
- * containing the unencoded password.  It also gets passed
- * an already allocated buffer to store the encoded password.
- * This buffer should be the exact length of the password without
- * the null.  The encoded password buffer /is not %NULL terminated/.
- *
- * The encoding_table seems to be a fixed set of values.  We'll
- * hope it doesn't change over time!  
- *
- * This is only used for the XOR method, not the better MD5 method.
- *
- */
-static int aim_encode_password(const char *password, unsigned char *encoded)
-{
-  u_char encoding_table[] = {
-#if 0 /* old v1 table */
-    0xf3, 0xb3, 0x6c, 0x99,
-    0x95, 0x3f, 0xac, 0xb6,
-    0xc5, 0xfa, 0x6b, 0x63,
-    0x69, 0x6c, 0xc3, 0x9f
-#else /* v2.1 table, also works for ICQ */
-    0xf3, 0x26, 0x81, 0xc4,
-    0x39, 0x86, 0xdb, 0x92,
-    0x71, 0xa3, 0xb9, 0xe6,
-    0x53, 0x7a, 0x95, 0x7c
-#endif
-  };
-
-  int i;
-  
-  for (i = 0; i < strlen(password); i++)
-      encoded[i] = (password[i] ^ encoding_table[i]);
-
-  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 passed as errorcode. If
- * its nonzero, there was an error.
- *
- */
-faim_internal int aim_authparse(struct aim_session_t *sess, 
-				struct command_rx_struct *command)
-{
-  struct aim_tlvlist_t *tlvlist;
-  int ret = 1;
-  rxcallback_t userfunc = NULL;
-  char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
-  unsigned char *cookie = NULL;
-  int errorcode = 0, regstatus = 0;
-  int latestbuild = 0, latestbetabuild = 0;
-  char *latestrelease = NULL, *latestbeta = NULL;
-  char *latestreleaseurl = NULL, *latestbetaurl = NULL;
-  char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
-
-  /*
-   * Read block of TLVs.  All further data is derived
-   * from what is parsed here.
-   *
-   * For SNAC login, there's a 17/3 SNAC header in front.
-   *
-   */
-  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
-    tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
-  else
-    tlvlist = aim_readtlvchain(command->data, command->commandlen);
-
-  /*
-   * No matter what, we should have a screen name.
-   */
-  memset(sess->sn, 0, sizeof(sess->sn));
-  if (aim_gettlv(tlvlist, 0x0001, 1)) {
-    sn = aim_gettlv_str(tlvlist, 0x0001, 1);
-    strncpy(sess->sn, sn, sizeof(sess->sn));
-  }
-
-  /*
-   * Check for an error code.  If so, we should also
-   * have an error url.
-   */
-  if (aim_gettlv(tlvlist, 0x0008, 1)) 
-    errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
-  if (aim_gettlv(tlvlist, 0x0004, 1))
-    errurl = aim_gettlv_str(tlvlist, 0x0004, 1);
-
-  /*
-   * BOS server address.
-   */
-  if (aim_gettlv(tlvlist, 0x0005, 1))
-    bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
-
-  /*
-   * Authorization cookie.
-   */
-  if (aim_gettlv(tlvlist, 0x0006, 1)) {
-    struct aim_tlv_t *tmptlv;
-
-    tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
-
-    if ((cookie = malloc(tmptlv->length)))
-      memcpy(cookie, tmptlv->value, tmptlv->length);
-  }
-
-  /*
-   * The email address attached to this account
-   *   Not available for ICQ logins.
-   */
-  if (aim_gettlv(tlvlist, 0x0011, 1))
-    email = aim_gettlv_str(tlvlist, 0x0011, 1);
-
-  /*
-   * The registration status.  (Not real sure what it means.)
-   *   Not available for ICQ logins.
-   *
-   *   1 = No disclosure
-   *   2 = Limited disclosure
-   *   3 = Full disclosure
-   *
-   * This has to do with whether your email address is available
-   * to other users or not.  AFAIK, this feature is no longer used.
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0013, 1))
-    regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
-
-  if (aim_gettlv(tlvlist, 0x0040, 1))
-    latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1);
-  if (aim_gettlv(tlvlist, 0x0041, 1))
-    latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1);
-  if (aim_gettlv(tlvlist, 0x0042, 1))
-    latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1);
-  if (aim_gettlv(tlvlist, 0x0043, 1))
-    latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1);
-  if (aim_gettlv(tlvlist, 0x0048, 1))
-    ; /* no idea what this is */
-
-  if (aim_gettlv(tlvlist, 0x0044, 1))
-    latestbuild = aim_gettlv32(tlvlist, 0x0044, 1);
-  if (aim_gettlv(tlvlist, 0x0045, 1))
-    latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1);
-  if (aim_gettlv(tlvlist, 0x0046, 1))
-    latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1);
-  if (aim_gettlv(tlvlist, 0x0047, 1))
-    latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1);
-  if (aim_gettlv(tlvlist, 0x0049, 1))
-    ; /* no idea what this is */
-
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0003)))
-    ret = userfunc(sess, command, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
-
-
-  if (sn)
-    free(sn);
-  if (bosip)
-    free(bosip);
-  if (errurl)
-    free(errurl);
-  if (email)
-    free(email);
-  if (cookie)
-    free(cookie);
-  if (latestrelease)
-    free(latestrelease);
-  if (latestreleaseurl)
-    free(latestreleaseurl);
-  if (latestbeta)
-    free(latestbeta);
-  if (latestbetaurl)
-    free(latestbetaurl);
-  if (latestreleaseinfo)
-    free(latestreleaseinfo);
-  if (latestbetainfo)
-    free(latestbetainfo);
-
-  aim_freetlvchain(&tlvlist);
-
-  return ret;
-}
-
-/*
- * Middle handler for 0017/0007 SNACs.  Contains the auth key prefixed
- * by only its length in a two byte word.
- *
- * Calls the client, which should then use the value to call aim_send_login.
- *
- */
-faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command)
-{
-  unsigned char *key;
-  int keylen;
-  int ret = 1;
-  rxcallback_t userfunc;
-
-  keylen = aimutil_get16(command->data+10);
-  if (!(key = malloc(keylen+1)))
-    return ret;
-  memcpy(key, command->data+12, keylen);
-  key[keylen] = '\0';
-  
-  if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0007)))
-    ret = userfunc(sess, command, (char *)key);
-
-  free(key);  
-
-  return ret;
-}
-
-/*
- * Generate an authorization response.  
- *
- * You probably don't want this unless you're writing an AIM server.
- *
- */
-faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, 
-					   struct aim_conn_t *conn, 
-					   char *sn, int errorcode,
-					   char *errorurl, char *bosip, 
-					   char *cookie, char *email, 
-					   int regstatus)
-{	
-  struct command_tx_struct *tx;
-  struct aim_tlvlist_t *tlvlist = NULL;
-
-  if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0004, conn, 1152)))
-    return -1;
-  
-  tx->lock = 1;
-
-  if (sn)
-    aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn));
-  else
-    aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn));
-
-  if (errorcode) {
-    aim_addtlvtochain16(&tlvlist, 0x0008, errorcode);
-    aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl));
-  } else {
-    aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip));
-    aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
-    aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email));
-    aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus);
-  }
-
-  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)
- */
-faim_export 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)
- */
-faim_export 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(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+0x22)))
-    return -1;
-
-  tx->lock = 1;
-
-  i += aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++);
-  
-  i += aimutil_put16(tx->data+i, 0x0001);  
-  i += aimutil_put16(tx->data+i, 0x0002);
-  i += aimutil_put16(tx->data+i, 0x0003);
-  i += aimutil_put16(tx->data+i, 0x0004);
-  i += aimutil_put16(tx->data+i, 0x0006);
-  i += aimutil_put16(tx->data+i, 0x0008);
-  i += aimutil_put16(tx->data+i, 0x0009);
-  i += aimutil_put16(tx->data+i, 0x000a);
-  i += aimutil_put16(tx->data+i, 0x000b);
-  i += aimutil_put16(tx->data+i, 0x000c);
-  i += aimutil_put16(tx->data+i, 0x0013);
-  i += aimutil_put16(tx->data+i, 0x0015);
-
-  tx->commandlen = i;
-  tx->lock = 0;
-  return aim_tx_enqueue(sess, tx);
-}
-
-
-/* 
- * Send service redirect.  (Non-Client)
- */
-faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, 
-					   struct aim_conn_t *conn, 
-					   unsigned short servid, 
-					   char *ip,
-					   char *cookie)
-{	
-  struct command_tx_struct *tx;
-  struct aim_tlvlist_t *tlvlist = NULL;
-  int i = 0;
-
-  if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152)))
-    return -1;
-
-  tx->lock = 1;
-
-  i += aim_putsnac(tx->data+i, 0x0001, 0x0005, 0x0000, 0x00000000);
-  
-  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	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-/*
- * aim_logoff.c
- *
- *
- */
-
-#include <faim/aim.h> 
-
-/* 
- * aim_logoff()
- * 
- * Closes -ALL- open connections.
- *
- */
-int aim_logoff(struct aim_session_t *sess)
-{
-  aim_connrst(sess);  /* in case we want to connect again */
-
-  return 0;
-
-}
--- a/libfaim/aim_meta.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * Administrative things for libfaim.
- *
- * Changes by EWarmenhoven Wed May 31 00:31:52 UTC 2000
- * - I don't wanna use aim_buildcode! :) I'll put the things that get made
- *   by the .sh file into Makefile.am and hopefully that'll work better.
- *  
- */
-
-#include <faim/aim.h>
-/* #include <aim_buildcode.h> generated by mkbuildinfo.sh */
-
-faim_export char *aim_getbuilddate(void)
-{
-  return AIM_BUILDDATE;
-}
-
-faim_export char *aim_getbuildtime(void)
-{
-  return AIM_BUILDTIME;
-}
-
-faim_export char *aim_getbuildstring(void)
-{
-  static char string[100];
-
-  snprintf(string, 99, "%d.%d.%d-%s%s", 
-	   FAIM_VERSION_MAJOR,
-	   FAIM_VERSION_MINOR,
-	   FAIM_VERSION_MINORMINOR,
-	   aim_getbuilddate(),
-	   aim_getbuildtime());
-  return string;
-}
-
-#if debug > 0
-faim_internal void faimdprintf(int dlevel, const char *format, ...)
-{
-  if (dlevel >= debug) {
-    va_list ap;
-    
-    va_start(ap, format);
-    vfprintf(stderr, format, ap);
-    va_end(ap);
-  }
-  return;
-}
-#else
-faim_internal void faimdprintf(int dlevel, const char *format, ...)
-{
-  return;
-}
-#endif
--- a/libfaim/aim_misc.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,888 +0,0 @@
-
-/*
- * aim_misc.c
- *
- * TODO: Seperate a lot of this into an aim_bos.c.
- *
- * Other things...
- *
- *   - Idle setting 
- * 
- *
- */
-
-#include <faim/aim.h> 
-
-/*
- * aim_bos_setidle()
- *
- *  Should set your current idle time in seconds.  Idealy, OSCAR should
- *  do this for us.  But, it doesn't.  The client must call this to set idle
- *  time.  
- *
- */
-faim_export unsigned long aim_bos_setidle(struct aim_session_t *sess,
-					  struct aim_conn_t *conn, 
-					  u_long idletime)
-{
-  return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
-}
-
-
-/*
- * aim_bos_changevisibility(conn, changtype, namelist)
- *
- * Changes your visibility depending on changetype:
- *
- *  AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
- *  AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
- *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
- *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
- *
- * list should be a list of 
- * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
- *
- * Equivelents to options in WinAIM:
- *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
- *      with only your name on it.
- *   - Allow only users on my Buddy List: Send an 
- *      AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
- *      buddy list
- *   - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD 
- *      with everyone listed that you want to see you.
- *   - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only 
- *      yourself in the list
- *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
- *      the list of users to be blocked
- *
- *
- */
-faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *sess,
-						   struct aim_conn_t *conn, 
-						   int changetype, 
-						   char *denylist)
-{
-  struct command_tx_struct *newpacket;
-  int packlen = 0;
-  u_short subtype;
-
-  char *localcpy = NULL;
-  char *tmpptr = NULL;
-  int i,j;
-  int listcount;
-
-  if (!denylist)
-    return 0;
-
-  localcpy = (char *) malloc(strlen(denylist)+1);
-  memcpy(localcpy, denylist, strlen(denylist)+1);
-  
-  listcount = aimutil_itemcnt(localcpy, '&');
-  packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  switch(changetype)
-    {
-    case AIM_VISIBILITYCHANGE_PERMITADD:    subtype = 0x05; break;
-    case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break;
-    case AIM_VISIBILITYCHANGE_DENYADD:      subtype = 0x07; break;
-    case AIM_VISIBILITYCHANGE_DENYREMOVE:   subtype = 0x08; break;
-    default:
-      free(newpacket->data);
-      free(newpacket);
-      return 0;
-    }
-
-  /* We actually DO NOT send a SNAC ID with this one! */
-  aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0);
- 
-  j = 10;  /* the next byte */
-  
-  for (i=0; (i < (listcount - 1)) && (i < 99); i++)
-    {
-      tmpptr = aimutil_itemidx(localcpy, i, '&');
-
-      newpacket->data[j] = strlen(tmpptr);
-      memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
-      j += strlen(tmpptr)+1;
-      free(tmpptr);
-    }
-  free(localcpy);
-
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid); /* dont increment */
-
-}
-
-
-
-/*
- * aim_bos_setbuddylist(buddylist)
- *
- * This just builds the "set buddy list" command then queues it.
- *
- * buddy_list = "Screen Name One&ScreenNameTwo&";
- *
- * TODO: Clean this up.  
- *
- * XXX: I can't stress the TODO enough.
- *
- */
-faim_export unsigned 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;
-
-  int len = 0;
-
-  char *localcpy = NULL;
-  char *tmpptr = NULL;
-
-  len = 10; /* 10B SNAC headers */
-
-  if (!buddy_list || !(localcpy = (char *) malloc(strlen(buddy_list)+1))) 
-    return -1;
-  strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
-
-  i = 0;
-  tmpptr = strtok(localcpy, "&");
-  while ((tmpptr != NULL) && (i < 150)) {
-#if debug > 0
-    printf("---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
-#endif
-    len += 1+strlen(tmpptr);
-    i++;
-    tmpptr = strtok(NULL, "&");
-  }
-#if debug > 0
-  printf("*** send buddy list len: %d (%x)\n", len, len);
-#endif
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, len)))
-    return -1;
-
-  newpacket->lock = 1;
-  
-  aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, 0);
-
-  j = 10;  /* the next byte */
-
-  strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
-  i = 0;
-  tmpptr = strtok(localcpy, "&");
-  while ((tmpptr != NULL) & (i < 150)) {
-#if debug > 0
-    printf("---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
-#endif
-    newpacket->data[j] = strlen(tmpptr);
-    memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
-    j += 1+strlen(tmpptr);
-    i++;
-    tmpptr = strtok(NULL, "&");
-  }
-
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  free(localcpy);
-
-  return (sess->snac_nextid);
-}
-
-/* 
- * aim_bos_setprofile(profile)
- *
- * Gives BOS your profile.
- *
- * 
- */
-faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     char *profile,
-					     char *awaymsg,
-					     unsigned short caps)
-{
-  struct command_tx_struct *newpacket;
-  int i = 0, tmp, caplen;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(profile)+1+(awaymsg?strlen(awaymsg):0))))
-    return -1;
-
-  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
-    i += aim_puttlv_str(newpacket->data+i, 0x0004, 0x0000, NULL);
-
-  /* Capability information. */
- 
-  tmp = (i += aimutil_put16(newpacket->data+i, 0x0005));
-  i += aimutil_put16(newpacket->data+i, 0x0000); /* rewritten later */
-  i += (caplen = aim_putcap(newpacket->data+i, 512, caps));
-  aimutil_put16(newpacket->data+tmp, caplen); /* rewrite TLV size */
-
-  newpacket->commandlen = i;
-  aim_tx_enqueue(sess, newpacket);
-  
-  return (sess->snac_nextid++);
-}
-
-/* 
- * aim_bos_setgroupperm(mask)
- * 
- * Set group permisson mask.  Normally 0x1f (all classes).
- *
- * The group permission mask allows you to keep users of a certain
- * class or classes from talking to you.  The mask should be
- * a bitwise OR of all the user classes you want to see you.
- *
- */
-faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *sess,
-					       struct aim_conn_t *conn, 
-					       u_long mask)
-{
-  return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
-}
-
-faim_internal int aim_parse_bosrights(struct aim_session_t *sess,
-				      struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  int ret=1;
-  struct aim_tlvlist_t *tlvlist;
-  unsigned short maxpermits = 0, maxdenies = 0;
-
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
-    return ret;
-
-  /*
-   * TLV type 0x0001: Maximum number of buddies on permit list.
-   */
-  if (aim_gettlv(tlvlist, 0x0001, 1))
-    maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
-
-  /*
-   * TLV type 0x0002: Maximum number of buddies on deny list.
-   *
-   */
-  if (aim_gettlv(tlvlist, 0x0002, 1)) 
-    maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
-  
-  if ((userfunc = aim_callhandler(command->conn, 0x0009, 0x0003)))
-    ret = userfunc(sess, command, maxpermits, maxdenies);
-
-  aim_freetlvchain(&tlvlist);
-
-  return ret;  
-}
-
-/*
- * aim_bos_clientready()
- * 
- * Send Client Ready.  
- *
- */
-faim_export unsigned long aim_bos_clientready(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
-{
-#define AIM_TOOL_JAVA   0x0001
-#define AIM_TOOL_MAC    0x0002
-#define AIM_TOOL_WIN16  0x0003
-#define AIM_TOOL_WIN32  0x0004
-#define AIM_TOOL_MAC68K 0x0005
-#define AIM_TOOL_MACPPC 0x0006
-  struct aim_tool_version {
-    unsigned short group;
-    unsigned short version;
-    unsigned short tool;
-    unsigned short toolversion;
-  } tools[] = {
-    {0x0001, 0x0003,    AIM_TOOL_WIN32, 0x0686},
-    {0x0002, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x0003, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0004, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0006, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x0008, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x0009, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
-    {0x000a, 0x0001,    AIM_TOOL_WIN32, 0x0001},
-    {0x000b, 0x0001,    AIM_TOOL_WIN32, 0x0001}
-  };
-  int i,j;
-  struct command_tx_struct *newpacket;
-  int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
-
-  for (j = 0; j < toolcount; j++) {
-    i += aimutil_put16(newpacket->data+i, tools[j].group);
-    i += aimutil_put16(newpacket->data+i, tools[j].version);
-    i += aimutil_put16(newpacket->data+i, tools[j].tool);
-    i += aimutil_put16(newpacket->data+i, tools[j].toolversion);
-  }
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
-}
-
-/* 
- *  Request Rate Information.
- * 
- */
-faim_export unsigned long aim_bos_reqrate(struct aim_session_t *sess,
-					  struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
-}
-
-/* 
- *  Rate Information Response Acknowledge.
- *
- */
-faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *sess,
-					      struct aim_conn_t *conn)
-{
-  struct command_tx_struct *newpacket;
-  int packlen = 20, i=0;
-
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen)))
-    return (sess->snac_nextid);
-  
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, 0);
-  i += aimutil_put16(newpacket->data+i, 0x0001); 
-  i += aimutil_put16(newpacket->data+i, 0x0002);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-  i += aimutil_put16(newpacket->data+i, 0x0004);
-  i += aimutil_put16(newpacket->data+i, 0x0005);
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid);
-}
-
-/* 
- * aim_bos_setprivacyflags()
- *
- * Sets privacy flags. Normally 0x03.
- *
- *  Bit 1:  Allows other AIM users to see how long you've been idle.
- *  Bit 2:  Allows other AIM users to see how long you've been a member.
- *
- */
-faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *sess,
-						  struct aim_conn_t *conn, 
-						  u_long flags)
-{
-  return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
-}
-
-/*
- * aim_bos_reqpersonalinfo()
- *
- * Requests the current user's information. Can't go generic on this one
- * because aparently it uses SNAC flags.
- *
- */
-faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *sess,
-						  struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
-}
-
-faim_export unsigned 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(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10 + (4*12))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
-
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-
-  i += aimutil_put16(newpacket->data+i, 0x0013);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  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);
-
-  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, 0x0001);
-
-  i += aimutil_put16(newpacket->data+i, 0x000c);
-  i += aimutil_put16(newpacket->data+i, 0x0001);
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
-}
-
-
-/*
- * aim_bos_reqservice(serviceid)
- *
- * Service request. 
- *
- */
-faim_export unsigned long aim_bos_reqservice(struct aim_session_t *sess,
-			  struct aim_conn_t *conn, 
-			  u_short serviceid)
-{
-  return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
-}
-
-/*
- * aim_bos_nop()
- *
- * No-op.  WinAIM sends these every 4min or so to keep
- * the connection alive.  Its not real necessary.
- *
- */
-faim_export unsigned long aim_bos_nop(struct aim_session_t *sess,
-				      struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
-}
-
-/*
- * aim_flap_nop()
- *
- * No-op.  WinAIM 4.x sends these _every minute_ to keep
- * the connection alive.  
- */
-faim_export unsigned long aim_flap_nop(struct aim_session_t *sess,
-				       struct aim_conn_t *conn)
-{
-  struct command_tx_struct *newpacket;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0005, conn, 0)))
-    return sess->snac_nextid;
-
-  newpacket->lock = 1;
-  newpacket->commandlen = 0;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid);
-}
-
-/*
- * aim_bos_reqrights()
- *
- * Request BOS rights.
- *
- */
-faim_export unsigned long aim_bos_reqrights(struct aim_session_t *sess,
-					    struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
-}
-
-/*
- * aim_bos_reqbuddyrights()
- *
- * Request Buddy List rights.
- *
- */
-faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *sess,
-						 struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
-}
-
-/*
- * aim_send_warning(struct aim_session_t *sess, 
- *                  struct aim_conn_t *conn, char *destsn, int anon)
- * send a warning to destsn.
- * anon is anonymous or not;
- *  AIM_WARN_ANON anonymous
- *
- * returns -1 on error (couldn't alloc packet), next snacid on success.
- *
- */
-faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon)
-{
-  struct command_tx_struct *newpacket;
-  int curbyte;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, strlen(destsn)+13)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  curbyte  = 0;
-  curbyte += aim_putsnac(newpacket->data+curbyte,
-                        0x0004, 0x0008, 0x0000, sess->snac_nextid);
-
-  curbyte += aimutil_put16(newpacket->data+curbyte, (anon & AIM_WARN_ANON)?1:0);
-
-  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(destsn));
-
-  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
-
-  newpacket->commandlen = curbyte;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return (sess->snac_nextid++);
-}
-
-/*
- * aim_debugconn_sendconnect()
- *
- * For aimdebugd.  If you don't know what it is, you don't want to.
- */
-faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess,
-						    struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT);
-}
-
-/*
- * Generic routine for sending commands.
- *
- *
- * I know I can do this in a smarter way...but I'm not thinking straight
- * right now...
- *
- * I had one big function that handled all three cases, but then it broke
- * and I split it up into three.  But then I fixed it.  I just never went
- * back to the single.  I don't see any advantage to doing it either way.
- *
- */
-faim_internal unsigned long aim_genericreq_n(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype)
-{
-  struct command_tx_struct *newpacket;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10)))
-    return 0;
-
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
-
-  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
-
-  aim_tx_enqueue(sess, newpacket);
-  return sess->snac_nextid;
-}
-
-/*
- *
- *
- */
-faim_internal unsigned long aim_genericreq_l(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype, 
-					     u_long *longdata)
-{
-  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(sess, conn, family, subtype);
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+sizeof(u_long))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
-
-  /* copy in data */
-  newlong = htonl(*longdata);
-  memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long));
-
-  aim_tx_enqueue(sess, newpacket);
-  return sess->snac_nextid;
-}
-
-faim_internal unsigned long aim_genericreq_s(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     u_short family, u_short subtype, 
-					     u_short *shortdata)
-{
-  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(sess, conn, family, subtype);
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+sizeof(u_short))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
-
-  /* copy in data */
-  newshort = htons(*shortdata);
-  memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short));
-
-  aim_tx_enqueue(sess, newpacket);
-  return sess->snac_nextid;
-}
-
-/*
- * aim_bos_reqlocaterights()
- *
- * Request Location services rights.
- *
- */
-faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *sess,
-						  struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
-}
-
-/*
-* aim_bos_reqicbmparaminfo()
- *
- * Request ICBM parameter information.
- *
- */
-faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *sess,
-						   struct aim_conn_t *conn)
-{
-  return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
-}
-
-/*
- * Add ICBM parameter? Huh?
- */
-faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,
-					   struct aim_conn_t *conn)
-{
-  struct command_tx_struct *newpacket;
-  int packlen = 10+16, i=0;
-
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen)))
-    return (sess->snac_nextid);
-  
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
-  aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
-
-  i += aimutil_put16(newpacket->data+i, 0x0000); 
-  i += aimutil_put16(newpacket->data+i, 0x0000);
-  i += aimutil_put16(newpacket->data+i, 0x0003);
-  i += aimutil_put16(newpacket->data+i, 0x1f40);
-  i += aimutil_put16(newpacket->data+i, 0x03e7);
-  i += aimutil_put16(newpacket->data+i, 0x03e7);
-  i += aimutil_put16(newpacket->data+i, 0x0000); 
-  i += aimutil_put16(newpacket->data+i, 0x0000); 
-  
-  aim_tx_enqueue(sess, newpacket);
-
-  return sess->snac_nextid;
-}
-
-/* 
- * Set directory profile data (not the same as aim_bos_setprofile!)
- */
-faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy) 
-{
-  struct command_tx_struct *newpacket;
-  int packlen = 0, i = 0;
-
-  packlen += 2+2+2;
-
-  if(first) /* TLV 0001 */
-    packlen += (strlen(first) + 4);
-  if(middle) 
-    packlen += (strlen(middle) + 4);
-  if(last)
-    packlen += (strlen(last) + 4);
-  if(maiden)
-    packlen += (strlen(maiden) + 4);
-  if(nickname)
-    packlen += (strlen(nickname) + 4);
-  if(street)
-    packlen += (strlen(street) + 4);
-  if(state)
-    packlen += (strlen(state) + 4);
-  if(city)
-    packlen += (strlen(city) + 4);
-  if(zip)
-    packlen += (strlen(zip) + 4);
-    
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen+10)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x0009, 0x0000, 0);
-
-  /* 000a/0002: privacy: 1 to allow search/disp, 0 to disallow */
-  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy);
-
-
-  if (first)
-    i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(first), first);
-  if (middle)
-    i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(middle), middle);
-  if (last)
-    i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(last), last);
-  if (maiden)
-    i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(maiden), maiden);
-  if (nickname)
-    i += aim_puttlv_str(newpacket->data+i, 0x000c, strlen(nickname), nickname);
-  if (street)
-    i += aim_puttlv_str(newpacket->data+i, 0x0021, strlen(street), street);
-  if (city)
-    i += aim_puttlv_str(newpacket->data+i, 0x0008, strlen(city), city);
-  if (state)
-    i += aim_puttlv_str(newpacket->data+i, 0x0007, strlen(state), state);
-  if (zip)
-    i += aim_puttlv_str(newpacket->data+i, 0x000d, strlen(zip), zip);
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-   
-  return(sess->snac_nextid);
-}
-
-faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy)
-{
-  struct command_tx_struct *newpacket;
-  int packlen = 0, i = 0;
-
-  packlen += 2+2+2;
-
-  if(interest1)
-    packlen += (strlen(interest1) + 4);
-  if(interest2)
-    packlen += (strlen(interest2) + 4);
-  if(interest3)
-    packlen += (strlen(interest3) + 4);
-  if(interest4)
-    packlen += (strlen(interest4) + 4);
-  if(interest5)
-    packlen += (strlen(interest5) + 4) ;
-
-    
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, packlen+10)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0002, 0x000f, 0x0000, 0);
-
-  /* 000a/0002: 0000 ?? ?privacy? */
-  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); 
-
-  if(interest1) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest1);
-  if(interest2) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest2), interest2);
-  if(interest3) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest3), interest3);
-  if(interest4) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest4), interest4);
-  if(interest5) 
-    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest5);
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-    
-  aim_tx_enqueue(sess, newpacket);
-    
-  return(sess->snac_nextid);
-}
-
-faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess,
-					    struct aim_conn_t *conn, 
-					    unsigned long status)
-{
-  struct command_tx_struct *newpacket;
-  int i;
-  unsigned long data;
-  
-  data = 0x00030000 | status; /* yay for error checking ;^) */
-
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10 + 4)))
-    return -1;
-
-  newpacket->lock = 1;
-
-  i = aim_putsnac(newpacket->data, 0x0001, 0x001e, 0x0000, 0x0000001e);
-  i += aim_puttlv_32(newpacket->data+i, 0x0006, data);
-
-  newpacket->commandlen = i;
-  newpacket->lock = 0;
-
-  aim_tx_enqueue(sess, newpacket);
-
-  return(sess->snac_nextid);
-}
--- a/libfaim/aim_msgcookie.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,203 +0,0 @@
-/*
- * Cookie Caching stuff. Adam wrote this, apparently just some
- * derivatives of n's SNAC work. I cleaned it up, added comments.
- * 
- * I'm going to rewrite this stuff eventually, honest. -jbm
- * 
- */
-
-/*
- * I'm assuming that cookies are type-specific. that is, we can have
- * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
- * lose some error checking. if we assume cookies are not type-specific and are
- * wrong, we get quirky behavior when cookies step on each others' toes.
- */
-
-#include <faim/aim.h>
-
-/*
- * aim_cachecookie: 
- * appends a cookie to the cookie list for sess.
- * - if cookie->cookie for type cookie->type is found, addtime is updated.
- * - copies cookie struct; you need to free() it afterwards;
- * - cookie->data is not copied, but passed along. don't free it.
- * - newcook->addtime is updated accordingly;
- * - cookie->type is just passed across.
- * 
- * returns -1 on error, 0 on success.  
- */
-faim_internal int aim_cachecookie(struct aim_session_t *sess,
-				  struct aim_msgcookie_t *cookie)
-{
-  struct aim_msgcookie_t *newcook;
-
-  if (!cookie)
-    return -1;
-
-  if( (newcook = aim_checkcookie(sess, cookie->cookie, cookie->type)) ) {
-    newcook->addtime = time(NULL);
-    if(cookie->data != newcook->data) {
-      
-      printf("faim: cachecookie: matching cookie/type pair "
-	     "%x%x%x%x%x%x%x%x/%x has different *data. free()ing cookie copy..\n",
-	     cookie->cookie[0], cookie->cookie[1], cookie->cookie[2],
-	     cookie->cookie[3], cookie->cookie[4], cookie->cookie[5],
-	     cookie->cookie[6], cookie->cookie[7], cookie->type);
-      
-      free(cookie->data);
-    }
-
-    return 0;
-  }
-  
-  if (!(newcook = malloc(sizeof(struct aim_msgcookie_t))))
-    return -1;
-  memcpy(newcook, cookie, sizeof(struct aim_msgcookie_t));
-  newcook->addtime = time(NULL);
-  
-  newcook->next = sess->msgcookies;
-  sess->msgcookies = newcook;
-
-  return 0;
-}
-
-/*
- * aim_uncachecookie:
- * takes a cookie string and grabs the cookie struct associated with
- * it. removes struct from chain.  returns the struct if found, or
- * NULL on not found.
- */
-faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type)
-{
-  struct aim_msgcookie_t *cur;
-
-  if (!cookie || !sess->msgcookies)
-    return NULL;
-
-  if ((sess->msgcookies->type == type) && 
-      (memcmp(sess->msgcookies->cookie, cookie, 8) == 0)) {
-    struct aim_msgcookie_t *tmp;
-
-    tmp = sess->msgcookies;
-    sess->msgcookies = sess->msgcookies->next;
-
-    return tmp;
-  } 
-
-  for (cur = sess->msgcookies; cur->next; cur = cur->next) {
-    if ((cur->next->type == type) &&
-	(memcmp(cur->next->cookie, cookie, 8) == 0)) {
-      struct aim_msgcookie_t *tmp;
-      
-      tmp = cur->next;
-      cur->next = cur->next->next;
-
-      return tmp;
-    }
-  }
-
-  return NULL;
-}
-
-/*
- * aim_purgecookies:
- * purge out old cookies
- *
- * finds old cookies, calls uncache on them.  
- *
- * this is highly inefficient, but It Works. and i don't feel like
- * totally rewriting this. it might have some concurrency issues as
- * well, if i rewrite it.
- *
- * i'll avoid the puns.  
- */
-
-faim_export int aim_purgecookies(struct aim_session_t *sess, int maxage)
-{
-  struct aim_msgcookie_t *cur;
-  time_t curtime;
- 
-  curtime = time(NULL);
-
-  for (cur = sess->msgcookies; cur; cur = cur->next) {
-    if (cur->addtime > (time(NULL) - maxage)) {
-      struct aim_msgcookie_t *remed = NULL;
-
-#if 1
-      printf("aimmsgcookie: WARNING purged obsolete message cookie %x%x%x%x %x%x%x%x\n",
-	     cur->cookie[0], cur->cookie[1], cur->cookie[2], cur->cookie[3],
-	     cur->cookie[4], cur->cookie[5], cur->cookie[6], cur->cookie[7]);
-#endif
-
-      remed = aim_uncachecookie(sess, cur->cookie, cur->type);
-      if (remed) {
-	if (remed->data)
-	  free(remed->data);
-	free(remed);
-      }
-    }
-  }
-  
-  return 0;
-}
-
-faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *c, int type, void *data) 
-{
-  struct aim_msgcookie_t *cookie;
-
-  if (!c)
-    return NULL;
-
-  if (!(cookie = calloc(1, sizeof(struct aim_msgcookie_t))))
-    return NULL;
-  
-  cookie->data = data;
-  cookie->type = type;
-  memcpy(cookie->cookie, c, 8);
-  
-  return cookie;
-}
-  
-faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *sess, unsigned char *cookie, int type)
-{
-  struct aim_msgcookie_t *cur;
-  
-  for (cur = sess->msgcookies; cur; cur = cur->next) {
-    if ((cur->type == type) && 
-	(memcmp(cur->cookie, cookie, 8) == 0))
-      return cur;
-  }
-
-  return NULL;
-}
-
-static int aim_freecookie(struct aim_msgcookie_t *cookie) {
-  return 0;
-} 
-
-faim_internal int aim_msgcookie_gettype(int reqclass) {
-  /* XXX: hokey-assed. needs fixed. */
-  switch(reqclass) {
-  case AIM_CAPS_BUDDYICON:
-    return AIM_COOKIETYPE_OFTICON;
-    break;
-  case AIM_CAPS_VOICE:
-    return AIM_COOKIETYPE_OFTVOICE;
-    break;
-  case AIM_CAPS_IMIMAGE:
-    return AIM_COOKIETYPE_OFTIMAGE;
-    break;
-  case AIM_CAPS_CHAT:
-    return AIM_COOKIETYPE_CHAT;
-    break;
-  case AIM_CAPS_GETFILE:
-    return AIM_COOKIETYPE_OFTGET;
-    break;
-  case AIM_CAPS_SENDFILE:
-    return AIM_COOKIETYPE_OFTSEND;
-    break;
-  default:
-    return AIM_COOKIETYPE_UNKNOWN;
-    break;
-  }           
-}
--- a/libfaim/aim_rxhandlers.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,978 +0,0 @@
-/*
- * 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 <faim/aim.h>
-
-/*
- * Bleck functions get called when there's no non-bleck functions
- * around to cleanup the mess...
- */
-faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
-{
-  u_short family;
-  u_short subtype;
-
-  u_short maxf;
-  u_short maxs;
-
-  /* XXX: this is ugly. and big just for debugging. */
-  char *literals[14][25] = {
-    {"Invalid", 
-     NULL
-    },
-    {"General", 
-     "Invalid",
-     "Error",
-     "Client Ready",
-     "Server Ready",
-     "Service Request",
-     "Redirect",
-     "Rate Information Request",
-     "Rate Information",
-     "Rate Information Ack",
-     NULL,
-     "Rate Information Change",
-     "Server Pause",
-     NULL,
-     "Server Resume",
-     "Request Personal User Information",
-     "Personal User Information",
-     "Evil Notification",
-     NULL,
-     "Migration notice",
-     "Message of the Day",
-     "Set Privacy Flags",
-     "Well Known URL",
-     "NOP"
-    },
-    {"Location", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Information", 
-      "Set user information", 
-      "Request User Information", 
-      "User Information", 
-      "Watcher Sub Request",
-      "Watcher Notification"
-    },
-    {"Buddy List Management", 
-      "Invalid", 
-      "Error", 
-      "Request Rights",
-      "Rights Information",
-      "Add Buddy", 
-      "Remove Buddy", 
-      "Watcher List Query", 
-      "Watcher List Response", 
-      "Watcher SubRequest", 
-      "Watcher Notification", 
-      "Reject Notification", 
-      "Oncoming Buddy", 
-      "Offgoing Buddy"
-    },
-    {"Messeging", 
-      "Invalid",
-      "Error", 
-      "Add ICBM Parameter",
-      "Remove ICBM Parameter", 
-      "Request Parameter Information",
-      "Parameter Information",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request",
-      "Evil Reply", 
-      "Missed Calls",
-      "Message Error", 
-      "Host Ack"
-    },
-    {"Advertisements", 
-      "Invalid", 
-      "Error", 
-      "Request Ad",
-      "Ad Data (GIFs)"
-    },
-    {"Invitation / Client-to-Client", 
-     "Invalid",
-     "Error",
-     "Invite a Friend",
-     "Invitation Ack"
-    },
-    {"Administrative", 
-      "Invalid",
-      "Error",
-      "Information Request",
-      "Information Reply",
-      "Information Change Request",
-      "Information Chat Reply",
-      "Account Confirm Request",
-      "Account Confirm Reply",
-      "Account Delete Request",
-      "Account Delete Reply"
-    },
-    {"Popups", 
-      "Invalid",
-      "Error",
-      "Display Popup"
-    },
-    {"BOS", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Response",
-      "Set group permission mask",
-      "Add permission list entries",
-      "Delete permission list entries",
-      "Add deny list entries",
-      "Delete deny list entries",
-      "Server Error"
-    },
-    {"User Lookup", 
-      "Invalid",
-      "Error",
-      "Search Request",
-      "Search Response"
-    },
-    {"Stats", 
-      "Invalid",
-      "Error",
-      "Set minimum report interval",
-      "Report Events"
-    },
-    {"Translate", 
-      "Invalid",
-      "Error",
-      "Translate Request",
-      "Translate Reply",
-    },
-    {"Chat Navigation", 
-      "Invalid",
-      "Error",
-      "Request rights",
-      "Request Exchange Information",
-      "Request Room Information",
-      "Request Occupant List",
-      "Search for Room",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request", 
-      "Evil Reply", 
-      "Chat Error",
-    }
-  };
-
-  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;
-}
-
-faim_export 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 *newcb;
-
-  if (!conn)
-    return -1;
-
-  faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
-
-  if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
-    return -1;
-  newcb->family = family;
-  newcb->type = type;
-  newcb->flags = flags;
-  if (!newhandler)
-    newcb->handler = &bleck;
-  else
-    newcb->handler = newhandler;
-  newcb->next = NULL;
-  
-  if (!conn->handlerlist)
-    conn->handlerlist = newcb;
-  else {
-    struct aim_rxcblist_t *cur;
-
-    cur = conn->handlerlist;
-
-    while (cur->next)
-      cur = cur->next;
-    cur->next = newcb;
-  }
-
-  return 0;
-}
-
-faim_export int aim_clearhandlers(struct aim_conn_t *conn)
-{
- struct aim_rxcblist_t *cur;
-
- if (!conn)
-   return -1;
-
- for (cur = conn->handlerlist; cur; ) {
-   struct aim_rxcblist_t *tmp;
-
-   tmp = cur->next;
-   free(cur);
-   cur = tmp;
- }
- conn->handlerlist = NULL;
-
- return 0;
-}
-
-faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn,
-					 u_short family,
-					 u_short type)
-{
-  struct aim_rxcblist_t *cur;
-
-  if (!conn)
-    return NULL;
-
-  faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
-  
-  cur = conn->handlerlist;
-  while(cur)
-    {
-      if ( (cur->family == family) && (cur->type == type) )
-	return cur->handler;
-      cur = cur->next;
-    }
-
-  if (type==0xffff)
-    return NULL;
-
-  return aim_callhandler(conn, family, 0xffff);
-}
-
-faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
-					  struct aim_conn_t *conn,
-					  u_short family,
-					  u_short type,
-					  struct command_rx_struct *ptr)
-{
-  rxcallback_t userfunc = NULL;
-  userfunc = aim_callhandler(conn, family, type);
-  if (userfunc)
-    return userfunc(sess, ptr);
-  return 1; /* XXX */
-}
-
-/*
-  aim_rxdispatch()
-
-  Basically, heres what this should do:
-    1) Determine correct packet handler for this packet
-    2) Mark the packet handled (so it can be dequeued in purge_queue())
-    3) Send the packet to the packet handler
-    4) Go to next packet in the queue and start over
-    5) When done, run purge_queue() to purge handled commands
-
-  Note that any unhandlable packets should probably be left in the
-  queue.  This is the best way to prevent data loss.  This means
-  that a single packet may get looked at by this function multiple
-  times.  This is more good than bad!  This behavior may change.
-
-  Aren't queue's fun? 
-
-  TODO: Get rid of all the ugly if's.
-  TODO: Clean up.
-  TODO: More support for mid-level handlers.
-  TODO: Allow for NULL handlers.
-  
- */
-faim_export int aim_rxdispatch(struct aim_session_t *sess)
-{
-  int i = 0;
-  struct command_rx_struct *workingPtr = NULL;
-  
-  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;
-
-      /*
-       * This is a debugging/sanity check only and probably could/should be removed
-       * for stable code.
-       */
-      if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && 
-	   (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
-	  ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && 
-	   (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
-	printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
-	workingPtr->handled = 1;
-	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: {
-	unsigned long head;
-	
-	head = aimutil_get32(workingPtr->data);
-	if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
-	  faimdprintf(1, "got connection ack on auth line\n");
-	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-	} else if (workingPtr->hdr.oscar.type == 0x04) {
-	  /* Used only by the older login protocol */
-	  workingPtr->handled = aim_authparse(sess, workingPtr);
-        } else {
-	  unsigned short family,subtype;
-	  
-	  family = aimutil_get16(workingPtr->data);
-	  subtype = aimutil_get16(workingPtr->data+2);
-	  
-	  switch (family) {
-	    /* New login protocol */
-	  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_authkeyparse(sess, workingPtr);
-	    else
-	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
-	    break;
-	  case 0x0001:
-	    if (subtype == 0x0003)
-	      workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-	    else
-	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, 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;
-	  case AIM_CB_FAM_SPECIAL:
-	    if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
-	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-	      break;
-	    } else
-	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
-	    break;
-	  default:
-	    break;
-	  }
-	}
-	break;
-      }
-      case AIM_CONN_TYPE_BOS: {
-	u_short family;
-	u_short subtype;
-
-	if (workingPtr->hdr.oscar.type == 0x04) {
-	  workingPtr->handled = aim_negchan_middle(sess, workingPtr);
-	  break;
-	}
-
-	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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, 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_parse_hostonline(sess, 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_parse_ratechange_middle(sess, workingPtr);
-	    break;
-	  case 0x000f:
-	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
-	    break;
-	  case 0x0010:
-	    workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
-	    break;
-	  case 0x0013:
-	    workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
-	    break;
-	  case 0x0018:
-	    workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
-	    break;
-	  default:
-	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
-	    break;
-	  }
-	  break;
-	case 0x0002: /* Family: Location */
-	  switch (subtype) {
-	  case 0x0001:
-	    workingPtr->handled = aim_parse_locateerr(sess, 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;
-	  }
-	  break;
-	case 0x0003: /* Family: Buddy List */
-	  switch (subtype) {
-	  case 0x0001:
-	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
-	    break;
-	  case 0x0003:
-	    workingPtr->handled = aim_parse_buddyrights(sess, 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: Messaging */
-	  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 0x0006:
-	    workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
-	    break;
-	  case 0x0007:
-	    workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
-	    break;
-	  case 0x000a:
-	    workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
-	    break;
-	  case 0x000c:
-	    workingPtr->handled = aim_parse_msgack_middle(sess, 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_parse_bosrights(sess, 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;
-	}
-	case 0x0013: {
-	  printf("lalala: 0x%04x/0x%04x\n", family, subtype);
-	  break;
-	}
-	case AIM_CB_FAM_SPECIAL: 
-	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-	  break;
-	default:
-	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
-	  break;
-	} /* switch(family) */
-	break;
-      } /* AIM_CONN_TYPE_BOS */
-      case AIM_CONN_TYPE_CHATNAV: {
-	u_short family;
-	u_short 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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-	} else if ((family == 0x0001) && (subtype == 0x0003)) {
-	  workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-	} 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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, 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_parse_hostonline(sess, workingPtr);
-	  else if (subtype == 0x0007)
-	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
-	  else if (subtype == 0x000a)
-	    workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
-	  else
-	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-	} 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;
-      }
-      case AIM_CONN_TYPE_RENDEZVOUS: {
-	/* make sure that we only get OFT frames on these connections */
-	if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
-	  printf("faim: internal error: non-OFT frames on OFT connection\n");
-	  workingPtr->handled = 1; /* get rid of it */
-	  break;
-	}
-	
-	/* XXX: implement this */
-	printf("faim: OFT frame!\n");
-	
-	break;
-      }
-      case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
-	/* not possible */
-	break;
-      }
-      default:
-	printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
-	workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
-	break;
-      }	
-    }
-  }
-
-  /* 
-   * 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;
-}
-
-faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
-{
-  rxcallback_t userfunc = NULL;
-  char sn[MAXSNLEN];
-  unsigned short type;
-  int i = 10+8; /* skip SNAC and cookie */
-  int ret = 1;
-  unsigned char snlen;
-
-  type = aimutil_get16(command->data+i);
-  i += 2;
-
-  snlen = aimutil_get8(command->data+i);
-  i++;
-
-  memset(sn, 0, sizeof(sn));
-  strncpy(sn, (char *)command->data+i, snlen);
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
-    ret =  userfunc(sess, command, type, sn);
-
-  return ret;
-}
-
-/*
- * The Rate Limiting System, An Abridged Guide to Nonsense.
- *
- * OSCAR defines several 'rate classes'.  Each class has seperate
- * rate limiting properties (limit level, alert level, disconnect
- * level, etc), and a set of SNAC family/type pairs associated with
- * it.  The rate classes, their limiting properties, and the definitions
- * of which SNACs are belong to which class, are defined in the
- * Rate Response packet at login to each host.  
- *
- * Logically, all rate offenses within one class count against further
- * offenses for other SNACs in the same class (ie, sending messages
- * too fast will limit the number of user info requests you can send,
- * since those two SNACs are in the same rate class).
- *
- * Since the rate classes are defined dynamically at login, the values
- * below may change. But they seem to be fairly constant.
- *
- * Currently, BOS defines five rate classes, with the commonly used
- * members as follows...
- *
- *  Rate class 0x0001:
- *  	- Everything thats not in any of the other classes
- *
- *  Rate class 0x0002:
- * 	- Buddy list add/remove
- *	- Permit list add/remove
- *	- Deny list add/remove
- *
- *  Rate class 0x0003:
- *	- User information requests
- *	- Outgoing ICBMs
- *
- *  Rate class 0x0004:
- *	- A few unknowns: 2/9, 2/b, and f/2
- *
- *  Rate class 0x0005:
- *	- Chat room create
- *	- Outgoing chat ICBMs
- *
- * The only other thing of note is that class 5 (chat) has slightly looser
- * limiting properties than class 3 (normal messages).  But thats just a 
- * small bit of trivia for you.
- *
- * The last thing that needs to be learned about the rate limiting
- * system is how the actual numbers relate to the passing of time.  This
- * seems to be a big mystery.
- * 
- */
-faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int i;
-  int code;
-  unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
-  unsigned long currentavg, maxavg;
-
-  i = 10;
-
-  code = aimutil_get16(command->data+i);
-  i += 2;
-
-  rateclass = aimutil_get16(command->data+i);
-  i += 2;
-
-  windowsize = aimutil_get32(command->data+i);
-  i += 4;
-  clear = aimutil_get32(command->data+i);
-  i += 4;
-  alert = aimutil_get32(command->data+i);
-  i += 4;
-  limit = aimutil_get32(command->data+i);
-  i += 4;
-  disconnect = aimutil_get32(command->data+i);
-  i += 4;
-  currentavg = aimutil_get32(command->data+i);
-  i += 4;
-  maxavg = aimutil_get32(command->data+i);
-  i += 4;
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
-    ret =  userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
-
-  return ret;
-}
-
-faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int i;
-  unsigned short newevil;
-  struct aim_userinfo_s userinfo;
-
-  i = 10;
-  newevil = aimutil_get16(command->data+10);
-  i += 2;
-
-  memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
-  if (command->commandlen-i)
-    i += aim_extractuserinfo(command->data+i, &userinfo);
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
-    ret = userfunc(sess, command, newevil, &userinfo);
-  
-  return ret;
-}
-
-faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
-				       struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  char *msg;
-  int ret=1;
-  struct aim_tlvlist_t *tlvlist;
-  u_short id;
-
-  /*
-   * Code.
-   *
-   * Valid values:
-   *   1 Mandatory upgrade
-   *   2 Advisory upgrade
-   *   3 System bulletin
-   *   4 Nothing's wrong ("top o the world" -- normal)
-   *
-   */
-  id = aimutil_get16(command->data+10);
-
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
-    return ret;
-
-  if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
-    aim_freetlvchain(&tlvlist);
-    return ret;
-  }
-  
-  userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
-  if (userfunc)
-    ret =  userfunc(sess, command, id, msg);
-
-  aim_freetlvchain(&tlvlist);
-  free(msg);
-
-  return ret;  
-}
-
-faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
-				       struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  unsigned short *families = NULL;
-  int famcount = 0, i;
-
-  famcount = (command->commandlen-10)/2;
-  if (!(families = malloc(command->commandlen-10)))
-    return ret;
-
-  for (i = 0; i < famcount; i++)
-    families[i] = aimutil_get16(command->data+((i*2)+10));
-
-  if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
-    ret = userfunc(sess, command, famcount, families);
-
-  free(families);
-
-  return ret;  
-}
-
-faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
-					 struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int vercount;
-
-  vercount = (command->commandlen-10)/4;
-  
-  if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
-    ret = userfunc(sess, command, vercount, command->data+10);
-
-  return ret;  
-}
-
-faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
-					    struct command_rx_struct *command, ...)
-{
-  int serviceid = 0;
-  unsigned char *cookie = NULL;
-  char *ip = NULL;
-  rxcallback_t userfunc = NULL;
-  struct aim_tlvlist_t *tlvlist;
-  int ret = 1;
-  
-  tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
-
-  if (aim_gettlv(tlvlist, 0x000d, 1))
-    serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
-  if (aim_gettlv(tlvlist, 0x0005, 1))
-    ip = aim_gettlv_str(tlvlist, 0x0005, 1);
-  if (aim_gettlv(tlvlist, 0x0006, 1))
-    cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
-
-  if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
-
-    /*
-     * Chat hack.
-     *
-     */
-    if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
-      ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
-      free(sess->pendingjoin);
-      sess->pendingjoin = NULL;
-      sess->pendingjoinexchange = 0;
-  } else if (!serviceid || !ip || !cookie) { /* yeep! */
-    ret = 1;
-  } else {
-    if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
-      ret =  userfunc(sess, command, serviceid, ip, cookie);
-  }
-
-  if (ip)
-    free(ip);
-  if (cookie)
-    free(cookie);
-
-  aim_freetlvchain(&tlvlist);
-
-  return ret;
-}
-
-faim_internal int aim_parse_unknown(struct aim_session_t *sess,
-					  struct command_rx_struct *command, ...)
-{
-  u_int i = 0;
-
-  if (!sess || !command)
-    return 1;
-
-  faimdprintf(1, "\nRecieved unknown packet:");
-
-  for (i = 0; i < command->commandlen; i++)
-    {
-      if ((i % 8) == 0)
-	faimdprintf(1, "\n\t");
-
-      faimdprintf(1, "0x%2x ", command->data[i]);
-    }
-  
-  faimdprintf(1, "\n\n");
-
-  return 1;
-}
-
-
-faim_internal int aim_negchan_middle(struct aim_session_t *sess,
-				     struct command_rx_struct *command)
-{
-  struct aim_tlvlist_t *tlvlist;
-  char *msg = NULL;
-  unsigned short code = 0;
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-
-  tlvlist = aim_readtlvchain(command->data, command->commandlen);
-
-  if (aim_gettlv(tlvlist, 0x0009, 1))
-    code = aim_gettlv16(tlvlist, 0x0009, 1);
-
-  if (aim_gettlv(tlvlist, 0x000b, 1))
-    msg = aim_gettlv_str(tlvlist, 0x000b, 1);
-
-  if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
-    ret =  userfunc(sess, command, code, msg);
-
-  aim_freetlvchain(&tlvlist);
-
-  if (msg)
-    free(msg);
-
-  return ret;
-}
-
-/*
- * aim_parse_generalerrs()
- *
- * Middle handler for 0x0001 snac of each family.
- *
- */
-faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
-					struct command_rx_struct *command, ...)
-{
-  unsigned short family;
-  unsigned short subtype;
-  int ret = 1;
-  int error = 0;
-  rxcallback_t userfunc = NULL;
-  
-  family = aimutil_get16(command->data+0);
-  subtype= aimutil_get16(command->data+2);
-  
-  if (command->commandlen > 10)
-    error = aimutil_get16(command->data+10);
-
-  if ((userfunc = aim_callhandler(command->conn, family, subtype))) 
-    ret = userfunc(sess, command, error);
-
-  return ret;
-}
-
-
-
--- a/libfaim/aim_rxqueue.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,249 +0,0 @@
-/*
- *  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 <faim/aim.h> 
-
-#ifndef _WIN32
-#include <sys/socket.h>
-#endif
-
-/*
- * Since not all implementations support MSG_WAITALL, define
- * an alternate guarenteed read function...
- *
- * We keep recv() for systems that can do it because it means
- * a single system call for the entire packet, where read may
- * take more for a badly fragmented packet.
- *
- */
-faim_internal int aim_recv(int fd, void *buf, size_t count)
-{
-#ifdef MSG_WAITALL
-  return recv(fd, buf, count, MSG_WAITALL);
-#else
-  int left, ret, cur = 0; 
-
-  left = count;
-
-  while (left) {
-    ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
-    if (ret == -1)
-      return -1;
-    if (ret == 0)
-      return cur;
-    
-    cur += ret;
-    left -= ret;
-  }
-
-  return cur;
-#endif
-}
-
-/*
- * Grab a single command sequence off the socket, and enqueue
- * it in the incoming event queue in a seperate struct.
- */
-faim_export int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn)
-{
-  unsigned char generic[6]; 
-  struct command_rx_struct *newrx = NULL;
-
-  if (!sess || !conn)
-    return 0;
-
-  if (conn->fd == -1)
-    return -1; /* its a aim_conn_close()'d connection */
-
-  if (conn->fd < 3)  /* can happen when people abuse the interface */
-    return 0;
-
-  if (conn->status & AIM_CONN_STATUS_INPROGRESS)
-    return aim_conn_completeconnect(sess, conn);
-
-  /*
-   * Rendezvous (client-client) connections do not speak
-   * FLAP, so this function will break on them.
-   */
-  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) 
-    return aim_get_command_rendezvous(sess, conn);
-  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) 
-    return 0; 
-
-  /*
-   * Read FLAP header.  Six bytes:
-   *    
-   *   0 char  -- Always 0x2a
-   *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
-   *   2 short -- Sequence number 
-   *   4 short -- Number of data bytes that follow.
-   */
-  faim_mutex_lock(&conn->active);
-  if (aim_recv(conn->fd, generic, 6) < 6){
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-
-  /*
-   * 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!");
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }	
-
-  /* allocate a new struct */
-  if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) {
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-  memset(newrx, 0x00, sizeof(struct command_rx_struct));
-
-  newrx->lock = 1;  /* lock the struct */
-
-  /* we're doing OSCAR if we're here */
-  newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
-
-  /* store channel -- byte 2 */
-  newrx->hdr.oscar.type = (char) generic[1];
-
-  /* store seqnum -- bytes 3 and 4 */
-  newrx->hdr.oscar.seqnum = aimutil_get16(generic+2);
-
-  /* store commandlen -- bytes 5 and 6 */
-  newrx->commandlen = aimutil_get16(generic+4);
-
-  newrx->nofree = 0; /* free by default */
-
-  /* malloc for data portion */
-  if (!(newrx->data = (u_char *) malloc(newrx->commandlen))) {
-    free(newrx);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-
-  /* read the data portion of the packet */
-  if (aim_recv(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){
-    free(newrx->data);
-    free(newrx);
-    aim_conn_close(conn);
-    faim_mutex_unlock(&conn->active);
-    return -1;
-  }
-  faim_mutex_unlock(&conn->active);
-
-  newrx->conn = conn;
-
-  newrx->next = NULL;  /* this will always be at the bottom */
-  newrx->lock = 0; /* unlock */
-
-  /* enqueue this packet */
-  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 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.
- *
- */
-faim_export void aim_purge_rxqueue(struct aim_session_t *sess)
-{
-  struct command_rx_struct *cur = NULL;
-  struct command_rx_struct *tmp;
-
-  if (sess->queue_incoming == NULL)
-    return;
-  
-  if (sess->queue_incoming->next == NULL) {
-    if (sess->queue_incoming->handled) {
-      tmp = sess->queue_incoming;
-      sess->queue_incoming = NULL;
-
-      if (!tmp->nofree) {
-	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	  free(tmp->hdr.oft.hdr2);
-	free(tmp->data);
-	free(tmp);
-      } else
-	tmp->next = NULL;
-    }
-    return;
-  }
-
-  for(cur = sess->queue_incoming; cur->next != NULL; ) {
-    if (cur->next->handled) {
-      tmp = cur->next;
-      cur->next = tmp->next;
-      if (!tmp->nofree) {
-	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	  free(tmp->hdr.oft.hdr2);
-	free(tmp->data);
-	free(tmp);
-      } else
-	tmp->next = NULL;
-    }	
-    cur = cur->next;
-
-    /* 
-     * 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;
-}
-
-/*
- * Since aim_get_command will aim_conn_kill dead connections, we need
- * to clean up the rxqueue of unprocessed connections on that socket.
- *
- * XXX: this is something that was handled better in the old connection
- * handling method, but eh.
- */
-faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn)
-{
-  struct command_rx_struct *currx;
-
-  for (currx = sess->queue_incoming; currx; currx = currx->next) {
-    if ((!currx->handled) && (currx->conn == conn))
-      currx->handled = 1;
-  }	
-  return;
-}
--- a/libfaim/aim_search.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-
-/*
- * aim_search.c
- *
- * TODO: Add aim_usersearch_name()
- *
- */
-
-#include <faim/aim.h>
-
-faim_export unsigned long aim_usersearch_address(struct aim_session_t *sess,
-						 struct aim_conn_t *conn, 
-						 char *address)
-{
-  struct command_tx_struct *newpacket;
-  
-  if (!address)
-    return -1;
-
-  if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+strlen(address))))
-    return -1;
-
-  newpacket->lock = 1;
-
-  aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid);
-
-  aimutil_putstr(newpacket->data+10, address, strlen(address));
-
-  aim_tx_enqueue(sess, newpacket);
-
-  aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, address, strlen(address)+1);
-
-  return sess->snac_nextid;
-}
-
--- a/libfaim/aim_snac.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-
-/*
- *
- * Various SNAC-related dodads... 
- *
- * outstanding_snacs is a list of aim_snac_t structs.  A SNAC should be added
- * whenever a new SNAC is sent and it should remain in the list until the
- * response for it has been receieved.  
- *
- * cleansnacs() should be called periodically by the client in order
- * to facilitate the aging out of unreplied-to SNACs. This can and does
- * happen, so it should be handled.
- *
- */
-
-#include <faim/aim.h>
-
-/*
- * Called from aim_session_init() to initialize the hash.
- */
-faim_internal void aim_initsnachash(struct aim_session_t *sess)
-{
-  int i;
-
-  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
-    sess->snac_hash[i] = NULL;
-    faim_mutex_init(&sess->snac_hash_locks[i]);
-  }
-
-  return;
-}
-
-faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess,
-					  const unsigned short family,
-					  const unsigned short type,
-					  const unsigned short flags,
-					  const void *data, const int datalen)
-{
-  struct aim_snac_t snac;
-
-  snac.id = sess->snac_nextid++;
-  snac.family = family;
-  snac.type = type;
-  snac.flags = flags;
-
-  snac.data = malloc(datalen);
-  memcpy(snac.data, data, datalen);
-
-  return aim_newsnac(sess, &snac);
-}
-
-/*
- * Clones the passed snac structure and caches it in the
- * list/hash.
- */
-faim_internal unsigned long aim_newsnac(struct aim_session_t *sess,
-					struct aim_snac_t *newsnac) 
-{
-  struct aim_snac_t *snac = NULL;
-  int index;
-
-  if (!newsnac)
-    return 0;
-
-  if (!(snac = calloc(1, sizeof(struct aim_snac_t))))
-    return 0;
-  memcpy(snac, newsnac, sizeof(struct aim_snac_t));
-  snac->issuetime = time(&snac->issuetime);
-  snac->next = NULL;
-
-  index = snac->id % FAIM_SNAC_HASH_SIZE;
-
-  faim_mutex_lock(&sess->snac_hash_locks[index]);
-  snac->next = sess->snac_hash[index];
-  sess->snac_hash[index] = snac;
-  faim_mutex_unlock(&sess->snac_hash_locks[index]);
-
-  return(snac->id);
-}
-
-/*
- * Finds a snac structure with the passed SNAC ID, 
- * removes it from the list/hash, and returns a pointer to it.
- *
- * The returned structure must be freed by the caller.
- *
- */
-faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, 
-					     u_long id) 
-{
-  struct aim_snac_t *cur = NULL;
-  int index;
-
-  index = id % FAIM_SNAC_HASH_SIZE;
-
-  faim_mutex_lock(&sess->snac_hash_locks[index]);
-  if (!sess->snac_hash[index])
-    ;
-  else if (sess->snac_hash[index]->id == id) {
-    cur = sess->snac_hash[index];
-    sess->snac_hash[index] = cur->next;
-  } else {
-    cur = sess->snac_hash[index];
-    while (cur->next) {
-      if (cur->next->id == id) {
-	struct aim_snac_t *tmp;
-	
-	tmp = cur->next;
-	cur->next = cur->next->next;
-	cur = tmp;
-	break;
-      }
-      cur = cur->next;
-    }
-  }
-  faim_mutex_unlock(&sess->snac_hash_locks[index]);
-
-  return cur;
-}
-
-/*
- * This is for cleaning up old SNACs that either don't get replies or
- * a reply was never received for.  Garabage collection. Plain and simple.
- *
- * maxage is the _minimum_ age in seconds to keep SNACs.
- *
- */
-faim_internal int aim_cleansnacs(struct aim_session_t *sess,
-				 int maxage)
-{
-  int i;
-
-  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
-    struct aim_snac_t *cur = NULL, *next = NULL, *prev = NULL;
-    time_t curtime;
-
-    faim_mutex_lock(&sess->snac_hash_locks[i]);
-    if (!sess->snac_hash[i]) {
-      faim_mutex_unlock(&sess->snac_hash_locks[i]);
-      continue;
-    }
-
-    curtime = time(NULL); /* done here in case we waited for the lock */
-
-    cur = sess->snac_hash[i];
-    while (cur) {
-      next = cur->next;
-      if ((curtime - cur->issuetime) > maxage) {
-	if (sess->snac_hash[i] == cur)
-	  prev = sess->snac_hash[i] = next;
-	else
-	  prev->next = next;
-
-	/* XXX should we have destructors here? */
-	if (cur->data)
-	  free(cur->data);
-	free(cur);
-
-      } else {
-	prev = cur;
-      }
-      cur = next;
-    }
-
-    faim_mutex_unlock(&sess->snac_hash_locks[i]);
-  }
-
-  return 0;
-}
-
-faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
-{
-  int curbyte = 0;
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff));
-  curbyte += aimutil_put32(buf+curbyte, snacid);
-  return curbyte;
-}
--- a/libfaim/aim_tlv.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,723 +0,0 @@
-#include <faim/aim.h>
-
-/**
- * aim_readtlvchain - Read a TLV chain from a buffer.
- * @buf: Input buffer
- * @maxlen: Length of input buffer
- *
- * Reads and parses a series of TLV patterns from a data buffer; the
- * returned structure is manipulatable with the rest of the TLV
- * routines.  When done with a TLV chain, aim_freetlvchain() should
- * be called to free the dynamic substructures.
- *
- */
-faim_export 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)
-	    {
-	      /*
-	       * Okay, so now AOL has decided that any TLV of
-	       * type 0x0013 can only be two bytes, despite
-	       * what the actual given length is.  So here 
-	       * we dump any invalid TLVs of that sort.  Hopefully
-	       * theres no special cases to this special case.
-	       *   - mid (30jun2000)
-	       */
-	      if ((type == 0x0013) && (length != 0x0002)) {
-		printf("faim: skipping TLV t(0013) with invalid length (0x%04x)\n", length);
-		length = 0x0002;
-	      } else {
-		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; 
-		if (length) {
-		  cur->tlv->value = (unsigned char *)malloc(length);
-		  memcpy(cur->tlv->value, buf+pos, length);
-		} 
-
-		cur->next = list;
-		list = cur;
-	      }
-	      pos += length;
-	    }
-	}
-    }
-
-  return list;
-}
-
-/**
- * aim_freetlvchain - Free a TLV chain structure
- * @list: Chain to be freed
- *
- * Walks the list of TLVs in the passed TLV chain and
- * frees each one. Note that any references to this data
- * should be removed before calling this.
- *
- */
-faim_export 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;
-}
-
-/**
- * aim_counttlvchain - Count the number of TLVs in a chain
- * @list: Chain to be counted
- *
- * Returns the number of TLVs stored in the passed chain.
- *
- */
-faim_export 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;
-}
-
-/**
- * aim_sizetlvchain - Count the number of bytes in a TLV chain
- * @list: Chain to be sized
- *
- * Returns the number of bytes that would be needed to 
- * write the passed TLV chain to a data buffer.
- *
- */
-faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list)
-{
-  struct aim_tlvlist_t *cur;
-  int size = 0;
-
-  if (!list || !(*list))
-    return 0;
-
-  for (cur = *list; cur; cur = cur->next)
-    size += (4 + cur->tlv->length);
- 
-  return size;
-}
-
-/**
- * aim_addtlvtochain_str - Add a string to a TLV chain
- * @list: Desination chain (%NULL pointer if empty)
- * @type: TLV type
- * @str: String to add
- * @len: Length of string to add (not including %NULL)
- *
- * Adds the passed string as a TLV element of the passed type
- * to the TLV chain.
- *
- */
-faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len)
-{
-  struct aim_tlvlist_t *newtlv;
-  struct aim_tlvlist_t *cur;
-
-  if (!list)
-    return 0;
-
-  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtlv->tlv = aim_createtlv();	
-  newtlv->tlv->type = type;
-  newtlv->tlv->length = len;
-  newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char));
-  memcpy(newtlv->tlv->value, str, newtlv->tlv->length);
-
-  newtlv->next = NULL;
-
-  if (*list == NULL) {
-    *list = newtlv;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtlv;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtlv;
-  }
-  return newtlv->tlv->length;
-}
-
-/**
- * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
- * @list: Destination chain
- * @type: TLV type to add
- * @val: Value to add
- *
- * Adds a two-byte unsigned integer to a TLV chain.
- *
- */
-faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val)
-{
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if (!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
-  newtl->tlv->length = 2;
-  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
-  aimutil_put16(newtl->tlv->value, val);
-
-  newtl->next = NULL;
-
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return 2;
-}
-
-/**
- * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain
- * @list: Destination chain
- * @type: TLV type to add
- * @val: Value to add
- *
- * Adds a four-byte unsigned integer to a TLV chain.
- *
- */
-faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val)
-{
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if (!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
-  newtl->tlv->length = 4;
-  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
-  aimutil_put32(newtl->tlv->value, val);
-
-  newtl->next = NULL;
-
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return 4;
-}
-
-/**
- * aim_addtlvtochain_caps - Add a capability block to a TLV chain
- * @list: Destination chain
- * @type: TLV type to add
- * @caps: Bitfield of capability flags to send
- *
- * Adds a block of capability blocks to a TLV chain. The bitfield
- * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
- *
- *      %AIM_CAPS_BUDDYICON   Supports Buddy Icons
- *
- *      %AIM_CAPS_VOICE       Supports Voice Chat
- *
- *      %AIM_CAPS_IMIMAGE     Supports DirectIM/IMImage
- *
- *      %AIM_CAPS_CHAT        Supports Chat
- *
- *      %AIM_CAPS_GETFILE     Supports Get File functions
- *
- *      %AIM_CAPS_SENDFILE    Supports Send File functions
- *
- */
-faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps)
-{
-  unsigned char buf[128]; /* icky fixed length buffer */
-  struct aim_tlvlist_t *newtl;
-  struct aim_tlvlist_t *cur;
-
-  if(!list)
-    return 0;
-
-  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtl->tlv = aim_createtlv();	
-  newtl->tlv->type = type;
-
-  newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps);
-  newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length);
-  memcpy(newtl->tlv->value, buf, newtl->tlv->length);
-
-  newtl->next = NULL;
-
-  if (*list == NULL) {
-    *list = newtl;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtl;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtl;
-  }
-  return newtl->tlv->length;
-}
-
-/**
- * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain
- * @list: Destination chain
- * @type: TLV type to add
- *
- * Adds a TLV with a zero length to a TLV chain.
- *
- */
-faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type)
-{
-  struct aim_tlvlist_t *newtlv;
-  struct aim_tlvlist_t *cur;
-
-  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
-  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
-
-  newtlv->tlv = aim_createtlv();	
-  newtlv->tlv->type = type;
-  newtlv->tlv->length = 0;
-  newtlv->tlv->value = NULL;
-
-  newtlv->next = NULL;
-
-  if (*list == NULL) {
-    *list = newtlv;
-  } else if ((*list)->next == NULL) {
-    (*list)->next = newtlv;
-  } else {
-    for(cur = *list; cur->next; cur = cur->next)
-      ;
-    cur->next = newtlv;
-  }
-  return newtlv->tlv->length;
-}
-
-/**
- * aim_writetlvchain - Write a TLV chain into a data buffer.
- * @buf: Destination buffer
- * @buflen: Maximum number of bytes that will be written to buffer
- * @list: Source TLV chain
- *
- * Copies a TLV chain into a raw data buffer, writing only the number
- * of bytes specified. This operation does not free the chain; 
- * aim_freetlvchain() must still be called to free up the memory used
- * by the chain structures.
- *
- */
-faim_export 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;
-}
-
-
-/**
- * aim_gettlv - Grab the Nth TLV of type type in the TLV list list.
- * @list: Source chain
- * @type: Requested TLV type
- * @nth: Index of TLV of type to get
- *
- * Returns a pointer to an aim_tlv_t of the specified type; 
- * %NULL on error.  The @nth parameter is specified starting at %1.
- * In most cases, there will be no more than one TLV of any type
- * in a chain.
- *
- */
-faim_export 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;
-}
-
-/**
- * aim_gettlv_str - Retrieve the Nth TLV in chain as a string.
- * @list: Source TLV chain
- * @type: TLV type to search for
- * @nth: Index of TLV to return
- *
- * Same as aim_gettlv(), except that the return value is a %NULL-
- * terminated string instead of an aim_tlv_t.  This is a 
- * dynamic buffer and must be freed by the caller.
- *
- */
-faim_export 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;
-}
-
-/**
- * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer.
- * @list: Source TLV chain
- * @type: TLV type to search for
- * @nth: Index of TLV to return
- *
- * Same as aim_gettlv(), except that the return value is a 
- * 8bit integer instead of an aim_tlv_t. 
- *
- */
-faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num)
-{
-  struct aim_tlv_t *tlv;
-
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get8(tlv->value);
-}
-
-/**
- * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer.
- * @list: Source TLV chain
- * @type: TLV type to search for
- * @nth: Index of TLV to return
- *
- * Same as aim_gettlv(), except that the return value is a 
- * 16bit integer instead of an aim_tlv_t. 
- *
- */
-faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num)
-{
-  struct aim_tlv_t *tlv;
-
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get16(tlv->value);
-}
-
-/**
- * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer.
- * @list: Source TLV chain
- * @type: TLV type to search for
- * @nth: Index of TLV to return
- *
- * Same as aim_gettlv(), except that the return value is a 
- * 32bit integer instead of an aim_tlv_t. 
- *
- */
-faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num)
-{
-  struct aim_tlv_t *tlv;
-
-  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
-    return 0; /* erm */
-  return aimutil_get32(tlv->value);
-}
-
-/**
- * aim_grabtlv - Grab a single TLV from a data buffer
- * @src: Source data buffer (must be at least 4 bytes long)
- *
- * Creates a TLV structure aim_tlv_t and returns it
- * filled with values from a buffer, possibly including a 
- * dynamically allocated buffer for the value portion.
- *
- * Both the aim_tlv_t and the tlv->value pointer
- * must be freed by the caller if non-%NULL.
- *
- */
-faim_export struct aim_tlv_t *aim_grabtlv(u_char *src)
-{
-  struct aim_tlv_t *dest = NULL;
-
-  dest = aim_createtlv();
-
-  dest->type = src[0] << 8;
-  dest->type += src[1];
-
-  dest->length = src[2] << 8;
-  dest->length += src[3];
-
-  dest->value = (u_char *) malloc(dest->length*sizeof(u_char));
-  memset(dest->value, 0, dest->length*sizeof(u_char));
-
-  memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char));
-  
-  return dest;
-}
-
-/**
- * aim_grabtlvstr - Grab a single TLV from a data buffer as string
- * @src: Source data buffer (must be at least 4 bytes long)
- *
- * Creates a TLV structure aim_tlv_t and returns it
- * filled with values from a buffer, possibly including a 
- * dynamically allocated buffer for the value portion, which 
- * is %NULL-terminated as a string.
- *
- * Both the aim_tlv_t and the tlv->value pointer
- * must be freed by the caller if non-%NULL.
- *
- */
-faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src)
-{
-  struct aim_tlv_t *dest = NULL;
-
-  dest = aim_createtlv();
-
-  dest->type = src[0] << 8;
-  dest->type += src[1];
-
-  dest->length = src[2] << 8;
-  dest->length += src[3];
-
-  dest->value = (u_char *) malloc((dest->length+1)*sizeof(u_char));
-  memset(dest->value, 0, (dest->length+1)*sizeof(u_char));
-
-  memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char));
-  dest->value[dest->length] = '\0';
-
-  return dest;
-}
-
-/**
- * aim_puttlv - Write a aim_tlv_t into a data buffer
- * @dest: Destination data buffer
- * @newtlv: Source TLV structure
- *
- * Writes out the passed TLV structure into the buffer. No bounds
- * checking is done on the output buffer.
- *
- * The passed aim_tlv_t is not freed. aim_freetlv() should
- * still be called by the caller to free the structure.
- *
- */
-faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv)
-{
-  int i=0;
-
-  dest[i++] = newtlv->type >> 8;
-  dest[i++] = newtlv->type & 0x00FF;
-  dest[i++] = newtlv->length >> 8;
-  dest[i++] = newtlv->length & 0x00FF;
-  memcpy(&(dest[i]), newtlv->value, newtlv->length);
-  i+=newtlv->length;
-  return i;
-}
-
-/**
- * aim_createtlv - Generate an aim_tlv_t structure.
- * 
- * Allocates an empty TLV structure and returns a pointer
- * to it; %NULL on error.
- *
- */
-faim_export struct aim_tlv_t *aim_createtlv(void)
-{
-  struct aim_tlv_t *newtlv;
-
-  if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t))))
-    return NULL;
-  memset(newtlv, 0, sizeof(struct aim_tlv_t));
-  return newtlv;
-}
-
-/**
- * aim_freetlv - Free a aim_tlv_t structure
- * @oldtlv: TLV to be destroyed
- *
- * Frees both the TLV structure and the value portion.
- *
- */
-faim_export int aim_freetlv(struct aim_tlv_t **oldtlv)
-{
-  if (!oldtlv)
-    return -1;
-  if (!*oldtlv)
-    return -1;
-  if ((*oldtlv)->value)
-    free((*oldtlv)->value);
-  free(*(oldtlv));
-  (*oldtlv) = NULL;
-
-  return 0;
-}
-
-/**
- * aim_puttlv_8 - Write a one-byte TLV.
- * @buf: Destination buffer
- * @t: TLV type
- * @v: Value
- *
- * Writes a TLV with a one-byte integer value portion.
- *
- */
-faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char  v)
-{
-  int curbyte=0;
-
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
-  curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0001);
-  curbyte += aimutil_put8(buf+curbyte, (unsigned char)(v&0xff));
-
-  return curbyte;
-}
-
-/**
- * aim_puttlv_16 - Write a two-byte TLV.
- * @buf: Destination buffer
- * @t: TLV type
- * @v: Value
- *
- * Writes a TLV with a two-byte integer value portion.
- *
- */
-faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v)
-{
-  int curbyte=0;
-  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;
-}
-
-/**
- * aim_puttlv_32 - Write a four-byte TLV.
- * @buf: Destination buffer
- * @t: TLV type
- * @v: Value
- *
- * Writes a TLV with a four-byte integer value portion.
- *
- */
-faim_export 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;
-}
-
-/**
- * aim_puttlv_str - Write a string TLV.
- * @buf: Destination buffer
- * @t: TLV type
- * @l: Length of string
- * @v: String to write
- *
- * Writes a TLV with a string value portion.  (Only the first @l
- * bytes of the passed string will be written, which should not
- * include the terminating NULL.)
- *
- */
-faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, 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, (unsigned char *)v, l);
-  curbyte += l;
-  return curbyte;
-}
--- a/libfaim/aim_txqueue.c	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,435 +0,0 @@
-/*
- *  aim_txqueue.c
- *
- * Herein lies all the mangement routines for the transmit (Tx) queue.
- *
- */
-
-#include <faim/aim.h>
-
-#ifndef _WIN32
-#include <sys/socket.h>
-#endif
-
-/*
- * 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.
- *
- * framing = AIM_FRAMETYPE_OFT/OSCAR
- * chan = channel for OSCAR, hdrtype for OFT
- *
- */
-faim_internal struct command_tx_struct *aim_tx_new(unsigned char framing, int chan, struct aim_conn_t *conn, int datalen)
-{
-  struct command_tx_struct *newtx;
-
-  if (!conn) {
-    printf("aim_tx_new: ERROR: no connection specified\n");
-    return NULL;
-  }
-
-  newtx = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
-  if (!newtx)
-    return NULL;
-  memset(newtx, 0, sizeof(struct command_tx_struct));
-
-  newtx->conn = conn; 
-
-  if(datalen) {
-    newtx->data = (unsigned char *)malloc(datalen);
-    newtx->commandlen = datalen;
-  } else
-    newtx->data = NULL;
-
-  newtx->hdrtype = framing;
-  if (newtx->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    newtx->hdr.oscar.type = chan;
-  } else if (newtx->hdrtype == AIM_FRAMETYPE_OFT) {
-    newtx->hdr.oft.type = chan;
-    newtx->hdr.oft.hdr2len = 0; /* this will get setup by caller */
-  } else { 
-    printf("tx_new: unknown framing\n");
-  }
-
-  return newtx;
-}
-
-/*
- * aim_tx_enqeue__queuebased()
- *
- * The overall purpose here is to enqueue the passed in command struct
- * into the outgoing (tx) queue.  Basically...
- *   1) Make a scope-irrelevent copy of the struct
- *   2) Lock the struct
- *   3) Mark as not-sent-yet
- *   4) Enqueue the struct into the list
- *   5) Unlock the struct once it's linked in
- *   6) Return
- *
- * Note that this is only used when doing queue-based transmitting;
- * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
- *
- */
-faim_internal int aim_tx_enqueue__queuebased(struct aim_session_t *sess,
-					     struct command_tx_struct *newpacket)
-{
-  struct command_tx_struct *cur;
-
-  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);
-  }
- 
-  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    /* assign seqnum */
-    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
-  }
-  /* set some more fields */
-  newpacket->lock = 1; /* lock */
-  newpacket->sent = 0; /* not sent yet */
-  newpacket->next = NULL; /* always last */
-
-  /* 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_tx_enqueue__immediate()
- *
- * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
- * the whole queue mess when you want immediate writes to happen.
- *
- * Basically the same as its __queuebased couterpart, however
- * instead of doing a list append, it just calls aim_tx_sendframe()
- * right here. 
- * 
- */
-faim_internal int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_struct *newpacket)
-{
-  if (newpacket->conn == NULL) {
-    faimdprintf(1, "aim_tx_enqueue: ERROR: packet has no connection\n");
-    if (newpacket->data)
-      free(newpacket->data);
-    free(newpacket);
-    return -1;
-  }
-
-  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR)
-    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
-
-  newpacket->lock = 1; /* lock */
-  newpacket->sent = 0; /* not sent yet */
-
-  aim_tx_sendframe(sess, newpacket);
-
-  if (newpacket->data)
-    free(newpacket->data);
-  free(newpacket);
-
-  return 0;
-}
-
-faim_internal int aim_tx_enqueue(struct aim_session_t *sess, struct command_tx_struct *command)
-{
-  /*
-   * If we want to send a connection thats inprogress, we have to force
-   * them to use the queue based version. Otherwise, use whatever they
-   * want.
-   */
-  if (command && command->conn && (command->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
-    return aim_tx_enqueue__queuebased(sess, command);
-  }
-  return (*sess->tx_enqueue)(sess, command);
-}
-
-/* 
- *  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()).
- *
- */
-faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn)
-{
-  u_int ret;
-  
-  faim_mutex_lock(&conn->seqnum_lock);
-  ret = ++conn->seqnum;
-  faim_mutex_unlock(&conn->seqnum_lock);
-  return ret;
-}
-
-/*
- *  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
-faim_internal int aim_tx_printqueue(struct aim_session_t *sess)
-{
-  struct command_tx_struct *cur;
-
-  faimdprintf(2, "\ncurrent aim_queue_outgoing...\n");
-  faimdprintf(2, "\ttype seqnum  len  lock sent\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  %2x   %4x %4x   %1d    %1d\n", 
-		      cur->hdrtype,
-		      (cur->hdrtype==AIM_FRAMETYPE_OFT)?cur->hdr.oft.type:cur->hdr.oscar.type, 
-		      (cur->hdrtype==AIM_FRAMETYPE_OSCAR)?cur->seqnum:0, 
-		      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.
- *
- */
-faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur)
-{
-  int buflen = 0;
-  unsigned char *curPacket;
-
-  if (!cur)
-    return -1; /* fatal */
-
-  cur->lock = 1; /* lock the struct */
-
-  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR)
-    buflen = cur->commandlen + 6;
-  else if (cur->hdrtype == AIM_FRAMETYPE_OFT)
-    buflen = cur->hdr.oft.hdr2len + 8;
-  else {
-    cur->lock = 0;
-    return -1;
-  }
-
-  /* allocate full-packet buffer */
-  if (!(curPacket = (unsigned char *) malloc(buflen))) {
-    cur->lock = 0;
-    return -1;
-  }
-      
-  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) {
-    /* command byte */
-    curPacket[0] = 0x2a;
-      
-    /* type/family byte */
-    curPacket[1] = cur->hdr.oscar.type;
-      
-    /* bytes 3+4: word: FLAP sequence number */
-    aimutil_put16(curPacket+2, cur->hdr.oscar.seqnum);
-
-    /* bytes 5+6: word: SNAC len */
-    aimutil_put16(curPacket+4, cur->commandlen);
-      
-    /* bytes 7 and on: raw: SNAC data */  /* XXX: ye gods! get rid of this! */
-    memcpy(&(curPacket[6]), cur->data, cur->commandlen);
-
-  } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
-    int z = 0;
-
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[0]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[1]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[2]);
-    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[3]);
-
-    z += aimutil_put16(curPacket+z, cur->hdr.oft.hdr2len + 8);
-    z += aimutil_put16(curPacket+z, cur->hdr.oft.type);
-
-    memcpy(curPacket+z, cur->hdr.oft.hdr2, cur->hdr.oft.hdr2len);
-  }
-
-  /* 
-   * For OSCAR, a full image of the raw packet data now in curPacket.
-   * For OFT, an image of just the bloated header is in curPacket, 
-   * since OFT allows us to do the data in a different write (yay!).
-   */
-  faim_mutex_lock(&cur->conn->active);
-  if (send(cur->conn->fd, curPacket, buflen, 0) != buflen) {
-    faim_mutex_unlock(&cur->conn->active);
-    cur->sent = 1;
-    aim_conn_close(cur->conn);
-    return 0; /* bail out */
-  }
-
-  if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) {
-    if (send(cur->conn->fd, cur->data, cur->commandlen, 0) != (int)cur->commandlen) {
-      /* 
-       * Theres nothing we can do about this since we've already sent the 
-       * header!  The connection is unstable.
-       */
-    }
-  }
-
-  cur->sent = 1; /* mark the struct as sent */
-  cur->conn->lastactivity = time(NULL);
-
-  faim_mutex_unlock(&cur->conn->active);
-
-#if debug > 2
-  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
-  cur->lock = 0; /* unlock the struct */
-  free(curPacket); /* free up full-packet buffer */
-
-  return 1; /* success */
-}
-
-faim_export int aim_tx_flushqueue(struct aim_session_t *sess)
-{
-  struct command_tx_struct *cur;
-   
-#if debug > 1
-  int i = 0;
-#endif
-
-  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) {
-
-      if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
-	continue;
-
-      /*
-       * 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));
-      }
-
-      if (aim_tx_sendframe(sess, cur) == -1)
-	break;
-    }
-  }
-
-  /* purge sent commands from queue */
-  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!  
- *
- */
-faim_export void aim_tx_purgequeue(struct aim_session_t *sess)
-{
-  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;
-      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	free(tmp->hdr.oft.hdr2);
-      free(tmp->data);
-      free(tmp);
-    }
-    return;
-  }
-
-  for(cur = sess->queue_outgoing; cur->next != NULL; ) {
-    if (!cur->next->lock && cur->next->sent) {
-      tmp = cur->next;
-      cur->next = tmp->next;
-      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
-	free(tmp->hdr.oft.hdr2);
-      free(tmp->data);
-      free(tmp);
-    }	
-    cur = cur->next;
-
-    /* 
-     * 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	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,265 +0,0 @@
-/*
- *
- *
- *
- */
-
-#include <faim/aim.h>
-#include <ctype.h>
-
-#ifdef AIMUTIL_USEMACROS
-/* macros in faim/aim.h */
-#else
-faim_shortfunc int aimutil_put8(u_char *buf, u_char data)
-{
-  buf[0] = (u_char)data&0xff;
-  return 1;
-}
-
-faim_shortfunc u_char aimutil_get8(u_char *buf)
-{
-  return buf[0];
-}
-
-/*
- * Endian-ness issues here?
- */
-faim_shortfunc int aimutil_put16(u_char *buf, u_short data)
-{
-  buf[0] = (u_char)(data>>8)&0xff;
-  buf[1] = (u_char)(data)&0xff;
-  return 2;
-}
-
-faim_shortfunc u_short aimutil_get16(u_char *buf)
-{
-  u_short val;
-  val = (buf[0] << 8) & 0xff00;
-  val+= (buf[1]) & 0xff;
-  return val;
-}
-
-faim_shortfunc int aimutil_put32(u_char *buf, u_long data)
-{
-  buf[0] = (u_char)(data>>24)&0xff;
-  buf[1] = (u_char)(data>>16)&0xff;
-  buf[2] = (u_char)(data>>8)&0xff;
-  buf[3] = (u_char)(data)&0xff;
-  return 4;
-}
-
-faim_shortfunc u_long aimutil_get32(u_char *buf)
-{
-  u_long val;
-  val = (buf[0] << 24) & 0xff000000;
-  val+= (buf[1] << 16) & 0x00ff0000;
-  val+= (buf[2] <<  8) & 0x0000ff00;
-  val+= (buf[3]      ) & 0x000000ff;
-  return val;
-}
-#endif /* AIMUTIL_USEMACROS */
-
-faim_export faim_shortfunc int aimutil_putstr(u_char *dest, const char *src, int len)
-{
-  memcpy(dest, src, len);
-  return len;
-}
-
-/*
- * Tokenizing functions.  Used to portably replace strtok/sep.
- *   -- DMP.
- *
- */
-faim_export 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;
-}
-
-faim_export int aimutil_itemcnt(char *toSearch, char dl)
-{
-  int curCount;
-  char *next;
-  
-  curCount = 1;
-  
-  next = strchr(toSearch, dl);
-  
-  while(next != NULL)
-    {
-      curCount++;
-      next = strchr(next + 1, dl);
-    }
-  
-  return curCount;
-}
-
-faim_export char *aimutil_itemidx(char *toSearch, int index, char dl)
-{
-  int curCount;
-  char *next;
-  char *last;
-  char *toReturn;
-  
-  curCount = 0;
-  
-  last = toSearch;
-  next = strchr(toSearch, dl);
-  
-  while(curCount < index && next != NULL)
-    {
-      curCount++;
-      last = next + 1;
-      next = strchr(last, dl);
-    }
-  
-  if (curCount < index)
-    {
-      toReturn = malloc(sizeof(char));
-      *toReturn = '\0';
-    }
-  next = strchr(last, dl);
-  
-  if (curCount < index)
-    {
-      toReturn = malloc(sizeof(char));
-      *toReturn = '\0';
-    }
-  else
-    {
-      if (next == NULL)
-	{
-	  toReturn = malloc((strlen(last) + 1) * sizeof(char));
-	  strcpy(toReturn, last);
-	}
-      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().
- *
- */
-faim_export 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
- *
- */
-
-faim_export 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;
-}
-
-/* strsep Copyright (C) 1992, 1993 Free Software Foundation, Inc.
-   strsep is part of the GNU C Library.
-   
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-   
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-   
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If
-   not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-   Cambridge, MA 02139, USA.  */
-
-/* Minor changes by and1000 on 15/1/97 to make it go under Nemesis */
-
-faim_export char *aim_strsep(char **pp, const char *delim)
-{
-  char *p, *q;
-  
-  if (!(p = *pp))
-    return 0;
-  
-  if ((q = strpbrk (p, delim)))
-    {
-      *pp = q + 1;
-      *q = '\0';
-    }
-  else
-    *pp = 0;
-  
-  return p;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/auth.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,185 @@
+/*
+  aim_auth.c
+
+  Deals with the authorizer.
+
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+/* this just pushes the passed cookie onto the passed connection -- NO SNAC! */
+faim_export int aim_auth_sendcookie(struct aim_session_t *sess, 
+				    struct aim_conn_t *conn, 
+				    unsigned char *chipsahoy)
+{
+  struct command_tx_struct *newpacket;
+  int curbyte=0;
+  
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 4+2+2+AIM_COOKIELEN)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  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);
+
+  return aim_tx_enqueue(sess, newpacket);
+}
+
+faim_export unsigned long aim_auth_clientready(struct aim_session_t *sess,
+					       struct aim_conn_t *conn)
+{
+  struct aim_tool_version tools[] = {
+    {0x0001, 0x0003,    AIM_TOOL_NEWWIN, 0x0361},
+    {0x0007, 0x0001,    AIM_TOOL_NEWWIN, 0x0361},
+  };
+  int i,j;
+  struct command_tx_struct *newpacket;
+  int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+
+  for (j = 0; j < toolcount; j++) {
+    i += aimutil_put16(newpacket->data+i, tools[j].group);
+    i += aimutil_put16(newpacket->data+i, tools[j].version);
+    i += aimutil_put16(newpacket->data+i, tools[j].tool);
+    i += aimutil_put16(newpacket->data+i, tools[j].toolversion);
+  }
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *sess,
+						struct aim_conn_t *conn, 
+						char *new, char *current)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+4+strlen(current)+4+strlen(new))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+
+  /* new password TLV t(0002) */
+  i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(new), new);
+
+  /* current password TLV t(0012) */
+  i += aim_puttlv_str(newpacket->data+i, 0x0012, strlen(current), current);
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+faim_export unsigned long aim_auth_setversions(struct aim_session_t *sess,
+					       struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*2))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+
+  i += aimutil_put16(newpacket->data+i, 0x0007);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+/*
+ * Request account confirmation. 
+ *
+ * This will cause an email to be sent to the address associated with
+ * the account.  By following the instructions in the mail, you can
+ * get the TRIAL flag removed from your account.
+ *
+ */
+faim_export unsigned long aim_auth_reqconfirm(struct aim_session_t *sess,
+					      struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
+}
+
+/*
+ * Request a bit of account info.
+ *
+ * The only known valid tag is 0x0011 (email address).
+ *
+ */ 
+faim_export unsigned long aim_auth_getinfo(struct aim_session_t *sess,
+					   struct aim_conn_t *conn,
+					   unsigned short info)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0007, 0x0002, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0);
+
+  i += aimutil_put16(newpacket->data+i, info);
+  i += aimutil_put16(newpacket->data+i, 0x0000);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+faim_export unsigned long aim_auth_setemail(struct aim_session_t *sess,
+					    struct aim_conn_t *conn, 
+					    char *newemail)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(newemail))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0007, 0x0004, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+
+  i += aim_puttlv_str(newpacket->data+i, 0x0011, strlen(newemail), newemail);
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/buddylist.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,106 @@
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/*
+ * aim_add_buddy()
+ *
+ * Adds a single buddy to your buddy list after login.
+ *
+ * XXX this should just be an extension of setbuddylist()
+ *
+ */
+faim_export unsigned long aim_add_buddy(struct aim_session_t *sess,
+					struct aim_conn_t *conn, 
+					char *sn )
+{
+   struct command_tx_struct *newpacket;
+   int i;
+
+   if(!sn)
+     return -1;
+
+   if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
+     return -1;
+
+   newpacket->lock = 1;
+
+   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));
+
+   aim_tx_enqueue(sess, newpacket );
+
+   aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
+
+   return sess->snac_nextid;
+}
+
+/*
+ * XXX generalise to support removing multiple buddies (basically, its
+ * the same as setbuddylist() but with a different snac subtype).
+ *
+ */
+faim_export unsigned long aim_remove_buddy(struct aim_session_t *sess,
+					   struct aim_conn_t *conn, 
+					   char *sn )
+{
+   struct command_tx_struct *newpacket;
+   int i;
+
+   if(!sn)
+     return -1;
+
+   if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+1+strlen(sn))))
+     return -1;
+
+   newpacket->lock = 1;
+
+   i = aim_putsnac(newpacket->data, 0x0003, 0x0005, 0x0000, sess->snac_nextid);
+
+   i += aimutil_put8(newpacket->data+i, strlen(sn));
+   i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
+
+   aim_tx_enqueue(sess, newpacket);
+
+   aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
+
+   return sess->snac_nextid;
+}
+
+faim_internal int aim_parse_buddyrights(struct aim_session_t *sess,
+					struct command_rx_struct *command, ...)
+{
+  rxcallback_t userfunc = NULL;
+  int ret=1;
+  struct aim_tlvlist_t *tlvlist;
+  unsigned short maxbuddies = 0, maxwatchers = 0;
+
+  /* 
+   * TLVs follow 
+   */
+  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
+    return ret;
+
+  /*
+   * TLV type 0x0001: Maximum number of buddies.
+   */
+  if (aim_gettlv(tlvlist, 0x0001, 1))
+    maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1);
+
+  /*
+   * TLV type 0x0002: Maximum number of watchers.
+   *
+   * XXX: what the hell is a watcher? 
+   *
+   */
+  if (aim_gettlv(tlvlist, 0x0002, 1))
+    maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
+  
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0003, 0x0003)))
+    ret =  userfunc(sess, command, maxbuddies, maxwatchers);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/chat.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,681 @@
+/*
+ * aim_chat.c
+ *
+ * Routines for the Chat service.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+faim_export 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 ! */
+}
+
+faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
+{
+  struct aim_conn_t *cur;
+  
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->type != AIM_CONN_TYPE_CHAT)
+      continue;
+    if (!cur->priv) {
+      faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
+      continue;
+    }
+    if (strcmp((char *)cur->priv, name) == 0)
+      break;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  return cur;
+}
+
+faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
+{
+  if (!conn || !roomname)
+    return -1;
+
+  if (conn->priv)
+    free(conn->priv);
+
+  conn->priv = strdup(roomname);
+
+  return 0;
+}
+
+/* XXX convert this to use tlvchains */
+faim_export unsigned 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;
+  struct aim_msgcookie_t *cookie;
+
+  if (!sess || !conn || !msg)
+    return 0;
+  
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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) rand());
+
+  cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL);
+  cookie->data = strdup(conn->priv); /* chat hack dependent */
+
+  aim_cachecookie(sess, cookie);
+
+  /*
+   * Channel ID. 
+   */
+  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++);
+}
+
+/*
+ * Join a room of name roomname.  This is the first
+ * step to joining an already created room.  It's 
+ * basically a Service Request for family 0x000e, 
+ * with a little added on to specify the exchange
+ * and room name.
+ *
+ */
+faim_export unsigned long aim_chat_join(struct aim_session_t *sess,
+					struct aim_conn_t *conn, 
+					u_short exchange,
+					const char *roomname)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+
+  if (!sess || !conn || !roomname)
+    return 0;
+  
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+9+strlen(roomname)+2)))
+    return -1;
+
+  newpacket->lock = 1;
+  
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);
+
+  i+= aimutil_put16(newpacket->data+i, 0x000e);
+
+  /* 
+   * this is techinally a TLV, but we can't use normal functions
+   * because we need the extraneous nulls and other weird things.
+   */
+  i+= aimutil_put16(newpacket->data+i, 0x0001);
+  i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
+  i+= aimutil_put16(newpacket->data+i, exchange);
+  i+= aimutil_put8(newpacket->data+i, strlen(roomname));
+  i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
+  i+= aimutil_put16(newpacket->data+i, 0x0000); /* instance? */
+
+  /*
+   * Chat hack.
+   *
+   * XXX: A problem occurs here if we request a channel
+   *      join but it fails....pendingjoin will be nonnull
+   *      even though the channel is never going to get a
+   *      redirect!
+   *
+   */
+  sess->pendingjoin = strdup(roomname);
+  sess->pendingjoinexchange = exchange;
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1);
+
+  return sess->snac_nextid;
+}
+
+faim_internal 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)
+ *
+ * SNAC 000e/0002
+ */
+faim_internal 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;
+  unsigned short unknown_c9 = 0;
+  unsigned long creationtime = 0;
+  unsigned short maxmsglen = 0;
+  unsigned short unknown_d2 = 0, unknown_d5 = 0;
+
+  i = 10;
+  i += aim_chat_readroominfo(command->data+i, &roominfo);
+  
+  detaillevel = aimutil_get8(command->data+i);
+  i++;
+
+  if (detaillevel != 0x02) {
+    if (detaillevel == 0x01)
+      faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level 1 not supported\n");
+    else
+      faimdprintf(sess, 0, "faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
+    return 1;
+  }
+  
+  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))
+    usercount = aim_gettlv16(tlvlist, 0x006f, 1);
+
+  /*
+   * 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(sess, tmptlv->value+i, &userinfo[curoccupant++]);
+  }
+  
+  /* 
+   * Type 0x00c9: Unknown. (2 bytes)
+   */
+  if (aim_gettlv(tlvlist, 0x00c9, 1))
+    unknown_c9 = aim_gettlv16(tlvlist, 0x00c9, 1);
+  
+  /* 
+   * Type 0x00ca: Creation time (4 bytes)
+   */
+  if (aim_gettlv(tlvlist, 0x00ca, 1))
+    creationtime = aim_gettlv32(tlvlist, 0x00ca, 1);
+
+  /* 
+   * Type 0x00d1: Maximum Message Length
+   */
+  if (aim_gettlv(tlvlist, 0x00d1, 1))
+    maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1);
+
+  /* 
+   * Type 0x00d2: Unknown. (2 bytes)
+   */
+  if (aim_gettlv(tlvlist, 0x00d2, 1))
+    unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1);
+
+  /* 
+   * Type 0x00d3: Room Description
+   */
+  if (aim_gettlv(tlvlist, 0x00d3, 1))
+    roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
+
+  /* 
+   * Type 0x00d5: Unknown. (1 byte)
+   */
+  if (aim_gettlv(tlvlist, 0x00d5, 1))
+    unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1);
+
+
+  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE))) {
+    ret = userfunc(sess,
+		   command, 
+		   &roominfo,
+		   roomname,
+		   usercount,
+		   userinfo,	
+		   roomdesc,
+		   unknown_c9,
+		   creationtime,
+		   maxmsglen,
+		   unknown_d2,
+		   unknown_d5);
+  }	
+  free(roominfo.name);
+  free(userinfo);
+  free(roomname);
+  free(roomdesc);
+  aim_freetlvchain(&tlvlist);
+ 
+  return ret;
+}
+
+faim_internal 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(sess, command->data+i, &userinfo[curcount-1]);
+  }
+
+  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN))) {
+    ret = userfunc(sess,
+		   command, 
+		   curcount,
+		   userinfo);
+  }
+
+  free(userinfo);
+
+  return ret;
+}	      
+
+faim_internal 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(sess, command->data+i, &userinfo[curcount-1]);
+  }
+
+  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE))) {
+    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.
+ *
+ * General outline of this SNAC:
+ *   snac
+ *   cookie
+ *   channel id
+ *   tlvlist
+ *     unknown
+ *     source user info
+ *       name
+ *       evility
+ *       userinfo tlvs
+ *         online time
+ *         etc
+ *     message metatlv
+ *       message tlv
+ *         message string
+ *       possibly others
+ *  
+ */
+faim_internal 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;
+  unsigned char cookie[8];
+  int channel;
+  struct aim_tlvlist_t *outerlist;
+  char *msg = NULL;
+  struct aim_msgcookie_t *ck;
+
+  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+
+  i = 10; /* skip snac */
+
+  /*
+   * ICBM Cookie.  Cache it.
+   */ 
+  for (z=0; z<8; z++,i++)
+    cookie[z] = command->data[i];
+
+  if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+    if (ck->data)
+      free(ck->data);
+    free(ck);
+  }
+
+  /*
+   * Channel ID
+   *
+   * Channels 1 and 2 are implemented in the normal ICBM
+   * parser.
+   *
+   * We only do channel 3 here.
+   *
+   */
+  channel = aimutil_get16(command->data+i);
+  i += 2;
+
+  if (channel != 0x0003) {
+    faimdprintf(sess, 0, "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(sess, 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(sess, 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;
+}	      
+
+faim_export unsigned 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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++);
+}
+
+faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
+{
+  struct aim_conn_t *conn;
+
+  if ((conn = aim_chat_getconn(sess, name)))
+    aim_conn_close(conn);
+
+  if (!conn)
+    return -1;
+  return 0;
+}
+
+/*
+ * conn must be a BOS connection!
+ */
+faim_export unsigned long aim_chat_invite(struct aim_session_t *sess,
+					  struct aim_conn_t *conn,
+					  char *sn,
+					  char *msg,
+					  u_short exchange,
+					  char *roomname,
+					  u_short instance)
+{
+  struct command_tx_struct *newpacket;
+  int i,curbyte=0;
+  struct aim_msgcookie_t *cookie;
+  struct aim_invite_priv *priv;
+
+  if (!sess || !conn || !sn || !msg || !roomname)
+    return -1;
+
+  if (conn->type != AIM_CONN_TYPE_BOS)
+    return -1;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /*
+   * Cookie
+   */
+  for (i=0;i<8;i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());
+
+  /* XXX this should get uncached by the unwritten 'invite accept' handler */
+  if(!(priv = calloc(sizeof(struct aim_invite_priv), 1)))
+    return -1;
+  priv->sn = strdup(sn);
+  priv->roomname = strdup(roomname);
+  priv->exchange = exchange;
+  priv->instance = instance;
+
+  if(!(cookie = aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_INVITE, priv)))
+    return -1;
+  aim_cachecookie(sess, cookie);
+
+  /*
+   * 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++);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/chatnav.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,428 @@
+/*
+ * Handle ChatNav.
+ *
+ * [The ChatNav(igation) service does various things to keep chat
+ *  alive.  It provides room information, room searching and creating, 
+ *  as well as giving users the right ("permission") to use chat.]
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/*
+ * conn must be a chatnav connection!
+ */
+faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess,
+						struct aim_conn_t *conn)
+{
+  aim_genericreq_n(sess, conn, 0x000d, 0x0002);
+
+  return sess->snac_nextid;
+}
+
+faim_export unsigned 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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.
+ */
+faim_internal 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;
+  int ret=1;
+  
+  snacid = aimutil_get32(command->data+6);
+  snac = aim_remsnac(sess, snacid);
+
+  if (!snac) {
+    faimdprintf(sess, 0, "faim: chatnav_parse_info: received response to unknown request! (%08lx)\n", snacid);
+    return 1;
+  }
+  
+  if (snac->family != 0x000d) {
+    faimdprintf(sess, 0, "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;
+	  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))
+	    maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
+
+	  /* 
+	   * Type 0x0003: Exchange information
+	   *
+	   * There can be any number of these, each one
+	   * representing another exchange.  
+	   * 
+	   */
+	  curexchange = 0;
+	  while ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1)))
+	    {	
+	      curexchange++;
+	      exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
+	      
+
+	      /* exchange number */
+	      exchanges[curexchange-1].number = aimutil_get16(exchangetlv->value);
+	      innerlist = aim_readtlvchain(exchangetlv->value+2, exchangetlv->length-2);
+	      
+	      /* 
+	       * Type 0x000d: Unknown.
+	       */
+	      if (aim_gettlv(innerlist, 0x000d, 1))
+		;
+	      
+	      /* 
+	       * Type 0x0004: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x0004, 1))
+		;
+
+	      /* 
+	       * Type 0x0002: Unknown
+	       */
+	      if (aim_gettlv(innerlist, 0x0002, 1)) {
+		unsigned short classperms;
+
+		classperms = aim_gettlv16(innerlist, 0x0002, 1);
+		
+		faimdprintf(sess, 1, "faim: class permissions %x\n", classperms);
+	      }
+
+	      /*
+	       * Type 0x00c9: Unknown
+	       */ 
+	      if (aim_gettlv(innerlist, 0x00c9, 1))
+		;
+	      
+	      /*
+	       * Type 0x00ca: Creation Date 
+	       */
+	      if (aim_gettlv(innerlist, 0x00ca, 1))
+		;
+	      
+	      /*
+	       * Type 0x00d0: Mandatory Channels?
+	       */
+	      if (aim_gettlv(innerlist, 0x00d0, 1))
+		;
+
+	      /*
+	       * Type 0x00d1: Maximum Message length
+	       */
+	      if (aim_gettlv(innerlist, 0x00d1, 1))
+		;
+
+	      /*
+	       * Type 0x00d2: Maximum Occupancy?
+	       */
+	      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: Creation Permissions
+	       *
+	       * 0  Creation not allowed
+	       * 1  Room creation allowed
+	       * 2  Exchange creation allowed
+	       * 
+	       */
+	      if (aim_gettlv(innerlist, 0x00d5, 1)) {
+		unsigned char createperms;
+
+		createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+	      }
+
+	      /*
+	       * Type 0x00d6: Character Set (First Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d6, 1))	
+		exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
+	      else
+		exchanges[curexchange-1].charset1 = NULL;
+	      
+	      /*
+	       * Type 0x00d7: Language (First Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d7, 1))	
+		exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
+	      else
+		exchanges[curexchange-1].lang1 = NULL;
+
+	      /*
+	       * Type 0x00d8: Character Set (Second Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d8, 1))	
+		exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
+	      else
+		exchanges[curexchange-1].charset2 = NULL;
+
+	      /*
+	       * Type 0x00d9: Language (Second Time)
+	       */	      
+	      if (aim_gettlv(innerlist, 0x00d9, 1))	
+		exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
+	      else
+		exchanges[curexchange-1].lang2 = NULL;
+	      
+	      aim_freetlvchain(&innerlist);
+	    }
+	  
+	  /*
+	   * Call client.
+	   */
+	  if ((userfunc = aim_callhandler(sess, command->conn, 0x000d, 0x0009)))
+	    ret = userfunc(sess, command, snac->type, maxrooms, curexchange, exchanges);
+	  curexchange--;
+	  while(curexchange >= 0)
+	    {
+	      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(&tlvlist);
+	  
+	  break;
+      }
+    case 0x0003: /* request exchange info */
+      faimdprintf(sess, 0, "chatnav_parse_info: resposne to exchange info\n");
+      break;
+    case 0x0004: /* request room info */
+       faimdprintf(sess, 0, "chatnav_parse_info: response to room info\n");
+      break;
+    case 0x0005: /* request more room info */
+       faimdprintf(sess, 0, "chatnav_parse_info: response to more room info\n");
+      break;
+    case 0x0006: /* request occupant list */
+       faimdprintf(sess, 0, "chatnav_parse_info: response to occupant info\n");
+      break;
+    case 0x0007: /* search for a room */
+       faimdprintf(sess, 0, "chatnav_parse_info: search results\n");
+      break;
+    case 0x0008: { /* create room */
+      /*
+	000d 0009 0000 0000 0010 
+	
+	0004 0053 
+	     0004 -- exchange
+	     0c 7a 6f6f 6f6d 7a6f 6f6f 6d34 32 cookie/name
+	     0000 -- instance
+	     02 -- detail level
+	     0007 -- unknown!
+	     006a 000c 7a 6f 6f6f 6d7a 6f6f 6f6d 3432 -- fully qualified name 
+	     00c9 0002 0011 -- flags
+	     00ca 0004 39c0 0883 -- create time
+	     00d1 0002 0200 -- max msg len 
+	     00d2 0002 0018 -- max occupants
+	     00d3 000c -- name
+	          7a6f 6f6f 6d7a 6f6f 6f6d 3432 
+	     00d5 0001 02 -- creation permission
+       */
+      struct aim_tlvlist_t *tlvlist, *innerlist;
+      char *ck = NULL, *fqcn = NULL, *name = NULL;
+      unsigned short exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
+      unsigned long createtime = 0;
+      unsigned char createperms = 0;
+      int i, cklen;
+      struct aim_tlv_t *bigblock;
+
+      i = 10;
+      if (!(tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i))) {
+	faimdprintf(sess, 0, "unable to read top tlv in create room response\n");
+	break;
+      }
+
+      if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
+	faimdprintf(sess, 0, "no bigblock in top tlv in create room response\n");
+	aim_freetlvchain(&tlvlist);
+	break;
+      }
+      i = 0;
+
+      exchange = aimutil_get16(bigblock->value+i);
+      i += 2;
+
+      cklen = aimutil_get8(bigblock->value+i);
+      i++;
+
+      ck = malloc(cklen+1);
+      memcpy(ck, bigblock->value+i, cklen);
+      ck[cklen] = '\0';
+      i += cklen;
+
+      instance = aimutil_get16(bigblock->value+i);
+      i += 2;
+
+      if (aimutil_get8(bigblock->value+i) != 0x02) {
+	 faimdprintf(sess, 0, "unknown detaillevel in create room response (0x%02x)\n", aimutil_get8(bigblock->value+i));
+	aim_freetlvchain(&tlvlist);
+	free(ck);	
+	break;
+      }
+      i += 1;
+      
+      unknown = aimutil_get16(bigblock->value+i);
+      i += 2;
+
+      if (!(innerlist = aim_readtlvchain(bigblock->value+i, bigblock->length-i))) {
+	faimdprintf(sess, 0, "unable to read inner tlv chain in create room response\n");
+	aim_freetlvchain(&tlvlist);
+	free(ck);
+	break;
+      }
+
+      if (aim_gettlv(innerlist, 0x006a, 1))
+	fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
+
+      if (aim_gettlv(innerlist, 0x00c9, 1))
+	flags = aim_gettlv16(innerlist, 0x00c9, 1);
+
+      if (aim_gettlv(innerlist, 0x00ca, 1))
+	createtime = aim_gettlv32(innerlist, 0x00ca, 1);
+
+      if (aim_gettlv(innerlist, 0x00d1, 1))
+	maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
+
+      if (aim_gettlv(innerlist, 0x00d2, 1))
+	maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
+
+      if (aim_gettlv(innerlist, 0x00d3, 1))
+	name = aim_gettlv_str(innerlist, 0x00d3, 1);
+
+      if (aim_gettlv(innerlist, 0x00d5, 1))
+	createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+
+      if ((userfunc = aim_callhandler(sess, command->conn, 0x000d, 0x0009))) {
+	ret = userfunc(sess, command, snac->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
+      }
+     
+      if (ck)
+	free(ck);
+      if (name)
+	free(name);
+      if (fqcn)
+	free(fqcn);
+      aim_freetlvchain(&innerlist);
+      aim_freetlvchain(&tlvlist);
+
+      break;
+    }
+    default: /* unknown */
+      faimdprintf(sess, 0, "chatnav_parse_info: unknown request subtype (%04x)\n", snac->type);
+    }
+
+  if (snac && snac->data)
+    free(snac->data);
+  if (snac)
+    free(snac);
+  
+  return ret;
+}
+
+faim_export unsigned 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;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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);
+
+  aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/conn.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,872 @@
+
+/*
+ *  aim_conn.c
+ *
+ * Does all this gloriously nifty connection handling stuff...
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+/**
+ * aim_connrst - Clears out connection list, killing remaining connections.
+ * @sess: Session to be cleared
+ *
+ * Clears out the connection list and kills any connections left.
+ *
+ */
+faim_internal void aim_connrst(struct aim_session_t *sess)
+{
+  faim_mutex_init(&sess->connlistlock);
+  if (sess->connlist) {
+    struct aim_conn_t *cur = sess->connlist, *tmp;
+
+    while(cur) {
+      tmp = cur->next;
+      aim_conn_close(cur);
+      free(cur);
+      cur = tmp;
+    }
+  }
+  sess->connlist = NULL;
+  return;
+}
+
+/**
+ * aim_conn_init - Reset a connection to default values.
+ * @deadconn: Connection to be reset
+ *
+ * Initializes and/or resets a connection structure.
+ *
+ */
+static void aim_conn_init(struct aim_conn_t *deadconn)
+{
+  if (!deadconn)
+    return;
+
+  deadconn->fd = -1;
+  deadconn->subtype = -1;
+  deadconn->type = -1;
+  deadconn->seqnum = 0;
+  deadconn->lastactivity = 0;
+  deadconn->forcedlatency = 0;
+  deadconn->handlerlist = NULL;
+  deadconn->priv = NULL;
+  faim_mutex_init(&deadconn->active);
+  faim_mutex_init(&deadconn->seqnum_lock);
+  
+  return;
+}
+
+/**
+ * aim_conn_getnext - Gets a new connection structure.
+ * @sess: Session
+ *
+ * Allocate a new empty connection structure.
+ *
+ */
+faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
+{
+  struct aim_conn_t *newconn, *cur;
+
+  if (!(newconn = malloc(sizeof(struct aim_conn_t)))) 	
+    return NULL;
+
+  memset(newconn, 0, sizeof(struct aim_conn_t));
+  aim_conn_init(newconn);
+  newconn->next = NULL;
+
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL)
+    sess->connlist = newconn;
+  else {
+    for (cur = sess->connlist; cur->next; cur = cur->next)
+      ;
+    cur->next = newconn;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  return newconn;
+}
+
+/**
+ * aim_conn_kill - Close and free a connection.
+ * @sess: Session for the connection
+ * @deadconn: Connection to be freed
+ *
+ * Close, clear, and free a connection structure. Should never be
+ * called from within libfaim.
+ *
+ */
+faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
+{
+  struct aim_conn_t *cur;
+
+  if (!deadconn || !*deadconn)	
+    return;
+
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL)
+    ;
+  else if (sess->connlist->next == NULL) {
+    if (sess->connlist == *deadconn)
+      sess->connlist = NULL;
+  } else {
+    cur = sess->connlist;
+    while (cur->next) {
+      if (cur->next == *deadconn) {
+	cur->next = cur->next->next;
+	break;
+      }
+      cur = cur->next;
+    }
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  /* XXX: do we need this for txqueue too? */
+  aim_rxqueue_cleanbyconn(sess, *deadconn);
+
+  if ((*deadconn)->fd != -1) 
+    aim_conn_close(*deadconn);
+  if ((*deadconn)->priv)
+    free((*deadconn)->priv);
+  free(*deadconn);
+  deadconn = NULL;
+
+  return;
+}
+
+/**
+ * aim_conn_close - Close a connection
+ * @deadconn: Connection to close
+ *
+ * Close (but not free) a connection.
+ *
+ * This leaves everything untouched except for clearing the 
+ * handler list and setting the fd to -1 (used to recognize
+ * dead connections).
+ *
+ */
+faim_export void aim_conn_close(struct aim_conn_t *deadconn)
+{
+
+  faim_mutex_destroy(&deadconn->active);
+  faim_mutex_destroy(&deadconn->seqnum_lock);
+  if (deadconn->fd >= 3)
+    close(deadconn->fd);
+  deadconn->fd = -1;
+  if (deadconn->handlerlist)
+    aim_clearhandlers(deadconn);
+
+  return;
+}
+
+/**
+ * aim_getconn_type - Find a connection of a specific type
+ * @sess: Session to search
+ * @type: Type of connection to look for
+ *
+ * Searches for a connection of the specified type in the 
+ * specified session.  Returns the first connection of that
+ * type found.
+ *
+ */
+faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
+						int type)
+{
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
+      break;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+  return cur;
+}
+
+/**
+ * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. 
+ * @sess: Session to connect
+ * @host: Host to connect to
+ * @port: Port to connect to
+ * @statusret: Return value of the connection
+ *
+ * Attempts to connect to the specified host via the configured
+ * proxy settings, if present.  If no proxy is configured for
+ * this session, the connection is done directly.
+ *
+ */
+static int aim_proxyconnect(struct aim_session_t *sess, 
+			    char *host, unsigned short port,
+			    int *statusret)
+{
+  int fd = -1;
+
+  if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
+    int i;
+    unsigned char buf[512];
+    struct sockaddr_in sa;
+    struct hostent *hp;
+    char *proxy;
+    unsigned short proxyport = 1080;
+
+    for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
+      if (sess->socksproxy.server[i] == ':') {
+	proxyport = atoi(&(sess->socksproxy.server[i+1]));
+	break;
+      }
+    }
+    proxy = (char *)malloc(i+1);
+    strncpy(proxy, sess->socksproxy.server, i);
+    proxy[i] = '\0';
+
+    if (!(hp = gethostbyname(proxy))) {
+      faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n");
+      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+      return -1;
+    }
+    free(proxy);
+
+    memset(&sa.sin_zero, 0, 8);
+    sa.sin_port = htons(proxyport);
+    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+    sa.sin_family = hp->h_addrtype;
+  
+    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+      faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n");
+      close(fd);
+      return -1;
+    }
+
+    i = 0;
+    buf[0] = 0x05; /* SOCKS version 5 */
+    if (strlen(sess->socksproxy.username)) {
+      buf[1] = 0x02; /* two methods */
+      buf[2] = 0x00; /* no authentication */
+      buf[3] = 0x02; /* username/password authentication */
+      i = 4;
+    } else {
+      buf[1] = 0x01;
+      buf[2] = 0x00;
+      i = 3;
+    }
+
+    if (write(fd, buf, i) < i) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+
+    if (read(fd, buf, 2) < 2) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+
+    if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+      *statusret = EINVAL;
+      close(fd);
+      return -1;
+    }
+
+    /* check if we're doing username authentication */
+    if (buf[1] == 0x02) {
+      i  = aimutil_put8(buf, 0x01); /* version 1 */
+      i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
+      i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
+      i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
+      i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
+      if (write(fd, buf, i) < i) {
+	*statusret = errno;
+	close(fd);
+	return -1;
+      }
+      if (read(fd, buf, 2) < 2) {
+	*statusret = errno;
+	close(fd);
+	return -1;
+      }
+      if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+	*statusret = EINVAL;
+	close(fd);
+	return -1;
+      }
+    }
+
+    i  = aimutil_put8(buf, 0x05);
+    i += aimutil_put8(buf+i, 0x01); /* CONNECT */
+    i += aimutil_put8(buf+i, 0x00); /* reserved */
+    i += aimutil_put8(buf+i, 0x03); /* address type: host name */
+    i += aimutil_put8(buf+i, strlen(host));
+    i += aimutil_putstr(buf+i, host, strlen(host));
+    i += aimutil_put16(buf+i, port);
+
+    if (write(fd, buf, i) < i) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+    if (read(fd, buf, 10) < 10) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+    if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+      *statusret = EINVAL;
+      close(fd);
+      return -1;
+    }
+
+  } else { /* connecting directly */
+    struct sockaddr_in sa;
+    struct hostent *hp;
+
+    if (!(hp = gethostbyname(host))) {
+      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+      return -1;
+    }
+
+    memset(&sa, 0, sizeof(struct sockaddr_in));
+    sa.sin_port = htons(port);
+    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+    sa.sin_family = hp->h_addrtype;
+  
+    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+
+    if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
+      fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
+
+    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+      if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
+	if ((errno == EINPROGRESS) || (errno == EINTR)) {
+	  if (statusret)
+	    *statusret |= AIM_CONN_STATUS_INPROGRESS;
+	  return fd;
+	}
+      }
+      close(fd);
+      fd = -1;
+    }
+  }
+  return fd;
+}
+
+/**
+ * aim_cloneconn - clone an aim_conn_t
+ * @sess: session containing parent
+ * @src: connection to clone
+ *
+ * A new connection is allocated, and the values are filled in
+ * appropriately. Note that this function sets the new connnection's
+ * ->priv pointer to be equal to that of its parent: only the pointer
+ * is copied, not the data it points to.
+ *
+ * This function returns a pointer to the new aim_conn_t, or %NULL on
+ * error
+ */
+faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess,
+					       struct aim_conn_t *src)
+{
+  struct aim_conn_t *conn;
+    struct aim_rxcblist_t *cur;
+
+  if (!(conn = aim_conn_getnext(sess)))
+    return NULL;
+
+  faim_mutex_lock(&conn->active);
+
+  conn->fd = src->fd;
+  conn->type = src->type;
+  conn->subtype = src->subtype;
+  conn->seqnum = src->seqnum;
+  conn->priv = src->priv;
+  conn->lastactivity = src->lastactivity;
+  conn->forcedlatency = src->forcedlatency;
+
+  /* clone handler list */
+  for (cur = src->handlerlist; cur; cur = cur->next) {
+    aim_conn_addhandler(sess, conn, cur->family, cur->type, 
+			cur->handler, cur->flags);
+  }
+
+  faim_mutex_unlock(&conn->active);
+
+  return conn;
+}
+
+/**
+ * aim_newconn - Open a new connection
+ * @sess: Session to create connection in
+ * @type: Type of connection to create
+ * @dest: Host to connect to (in "host:port" syntax)
+ *
+ * Opens a new connection to the specified dest host of specified
+ * type, using the proxy settings if available.  If @host is %NULL,
+ * the connection is allocated and returned, but no connection 
+ * is made.
+ *
+ * FIXME: Return errors in a more sane way.
+ *
+ */
+faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
+					   int type, char *dest)
+{
+  struct aim_conn_t *connstruct;
+  int ret;
+  u_short port = FAIM_LOGIN_PORT;
+  char *host = NULL;
+  int i=0;
+
+  if ((connstruct=aim_conn_getnext(sess))==NULL)
+    return NULL;
+
+  faim_mutex_lock(&connstruct->active);
+  
+  connstruct->type = type;
+
+  if (!dest) { /* just allocate a struct */
+    connstruct->fd = -1;
+    connstruct->status = 0;
+    faim_mutex_unlock(&connstruct->active);
+    return connstruct;
+  }
+
+  /* 
+   * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
+   * colon, in the BOS redirect.  This fatally breaks all previous 
+   * libfaims.  Bad, bad AOL.
+   *
+   * We put this here to catch every case. 
+   *
+   */
+
+  for(i=0;i<(int)strlen(dest);i++) {
+    if (dest[i] == ':') {
+      port = atoi(&(dest[i+1]));
+      break;
+    }
+  }
+  host = (char *)malloc(i+1);
+  strncpy(host, dest, i);
+  host[i] = '\0';
+
+  if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
+    connstruct->fd = -1;
+    connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
+    free(host);
+    faim_mutex_unlock(&connstruct->active);
+    return connstruct;
+  } else
+    connstruct->fd = ret;
+  
+  faim_mutex_unlock(&connstruct->active);
+
+  free(host);
+
+  return connstruct;
+}
+
+/**
+ * aim_conngetmaxfd - Return the highest valued file discriptor in session
+ * @sess: Session to search
+ *
+ * Returns the highest valued filed descriptor of all open 
+ * connections in @sess.
+ *
+ */
+faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
+{
+  int j = 0;
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->fd > j)
+      j = cur->fd;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  return j;
+}
+
+/**
+ * aim_conn_in_sess - Predicate to test the precense of a connection in a sess
+ * @sess: Session to look in
+ * @conn: Connection to look for
+ *
+ * Searches @sess for the passed connection.  Returns 1 if its present,
+ * zero otherwise.
+ *
+ */
+faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for(cur = sess->connlist; cur; cur = cur->next)
+    if(cur == conn) {
+      faim_mutex_unlock(&sess->connlistlock);
+      return 1;
+    }
+  faim_mutex_unlock(&sess->connlistlock);
+  return 0;
+}
+
+/**
+ * aim_select - Wait for a socket with data or timeout
+ * @sess: Session to wait on
+ * @timeout: How long to wait
+ * @status: Return status
+ *
+ * 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)
+ *
+ * XXX: we could probably stand to do a little courser locking here.
+ *
+ */ 
+faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
+					  struct timeval *timeout, int *status)
+{
+  struct aim_conn_t *cur;
+  fd_set fds, wfds;
+  int maxfd = 0;
+  int i, haveconnecting = 0;
+
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL) {
+    faim_mutex_unlock(&sess->connlistlock);
+    *status = -1;
+    return NULL;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  FD_ZERO(&fds);
+  FD_ZERO(&wfds);
+  maxfd = 0;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->fd == -1) {
+      /* don't let invalid/dead connections sit around */
+      *status = 2;
+      faim_mutex_unlock(&sess->connlistlock);
+      return cur;
+    } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
+      FD_SET(cur->fd, &wfds);
+      haveconnecting++;
+    }
+    FD_SET(cur->fd, &fds);
+    if (cur->fd > maxfd)
+      maxfd = cur->fd;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  /* 
+   * If we have data waiting to be sent, return
+   *
+   * We have to not do this if theres at least one
+   * connection thats still connecting, since that connection
+   * may have queued data and this return would prevent
+   * the connection from ever completing!  This is a major
+   * inadequacy of the libfaim way of doing things.  It means
+   * that nothing can transmit as long as there's connecting
+   * sockets. Evil.
+   *
+   * But its still better than having blocking connects.
+   *
+   */
+  if (!haveconnecting && (sess->queue_outgoing != NULL)) {
+    *status = 1;
+    return NULL;
+  } 
+
+  if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
+    faim_mutex_lock(&sess->connlistlock);
+    for (cur = sess->connlist; cur; cur = cur->next) {
+      if ((FD_ISSET(cur->fd, &fds)) || 
+	  ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
+	   FD_ISSET(cur->fd, &wfds))) {
+	*status = 2;
+	faim_mutex_unlock(&sess->connlistlock);
+	return cur; /* XXX race condition here -- shouldnt unlock connlist */
+      }
+    }
+    *status = 0; /* shouldn't happen */
+  } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
+    *status = 0;
+  else
+    *status = i; /* can be 0 or -1 */
+
+  faim_mutex_unlock(&sess->connlistlock);
+
+  return NULL;  /* no waiting or error, return */
+}
+
+/**
+ * aim_conn_isready - Test if a connection is marked ready
+ * @conn: Connection to test
+ *
+ * Returns true if the connection is ready, false otherwise.
+ * Returns -1 if the connection is invalid.
+ *
+ * XXX: This is deprecated.
+ *
+ */
+faim_export int aim_conn_isready(struct aim_conn_t *conn)
+{
+  if (conn)
+    return (conn->status & 0x0001);
+  return -1;
+}
+
+/**
+ * aim_conn_setstatus - Set the status of a connection
+ * @conn: Connection
+ * @status: New status
+ *
+ * @newstatus is %XOR'd with the previous value of the connection
+ * status and returned.  Returns -1 if the connection is invalid.
+ *
+ * This isn't real useful.
+ *
+ */
+faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
+{
+  int val;
+
+  if (!conn)
+    return -1;
+  
+  faim_mutex_lock(&conn->active);
+  val = conn->status ^= status;
+  faim_mutex_unlock(&conn->active);
+
+  return val;
+}
+
+/**
+ * aim_conn_setlatency - Set a forced latency value for connection
+ * @conn: Conn to set latency for
+ * @newval: Number of seconds to force between transmits
+ *
+ * Causes @newval seconds to be spent between transmits on a connection.
+ *
+ * This is my lame attempt at overcoming not understanding the rate
+ * limiting. 
+ *
+ * XXX: This should really be replaced with something that scales and
+ * backs off like the real rate limiting does.
+ *
+ */
+faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
+{
+  if (!conn)
+    return -1;
+
+  faim_mutex_lock(&conn->active);
+  conn->forcedlatency = newval;
+  conn->lastactivity = 0; /* reset this just to make sure */
+  faim_mutex_unlock(&conn->active);
+
+  return 0;
+}
+
+/**
+ * aim_setupproxy - Configure a proxy for this session
+ * @sess: Session to set proxy for
+ * @server: SOCKS server
+ * @username: SOCKS username
+ * @password: SOCKS password
+ *
+ * Call this with your SOCKS5 proxy server parameters before
+ * the first call to aim_newconn().  If called with all %NULL
+ * args, it will clear out a previously set proxy.  
+ *
+ * Set username and password to %NULL if not applicable.
+ *
+ */
+faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
+{
+  /* clear out the proxy info */
+  if (!server || !strlen(server)) {
+    memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
+    memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
+    memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
+    return;
+  }
+
+  strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
+  if (username && strlen(username)) 
+    strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
+  if (password && strlen(password))
+    strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
+  return;
+}
+
+/**
+ * aim_session_init - Initializes a session structure
+ * @sess: Session to initialize
+ * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
+ * @debuglevel: Level of debugging output (zero is least)
+ *
+ * Sets up the initial values for a session.
+ *
+ */
+faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags, int debuglevel)
+{
+  if (!sess)
+    return;
+
+  memset(sess, 0, sizeof(struct aim_session_t));
+  aim_connrst(sess);
+  sess->queue_outgoing = NULL;
+  sess->queue_incoming = NULL;
+  sess->pendingjoin = NULL;
+  sess->pendingjoinexchange = 0;
+  aim_initsnachash(sess);
+  sess->msgcookies = NULL;
+  sess->snac_nextid = 0x00000001;
+
+  sess->flags = 0;
+  sess->debug = 0;
+  sess->debugcb = NULL;
+
+  /*
+   * Default to SNAC login unless XORLOGIN is explicitly set.
+   */
+  if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
+    sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+  sess->flags |= flags;
+
+  /*
+   * This must always be set.  Default to the queue-based
+   * version for back-compatibility.  
+   */
+  aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
+
+  return;
+}
+
+/**
+ * aim_setdebuggingcb - Set the function to call when outputting debugging info
+ * @sess: Session to change
+ * @cb: Function to call
+ *
+ * The function specified is called whenever faimdprintf() is used within
+ * libfaim, and the session's debugging level is greater tha nor equal to
+ * the value faimdprintf was called with.
+ *
+ */
+faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t cb)
+{
+
+  if (!sess)
+    return -1;
+
+  sess->debugcb = cb;
+
+  return 0;
+}
+
+/**
+ * aim_conn_isconnecting - Determine if a connection is connecting
+ * @conn: Connection to examine
+ *
+ * Returns nonzero if the connection is in the process of
+ * connecting (or if it just completed and aim_conn_completeconnect()
+ * has yet to be called on it).
+ *
+ */
+faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
+{
+  if (!conn)
+    return 0;
+  return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
+}
+
+faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  fd_set fds, wfds;
+  struct timeval tv;
+  int res, error = ETIMEDOUT;
+  rxcallback_t userfunc;
+
+  if (!conn || (conn->fd == -1))
+    return -1;
+
+  if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
+    return -1;
+
+  FD_ZERO(&fds);
+  FD_SET(conn->fd, &fds);
+  FD_ZERO(&wfds);
+  FD_SET(conn->fd, &wfds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
+    error = errno;
+    aim_conn_close(conn);
+    errno = error;
+    return -1;
+  } else if (res == 0) {
+    faimdprintf(sess, 0, "aim_conn_completeconnect: false alarm on %d\n", conn->fd);
+    return 0; /* hasn't really completed yet... */
+  } 
+
+  if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
+    int len = sizeof(error);
+
+    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+      error = errno;
+  }
+
+  if (error) {
+    aim_conn_close(conn);
+    errno = error;
+    return -1;
+  }
+
+  fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
+
+  conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
+
+  if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
+    userfunc(sess, NULL, conn);
+
+  /* Flush out the queues if there was something waiting for this conn  */
+  aim_tx_flushqueue(sess);
+
+  return 0;
+}
+
+/*
+ * aim_logoff()
+ *
+ * Closes -ALL- open connections.
+ *
+ */
+faim_export int aim_logoff(struct aim_session_t *sess)
+{
+
+  aim_connrst(sess);  /* in case we want to connect again */
+
+  return 0;
+
+}
+
--- a/libfaim/faim/aim.h	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,824 +0,0 @@
-/* 
- * Main libfaim header.  Must be included in client for prototypes/macros.
- *
- * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
- *                                                -- Josh Meyer
- *
- */
-
-#ifndef __AIM_H__
-#define __AIM_H__
-
-#define FAIM_VERSION_MAJOR 0
-#define FAIM_VERSION_MINOR 99
-#define FAIM_VERSION_MINORMINOR 0
-
-#include <faim/faimconfig.h>
-#include <faim/aim_cbtypes.h>
-
-#if !defined(FAIM_USEPTHREADS) && !defined(FAIM_USEFAKELOCKS)
-#error pthreads or fakelocks are currently required.
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#ifdef _WIN32
-#include <windows.h>
-#include <time.h>
-#include <io.h>
-#else
-#include <sys/time.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#endif
-
-#ifdef FAIM_USEPTHREADS
-#include <pthread.h>
-#define faim_mutex_t pthread_mutex_t 
-#define faim_mutex_init(x) pthread_mutex_init(x, NULL)
-#define faim_mutex_lock(x) pthread_mutex_lock(x)
-#define faim_mutex_unlock(x) pthread_mutex_unlock(x)
-#define faim_mutex_destroy(x) pthread_mutex_destroy(x)
-#elif defined(FAIM_USEFAKELOCKS)
-/*
- * For platforms without pthreads, we also assume
- * we're not linking against a threaded app.  Which
- * means we don't have to do real locking.  The 
- * macros below do nothing really.  They're a joke.
- * But they get it to compile.
- */
-#define faim_mutex_t char
-#define faim_mutex_init(x) *x = 0
-#define faim_mutex_lock(x) *x = 1;
-#define faim_mutex_unlock(x) *x = 0;
-#define faim_mutex_destroy(x) *x = 0;
-#endif
-
-/* Portability stuff (DMP) */
-
-#ifdef _WIN32
-#define sleep Sleep
-#define socklen_t int /* this must be a POSIXy thing */
-#define snprintf _snprintf /* I'm not sure whats wrong with Microsoft here */
-#define close(x) closesocket(x) /* no comment */
-#endif
-
-#if defined(mach) && defined(__APPLE__)
-#define gethostbyname(x) gethostbyname2(x, AF_INET) 
-#endif
-
-#if defined(_WIN32) || defined(STRICT_ANSI)
-#define faim_shortfunc
-#else
-#define faim_shortfunc inline
-#endif
-
-#if defined(_WIN32) && !defined(WIN32_STATIC)
-/*
- * For a win32 DLL, we define WIN32_INDLL if this file
- * is included while compiling the DLL. If its not 
- * defined (its included in a client app), the symbols
- * will be imported instead of exported.
- */
-#ifdef WIN32_INDLL
-#define faim_export __declspec(dllexport)
-#else 
-#define faim_export __declspec(dllimport)
-#endif /* WIN32_INDLL */
-#define faim_internal
-#else
-/*
- * Nothing normally needed for unix...
- */
-#define faim_export
-#define faim_internal
-#endif
-
-/* 
- * Current Maximum Length for Screen Names (not including NULL) 
- *
- * Currently only names up to 16 characters can be registered
- * however it is aparently legal for them to be larger.
- */
-#define MAXSNLEN 32
-
-/*
- * Current Maximum Length for Instant Messages
- *
- * This was found basically by experiment, but not wholly
- * accurate experiment.  It should not be regarded
- * as completely correct.  But its a decent approximation.
- *
- * Note that although we can send this much, its impossible
- * for WinAIM clients (up through the latest (4.0.1957)) to
- * send any more than 1kb.  Amaze all your windows friends
- * with utterly oversized instant messages!
- *
- * XXX: the real limit is the total SNAC size at 8192. Fix this.
- * 
- */
-#define MAXMSGLEN 7987
-
-/*
- * Current Maximum Length for Chat Room Messages
- *
- * This is actually defined by the protocol to be
- * dynamic, but I have yet to see due cause to 
- * define it dynamically here.  Maybe later.
- *
- */
-#define MAXCHATMSGLEN 512
-
-/*
- * Standard size of an AIM authorization cookie
- */
-#define AIM_COOKIELEN            0x100
-
-#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
-
-/*
- * 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 size */
-  int major;
-  int minor;
-  int build;
-  char country[3];
-  char lang[3];
-  int major2;
-  int minor2;
-  long unknown;
-};
-
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#endif
-
-/* 
- * 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
-
-/* they start getting arbitrary in rendezvous stuff =) */
-#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak OSCAR! */
-#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
-
-/*
- * Subtypes, we need these for OFT stuff.
- */
-#define AIM_CONN_SUBTYPE_OFT_DIRECTIM  0x0001
-#define AIM_CONN_SUBTYPE_OFT_GETFILE   0x0002
-#define AIM_CONN_SUBTYPE_OFT_SENDFILE  0x0003
-#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004
-#define AIM_CONN_SUBTYPE_OFT_VOICE     0x0005
-
-/*
- * 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   0x0040
-#define AIM_CONN_STATUS_CONNERR     0x0080
-#define AIM_CONN_STATUS_INPROGRESS  0x0100
-
-#define AIM_FRAMETYPE_OSCAR 0x0000
-#define AIM_FRAMETYPE_OFT 0x0001
-
-struct aim_conn_t {
-  int fd;
-  unsigned short type;
-  unsigned short subtype;
-  int seqnum;
-  int status;
-  void *priv; /* misc data the client may want to store */
-  time_t lastactivity; /* time of last transmit */
-  int forcedlatency; 
-  struct aim_rxcblist_t *handlerlist;
-  faim_mutex_t active; /* lock around read/writes */
-  faim_mutex_t seqnum_lock; /* lock around ->seqnum changes */
-  struct aim_conn_t *next;
-};
-
-/* struct for incoming commands */
-struct command_rx_struct {
-  unsigned char hdrtype; /* defines which piece of the union to use */
-  union {
-    struct { 
-      char type;
-      unsigned short seqnum;     
-    } oscar;
-    struct {
-      unsigned short type;
-      unsigned char magic[4]; /* ODC2 OFT2 */
-      unsigned short hdr2len;
-      unsigned char *hdr2; /* rest of bloated header */
-    } oft;
-  } hdr;
-  unsigned short commandlen;         /* total payload length */
-  unsigned char *data;             /* packet data (from 7 byte on) */
-  unsigned char lock;               /* 0 = open, !0 = locked */
-  unsigned char handled;            /* 0 = new, !0 = been handled */
-  unsigned char nofree;		    /* 0 = free data on purge, 1 = only unlink */
-  struct aim_conn_t *conn;  /* the connection it came in on... */
-  struct command_rx_struct *next; /* ptr to next struct in list */
-};
-
-/* struct for outgoing commands */
-struct command_tx_struct {
-  unsigned char hdrtype; /* defines which piece of the union to use */
-  union {
-    struct {
-      unsigned char type;
-      unsigned short seqnum;
-    } oscar;
-    struct {
-      unsigned short type;
-      unsigned char magic[4]; /* ODC2 OFT2 */
-      unsigned short hdr2len;
-      unsigned char *hdr2;
-    } oft;
-  } hdr;
-  u_int commandlen;         
-  u_char *data;      
-  u_int lock;               /* 0 = open, !0 = locked */
-  u_int sent;               /* 0 = pending, !0 = has been sent */
-  struct aim_conn_t *conn; 
-  struct command_tx_struct *next; /* ptr to next struct in list */
-};
-
-/*
- * OFT session: random oft cruft, per-session.
- *
- */
-struct aim_oft_session_t {
-  FILE *listing;
-  char *listingdir;
-};
-
-/*
- * AIM Session: The main client-data interface.  
- *
- */
-struct aim_session_t {
-
-  /* ---- Client Accessible ------------------------ */
-  /* 
-   * Our screen name.
-   *
-   */
-  char sn[MAXSNLEN+1];
-  
-  /*
-   * Pointer to anything the client wants to 
-   * explicitly associate with this session.
-   *
-   * This is for use in the callbacks mainly. In any
-   * callback, you can access this with sess->aux_data.
-   *
-   */
-  void *aux_data;
-
-  /* 
-   * OFT Data 
-   */
-  struct aim_oft_session_t oft;
-
-  /* ---- Internal Use Only ------------------------ */
-  /* 
-   * Connection information
-   */
-  struct aim_conn_t *connlist;
-  faim_mutex_t connlistlock;
-  
-  /* 
-   * TX/RX queues 
-   */
-  struct command_tx_struct *queue_outgoing;   
-  struct command_rx_struct *queue_incoming; 
-  
-  /*
-   * Tx Enqueuing function
-   */
-  int (*tx_enqueue)(struct aim_session_t *, struct command_tx_struct *);
-
-  /*
-   * This is a dreadful solution to the what-room-are-we-joining
-   * problem.  (There's no connection between the service
-   * request and the resulting redirect.)
-   */ 
-  char *pendingjoin;
-  unsigned short pendingjoinexchange;
-
-  /*
-   * Outstanding snac handling 
-   *
-   * XXX: Should these be per-connection? -mid
-   */
-  struct aim_snac_t *snac_hash[FAIM_SNAC_HASH_SIZE];
-  faim_mutex_t snac_hash_locks[FAIM_SNAC_HASH_SIZE];
-  u_long snac_nextid;
-
-  struct {
-    char server[128];
-    char username[128];
-    char password[128];
-  } socksproxy;
-
-  unsigned long flags;
-
-  struct aim_msgcookie_t *msgcookies;
-};
-
-/* Values for sess->flags */
-#define AIM_SESS_FLAGS_SNACLOGIN       0x00000001
-#define AIM_SESS_FLAGS_XORLOGIN        0x00000002
-#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004
-
-/*
- * AIM User Info, Standard Form.
- */
-struct aim_userinfo_s {
-  char sn[MAXSNLEN+1];
-  u_short warnlevel;
-  u_short idletime;
-  u_short flags;
-  u_long membersince;
-  u_long onlinesince;
-  u_long sessionlen;  
-  u_short capabilities;
-  struct {
-    unsigned short status;
-    unsigned int ipaddr;
-    char crap[0x25]; /* until we figure it out... */
-  } icqinfo;
-};
-
-#define AIM_FLAG_UNCONFIRMED 	0x0001 /* "damned transients" */
-#define AIM_FLAG_ADMINISTRATOR	0x0002
-#define AIM_FLAG_AOL		0x0004
-#define AIM_FLAG_OSCAR_PAY	0x0008
-#define AIM_FLAG_FREE 		0x0010
-#define AIM_FLAG_AWAY		0x0020
-#define AIM_FLAG_UNKNOWN40	0x0040
-#define AIM_FLAG_UNKNOWN80	0x0080
-
-#define AIM_FLAG_ALLUSERS	0x001f
-
-/*
- * 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 */
-faim_internal struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen);
-faim_internal void aim_freetlvchain(struct aim_tlvlist_t **list);
-faim_internal struct aim_tlv_t *aim_grabtlv(u_char *src);
-faim_internal struct aim_tlv_t *aim_grabtlvstr(u_char *src);
-faim_internal struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *, u_short, int);
-faim_internal char *aim_gettlv_str(struct aim_tlvlist_t *, u_short, int);
-faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num);
-faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num);
-faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num);
-faim_internal int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv);
-faim_internal struct aim_tlv_t *aim_createtlv(void);
-faim_internal int aim_freetlv(struct aim_tlv_t **oldtlv);
-faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char  v);
-faim_internal int aim_puttlv_16(u_char *, u_short, u_short);
-faim_internal int aim_puttlv_32(u_char *, u_short, u_long);
-faim_internal int aim_puttlv_str(u_char *buf, u_short t, int l, char *v);
-faim_internal int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list);
-faim_internal int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val);
-faim_internal int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val);
-faim_internal int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len);
-faim_internal int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps);
-faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type);
-faim_internal int aim_counttlvchain(struct aim_tlvlist_t **list);
-
-/*
- * Get command from connections / Dispatch commands
- * already in queue.
- */
-faim_export int aim_get_command(struct aim_session_t *, struct aim_conn_t *);
-int aim_rxdispatch(struct aim_session_t *);
-
-faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
-
-int aim_logoff(struct aim_session_t *);
-
-faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn);
-
-typedef int (*rxcallback_t)(struct aim_session_t *, struct command_rx_struct *, ...);
-int aim_register_callbacks(rxcallback_t *);
-
-faim_internal unsigned long aim_genericreq_n(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype);
-faim_internal unsigned long aim_genericreq_l(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_long *);
-faim_internal unsigned long aim_genericreq_s(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short subtype, u_short *);
-
-faim_internal struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr);
-faim_export int aim_oft_registerlisting(struct aim_session_t *sess, FILE *file, char* listingdir);
-faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh);
-faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize);
-
-/* aim_login.c */
-faim_export int aim_sendconnack(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
-faim_export int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *, char *key);
-faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, int errorcode, char *errorurl, char *bosip, char *cookie, char *email, int regstatus);
-faim_export int aim_gencookie(unsigned char *buf);
-faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned short servid, char *ip, char *cookie);
-faim_export void aim_purge_rxqueue(struct aim_session_t *);
-faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_internal int aim_recv(int fd, void *buf, size_t count);
-
-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 *, ...);
-faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn);
-
-faim_internal struct command_tx_struct *aim_tx_new(unsigned char framing, int chan, struct aim_conn_t *conn, int datalen);
-faim_internal int aim_tx_enqueue__queuebased(struct aim_session_t *, struct command_tx_struct *);
-faim_internal int aim_tx_enqueue__immediate(struct aim_session_t *, struct command_tx_struct *);
-faim_internal int aim_tx_enqueue(struct aim_session_t *, struct command_tx_struct *);
-faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur);
-faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *);
-faim_export int aim_tx_flushqueue(struct aim_session_t *);
-faim_internal int aim_tx_printqueue(struct aim_session_t *);
-faim_export void aim_tx_purgequeue(struct aim_session_t *);
-faim_internal int aim_parse_hostonline(struct aim_session_t *sess, struct command_rx_struct *command, ...);
-faim_internal int aim_parse_hostversions(struct aim_session_t *sess, struct command_rx_struct *command, ...);
-
-
-struct aim_rxcblist_t {
-  u_short family;
-  u_short type;
-  rxcallback_t handler;
-  u_short flags;
-  struct aim_rxcblist_t *next;
-};
-
-faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval);
-
-faim_export int aim_conn_addhandler(struct aim_session_t *, struct aim_conn_t *conn, u_short family, u_short type, rxcallback_t newhandler, u_short flags);
-faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn, u_short family, u_short type);
-faim_export 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;
-  u_short type;
-  u_short flags;
-  void *data;
-  time_t issuetime;
-  struct aim_snac_t *next;
-};
-faim_internal void aim_initsnachash(struct aim_session_t *sess);
-faim_internal unsigned long aim_newsnac(struct aim_session_t *, struct aim_snac_t *newsnac);
-faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, const unsigned short family, const unsigned short type, const unsigned short flags, const void *data, const int datalen);
-faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *, u_long id);
-faim_internal int aim_cleansnacs(struct aim_session_t *, int maxage);
-faim_internal int aim_putsnac(u_char *, int, int, int, u_long);
-
-
-faim_internal void aim_connrst(struct aim_session_t *);
-faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *);
-faim_export void aim_conn_close(struct aim_conn_t *deadconn);
-faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *, int type);
-faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *, int type, char *dest);
-faim_export int aim_conngetmaxfd(struct aim_session_t *);
-faim_export struct aim_conn_t *aim_select(struct aim_session_t *, struct timeval *, int *);
-faim_export int aim_conn_isready(struct aim_conn_t *);
-faim_export int aim_conn_setstatus(struct aim_conn_t *, int);
-faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_conn_isconnecting(struct aim_conn_t *conn);
-faim_export void aim_session_init(struct aim_session_t *, unsigned long flags);
-faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password);
-
-/* aim_misc.c */
-
-#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
-#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
-#define AIM_VISIBILITYCHANGE_DENYADD      0x07
-#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
-
-#define AIM_PRIVFLAGS_ALLOWIDLE           0x01
-#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE    0x02
-
-#define AIM_WARN_ANON                     0x01
-
-faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon);
-faim_export unsigned long aim_bos_nop(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_flap_nop(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_bos_setidle(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *, struct aim_conn_t *, int, char *);
-faim_export unsigned long aim_bos_setbuddylist(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export unsigned long aim_bos_setprofile(struct aim_session_t *, struct aim_conn_t *, char *, char *, unsigned short);
-faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_clientready(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqrate(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *, struct aim_conn_t *, u_long);
-faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqservice(struct aim_session_t *, struct aim_conn_t *, u_short);
-faim_export unsigned long aim_bos_reqrights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,struct aim_conn_t *conn);
-faim_export unsigned long aim_setversions(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy);
-faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy);
-faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned long status);
-
-faim_internal struct aim_fileheader_t *aim_getlisting(FILE *);
-faim_internal int aim_oft_buildheader(char *,struct aim_fileheader_t *);
-faim_internal int aim_listenestablish(u_short);
-faim_internal int aim_tx_destroy(struct command_tx_struct *);
-
-/* aim_rxhandlers.c */
-faim_export int aim_rxdispatch(struct aim_session_t *);
-faim_internal int aim_authparse(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_handleredirect_middle(struct aim_session_t *, struct command_rx_struct *, ...);
-faim_internal 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 *, ...);
-faim_internal int aim_parse_generalerrs(struct aim_session_t *, struct command_rx_struct *command, ...);
-faim_internal int aim_parsemotd_middle(struct aim_session_t *sess, struct command_rx_struct *command, ...);
-
-#define AIM_RATE_CODE_CHANGE     0x0001
-#define AIM_RATE_CODE_WARNING    0x0002
-#define AIM_RATE_CODE_LIMIT      0x0003
-#define AIM_RATE_CODE_CLEARLIMIT 0x0004
-faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command);
-
-faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command);
-
-faim_export unsigned long aim_ads_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_ads_requestads(struct aim_session_t *sess, struct aim_conn_t *conn);
-
-/* aim_im.c */
-struct aim_directim_priv {
-  unsigned char cookie[8];
-  char sn[MAXSNLEN+1];
-  char ip[30];
-};
-
-#define AIM_IMFLAGS_AWAY 0x01 /* mark as an autoreply */
-#define AIM_IMFLAGS_ACK  0x02 /* request a receipt notice */
-
-faim_export unsigned long aim_send_im(struct aim_session_t *, struct aim_conn_t *, char *, u_int, char *);
-faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_export unsigned long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn);
-faim_internal int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_internal int aim_parse_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
-faim_internal int aim_parse_missedcall(struct aim_session_t *sess, struct command_rx_struct *command);
-
-faim_export struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *);
-faim_export int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *);
-
-/* aim_info.c */
-#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
-#define AIM_CAPS_GAMES 0x40
-#define AIM_CAPS_SAVESTOCKS 0x80
-
-extern u_char aim_caps[8][16];
-faim_internal unsigned short aim_getcap(unsigned char *capblock, int buflen);
-faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps);
-
-#define AIM_GETINFO_GENERALINFO 0x00001
-#define AIM_GETINFO_AWAYMESSAGE 0x00003
-
-struct aim_msgcookie_t {
-  unsigned char cookie[8];
-  int type;
-  void *data;
-  time_t addtime;
-  struct aim_msgcookie_t *next;
-};
-
-struct aim_fileheader_t {
-#if 0
-  char  magic[4];            /* 0 */
-  short hdrlen;           /* 4 */
-  short hdrtype;             /* 6 */
-#endif
-  char  bcookie[8];       /* 8 */
-  short encrypt;          /* 16 */
-  short compress;         /* 18 */
-  short totfiles;         /* 20 */
-  short filesleft;        /* 22 */
-  short totparts;         /* 24 */
-  short partsleft;        /* 26 */
-  long  totsize;          /* 28 */
-  long  size;             /* 32 */
-  long  modtime;          /* 36 */
-  long  checksum;         /* 40 */
-  long  rfrcsum;          /* 44 */
-  long  rfsize;           /* 48 */
-  long  cretime;          /* 52 */
-  long  rfcsum;           /* 56 */
-  long  nrecvd;           /* 60 */
-  long  recvcsum;         /* 64 */
-  char  idstring[32];     /* 68 */
-  char  flags;            /* 100 */
-  char  lnameoffset;      /* 101 */
-  char  lsizeoffset;      /* 102 */
-  char  dummy[69];        /* 103 */
-  char  macfileinfo[16];  /* 172 */
-  short nencode;          /* 188 */
-  short nlanguage;        /* 190 */
-  char  name[64];         /* 192 */
-                          /* 256 */
-};
-
-struct aim_filetransfer_priv {
-  char sn[MAXSNLEN];
-  char cookie[8];
-  char ip[30];
-  int state;
-  struct aim_fileheader_t fh;
-};
-
-#define AIM_COOKIETYPE_UNKNOWN  0x00
-#define AIM_COOKIETYPE_ICBM     0x01
-#define AIM_COOKIETYPE_ADS      0x02
-#define AIM_COOKIETYPE_BOS      0x03
-#define AIM_COOKIETYPE_IM       0x04
-#define AIM_COOKIETYPE_CHAT     0x05
-#define AIM_COOKIETYPE_CHATNAV  0x06
-/* we'll move OFT up a bit to give breathing room.  not like it really
- * matters. */
-#define AIM_COOKIETYPE_OFTIM    0x10
-#define AIM_COOKIETYPE_OFTGET   0x11
-#define AIM_COOKIETYPE_OFTSEND  0x12
-#define AIM_COOKIETYPE_OFTVOICE 0x13
-#define AIM_COOKIETYPE_OFTIMAGE 0x14
-#define AIM_COOKIETYPE_OFTICON  0x15
-
-faim_internal int aim_cachecookie(struct aim_session_t *sess, struct aim_msgcookie_t *cookie);
-faim_export int aim_purgecookies(struct aim_session_t *sess, int maxage);
-faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type);
-faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *, int, void *);
-faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *, unsigned char *, int);
-faim_internal int aim_msgcookie_gettype(int reqclass);
-
-faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur);
-
-#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
-#define AIM_TRANSFER_DENY_DECLINE 0x0001
-#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
-faim_export unsigned long aim_denytransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sender, char *cookie, unsigned short code);
-faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn,char *cookie,char *ip, FILE *file, unsigned short rendid);
-
-
-faim_export unsigned long aim_getinfo(struct aim_session_t *, struct aim_conn_t *, const char *, unsigned short);
-faim_internal int aim_extractuserinfo(u_char *, struct aim_userinfo_s *);
-faim_internal int aim_parse_userinfo_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_parse_oncoming_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_parse_offgoing_middle(struct aim_session_t *, struct command_rx_struct *);
-faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info);
-faim_export int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info);
-faim_export int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
-faim_internal int aim_parse_locateerr(struct aim_session_t *sess, struct command_rx_struct *command);
-
-/* aim_auth.c */
-faim_export int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *);
-faim_export u_long aim_auth_clientready(struct aim_session_t *, struct aim_conn_t *);
-faim_export unsigned long aim_auth_changepasswd(struct aim_session_t *, struct aim_conn_t *, char *, char *);
-
-/* aim_buddylist.c */
-faim_export unsigned long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_export unsigned long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
-faim_internal int aim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
-
-/* aim_search.c */
-faim_export u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *);
-
-
-struct aim_chat_roominfo {
-  u_short exchange;
-  char *name;
-  u_short instance;
-};
-faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo);
-faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_internal int aim_chat_parse_joined(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_internal int aim_chat_parse_leave(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg);
-faim_export unsigned long aim_chat_join(struct aim_session_t *sess, struct aim_conn_t *conn, u_short exchange, const char *roomname);
-faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname);
-faim_export char *aim_chat_getname(struct aim_conn_t *conn);
-faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *, char *name);
-
-faim_export unsigned long aim_chatnav_reqrights(struct aim_session_t *sess, struct aim_conn_t *conn);
-faim_export unsigned long aim_chatnav_clientready(struct aim_session_t *sess, struct aim_conn_t *conn);
-
-faim_export unsigned long aim_chat_invite(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn, char *msg, u_short exchange, char *roomname, u_short instance);
-
-struct aim_chat_exchangeinfo {
-  u_short number;
-  char *name;
-  char *charset1;
-  char *lang1;
-  char *charset2;
-  char *lang2;
-};
-faim_internal int aim_chatnav_parse_info(struct aim_session_t *sess, struct command_rx_struct *command);
-faim_export u_long aim_chatnav_createroom(struct aim_session_t *sess, struct aim_conn_t *conn, char *name, u_short exchange);
-faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name);
-
-/* aim_util.c */
-#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);
-u_long aimutil_get32(u_char *);
-#endif
-
-faim_export int aimutil_putstr(u_char *, const char *, int);
-faim_export int aimutil_tokslen(char *toSearch, int index, char dl);
-faim_export int aimutil_itemcnt(char *toSearch, char dl);
-faim_export char *aimutil_itemidx(char *toSearch, int index, char dl);
-
-faim_export int aim_snlen(const char *sn);
-faim_export int aim_sncmp(const char *sn1, const char *sn2);
-
-/* for libc's that dont have it */
-faim_export char *aim_strsep(char **pp, const char *delim);
-
-/* aim_meta.c */
-faim_export char *aim_getbuilddate(void);
-faim_export char *aim_getbuildtime(void);
-faim_export char *aim_getbuildstring(void);
-faim_internal void faimdprintf(int dlevel, const char *format, ...);
-
-#endif /* __AIM_H__ */
-
--- a/libfaim/faim/aim_cbtypes.h	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-/*
- * 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_OFT 0xfffe /* OFT/Rvous */
-#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_RIGHTSQUERY 0x0002
-#define AIM_CB_BOS_RIGHTS 0x0003
-#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
-
-/*
- * OFT Services
- *
- * See non-SNAC note below.
- */
-#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
-#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
-#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
-#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
-#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
-
-#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
-#define AIM_CB_OFT_GETFILEFILEREQ 0x0007    /* recieved file request */
-#define AIM_CB_OFT_GETFILEFILESEND 0x0008   /* recieved file request confirm -- send data */
-#define AIM_CB_OFT_GETFILECOMPLETE 0x0009   /* recieved file send complete*/
-#define AIM_CB_OFT_GETFILEINITIATE 0x000a   /* request for file get acknowledge */
-#define AIM_CB_OFT_GETFILEDISCONNECT 0x000b   /* OFT connection disconnected.*/
-
-#define AIM_CB_OFT_SENDFILEDISCONNECT 0x000c   /* OFT connection disconnected.*/
-
-
-
-/*
- * 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_CONNERR 0x0003
-#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
-#define AIM_CB_SPECIAL_FLAPVER 0x0005
-#define AIM_CB_SPECIAL_DEBUGCONN_CONNECT 0xe001
-#define AIM_CB_SPECIAL_UNKNOWN 0xffff
-#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
-
-
-#endif/*__AIM_CBTYPES_H__ */
--- a/libfaim/faim/faimconfig.h	Sun Mar 04 22:37:18 2001 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/*
- *  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__
-
-/* 
- * 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
-
-/*
- * 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
-
-/*
- * 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
-
-/*
- * 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"
-#define FAIM_LOGIN_PORT 5190
-
-/*
- * The integer extraction/copying functions in aim_util.c have
- * both a function version and a macro version.  The macro 
- * version is suggested.  Since the function version is more
- * readable, I leave both around for reference.
- *
- * Default: defined.
- */
-#define AIMUTIL_USEMACROS
-
-/*
- * Select whether or not to use POSIX thread functionality.
- * 
- * We don't actually use threads, but we do use the POSIX mutex
- * in order to maintain thread safety.  You can use the fake locking
- * if you really don't like pthreads or you don't have it.
- *
- * Default: defined on Linux, otherwise use fake locks.
- */
-#ifdef __linux__
-#define FAIM_USEPTHREADS
-#else
-#define FAIM_USEFAKELOCKS
-#endif
-
-/*
- * Size of the SNAC caching hash.
- *
- * Default: 16
- *
- */
-#define FAIM_SNAC_HASH_SIZE 16
-
-/*
- * If building on Win32,define WIN32_STATIC if you don't want
- * to compile libfaim as a DLL (and instead link it right into
- * your app).
- */
-#define WIN32_STATIC
-
-#endif /* __FAIMCONFIG_H__ */
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/faimconfig.h	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,94 @@
+/*
+ *  faimconfig.h
+ *
+ * Contains various compile-time options that apply _only_ to libfaim.
+ *
+ */
+
+#ifndef __FAIMCONFIG_H__
+#define __FAIMCONFIG_H__
+
+/*
+ * 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
+
+/*
+ * 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"
+#define FAIM_LOGIN_PORT 5190
+
+/*
+ * The integer extraction/copying functions in aim_util.c have
+ * both a function version and a macro version.  The macro 
+ * version is suggested.  Since the function version is more
+ * readable, I leave both around for reference.
+ *
+ * Default: defined.
+ */
+#define AIMUTIL_USEMACROS
+
+/*
+ * What type of synchronisation to use.
+ * 
+ * We don't actually use threads, but can use the POSIX mutex
+ * in order to maintain thread safety.  You can use the fake locking
+ * if you really don't like pthreads (which I don't) or if you don't
+ * have it.
+ *
+ *   USEPTHREADS - Use POSIX mutecies
+ *   USEFAKELOCKS - Use little stub spinners to help find locking bugs
+ *   USENOPLOCKS - No-op out all synchro calls at compile time
+ * 
+ * Default: use noplocks by default.
+ *
+ * !!!NOTE: Even with USEPTHREADS turned on, libfaim is not fully thread
+ *          safe.  It will still take some effort to add locking calls to
+ *          the places that need them.  In fact, this feature in general
+ *          is in danger of being officially deprecated and removed from 
+ *          the code.
+ *
+ */
+#undef FAIM_USEPTHREADS
+#undef FAIM_USEFAKELOCKS
+#define FAIM_USENOPLOCKS
+
+/*
+ * Size of the SNAC caching hash.
+ *
+ * Default: 16
+ *
+ */
+#define FAIM_SNAC_HASH_SIZE 16
+
+/*
+ * If building on Win32,define WIN32_STATIC if you don't want
+ * to compile libfaim as a DLL (and instead link it right into
+ * your app).
+ */
+#define WIN32_STATIC
+
+#endif /* __FAIMCONFIG_H__ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/ft.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,1898 @@
+/*
+ * File transfer (OFT) and DirectIM (ODC).
+ * (OSCAR File Transfer, Oscar Direct Connect(ion?)
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/utsname.h> /* for aim_directim_initiate */
+
+#include <arpa/inet.h> /* for inet_ntoa */
+
+#endif
+
+/* TODO: 
+   o look for memory leaks.. there's going to be shitloads, i'm sure. 
+*/
+
+static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr);
+ 
+/**
+ * aim_handlerendconnect - call this to accept OFT connections and set up the requisite structures
+ * @sess: the session
+ * @cur: the conn the incoming connection is on
+ *
+ * call this when you get an outstanding read on a conn with subtype
+ * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current
+ * &aim_conn_t and tweak things as appropriate. the new conn and the
+ * listener conn are both returned to the client in the
+ * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback.
+ */
+faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur)
+{ 
+  int acceptfd = 0;
+  rxcallback_t userfunc;
+  struct sockaddr cliaddr;
+  socklen_t clilen = sizeof(cliaddr);
+  int ret = 0;
+  struct aim_conn_t *newconn;
+
+  if ( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
+    return -1;
+  if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
+    close(acceptfd);
+    aim_conn_close(cur);
+    return -1;
+  } 
+
+  /* safe? maybe cur->priv should be NULLed after this. --mid */
+
+  /* That would be bad. very bad. we want cur->priv->sn to make it up
+     to the client-level for conn management and such. even though
+     that is abusing the interface --jbm */
+
+  if (!(newconn = aim_cloneconn(sess, cur))) {
+    close(acceptfd);
+    aim_conn_close(cur);
+    return -1;
+  }
+
+  newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
+  newconn->fd = acceptfd;
+
+  switch(newconn->subtype) {
+  case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { 
+    struct aim_directim_priv *priv;
+
+    priv = cur->priv;
+
+    newconn->priv = cur->priv;
+
+    cur->priv = NULL;
+
+    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", 
+	     inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), 
+	     ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+    if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
+      ret = userfunc(sess, NULL, newconn, cur);
+
+    break;
+  }
+  case AIM_CONN_SUBTYPE_OFT_GETFILE: { 
+    struct aim_filetransfer_priv *priv;
+
+
+    newconn->priv = cur->priv;
+    cur->priv = NULL;
+    priv = (struct aim_filetransfer_priv *)newconn->priv;
+
+    snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+    if ( (userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
+      ret = userfunc(sess, NULL, newconn, cur);
+
+    break;
+  }
+  default: { 
+    faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous(??!) Closing conn.\n");
+    aim_conn_close(newconn);
+    break;
+  }
+  }
+
+  return ret;
+}
+ 
+/**
+ * aim_send_im_direct - send IM client-to-client over established connection
+ * @sess: session to conn
+ * @conn: directim connection
+ * @msg: null-terminated string to send; if this is NULL, it will send a "typing" notice. 
+ *
+ * Call this just like you would aim_send_im, to send a directim. You
+ * _must_ have previously established the directim connection.
+ */
+faim_export int aim_send_im_direct(struct aim_session_t *sess, struct aim_conn_t *conn, char *msg)
+{
+  struct command_tx_struct *newpacket;
+  struct aim_directim_priv *priv = NULL;
+  int i;
+ 
+  if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv) { 
+    faimdprintf(sess, 2,"faim: directim: invalid arguments\n");
+    return -1;
+  }
+
+  priv = (struct aim_directim_priv *)conn->priv;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, strlen(msg)))) { 
+    faimdprintf(sess, 2,"faim: directim: tx_new failed\n");
+    return -1;
+  } 
+
+  newpacket->lock = 1;
+
+  /* if msg is non-null, we'resending an IM, else a "typing" notice */
+  if (msg) { 
+    if (strlen(msg) >= MAXMSGLEN)
+      return -1;
+    newpacket->hdr.oft.hdr2len = 0x54;
+    if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { 
+      newpacket->lock = 0;
+      aim_tx_destroy(newpacket);
+      return -1;
+    } 
+  } else { 
+    newpacket->hdr.oft.hdr2len = 0x44;
+    if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) { 
+      newpacket->lock = 0;
+      aim_tx_destroy(newpacket);
+      return -1;
+    } 
+  } 
+
+  memcpy(newpacket->hdr.oft.magic, "ODC2", 4);
+  newpacket->data = NULL;
+
+  i = 0;
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg));
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+
+  /* flags -- 0x000e for "typing", 0x0000 for message */
+  if (msg)
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  else 
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000e);
+
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
+  i = 52;
+
+  i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+  i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+
+  /* end of hdr2 */
+
+  if (msg) { 
+    /* values grabbed from a dump */
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
+    i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
+    if(!(newpacket->data = strdup(msg)))
+      return -1;
+  } 
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+  return 0;
+} 
+
+/* XXX: give the client author the responsibility of setting up a
+ * listener, then we no longer have a libfaim problem with broken
+ * solaris *innocent smile* -jbm */
+
+/**
+ * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one)
+ * @destsn: the SN to connect to.
+ *
+ */
+faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess, 
+						     struct aim_conn_t *conn,
+						     struct aim_directim_priv *priv, 
+						     char *destsn)
+{ 
+
+  struct command_tx_struct *newpacket;
+  struct aim_conn_t *newconn;
+  struct aim_msgcookie_t *cookie;
+  int curbyte, i, listenfd;
+  short port = 4443;
+  struct hostent *hptr;
+  char localhost[129];
+  unsigned char cap[16];
+  char d[4]; /* IPv6 is a bit bigger... */
+
+  /* XXX: TLVlist-ize this */
+ 
+  /* Open our socket */
+
+  if ( (listenfd = aim_listenestablish(port)) == -1)
+    return NULL;
+
+  /* get our local IP */
+  /* XXX if available, use getaddrinfo() */
+  /* XXX allow client to specify which IP to use for multihomed boxes */
+  if (gethostname(localhost, 128) < 0)
+    return NULL;
+  if ( (hptr = gethostbyname(localhost)) == NULL)
+    return NULL;
+  memcpy(&d, hptr->h_addr_list[0], 4);
+
+  aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
+
+  /* create the OSCAR packet */
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x32)))
+    return NULL;
+  newpacket->lock = 1;
+
+  curbyte = 0;
+  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /* Generate a random message cookie */
+  /* This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
+  for (i=0; i<7; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20));
+
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* grab all the data for cookie caching */
+  cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
+  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+  cookie->type = AIM_COOKIETYPE_OFTIM;
+  priv = cookie->data;
+
+  if (!priv)
+    priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
+ 
+  memcpy(priv->cookie, cookie, 8);
+  memcpy(priv->sn, destsn, sizeof(priv->sn));
+  cookie->data = priv;
+  aim_cachecookie(sess, cookie);
+
+  /* Channel ID */
+  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+
+  /* Destination SN (prepended with byte length)*/
+  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /* enTLV start */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
+
+  /* Flag data / ICBM Parameters  */
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* Cookie  */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
+
+  /*Capability String  */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+
+  /* 000a/0002 : 0001  */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+  /* 0003/0004: IP address  */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+  for(i = 0;i < 4; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
+ 
+  /* 0005/0002: Port */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, port);
+
+  /* 000f/0000: ?? */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+
+  /* XXX switch to aim_cloneconn()? */
+  if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL)))
+    return NULL;
+
+  newconn->fd = listenfd;
+  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+  newconn->priv = priv;
+  newconn->lastactivity = time(NULL);
+
+  faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
+
+  return newconn;
+} 
+
+/**
+ * unsigned int aim_oft_listener_clean - close up old listeners
+ * @sess: session to clean up in
+ * @age: maximum age in seconds 
+ *
+ * returns number closed, -1 on error.
+ */
+faim_export unsigned int aim_oft_listener_clean(struct aim_session_t *sess, time_t age) 
+{ 
+  struct aim_conn_t *cur;
+  time_t now;
+  unsigned int hit = 0;
+  
+  if (!sess)
+    return -1;
+  now = time(NULL);
+  faim_mutex_lock(&sess->connlistlock);
+  for(cur = sess->connlist;cur; cur = cur->next)
+    if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { 
+      faim_mutex_lock(&cur->active);
+      if (cur->lastactivity < (now - age) ) { 
+	faim_mutex_unlock(&cur->active);
+	aim_conn_close(cur);
+	hit++;
+      } else 
+	faim_mutex_unlock(&cur->active);
+    } 
+  faim_mutex_unlock(&sess->connlistlock);
+  return hit;
+} 
+
+/**
+ * aim_directim_connect - connect to buddy for directim
+ * @sess: the session to append the conn to,
+ * @conn: the BOS connection,
+ * @priv: the filled-in priv data structure for the connection 
+ *
+ * returns conn if connected, %NULL on error
+ */
+faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv)
+{ 
+  struct aim_conn_t *newconn = NULL;
+
+  if (!sess || !conn || !priv)
+    return NULL;
+  
+  /* XXX verify that non-blocking connects actually work */
+  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip);
+  if (!newconn || (newconn->fd == -1)) { 
+    faimdprintf(sess, 2, "could not connect to %s\n", priv->ip);
+    perror("aim_newconn");
+    return newconn;
+  }
+
+  newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+  newconn->priv = priv;
+  faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+
+  return newconn;
+} 
+
+/**
+ * aim_directim_getconn - find a directim conn for buddy name
+ * @sess: your session,
+ * @name: the name to get,  
+ *
+ * returns conn for directim with name, %NULL if none found. 
+ *
+ */
+faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
+{
+  struct aim_conn_t *cur;
+  struct aim_directim_priv *priv;
+
+  if (!sess || !name)
+    return NULL;
+
+  faim_mutex_lock(&sess->connlistlock);
+
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+      continue;
+    priv = cur->priv;
+    if (aim_sncmp(priv->sn, name) == 0)
+      break;
+  } faim_mutex_unlock(&sess->connlistlock);
+  return cur;
+} 
+
+/**
+ * aim_accepttransfer - accept a file transfer request
+ * @sess: the session,
+ * @conn: the BOS conn for the CAP reply
+ * @sn: the screenname to send it to,
+ * @cookie: the cookie used
+ * @ip: the ip to connect to
+ * @listingfiles: number of files to share
+ * @listingtotsize: total size of shared files
+ * @listingsize: length of the listing file(buffer)
+ * @listingchecksum: checksum of the listing
+ * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)  
+ *
+ * Returns new connection or %NULL on error.
+ */
+faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess, 
+						  struct aim_conn_t *conn, 
+						  char *sn, char *cookie, 
+						  char *ip, 
+						  unsigned short listingfiles, 
+						  unsigned short listingtotsize, 
+						  unsigned short listingsize, 
+						  unsigned int listingchecksum, 
+						  unsigned short rendid)
+{ 
+  struct command_tx_struct *newpacket, *newoft;
+  struct aim_conn_t *newconn;
+  struct aim_fileheader_t *fh;
+  struct aim_filetransfer_priv *priv;
+  struct aim_msgcookie_t *cachedcook;
+  int curbyte, i;
+
+  if (!sess || !conn || !sn || !cookie || !ip) {
+    return NULL;
+  }
+
+  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
+
+  if (!newconn || (newconn->fd == -1)) {
+    perror("aim_newconn");
+    faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
+    return newconn;
+  } else {
+    priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
+
+    memcpy(priv->cookie, cookie, 8);
+    priv->state = 0;
+    strncpy(priv->sn, sn, MAXSNLEN);
+    strncpy(priv->ip, ip, sizeof(priv->ip));
+    newconn->priv = (void *)priv;
+
+    faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+  }
+
+  if (rendid == AIM_CAPS_GETFILE)  {
+    newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+
+      faimdprintf(sess, 2, "faim: getfile request accept\n");
+
+      if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { 
+	faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+	/* XXX: conn leak here */
+	return NULL;
+      } 
+
+      newoft->lock = 1;
+      memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+      newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+      if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) {
+	/* XXX: conn leak here */
+	perror("calloc");
+	return NULL;
+      }
+
+      fh->encrypt = 0x0000;
+      fh->compress = 0x0000;
+      fh->totfiles = listingfiles;
+      fh->filesleft = listingfiles;      /* is this right -- total parts and parts left?*/
+      fh->totparts = 0x0001;
+      fh->partsleft = 0x0001;
+      fh->totsize = listingtotsize;
+      fh->size = listingsize;      /* ls -l listing.txt */
+      fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+      fh->checksum = listingchecksum;
+      fh->rfcsum = 0x00000000;
+      fh->rfsize = 0x00000000;
+      fh->cretime = 0x00000000;
+      fh->rfcsum = 0x00000000;
+      fh->nrecvd = 0x00000000;
+      fh->recvcsum = 0x00000000;
+      memset(fh->idstring, 0, sizeof(fh->idstring));
+      memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+      fh->flags = 0x02;
+      fh->lnameoffset = 0x1a;
+      fh->lsizeoffset = 0x10;
+      memset(fh->dummy, 0, sizeof(fh->dummy));
+      memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+      /* we need to figure out these encodings for filenames */
+      fh->nencode = 0x0000;
+      fh->nlanguage = 0x0000;
+      memset(fh->name, 0, sizeof(fh->name));
+      memcpy(fh->name, "listing.txt", sizeof(fh->name));
+
+      if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
+	newoft->lock = 0;
+	aim_tx_destroy(newoft);
+	/* XXX: conn leak */
+	perror("calloc (1)");
+	return NULL;
+      } 
+
+      memcpy(fh->bcookie, cookie, 8);
+
+      if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
+	faimdprintf(sess, 1, "eek, bh fail!\n");
+
+      newoft->lock = 0;
+      aim_tx_enqueue(sess, newoft);
+   
+      if (!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) { 
+	faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
+	/* XXX: more cleanup, conn leak */
+	perror("calloc (2)");
+	return NULL;
+      }
+
+      memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t));
+      memcpy(cachedcook->cookie, cookie, 8);
+
+      cachedcook->type = AIM_COOKIETYPE_OFTGET;
+      cachedcook->data = (void *)priv;
+
+      if (aim_cachecookie(sess, cachedcook) == -1)
+	faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
+
+      free(fh);     
+ 
+      /* OSCAR CAP accept packet */
+   
+      if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
+	return NULL;
+      }
+  } else {
+    return NULL;
+  }
+  
+  newpacket->lock = 1;
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  for (i = 0; i < 8; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept*/);
+
+  for (i = 0;i < 8; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+  curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid);
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return newconn;
+}
+
+/**
+ * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE*
+ *  @file is an opened listing file
+ * 
+ * returns a pointer to the filled-in fileheader_t
+ *
+ * Currently omits checksum. we'll fix this when AOL breaks us, i
+ * guess.
+ *
+ */
+
+faim_export struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess, FILE *file) 
+{
+  struct aim_fileheader_t *fh;
+  u_long totsize = 0, size = 0, checksum = 0xffff0000;
+  short totfiles = 0;
+  char *linebuf, sizebuf[9];
+  
+  int linelength = 1024;
+
+  /* XXX: if we have a line longer than 1024chars, God help us. */
+  if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
+    faimdprintf(sess, 2, "linebuf calloc failed\n");
+    return NULL;
+  }  
+
+  if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
+    perror("getlisting END1 fseek:");
+    faimdprintf(sess, 2, "getlising fseek END1 error\n");
+  }
+
+  if ((size = ftell(file)) == -1) {
+    perror("getlisting END1 getpos:");
+    faimdprintf(sess, 2, "getlising getpos END1 error\n");
+  }
+
+  if (fseek(file, 0, SEEK_SET) != 0) {
+    perror("getlesting fseek(SET):");
+    faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n");
+  }
+
+  memset(linebuf, 0, linelength);
+
+  size = 0;
+
+  while(fgets(linebuf,  linelength, file)) {
+    totfiles++;
+    memset(sizebuf, 0, 9);
+
+    size += strlen(linebuf);
+    
+    if (strlen(linebuf) < 23) {
+      faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf);
+      continue;
+    }
+    if (linebuf[strlen(linebuf)-1] != '\n') {
+      faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
+    }
+
+    memcpy(sizebuf, linebuf+17, 8);
+
+    totsize += strtol(sizebuf, NULL, 10);
+    memset(linebuf, 0, linelength);
+  }   
+
+  if (fseek(file, 0, SEEK_SET) == -1) {
+    perror("getlisting END2 fseek:");
+    faimdprintf(sess, 2, "getlising fseek END2 error\n");
+  }  
+
+  free(linebuf);
+
+  /* we're going to ignore checksumming the data for now -- that
+   * requires walking the whole listing.txt. it should probably be
+   * done at register time and cached, but, eh. */  
+
+  if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
+    return NULL;
+
+  fh->encrypt     = 0x0000;
+  fh->compress    = 0x0000; 
+  fh->totfiles    = totfiles;
+  fh->filesleft   = totfiles; /* is this right ?*/
+  fh->totparts    = 0x0001;
+  fh->partsleft   = 0x0001;
+  fh->totsize     = totsize;
+  fh->size        = size; /* ls -l listing.txt */
+  fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
+  fh->checksum    = checksum; /* XXX: checksum ! */
+  fh->rfcsum      = 0x00000000;
+  fh->rfsize      = 0x00000000;
+  fh->cretime     = 0x00000000;
+  fh->rfcsum      = 0x00000000;
+  fh->nrecvd      = 0x00000000;
+  fh->recvcsum    = 0x00000000;
+
+  /*  memset(fh->idstring, 0, sizeof(fh->idstring)); */
+  memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+  memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
+
+  fh->flags       = 0x02;
+  fh->lnameoffset = 0x1a;
+  fh->lsizeoffset = 0x10;
+
+  /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
+  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+  fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
+  fh->nlanguage   = 0x0000;
+
+  /*  memset(fh->name, 0, sizeof(fh->name)); */
+  memcpy(fh->name, "listing.txt", sizeof(fh->name));
+  memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
+
+  faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
+  return fh;
+}
+
+/**
+ * aim_listenestablish - create a listening socket on a port.
+ * @portnum: the port number to bind to.  
+ *
+ * you need to call accept() when it's connected. returns your fd 
+ *
+ */
+faim_export int aim_listenestablish(u_short portnum)
+{
+#if defined(__linux__)
+  /* XXX what other OS's support getaddrinfo? */
+  int listenfd;
+  const int on = 1;
+  struct addrinfo hints, *res, *ressave;
+  char serv[5];
+
+  snprintf(serv, sizeof(serv), "%d", portnum);
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_flags = AI_PASSIVE;
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
+    perror("getaddrinfo");
+    return -1;
+  } 
+  ressave = res;
+  do { 
+    listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);    
+    if (listenfd < 0)
+      continue;
+    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+    if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
+      break;
+    /* success */
+    close(listenfd);
+  } while ( (res = res->ai_next) );
+
+  if (!res)
+    return -1;
+  
+  if (listen(listenfd, 1024)!=0) { 
+    perror("listen");
+    return -1;
+  } 
+
+  freeaddrinfo(ressave);
+  return listenfd;
+#else
+  int listenfd;
+  const int on = 1;
+  struct sockaddr_in sockin;
+
+  if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+    perror("socket(listenfd)");
+    return -1;
+  }
+
+  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) {
+    perror("setsockopt(listenfd)");
+    close(listenfd);
+    return -1;
+  } 
+  
+  memset(&sockin, 0, sizeof(struct sockaddr_in));
+  sockin.sin_family = AF_INET;
+  sockin.sin_port = htons(portnum);
+
+  if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
+    perror("bind(listenfd)");
+    close(listenfd);
+    return -1;
+  }
+  if (listen(listenfd, 4) != 0) {
+    perror("listen(listenfd)");
+    close(listenfd);
+    return -1;
+  }
+  return listenfd;
+#endif
+} 
+
+/**
+ * aim_get_command_rendezvous - OFT equivalent of aim_get_command
+ * @sess: session to work on
+ * @conn: conn to pull data from 
+ *
+ * this reads and handles data from conn->fd. currently a little rough
+ * around the edges
+ */
+faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  unsigned char hdrbuf1[6];
+  unsigned char *hdr = NULL;
+  int hdrlen, hdrtype;
+  int flags = 0;
+  rxcallback_t userfunc = NULL;
+  
+  if (!sess || !conn || !conn->priv)
+    return -1;
+
+  memset(hdrbuf1, 0, sizeof(hdrbuf1));
+  faim_mutex_lock(&conn->active);
+  
+ /* gets locked down for the entirety */
+
+  if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE ) { 
+    struct aim_filetransfer_priv *ft;
+    ft = conn->priv;
+    if (ft->state == 2) {
+      /* waiting on listing data */
+      int ret = 0;
+      char *listing;
+      struct command_tx_struct *newoft;
+      if (!(listing = malloc(ft->fh.size))) {
+	faim_mutex_unlock(&conn->active);
+	return -1;
+      }
+
+     ft->state = 0;
+     if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)	
+       faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
+
+     if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
+       faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
+       faim_mutex_unlock(&conn->active);
+       free(listing);
+       aim_conn_close(conn);
+       return -1;
+     }
+
+     newoft->lock = 1;
+
+     memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+     newoft->hdr.oft.hdr2len = 0x100 - 8;
+     
+     /* Protocol BS - set nrecvd to size of listing, recvcsum to
+	listing checksum, flags to 0 */
+
+     ft->fh.nrecvd = ft->fh.size;
+     ft->fh.recvcsum = ft->fh.checksum;
+     ft->fh.flags = 0;
+     
+     if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+       newoft->lock = 0;
+       aim_tx_destroy(newoft);
+       free(listing);
+       faim_mutex_unlock(&conn->active);
+       return -1;
+     }
+     
+     if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
+       faimdprintf(sess, 2, "eek! bh fail listing\n");
+
+     /* send the 120b	*/
+     newoft->lock = 0;
+     aim_tx_enqueue(sess, newoft);
+     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
+       ret = userfunc(sess, NULL, conn, ft, listing);
+     
+     faim_mutex_unlock(&conn->active);
+     free(listing);
+     return ret;
+   }
+   if (ft->state == 3) { 
+     /* waiting on file data */
+     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) )  {
+       faim_mutex_unlock(&conn->active);
+       return userfunc(sess, NULL, conn, ft);
+     }
+     faim_mutex_unlock(&conn->active);
+     return 0;
+   }
+   if(ft->state == 4) {
+     if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) ) {
+       faim_mutex_unlock(&conn->active);
+       return userfunc(sess, NULL, conn);
+     }
+     faim_mutex_unlock(&conn->active);
+     aim_conn_close(conn);
+     return 0;
+   }	
+ }
+ 
+  if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) {
+    faimdprintf(sess, 2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", 
+	       conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[2],hdrbuf1[3],hdrbuf1[4],hdrbuf1[5],hdrlen);
+   faim_mutex_unlock(&conn->active);
+   if (hdrlen < 0)
+     perror("read");
+   else { /* disconnected */
+     char *screenname = NULL;
+     int ret;
+     struct aim_msgcookie_t *cook;
+
+     switch(conn->subtype) { 
+     case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { 
+       struct aim_directim_priv *priv = NULL;
+       if (!(priv = (struct aim_directim_priv *)conn->priv) )
+	 return -1;
+
+       screenname = strdup(priv->sn);
+
+       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM);
+       aim_cookie_free(sess, cook);
+       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) {
+	 aim_conn_close(conn);
+	 ret = userfunc(sess, NULL, conn, screenname);
+	 free(screenname);
+	 return ret;
+       }
+       break;
+     } 
+     case AIM_CONN_SUBTYPE_OFT_GETFILE: {
+       struct aim_filetransfer_priv *priv;
+       if (!(priv = (struct aim_filetransfer_priv *)conn->priv))
+	 return -1;
+       screenname = strdup(priv->sn);
+
+       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
+
+       aim_cookie_free(sess, cook);
+
+       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) {
+	 aim_conn_close(conn);
+	 ret =  userfunc(sess, NULL, conn, screenname);
+	 free(screenname);
+	 return ret;
+       }
+       break;
+     }
+     case AIM_CONN_SUBTYPE_OFT_SENDFILE: {
+       struct aim_filetransfer_priv *priv;
+       if (!(priv = (struct aim_filetransfer_priv *)conn->priv))
+	 return -1;
+
+       screenname = strdup(priv->sn);
+
+       cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
+       aim_cookie_free(sess, cook);
+       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) { 
+	 aim_conn_close(conn);
+	 ret = userfunc(sess, NULL, conn, screenname);
+	 free(screenname);
+	 return ret;
+       }
+       break;
+     } 
+     }
+
+     aim_conn_close(conn);
+     return -1;
+   }
+ }
+
+ hdrlen = aimutil_get16(hdrbuf1+4);
+ hdrlen -= 6;
+
+ if (!(hdr = malloc(hdrlen))) { 
+   faim_mutex_unlock(&conn->active);
+   return -1;
+ }
+
+ if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
+   perror("read");
+   faimdprintf(sess, 2,"faim: rend: read2 error on %d (%d)\n", conn->fd, hdrlen);
+   free(hdr);
+   faim_mutex_unlock(&conn->active);
+   aim_conn_close(conn);
+   return -1;
+   }
+ hdrtype = aimutil_get16(hdr);
+
+ switch (hdrtype) {
+ case 0x0001: {    /* directim */
+   int payloadlength = 0;
+   char *snptr = NULL;
+   struct aim_directim_priv *priv;
+   int i;
+
+   if (!(priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)))) {
+     faim_mutex_unlock(&conn->active);
+     free(hdr);
+     return -1;
+   }
+   
+   payloadlength = aimutil_get32(hdr+22);
+   flags = aimutil_get16(hdr+32);
+   snptr = (char *)hdr+38;
+   strncpy(priv->sn, snptr, MAXSNLEN);
+
+   faimdprintf(sess, 2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr);
+
+   free(hdr);
+   hdr = NULL;
+
+   if (flags == 0x000e) { 
+     faim_mutex_unlock(&conn->active);
+     if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) )
+       return userfunc(sess, NULL, snptr);
+   } else {
+
+     if ((flags == 0x0000) && payloadlength) { 
+       unsigned char *msg;
+
+       if (!(msg = calloc(1, payloadlength+1))) {
+	 faim_mutex_unlock(&conn->active);
+	 return -1;
+       }
+
+       if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
+	 perror("read");
+	 faimdprintf(sess, 2,"faim: rend: read3 error\n");
+	 free(msg);
+	 faim_mutex_unlock(&conn->active);
+	 aim_conn_close(conn);
+	 return -1;
+       }
+
+       faim_mutex_unlock(&conn->active);
+       msg[payloadlength] = 0x00;
+       faimdprintf(sess, 2, "faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg);
+
+       if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
+	 i = userfunc(sess, NULL, conn, snptr, msg);
+       else {
+	  faimdprintf(sess, 0, "directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg);
+	 i = 1;
+       }
+
+       free(msg);
+
+       return i;
+     }
+   }
+   break;
+ }
+ case 0x1108: { /* getfile listing.txt incoming tx->rx */
+   struct aim_filetransfer_priv *ft;
+   struct aim_fileheader_t *fh;
+   struct aim_msgcookie_t *cook;
+   struct command_tx_struct *newoft;
+
+   faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+     faim_mutex_unlock(&conn->active);
+     free(fh);
+     return -1;
+   }
+
+   ft = cook->data;
+
+   /* we're waaaaiiiting.. for listing.txt */
+   ft->state = 2;
+
+   memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+   free(fh);
+
+   if(aim_cachecookie(sess, cook) == -1) {
+     faimdprintf(sess, 1, "error caching cookie\n");
+     return -1;
+   }     
+
+   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
+     aim_conn_close(conn);
+     return -1;
+   }
+
+   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+   newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+   if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+     newoft->lock = 0;
+     aim_tx_destroy(newoft);
+     return -1;
+   }
+
+   if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+     newoft->lock = 0;
+     aim_tx_destroy(newoft);
+     return -1;
+   }
+
+   newoft->lock = 0;
+   aim_tx_enqueue(sess, newoft);
+   break;
+   
+ } 
+ case 0x1209: { /* get file listing ack rx->tx */
+   struct aim_filetransfer_priv *ft;
+   struct aim_fileheader_t *fh;
+   struct aim_msgcookie_t *cook;
+   int ret = 0;
+
+   if(!(fh = aim_oft_getfh(hdr))) {
+     perror("getfh");
+     free(hdr);
+     return -1;
+   }   
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
+     faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", 
+		 AIM_COOKIETYPE_OFTGET, fh->bcookie);
+
+   ft = cook->data;   
+
+   if (ft->fh.size != fh->size)
+     faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", 
+		 ft->fh.size, fh->size);
+   
+   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
+     ret = userfunc(sess, NULL, conn, fh);
+
+   faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
+
+   free(fh);
+
+   return ret;
+
+   break;
+ }
+ case 0x120b: {    /* getfile listing.txt rx confirm */
+   struct aim_filetransfer_priv *ft;
+   struct aim_msgcookie_t *cook;
+   struct aim_fileheader_t *fh;
+
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))     {
+     free(fh);
+     return -1;
+   }
+
+   free(fh);
+
+   ft = cook->data;
+
+   if (aim_cachecookie(sess, cook) == -1) {
+     return -1;
+   }
+
+   if((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
+     return userfunc(sess, NULL, conn);
+
+   break;
+ }
+ case 0x120c: { /* getfile file request */
+   struct aim_filetransfer_priv *ft;
+   struct aim_msgcookie_t *cook;
+   struct aim_fileheader_t *fh;
+   struct command_tx_struct *newoft;
+   int i = 0;
+
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+     faimdprintf(sess, 2, "no cookie in 120c\n");
+     return -1;
+   }
+
+   ft = cook->data;
+   memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+   free(fh);
+
+   aim_cachecookie(sess, cook);
+
+   faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
+
+   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
+     i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
+
+   if (i < 0)
+     return i;
+
+   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
+     faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+     return -1;
+   }
+
+   newoft->lock = 1;
+   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+   newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+   if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+     newoft->lock = 0;
+     aim_tx_destroy(newoft);
+     return -1;
+   } 
+
+   /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
+   ft->fh.nrecvd = 0;
+   ft->fh.recvcsum = 0;
+   ft->fh.flags = 0x20;
+
+   aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+   newoft->lock = 0;
+   aim_tx_enqueue(sess, newoft);
+
+   faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
+
+   return i;
+
+   break;
+ }
+ case 0x0101: {    /* getfile: sending data  */
+   struct aim_fileheader_t *fh;
+   struct aim_filetransfer_priv *ft;
+   struct aim_msgcookie_t *cook;
+   struct command_tx_struct *newoft;
+
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+     free(fh);
+     return -1;
+   }
+   free(fh);
+
+   ft = cook->data;
+
+   ft->state = 3;
+
+   if (aim_cachecookie(sess, cook) == -1) {
+     perror("aim_cachecookie");
+     return -1;
+   }
+
+   faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
+
+   if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+     aim_conn_close(conn);
+     faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+     return -1;
+   }
+
+   newoft->lock = 1;
+   memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+
+   newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+   if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+     newoft->lock = 0;
+     aim_tx_destroy(newoft);
+     return -1;
+   }
+
+   aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+   newoft->lock = 0;
+   aim_tx_enqueue(sess, newoft);
+
+   faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
+
+   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
+     return 1;
+
+   break;
+ }
+ case 0x0202: {    /* get file: ready to receive data */
+   struct aim_fileheader_t *fh;
+   struct aim_filetransfer_priv *ft;
+   struct aim_msgcookie_t *cook;
+   int ret = 1;
+
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+     free(fh);
+     return -1;
+   }
+   
+   ft = cook->data;
+
+   faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
+
+   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
+     ret = userfunc(sess, NULL, conn, fh);
+
+   free(fh);
+
+   return ret;
+   break;
+ }
+ case 0x0204: {    /* get file: finished. close it up */
+   int i;
+   struct aim_fileheader_t *fh;
+
+   fh = aim_oft_getfh(hdr);
+
+   free(hdr);
+   hdr = NULL;
+
+   faim_mutex_unlock(&conn->active);
+
+   faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
+
+   if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
+     i = userfunc(sess, NULL, conn, fh);
+   else 
+     i = 1;
+
+   if (conn)
+     aim_conn_close(conn);
+
+   free(fh);
+
+   return i;
+   break;
+ }
+ default: {
+   free(hdr);
+   hdr = NULL;
+   faimdprintf(sess, 2,"faim: OFT frame: uknown type %04x\n", hdrtype);
+   faim_mutex_unlock(&conn->active);
+   break;
+ } 
+ } /* switch */
+ if (hdr) {
+    faimdprintf(sess, 0, "hdr wasn't freed by a rendezvous switch case (hdrtype: %0x04x)!\n", hdrtype);
+   free(hdr);
+   hdr = NULL;
+ }
+ return 0;
+}
+ 
+/**
+ * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
+ * @hdr: buffer to extract header from  
+ *
+ * returns pointer to new struct on success; %NULL on error.  
+ *
+ */
+static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) 
+{
+  struct aim_fileheader_t *fh;
+  int i, j;
+  if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+    return NULL;
+  
+  /* [0] and [1] are the type. we can ignore those here. */
+  i = 2;
+  for(j = 0; j < 8; j++, i++)
+    fh->bcookie[j] = hdr[i];
+  fh->encrypt = aimutil_get16(hdr+i);
+  i += 2;
+  fh->compress = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totfiles = aimutil_get16(hdr+i);
+  i += 2;
+  fh->filesleft = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totparts = aimutil_get16(hdr+i);
+  i += 2;
+  fh->partsleft = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totsize = aimutil_get32(hdr+i);
+  i += 4;
+  fh->size = aimutil_get32(hdr+i);
+  i += 4;
+  fh->modtime = aimutil_get32(hdr+i);
+  i += 4;
+  fh->checksum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfrcsum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfsize = aimutil_get32(hdr+i);
+  i += 4;
+  fh->cretime = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfcsum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->nrecvd = aimutil_get32(hdr+i);
+  i += 4;
+  fh->recvcsum = aimutil_get32(hdr+i);
+  i += 4;
+  memcpy(fh->idstring, hdr+i, 32);
+  i += 32;
+  fh->flags = aimutil_get8(hdr+i);
+  i += 1;
+  fh->lnameoffset = aimutil_get8(hdr+i);
+  i += 1;
+  fh->lsizeoffset = aimutil_get8(hdr+i);
+  i += 1;
+  memcpy(fh->dummy, hdr+i, 69);
+  i += 69;
+  memcpy(fh->macfileinfo, hdr+i, 16);
+  i += 16;
+  fh->nencode = aimutil_get16(hdr+i);
+  i += 2;
+  fh->nlanguage = aimutil_get16(hdr+i);
+  i += 2;
+  memcpy(fh->name, hdr+i, 64);
+  i += 64;
+  return fh;
+} 
+
+/**
+ * aim_oft_checksum - calculate oft checksum of buffer
+ * @buffer: buffer of data to checksum
+ * @bufsize: size of buffer
+ * @checksum: pointer to integer to place result in (pointer!) 
+ *
+ *
+ * Note that checksum is a pointer. Checksum should be filled with
+ * 0xFFFF0000 for each new file; you can have this checksum chunks of
+ * files in series if you just call it repeatedly in a for(; ; ) loop
+ * and don't reset the checksum between each call. And you thought we
+ * didn't care about you and your pathetic client's meomry footprint
+ * ;^) 
+ *
+ *
+ * Also, it's been said that this is incorrect as currently
+ * written. You were warned.
+ */
+faim_export int aim_oft_checksum(struct aim_session_t *sess, char *buffer, int bufsize, int *checksum)
+{
+  short check0, check1;
+  int i;
+  check0 = ((*checksum & 0xFF000000) >> 16);
+  check1 = ((*checksum & 0x00ff0000) >> 16);
+  for(i = 0; i < bufsize; i++) {
+    if (i % 2) { /* use check1 -- second byte */
+      if ( (short)buffer[i] > check1 ) { /* wrapping */
+	check1 += 0x100;  /* this is a cheap way to wrap */
+
+	/* if we're wrapping, decrement the other one */
+	/* XXX: check this corner case */
+	if (check0 == 0) 
+	  check0 = 0x00ff;
+	else 
+	  check0--;
+      } 
+      check1 -= buffer[i];
+    } else { /* use check0 -- first byte  */
+      if ( (short)buffer[i] > check0 ) { /* wrapping */
+	check0 += 0x100;       /* this is a cheap way to wrap */
+  
+	/* if we're wrapping, decrement the other one */
+	/* XXX: check this corner case */
+	if (check1 == 0) 
+	  check1 = 0x00ff;
+	else 
+	  check1--;
+      } 
+      check0 -= buffer[i];
+    } 
+  }
+
+  if (check0 > 0xff || check1 > 0xff)  { 
+    /* they shouldn't be able to do this. error! */
+    faimdprintf(sess, 2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
+    return -1;
+  } 
+
+  /* grab just the lowest byte; this should be clean, but just in
+     case */
+  check0 &= 0xff;
+  check1 &= 0xff;
+
+  *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
+  return *checksum;
+} 
+
+/**
+ * aim_oft_buildheader - fills a buffer with network-order fh data
+ * @dest: buffer to fill -- pre-alloced
+ * @fh: fh to get data from  
+ *
+ * returns length written; -1 on error.
+ * DOES NOT DO BOUNDS CHECKING!
+ *
+ */
+faim_internal int aim_oft_buildheader(unsigned char *dest,struct aim_fileheader_t *fh) 
+{ 
+  int i, curbyte;
+  if (!dest || !fh)
+    return -1;
+  curbyte = 0;
+  for(i = 0; i < 8; i++) 
+    curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]);
+  curbyte += aimutil_put16(dest+curbyte, fh->encrypt);
+  curbyte += aimutil_put16(dest+curbyte, fh->compress);
+  curbyte += aimutil_put16(dest+curbyte, fh->totfiles);
+  curbyte += aimutil_put16(dest+curbyte, fh->filesleft);
+  curbyte += aimutil_put16(dest+curbyte, fh->totparts);
+  curbyte += aimutil_put16(dest+curbyte, fh->partsleft);
+  curbyte += aimutil_put32(dest+curbyte, fh->totsize);
+  curbyte += aimutil_put32(dest+curbyte, fh->size);
+  curbyte += aimutil_put32(dest+curbyte, fh->modtime);
+  curbyte += aimutil_put32(dest+curbyte, fh->checksum);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfsize);
+  curbyte += aimutil_put32(dest+curbyte, fh->cretime);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfcsum);
+  curbyte += aimutil_put32(dest+curbyte, fh->nrecvd);
+  curbyte += aimutil_put32(dest+curbyte, fh->recvcsum);
+  memcpy(dest+curbyte, fh->idstring, 32);
+  curbyte += 32;
+  curbyte += aimutil_put8(dest+curbyte, fh->flags);
+  curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset);
+  curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset);
+  memcpy(dest+curbyte, fh->dummy, 69);
+  curbyte += 69;
+  memcpy(dest+curbyte, fh->macfileinfo, 16);
+  curbyte += 16;
+  curbyte += aimutil_put16(dest+curbyte, fh->nencode);
+  curbyte += aimutil_put16(dest+curbyte, fh->nlanguage);
+  memset(dest+curbyte, 0x00, 64);
+  memcpy(dest+curbyte, fh->name, 64);
+
+  /* XXX: Filenames longer than 64B  */
+  curbyte += 64;
+  return curbyte;
+}
+
+
+/**
+ * aim_tx_destroy - free's tx_command_t's
+ * @command: the command to free  
+ *
+ * if command is locked, doesn't free.
+ * returns -1 on error (locked struct); 0 on success.  
+ *
+ */
+faim_internal int aim_tx_destroy(struct command_tx_struct *command){
+  if (command->lock)
+    return -1;
+  if (command->data)
+    free(command->data);
+  if (command->hdrtype == AIM_FRAMETYPE_OFT && command->hdr.oft.hdr2)
+    free(command->hdr.oft.hdr2);
+  free(command);
+  return 0;
+} 
+
+/**
+ * aim_getfile_intitiate - Request an OFT getfile session
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn is the SN to connect to.
+ * 
+ * returns a new &aim_conn_t on success, %NULL on error
+ */
+faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn)
+{ 
+  struct command_tx_struct *newpacket;
+  struct aim_conn_t *newconn;
+  struct aim_filetransfer_priv *priv;
+  struct aim_msgcookie_t *cookie;
+  int curbyte, i, listenfd;
+  short port = 4443;
+  struct hostent *hptr;
+  struct utsname myname;
+  char cap[16];
+  char d[4];
+ 
+  /* Open our socket */
+
+  if ( (listenfd = aim_listenestablish(port)) == -1)
+    return NULL;
+
+  /* get our local IP */
+
+  if (uname(&myname) < 0)
+    return NULL;
+  if ( (hptr = gethostbyname(myname.nodename)) == NULL)
+    return NULL;
+  memcpy(&d, hptr->h_addr_list[0], 4);
+
+  aim_putcap(cap, 16, AIM_CAPS_GETFILE);
+
+  /* create the OSCAR packet */
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42)))
+    return NULL;
+  newpacket->lock = 1;
+
+  /* lock struct */
+  curbyte = 0;
+  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /* XXX: check the cookie before commiting to using it */
+
+  /* Generate a random message cookie
+   * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
+  for (i=0; i<7; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10));
+
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* grab all the data for cookie caching. */
+ 
+  if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t))))
+    return NULL;
+  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+  cookie->type = AIM_COOKIETYPE_OFTGET;
+
+  if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv))))
+    return NULL;
+  memcpy(priv->cookie, cookie, 8);
+  memcpy(priv->sn, destsn, sizeof(priv->sn));
+  memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
+  priv->state = 1;
+
+  cookie->data = priv;
+
+  aim_cachecookie(sess, cookie);
+
+  /* Channel ID */
+  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+
+  /* Destination SN (prepended with byte length) */
+  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /* enTLV start */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042);
+
+  /* Flag data / ICBM Parameters? */
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* Cookie */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
+
+  /* Capability String */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+
+  /* 000a/0002 : 0001 */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+  /* 0003/0004: IP address */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+  for(i = 0; i < 4; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
+
+  /* already in network byte order  */
+ 
+  /* 0005/0002: Port */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, port);
+
+  /* 000f/0000: ?? */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /* 2711/000c: ?? */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
+  curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
+
+  for(i = 0; i < 0x000c - 4; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  /* allocate and set up our connection */
+
+  i = fcntl(listenfd, F_GETFL, 0);
+  fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
+  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
+
+  if (!newconn){ 
+    perror("aim_newconn");
+    return NULL;
+  }
+
+  newconn->fd = listenfd;
+  newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+  newconn->priv = priv;
+  faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
+
+  return newconn;
+}
+ 
+/**
+ * aim_oft_getfile_request - request a particular file over an established getfile connection
+ * @sess: your session
+ * @conn: the established OFT getfile connection
+ * @name: filename to request
+ * @size: size of the file 
+ *
+ *
+ * returns -1 on error, 0 on successful enqueuing
+ */
+faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size)
+{
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+  if (!sess || !conn || !conn->priv || !name)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  }
+
+  newoft->lock = 1;
+
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+  ft = (struct aim_filetransfer_priv *)conn->priv;
+  ft->fh.filesleft = 1;
+  ft->fh.totfiles = 1;
+  ft->fh.totparts = 1;
+  ft->fh.partsleft = 1;
+  ft->fh.totsize = size;
+  ft->fh.size = size;
+  ft->fh.checksum = 0;
+  memcpy(ft->fh.name, name, strlen(name));
+  memset(ft->fh.name+strlen(name), 0, 1);
+
+  if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+    newoft->lock = 0;
+    aim_tx_destroy(newoft);
+    return -1;
+  }
+
+  if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
+    newoft->lock = 0;
+    aim_tx_destroy(newoft);
+    return -1;
+  }
+
+  newoft->lock = 0;
+
+  aim_tx_enqueue(sess, newoft);
+  return 0;
+}
+ 
+/**
+ * aim_oft_getfile_ack - acknowledge a getfile download as complete
+ * @sess: your session
+ * @conn: the getfile conn to send the ack over 
+ *
+ * Call this function after you have read all the data in a particular
+ * filetransfer. Returns -1 on error, 0 on apparent success
+ *
+ */
+faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn) 
+{
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+
+  if (!sess || !conn || !conn->priv)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  } 
+
+  newoft->lock = 1;
+
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100-8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
+   newoft->lock = 0;
+   aim_tx_destroy(newoft);
+   return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+   newoft->lock = 0;
+   aim_tx_destroy(newoft);
+   return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+}
+ 
+/**
+ * aim_oft_getfile_end - end a getfile.
+ * @sess: your session
+ * @conn: the getfile connection 
+ *
+ * call this before you close the getfile connection if you're on the
+ * receiving/requesting end.
+ */
+faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+  
+  if (!sess || !conn || !conn->priv)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0204, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  }
+  
+  newoft->lock = 1;
+  
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100 - 8;
+  
+  if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+    newoft->lock = 0;
+    aim_tx_destroy(newoft);
+    return -1;
+  }
+  
+  ft = (struct aim_filetransfer_priv *)conn->priv;
+  ft->state = 4; /* no longer wanting data */
+  ft->fh.nrecvd = ft->fh.size;
+  ft->fh.recvcsum = ft->fh.checksum;
+  ft->fh.flags = 0x21;
+  
+  if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+    newoft->lock = 0;
+    aim_tx_destroy(newoft);
+    return -1;
+  }
+  
+  newoft->lock = 0;
+  aim_tx_enqueue(sess, newoft);
+  
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/im.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,958 @@
+/*
+ *  aim_im.c
+ *
+ *  The routines for sending/receiving Instant Messages.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/*
+ * Takes a msghdr (and a length) and returns a client type
+ * code.  Note that this is *only a guess* and has a low likelihood
+ * of actually being accurate.
+ *
+ * Its based on experimental data, with the help of Eric Warmenhoven
+ * who seems to have collected a wide variety of different AIM clients.
+ *
+ *
+ * Heres the current collection:
+ *  0501 0003 0101 0101 01       AOL Mobile Communicator, WinAIM 1.0.414
+ *  0501 0003 0101 0201 01       WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
+ *                                      4.3.2229, 4.4.2286
+ *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
+ *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
+ *                                      TOC client
+ */
+faim_export unsigned short aim_fingerprintclient(unsigned char *msghdr, int len)
+{
+  static const struct {
+    unsigned short clientid;
+    int len;
+    unsigned char data[10];
+  } fingerprints[] = {
+    /* AOL Mobile Communicator, WinAIM 1.0.414 */
+    { AIM_CLIENTTYPE_MC, 
+      9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01}},
+
+    /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
+    { AIM_CLIENTTYPE_WINAIM, 
+      9, {0x05, 0x01, 0x00, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01}},
+
+    /* WinAIM 4.1.2010, libfaim */
+    { AIM_CLIENTTYPE_WINAIM41,
+      10, {0x05, 0x01, 0x00, 0x04, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01}},
+
+    /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
+    { AIM_CLIENTTYPE_AOL_TOC,
+      7, {0x05, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01}},
+
+    { 0, 0}
+  };
+  int i;
+
+  if (!msghdr || (len <= 0))
+    return 0;
+
+  for (i = 0; fingerprints[i].len; i++) {
+    if (fingerprints[i].len != len)
+      continue;
+    if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
+      return fingerprints[i].clientid;
+  }
+
+  return AIM_CLIENTTYPE_UNKNOWN;
+}
+
+/*
+ * Send an ICBM (instant message).  
+ *
+ *
+ * Possible flags:
+ *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
+ *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
+ *                        when the message is received (of type 0x0004/0x000c)
+ *
+ */
+faim_export unsigned long aim_send_im(struct aim_session_t *sess,
+				      struct aim_conn_t *conn, 
+				      char *destsn, u_int flags, char *msg)
+{   
+
+  int curbyte,i;
+  struct command_tx_struct *newpacket;
+  
+  if (strlen(msg) >= MAXMSGLEN)
+    return -1;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, strlen(msg)+256)))
+    return -1;
+
+  newpacket->lock = 1; /* lock struct */
+
+  curbyte  = 0;
+  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) rand());
+
+  /*
+   * 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) + 0x10);
+
+  /*
+   * Flag data / ICBM Parameters?
+   *
+   * I don't know what these are...
+   *
+   */
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x05);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
+
+  /* number of bytes to follow */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x01);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x02);
+
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0101);
+
+  /* 
+   * 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);
+
+  /*
+   * Message.  Not terminated.
+   */
+  curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg));
+
+  /*
+   * 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);
+
+  aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, destsn, strlen(destsn)+1);
+  aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
+
+  return sess->snac_nextid;
+}
+
+faim_internal int aim_parse_outgoing_im_middle(struct aim_session_t *sess,
+					       struct command_rx_struct *command)
+{
+  unsigned int i = 0, z;
+  rxcallback_t userfunc = NULL;
+  unsigned char cookie[8];
+  int channel;
+  struct aim_tlvlist_t *tlvlist;
+  char sn[MAXSNLEN];
+  unsigned short icbmflags = 0;
+  unsigned char flag1 = 0, flag2 = 0;
+  unsigned char *msgblock = NULL, *msg = NULL;
+
+  i = 10;
+  
+  /* ICBM Cookie. */
+  for (z=0; z<8; z++,i++)
+    cookie[z] = command->data[i];
+
+  /* Channel ID */
+  channel = aimutil_get16(command->data+i);
+  i += 2;
+
+  if (channel != 0x01) {
+    faimdprintf(sess, 0, "icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
+    return 1;
+  }
+
+  strncpy(sn, (char *) command->data+i+1, (int) *(command->data+i));
+  i += 1 + (int) *(command->data+i);
+
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+
+  if (aim_gettlv(tlvlist, 0x0003, 1))
+    icbmflags |= AIM_IMFLAGS_ACK;
+  if (aim_gettlv(tlvlist, 0x0004, 1))
+    icbmflags |= AIM_IMFLAGS_AWAY;
+
+  if (aim_gettlv(tlvlist, 0x0002, 1)) {
+    int j = 0;
+
+    msgblock = (unsigned char *)aim_gettlv_str(tlvlist, 0x0002, 1);
+
+    /* no, this really is correct.  I'm not high or anything either. */
+    j += 2;
+    j += 2 + aimutil_get16(msgblock+j);
+    j += 2;
+    
+    j += 2; /* final block length */
+
+    flag1 = aimutil_get16(msgblock);
+    j += 2;
+    flag2 = aimutil_get16(msgblock);
+    j += 2;
+    
+    msg = msgblock+j;
+  }
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0006)) || (i = 0))
+    i = userfunc(sess, command, channel, sn, msg, icbmflags, flag1, flag2);
+  
+  if (msgblock)
+    free(msgblock);
+  aim_freetlvchain(&tlvlist);
+
+  return 0;
+}
+
+/*
+ * 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...
+ *
+ */
+faim_internal int aim_parse_incoming_im_middle(struct aim_session_t *sess,
+					       struct command_rx_struct *command)
+{
+  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;
+
+  memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+ 
+  i = 10; /* Skip SNAC header */
+
+  /*
+   * Read ICBM Cookie.  And throw away.
+   */
+  for (z=0; z<8; z++,i++)
+    cookie[z] = command->data[i];
+  
+  /*
+   * 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;
+  
+  /*
+   *
+   */
+  if ((channel != 0x01) && (channel != 0x02)) {
+    faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
+    return 1;
+  }
+
+  /*
+   * Extract the standard user info block.
+   *
+   * Note that although this contains TLVs that appear contiguous
+   * with the TLVs read below, they are two different pieces.  The
+   * userinfo block contains the number of TLVs that contain user
+   * information, the rest are not even though there is no seperation.
+   * aim_extractuserinfo() returns the number of bytes used by the
+   * userinfo tlvs, so you can start reading the rest of them right
+   * afterward.  
+   *
+   * That also means that TLV types can be duplicated between the
+   * userinfo block and the rest of the message, however there should
+   * never be two TLVs of the same type in one block.
+   * 
+   */
+  i += aim_extractuserinfo(sess, command->data+i, &userinfo);
+  
+  /*
+   * Read block of TLVs (not including the userinfo data).  All 
+   * further data is derived from what is parsed here.
+   */
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+
+  /*
+   * 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;
+      u_char *msgblock;
+      u_short flag1,flag2;
+      int finlen = 0;
+      unsigned char fingerprint[10];
+      u_short wastebits;
+           
+      /*
+       * Check Autoresponse status.  If it is an autoresponse,
+       * it will contain a type 0x0004 TLV, with zero length.
+       */
+      if (aim_gettlv(tlvlist, 0x0004, 1))
+	icbmflags |= AIM_IMFLAGS_AWAY;
+      
+      /*
+       * Check Ack Request status.
+       */
+      if (aim_gettlv(tlvlist, 0x0003, 1))
+	icbmflags |= AIM_IMFLAGS_ACK;
+      
+      /*
+       * Message block.
+       */
+      msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
+      if (!msgblocktlv || !msgblocktlv->value) {
+	faimdprintf(sess, 0, "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++);
+
+      finlen = j;
+      if (finlen > sizeof(fingerprint))
+	finlen = sizeof(fingerprint);
+      memcpy(fingerprint, msgblocktlv->value, finlen);
+
+      /* 
+       * 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)
+	faimdprintf(sess, 0, "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(sess, command->conn, 0x0004, 0x0007);
+      if (userfunc)
+	i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2, finlen, fingerprint);
+      else 
+	i = 0;
+      
+      free(msg);
+    }
+  else if (channel == 0x0002)
+    {	
+      struct aim_tlv_t *block1;
+      struct aim_tlvlist_t *list2;
+      unsigned short reqclass = 0;
+      unsigned short status = 0;
+      
+      /*
+       * There's another block of TLVs embedded in the type 5 here. 
+       */
+      block1 = aim_gettlv(tlvlist, 0x0005, 1);
+      if (!block1) {
+	faimdprintf(sess, 0, "no tlv 0x0005 in rendezvous transaction!\n");
+	aim_freetlvchain(&tlvlist);
+	return 1; /* major problem */
+      }
+
+      /*
+       * First two bytes represent the status of the connection.
+       *
+       * 0 is a request, 2 is an accept
+       */ 
+      status = aimutil_get16(block1->value+0);
+      
+      /*
+       * Next comes the cookie.  Should match the ICBM cookie.
+       */
+      if (memcmp(block1->value+2, cookie, 8) != 0) 
+	faimdprintf(sess, 0, "rend: warning cookies don't match!\n");
+
+      /*
+       * The next 16bytes are a capability block so we can
+       * identify what type of rendezvous this is.
+       *
+       * Thanks to Eric Warmenhoven <warmenhoven@linux.com> (of GAIM)
+       * for pointing some of this out to me.  In fact, a lot of 
+       * the client-to-client info comes from the work of the GAIM 
+       * developers. Thanks!
+       *
+       * Read off one capability string and we should have it ID'd.
+       * 
+       */
+      reqclass = aim_getcap(sess, block1->value+2+8, 0x10);
+      if (reqclass == 0x0000) {
+	faimdprintf(sess, 0, "rend: no ID block\n");
+	aim_freetlvchain(&tlvlist);
+	return 1;
+      }
+
+      /* 
+       * What follows may be TLVs or nothing, depending on the
+       * purpose of the message.
+       *
+       * Ack packets for instance have nothing more to them.
+       */
+      list2 = aim_readtlvchain(block1->value+2+8+16, block1->length-2-8-16);
+      
+      if (!list2 || ((reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) {
+	struct aim_msgcookie_t *cook;
+	int type;
+	
+	type = aim_msgcookie_gettype(reqclass); /* XXX: fix this shitty code */
+
+	if ((cook = aim_checkcookie(sess, cookie, type)) == NULL) {
+	  faimdprintf(sess, 0, "non-data rendezvous thats not in cache %d/%s!\n", type, cookie);
+	  aim_freetlvchain(&list2);
+	  aim_freetlvchain(&tlvlist);
+	  return 1;
+	}
+
+	if (cook->type == AIM_COOKIETYPE_OFTGET) {
+	  struct aim_filetransfer_priv *ft;
+
+	  if (cook->data) {
+	    int errorcode = -1; /* XXX shouldnt this be 0? */
+
+	    ft = (struct aim_filetransfer_priv *)cook->data;
+
+	    if(status != 0x0002) {
+	      if (aim_gettlv(list2, 0x000b, 1))
+		errorcode = aim_gettlv16(list2, 0x000b, 1);
+	      
+	      if (errorcode)
+		faimdprintf(sess, 0, "transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sn, ft->ip, ft->fh.name, errorcode);
+	    }
+	  } else {
+	    faimdprintf(sess, 0, "no data attached to file transfer\n");
+	  }
+	} else if (cook->type == AIM_CAPS_VOICE) {
+	  faimdprintf(sess, 0, "voice request cancelled\n");
+	} else {
+	  faimdprintf(sess, 0, "unknown cookie cache type %d\n", cook->type);
+	}
+	
+	if (list2)
+	  aim_freetlvchain(&list2);
+	aim_freetlvchain(&tlvlist);
+	return 1;
+      }
+
+      /*
+       * The rest of the handling depends on what type it is.
+       */
+      if (reqclass & AIM_CAPS_BUDDYICON) {
+
+	/*
+	 * Call client.
+	 */
+#if 0
+	userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0))
+	  i = userfunc(sess, 
+		       command, 
+		       channel, 
+		       reqclass,
+		       &userinfo,
+		       ip,
+		       cookie);
+#endif
+
+      } else if (reqclass & AIM_CAPS_VOICE) {
+	struct aim_msgcookie_t *cachedcook;
+
+	faimdprintf(sess, 0, "rend: voice!\n");
+
+	if(!(cachedcook = (struct aim_msgcookie_t*)calloc(1, sizeof(struct aim_msgcookie_t))))
+	  return 1;
+
+	memcpy(cachedcook->cookie, cookie, 8);
+	cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
+	cachedcook->data = NULL;
+
+	if (aim_cachecookie(sess, cachedcook) == -1)
+	  faimdprintf(sess, 0, "ERROR caching message cookie\n");
+
+	/* XXX: implement all this */
+
+	/*
+	 * Call client.
+	 */
+	userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0)) {
+	  i = userfunc(sess, command, channel, reqclass, &userinfo);
+	}
+      } else if ((reqclass & AIM_CAPS_IMIMAGE) || (reqclass & AIM_CAPS_BUDDYICON)) {
+	char ip[30];
+	struct aim_directim_priv *priv;
+
+	memset(ip, 0, 30);
+	
+	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
+	  struct aim_tlv_t *iptlv, *porttlv;
+	  
+	  iptlv = aim_gettlv(list2, 0x0003, 1);
+	  porttlv = aim_gettlv(list2, 0x0005, 1);
+
+	  snprintf(ip, 30, "%d.%d.%d.%d:%d", 
+		  aimutil_get8(iptlv->value+0),
+		  aimutil_get8(iptlv->value+1),
+		  aimutil_get8(iptlv->value+2),
+		  aimutil_get8(iptlv->value+3),
+		  4443 /*aimutil_get16(porttlv->value)*/);
+	}
+
+	faimdprintf(sess, 0, "rend: directIM request from %s (%s)\n",
+		    userinfo.sn, ip);
+
+       /* XXX: there are a couple of different request packets for
+	*          different things */
+	
+	priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
+	memcpy(priv->ip, ip, sizeof(priv->ip));
+	memcpy(priv->sn, userinfo.sn, sizeof(priv->sn));
+	memcpy(priv->cookie, cookie, sizeof(priv->cookie));
+
+	/*
+	 * Call client.
+	 */
+	userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0))
+	  i = userfunc(sess, 
+		       command, 
+		       channel, 
+		       reqclass,
+		       &userinfo, priv);
+
+      } else if (reqclass & AIM_CAPS_CHAT) {
+	struct aim_tlv_t *miscinfo;
+	struct aim_chat_roominfo roominfo;
+	char *msg=NULL,*encoding=NULL,*lang=NULL;
+
+	miscinfo = aim_gettlv(list2, 0x2711, 1);
+	aim_chat_readroominfo(miscinfo->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(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0))
+	  i = userfunc(sess, 
+		       command, 
+		       channel, 
+		       reqclass,
+		       &userinfo, 
+		       &roominfo, 
+		       msg, 
+		       encoding?encoding+1:NULL, 
+		       lang?lang+1:NULL);
+	  free(roominfo.name);
+	  free(msg);
+	  free(encoding);
+	  free(lang);
+      } else if (reqclass & AIM_CAPS_GETFILE) {
+	char ip[30];
+	struct aim_msgcookie_t *cachedcook;
+	struct aim_tlv_t *miscinfo;
+
+	if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t))))
+	  return 0;
+
+	memset(ip, 0, 30);
+
+	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) {
+	  aim_cookie_free(sess, cachedcook);
+	  return 0;
+	}
+
+	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
+	  struct aim_tlv_t *iptlv, *porttlv;
+
+	  if (!(iptlv = aim_gettlv(list2, 0x0003, 1)) || !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
+	    aim_cookie_free(sess, cachedcook);
+	    return 0;
+	  }
+
+	  snprintf(ip, 30, "%d.%d.%d.%d:%d",
+		   aimutil_get8(iptlv->value+0),
+		   aimutil_get8(iptlv->value+1),
+		   aimutil_get8(iptlv->value+2),
+		   aimutil_get8(iptlv->value+3),
+		   aimutil_get16(porttlv->value));
+	}
+
+	faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo.sn, ip);
+
+	/*
+	 * Call client.
+	 */
+	userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0))
+	  i = userfunc(sess, 
+		       command, 
+		       channel, 
+		       reqclass,
+		       &userinfo,
+		       ip,
+		       cookie);
+
+      } else if (reqclass & AIM_CAPS_SENDFILE) {
+#if 0
+       char ip[30];
+       char *desc = NULL;
+       struct aim_msgcookie_t *cachedcook;
+       struct aim_filetransfer_priv *ft;
+       struct aim_tlv_t *miscinfo;
+
+	memset(ip, 0, sizeof(ip));
+	
+	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)))
+	  return 0;
+
+	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) {
+	  struct aim_tlv_t *iptlv, *porttlv;
+	  
+	  iptlv = aim_gettlv(list2, 0x0003, 1);
+	  porttlv = aim_gettlv(list2, 0x0005, 1);
+
+	  snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", 
+		  aimutil_get8(iptlv->value+0),
+		  aimutil_get8(iptlv->value+1),
+		  aimutil_get8(iptlv->value+2),
+		  aimutil_get8(iptlv->value+3),
+		  aimutil_get16(porttlv->value));
+	}
+
+	if (aim_gettlv(list2, 0x000c, 1)) {
+	  desc = aim_gettlv_str(list2, 0x000c, 1);
+	}
+
+	faimdprintf(sess, 0, "rend: file transfer request from %s for %s: %s (%s)\n",
+		    userinfo.sn,
+		    miscinfo->value+8,
+		    desc, 
+		    ip);
+	
+	memcpy(cachedcook->cookie, cookie, 8);
+	
+	ft = malloc(sizeof(struct aim_filetransfer_priv));
+	strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
+	strncpy(ft->ip, ip, sizeof(ft->ip));
+	strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
+	cachedcook->type = AIM_COOKIETYPE_OFTSEND;
+	cachedcook->data = ft;
+
+	if (aim_cachecookie(sess, cachedcook) == -1)
+	  faimdprintf(sess, 0, "ERROR caching message cookie\n");
+	
+	
+	aim_accepttransfer(sess, command->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
+	
+	if (desc)
+	  free(desc);
+#endif	
+	/*
+	 * Call client.
+	 */
+	userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0007);
+	if (userfunc || (i = 0))
+	  i = userfunc(sess, 
+		       command, 
+		       channel, 
+		       reqclass,
+		       &userinfo);
+      } else
+	faimdprintf(sess, 0, "rend: unknown rendezvous 0x%04x\n", reqclass);
+
+      aim_freetlvchain(&list2);
+    }
+
+  /*
+   * Free up the TLV chain.
+   */
+  aim_freetlvchain(&tlvlist);
+  
+
+  return i;
+}
+
+/*
+ * Possible codes:
+ *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
+ *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
+ *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
+ * 
+ */
+faim_export unsigned long aim_denytransfer(struct aim_session_t *sess,
+					   struct aim_conn_t *conn, 
+					   char *sender,
+					   char *cookie, 
+					   unsigned short code)
+{
+  struct command_tx_struct *newpacket;
+  int curbyte, i;
+
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sender)+6)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid);
+  for (i = 0; i < 8; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender));
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code);
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+/*
+ * 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. 
+ *
+ */
+faim_export unsigned 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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++);
+}
+
+faim_internal 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;
+  char *dest;
+  unsigned short reason = 0;
+
+  /*
+   * 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) {
+    faimdprintf(sess, 0, "msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
+    dest = NULL;
+  } else
+    dest = snac->data;
+
+  reason = aimutil_get16(command->data+10);
+
+  /*
+   * Call client.
+   */
+  userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x0001);
+  if (userfunc)
+    ret =  userfunc(sess, command, dest, reason);
+  else
+    ret = 0;
+  
+  if (snac) {
+    free(snac->data);
+    free(snac);
+  }
+
+  return ret;
+}
+
+
+faim_internal int aim_parse_missedcall(struct aim_session_t *sess,
+				       struct command_rx_struct *command)
+{
+  int i, ret = 1;
+  rxcallback_t userfunc = NULL;
+  unsigned short channel, nummissed, reason;
+  struct aim_userinfo_s userinfo;
+ 
+  i = 10; /* Skip SNAC header */
+
+
+  /*
+   * XXX: supposedly, this entire packet can repeat as many times
+   * as necessary. Should implement that.
+   */
+
+  /*
+   * Channel ID.
+   */
+  channel = aimutil_get16(command->data+i);
+  i += 2;
+  
+  /*
+   * Extract the standard user info block.
+   */
+  i += aim_extractuserinfo(sess, command->data+i, &userinfo);
+  
+  nummissed = aimutil_get16(command->data+i);
+  i += 2;
+  
+  reason = aimutil_get16(command->data+i);
+  i += 2;
+
+  /*
+   * Call client.
+   */
+  userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000a);
+  if (userfunc)
+    ret =  userfunc(sess, command, channel, &userinfo, nummissed, reason);
+  else
+    ret = 0;
+  
+  return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/info.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,698 @@
+/*
+ * aim_info.c
+ *
+ * The functions here are responsible for requesting and parsing information-
+ * gathering SNACs.  
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+struct aim_priv_inforeq {
+  char sn[MAXSNLEN+1];
+  unsigned short infotype;
+};
+
+faim_export unsigned long aim_getinfo(struct aim_session_t *sess,
+				      struct aim_conn_t *conn, 
+				      const char *sn,
+				      unsigned short infotype)
+{
+  struct command_tx_struct *newpacket;
+  struct aim_priv_inforeq privdata;
+  int i = 0;
+
+  if (!sess || !conn || !sn)
+    return 0;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 12+1+strlen(sn))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0002, 0x0005, 0x0000, sess->snac_nextid);
+
+  i += aimutil_put16(newpacket->data+i, infotype);
+  i += aimutil_put8(newpacket->data+i, strlen(sn));
+  i += aimutil_putstr(newpacket->data+i, sn, strlen(sn));
+
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  strncpy(privdata.sn, sn, sizeof(privdata.sn));
+  privdata.infotype = infotype;
+  aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
+
+  return sess->snac_nextid;
+}
+
+faim_internal int aim_parse_locateerr(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;
+  char *dest;
+  unsigned short reason = 0;
+
+  /*
+   * Get SNAC from packet and look it up 
+   * the list of unrepliedto/outstanding
+   * SNACs.
+   *
+   */
+  snacid = aimutil_get32(command->data+6);
+  snac = aim_remsnac(sess, snacid);
+
+  if (!snac) {
+    faimdprintf(sess, 0, "locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
+    dest = NULL;
+  } else
+    dest = snac->data;
+
+  reason = aimutil_get16(command->data+10);
+
+  /*
+   * Call client.
+   */
+  userfunc = aim_callhandler(sess, command->conn, 0x0002, 0x0001);
+  if (userfunc)
+    ret =  userfunc(sess, command, dest, reason);
+  else
+    ret = 0;
+  
+  if (snac) {
+    free(snac->data);
+    free(snac);
+  }
+
+  return ret;
+}
+
+/*
+ * Capability blocks.  
+ */
+u_char aim_caps[8][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},
+
+  /* Saves stock portfolios */
+  {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+  /* Games */
+  {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+   0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+};
+
+faim_internal unsigned short aim_getcap(struct aim_session_t *sess, unsigned char *capblock, int buflen)
+{
+  u_short ret = 0;
+  int y;
+  int offset = 0;
+  int identified;
+
+  while (offset < buflen) {
+    identified = 0;
+    for(y=0; y < (sizeof(aim_caps)/0x10); y++) {
+      if (memcmp(&aim_caps[y], capblock+offset, 0x10) == 0) {
+	switch(y) {
+	case 0: ret |= AIM_CAPS_BUDDYICON; identified++; break;
+	case 1: ret |= AIM_CAPS_VOICE; identified++; break;
+	case 2: ret |= AIM_CAPS_IMIMAGE; identified++; break;
+	case 3: ret |= AIM_CAPS_CHAT; identified++; break;
+	case 4: ret |= AIM_CAPS_GETFILE; identified++; break;
+	case 5: ret |= AIM_CAPS_SENDFILE; identified++; break;
+	case 6: ret |= AIM_CAPS_GAMES; identified++; break;
+	case 7: ret |= AIM_CAPS_SAVESTOCKS; identified++; break;
+	}
+      }
+    }
+    if (!identified) {
+      faimdprintf(sess, 0, "unknown capability!\n");
+      ret |= 0xff00;
+    }
+
+    offset += 0x10;
+  } 
+  return ret;
+}
+
+faim_internal int aim_putcap(unsigned char *capblock, int buflen, u_short caps)
+{
+  int offset = 0;
+
+  if (!capblock)
+    return -1;
+
+  if ((caps & AIM_CAPS_BUDDYICON) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[0], sizeof(aim_caps[0]));
+    offset += sizeof(aim_caps[1]);
+  }
+  if ((caps & AIM_CAPS_VOICE) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[1], sizeof(aim_caps[1]));
+    offset += sizeof(aim_caps[1]);
+  }
+  if ((caps & AIM_CAPS_IMIMAGE) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[2], sizeof(aim_caps[2]));
+    offset += sizeof(aim_caps[2]);
+  }
+  if ((caps & AIM_CAPS_CHAT) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[3], sizeof(aim_caps[3]));
+    offset += sizeof(aim_caps[3]);
+  }
+  if ((caps & AIM_CAPS_GETFILE) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[4], sizeof(aim_caps[4]));
+    offset += sizeof(aim_caps[4]);
+  }
+  if ((caps & AIM_CAPS_SENDFILE) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[5], sizeof(aim_caps[5]));
+    offset += sizeof(aim_caps[5]);
+  }
+  if ((caps & AIM_CAPS_GAMES) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[6], sizeof(aim_caps[6]));
+    offset += sizeof(aim_caps[6]);
+  }
+  if ((caps & AIM_CAPS_SAVESTOCKS) && (offset < buflen)) {
+    memcpy(capblock+offset, aim_caps[7], sizeof(aim_caps[7]));
+    offset += sizeof(aim_caps[7]);
+  }
+
+  return offset;
+}
+
+/*
+ * AIM is fairly regular about providing user info.  This
+ * is a generic routine to extract it in its standard form.
+ */
+faim_internal int aim_extractuserinfo(struct aim_session_t *sess, unsigned char *buf, struct aim_userinfo_s *outinfo)
+{
+  int i = 0;
+  int tlvcnt = 0;
+  int curtlv = 0;
+  int tlv1 = 0;
+  u_short curtype;
+  int lastvalid;
+
+
+  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.
+   */
+  if (buf[i] < MAXSNLEN) {
+    memcpy(outinfo->sn, &(buf[i+1]), buf[i]);
+    outinfo->sn[(int)buf[i]] = '\0';
+  } else {
+    memcpy(outinfo->sn, &(buf[i+1]), MAXSNLEN-1);
+    outinfo->sn[MAXSNLEN] = '\0';
+  }
+  i = 1 + (int)buf[i];
+
+  /*
+   * Warning Level.  Stored as an unsigned short.
+   */
+  outinfo->warnlevel = 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) {
+    lastvalid = 1;
+    curtype = aimutil_get16(&buf[i]);
+    switch (curtype) {
+      /*
+       * Type = 0x0000: Invalid
+       *
+       * AOL has been trying to throw these in just to break us.
+       * They're real nice guys over there at AOL.  
+       *
+       * Just skip the two zero bytes and continue on. (This doesn't
+       * count towards tlvcnt!)
+       */
+    case 0x0000:
+      lastvalid = 0;
+      i += 2;
+      break;
+
+      /*
+       * Type = 0x0001: User flags
+       * 
+       * Specified as any of the following bitwise ORed together:
+       *      0x0001  Trial (user less than 60days)
+       *      0x0002  Unknown bit 2
+       *      0x0004  AOL Main Service user
+       *      0x0008  Unknown bit 4
+       *      0x0010  Free (AIM) user 
+       *      0x0020  Away
+       *
+       * In some odd cases, we can end up with more
+       * than one of these.  We only want the first,
+       * as the others may not be something we want.
+       *
+       */
+    case 0x0001:
+      if (tlv1) /* use only the first */
+	break;
+      outinfo->flags = aimutil_get16(&buf[i+4]);
+      tlv1++;
+      break;
+      
+      /*
+       * Type = 0x0002: Member-Since date. 
+       *
+       * The time/date that the user originally
+       * registered for the service, stored in 
+       * time_t format
+       */
+    case 0x0002: 
+      outinfo->membersince = aimutil_get32(&buf[i+4]);
+      break;
+      
+      /*
+       * Type = 0x0003: On-Since date.
+       *
+       * The time/date that the user started 
+       * their current session, stored in time_t
+       * format.
+       */
+    case 0x0003:
+      outinfo->onlinesince = aimutil_get32(&buf[i+4]);
+      break;
+      
+      /*
+       * Type = 0x0004: Idle time.
+       *
+       * Number of seconds since the user
+       * actively used the service.
+       */
+    case 0x0004:
+      outinfo->idletime = aimutil_get16(&buf[i+4]);
+      break;
+      
+      /*
+       * Type = 0x0006: ICQ Online Status
+       *
+       * ICQ's Away/DND/etc "enriched" status
+       * Some decoding of values done by Scott <darkagl@pcnet.com>
+       */
+    case 0x0006:
+      outinfo->icqinfo.status = aimutil_get16(buf+i+2+2+2);
+      break;
+
+
+      /*
+       * Type = 0x000a
+       *
+       * ICQ User IP Address.
+       * Ahh, the joy of ICQ security.
+       */
+    case 0x000a:
+      outinfo->icqinfo.ipaddr = aimutil_get32(&buf[i+4]);
+      break;
+
+      /* Type = 0x000c
+       *
+       * random crap containing the IP address,
+       * apparently a port number, and some Other Stuff.
+       *
+       */
+    case 0x000c:
+      memcpy(outinfo->icqinfo.crap, &buf[i+4], 0x25);
+      break;
+
+      /*
+       * Type = 0x000d
+       *
+       * Capability information.  Not real sure of
+       * actual decoding.  See comment on aim_bos_setprofile()
+       * in aim_misc.c about the capability block, its the same.
+       *
+       */
+    case 0x000d:
+      {
+	int len;
+	len = aimutil_get16(buf+i+2);
+	if (!len)
+	  break;
+	
+	outinfo->capabilities = aim_getcap(sess, buf+i+4, len);
+      }
+      break;
+      
+      /*
+       * Type = 0x000e
+       *
+       * Unknown.  Always of zero length, and always only
+       * on AOL users.
+       *
+       * Ignore.
+       *
+       */
+    case 0x000e:
+      break;
+      
+      /*
+       * Type = 0x000f: Session Length. (AIM)
+       * Type = 0x0010: Session Length. (AOL)
+       *
+       * The duration, in seconds, of the user's
+       * current session.
+       *
+       * Which TLV type this comes in depends
+       * on the service the user is using (AIM or AOL).
+       *
+       */
+    case 0x000f:
+    case 0x0010:
+      outinfo->sessionlen = aimutil_get32(&buf[i+4]);
+      break;
+      
+      /*
+       * Reaching here indicates that either AOL has
+       * added yet another TLV for us to deal with, 
+       * or the parsing has gone Terribly Wrong.
+       *
+       * Either way, inform the owner and attempt
+       * recovery.
+       *
+       */
+    default:
+      {
+	int len,z = 0, y = 0, x = 0;
+	char tmpstr[160];
+
+        faimdprintf(sess, 0, "userinfo: **warning: unexpected TLV:\n");
+	faimdprintf(sess, 0, "userinfo:   sn    =%s\n", outinfo->sn);
+	faimdprintf(sess, 0, "userinfo:   curtlv=0x%04x\n", curtlv);
+	faimdprintf(sess, 0, "userinfo:   type  =0x%04x\n",aimutil_get16(&buf[i]));
+	faimdprintf(sess, 0, "userinfo:   length=0x%04x\n", len = aimutil_get16(&buf[i+2]));
+	faimdprintf(sess, 0, "userinfo:   data: \n");
+	while (z<len)
+	  {
+	    x = snprintf(tmpstr, sizeof(tmpstr), "userinfo:      ");
+	    for (y = 0; y < 8; y++)
+	      {
+		if (z<len)
+		  {
+		    snprintf(tmpstr+x, sizeof(tmpstr)-x, "%02x ", buf[i+4+z]);
+		    z++;
+		    x += 3;
+		  }
+		else
+		  break;
+	      }
+	    faimdprintf(sess, 0, "%s\n", tmpstr);
+	  }
+      }
+      break;
+    }  
+    /*
+     * No matter what, TLV triplets should always look like this:
+     *
+     *   u_short type;
+     *   u_short length;
+     *   u_char  data[length];
+     *
+     */
+    if (lastvalid) {
+      i += (2 + 2 + aimutil_get16(&buf[i+2])); 	   
+      curtlv++;
+    }
+  }
+  
+  return i;
+}
+
+/*
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure.  Its close enough to run
+ * through aim_extractuserinfo() however.
+ *
+ */
+faim_internal 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(sess, command->data+i, &userinfo);
+
+  userfunc = aim_callhandler(sess, 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.
+ *
+ */
+faim_internal 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, (char *)command->data+11, (int)command->data[10]);
+  sn[(int)command->data[10]] = '\0';
+
+  userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING);
+  if (userfunc)
+    i = userfunc(sess, command, sn);
+
+  return 1;
+}
+
+/*
+ * This parses the user info stuff out all nice and pretty then calls 
+ * the higher-level callback (in the user app).
+ *
+ */
+faim_internal int aim_parse_userinfo_middle(struct aim_session_t *sess,
+					    struct command_rx_struct *command)
+{
+  struct aim_userinfo_s userinfo;
+  char *text_encoding = NULL;
+  char *text = NULL;
+  u_int i = 0;
+  rxcallback_t userfunc=NULL;
+  struct aim_tlvlist_t *tlvlist;
+  struct aim_snac_t *origsnac = NULL;
+  u_long snacid;
+  struct aim_priv_inforeq *inforeq;
+  
+  snacid = aimutil_get32(&command->data[6]);
+  origsnac = aim_remsnac(sess, snacid);
+
+  if (!origsnac || !origsnac->data) {
+    faimdprintf(sess, 0, "parse_userinfo_middle: major problem: no snac stored!\n");
+    return 1;
+  }
+
+  inforeq = (struct aim_priv_inforeq *)origsnac->data;
+
+  switch (inforeq->infotype) {
+  case AIM_GETINFO_GENERALINFO:
+  case AIM_GETINFO_AWAYMESSAGE:
+    i = 10;
+
+    /*
+     * extractuserinfo will give us the basic metaTLV information
+     */
+    i += aim_extractuserinfo(sess, command->data+i, &userinfo);
+  
+    /*
+     * However, in this command, there's usually more TLVs following...
+     */ 
+    tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+
+    /* 
+     * Depending on what informational text was requested, different
+     * TLVs will appear here.
+     *
+     * Profile will be 1 and 2, away message will be 3 and 4.
+     */
+    if (aim_gettlv(tlvlist, 0x0001, 1)) {
+      text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
+      text = aim_gettlv_str(tlvlist, 0x0002, 1);
+    } else if (aim_gettlv(tlvlist, 0x0003, 1)) {
+      text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
+      text = aim_gettlv_str(tlvlist, 0x0004, 1);
+    }
+
+    userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO);
+    if (userfunc) {
+      i = userfunc(sess,
+		   command, 
+		   &userinfo, 
+		   text_encoding, 
+		   text,
+		   inforeq->infotype); 
+    }
+  
+    free(text_encoding);
+    free(text);
+    aim_freetlvchain(&tlvlist);
+    break;
+  default:
+    faimdprintf(sess, 0, "parse_userinfo_middle: unknown infotype in request! (0x%04x)\n", inforeq->infotype);
+    break;
+  }
+
+  if (origsnac) {
+    if (origsnac->data)
+      free(origsnac->data);
+    free(origsnac);
+  }
+
+  return 1;
+}
+
+/*
+ * Inverse of aim_extractuserinfo()
+ */
+faim_internal int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info)
+{
+  int i = 0, numtlv = 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);
+
+
+  aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
+  numtlv++;
+
+  aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
+  numtlv++;
+
+  aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
+  numtlv++;
+
+  aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
+  numtlv++;
+
+#if ICQ_OSCAR_SUPPORT
+  if(atoi(info->sn) != 0) {
+    aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
+    aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
+  }
+#endif
+
+  aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
+  numtlv++;
+
+  aim_addtlvtochain32(&tlvlist, (unsigned short)((info->flags)&AIM_FLAG_AOL?0x0010:0x000f), info->sessionlen);
+  numtlv++;
+
+  i += aimutil_put16(buf+i, numtlv); /* tlvcount */
+  i += aim_writetlvchain(buf+i, buflen-i, &tlvlist); /* tlvs */
+  aim_freetlvchain(&tlvlist);
+
+  return i;
+}
+
+faim_export 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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;
+}
+
+faim_export 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/libfaim_config.h	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,81 @@
+/* include/libfaim_config.h.  Generated automatically by configure.  */
+/* include/libfaim_config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the strftime function.  */
+#define HAVE_STRFTIME 1
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define as __inline if that's what the C compiler calls it.  */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef pid_t */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if your <sys/time.h> declares struct tm.  */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define if you have the gethostname function.  */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the gettimeofday function.  */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the select function.  */
+#define HAVE_SELECT 1
+
+/* Define if you have the socket function.  */
+#define HAVE_SOCKET 1
+
+/* Define if you have the strdup function.  */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strstr function.  */
+#define HAVE_STRSTR 1
+
+/* Define if you have the strtol function.  */
+#define HAVE_STRTOL 1
+
+/* Define if you have the uname function.  */
+#define HAVE_UNAME 1
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <sys/time.h> header file.  */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the faim library (-lfaim).  */
+/* #undef HAVE_LIBFAIM */
+
+/* Name of package */
+#define PACKAGE "libfaim"
+
+/* Version number of package */
+#define VERSION "0.99.1"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/login.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,573 @@
+/*
+ *  aim_login.c
+ *
+ *  This contains all the functions needed to actually login.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+#include "md5.h"
+
+static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest);
+static int aim_encode_password(const char *password, unsigned char *encoded);
+
+faim_export 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0001, 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);
+}
+
+/*
+ * 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).  
+ */
+faim_export int aim_request_login(struct aim_session_t *sess,
+				  struct aim_conn_t *conn, 
+				  char *sn)
+{
+  int curbyte;
+  struct command_tx_struct *newpacket;
+
+  if (!sess || !conn || !sn)
+    return -1;
+
+  /*
+   * For ICQ, we enable the ancient horrible login and stuff
+   * a key packet into the queue to make it look like we got
+   * a reply back. This is so the client doesn't know we're
+   * really not doing MD5 login.
+   *
+   * This may sound stupid, but I'm not in the best of moods and 
+   * I don't plan to keep support for this crap around much longer.
+   * Its all AOL's fault anyway, really. I hate AOL.  Really.  They
+   * always seem to be able to piss me off by doing the dumbest little
+   * things.  Like disabling MD5 logins for ICQ UINs, or adding purposefully
+   * wrong TLV lengths, or adding superfluous information to host strings,
+   * or... I'll stop.
+   *
+   */
+  if ((sn[0] >= '0') && (sn[0] <= '9')) {
+    struct command_rx_struct *newrx;
+    int i;
+
+    if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct))))
+      return -1;
+    memset(newrx, 0x00, sizeof(struct command_rx_struct));
+    newrx->lock = 1; 
+    newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
+    newrx->hdr.oscar.type = 0x02;
+    newrx->hdr.oscar.seqnum = 0;
+    newrx->commandlen = 10+2+1;
+    newrx->nofree = 0; 
+    if (!(newrx->data = malloc(newrx->commandlen))) {
+      free(newrx);
+      return -1;
+    }
+
+    i = aim_putsnac(newrx->data, 0x0017, 0x0007, 0x0000, 0x0000);
+    i += aimutil_put16(newrx->data+i, 0x01);
+    i += aimutil_putstr(newrx->data+i, "0", 1);
+
+    newrx->conn = conn;
+
+    newrx->next = sess->queue_incoming;
+    sess->queue_incoming = newrx;
+
+    newrx->lock = 0;
+
+    sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
+
+    return 0;
+  } 
+
+  sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+
+  aim_sendconnack(sess, conn);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+2+2+strlen(sn))))
+    return -1;
+
+  newpacket->lock = 1;
+  
+  curbyte  = aim_putsnac(newpacket->data, 0x0017, 0x0006, 0x0000, 0x00010000);
+  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+
+  return aim_tx_enqueue(sess, newpacket);
+}
+
+/*
+ * send_login(int socket, char *sn, char *password)
+ *  
+ * This is the initial login request packet.
+ *
+ * The password is encoded before transmition, as per
+ * encode_password().  See that function for their
+ * stupid method of doing it.
+ *
+ * Latest WinAIM:
+ *   clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32"
+ *   major2 = 0x0109
+ *   major = 0x0400
+ *   minor = 0x0003
+ *   minor2 = 0x0000
+ *   build = 0x088c
+ *   unknown = 0x00000086
+ *   lang = "en"
+ *   country = "us"
+ *   unknown4a = 0x01
+ */
+faim_export int aim_send_login (struct aim_session_t *sess,
+				struct aim_conn_t *conn, 
+				char *sn, char *password, 
+				struct client_info_s *clientinfo,
+				char *key)
+{
+  int curbyte=0;
+  struct command_tx_struct *newpacket;
+
+  if (!clientinfo || !sn || !password)
+    return -1;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  newpacket->hdr.oscar.type = (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)?0x02:0x01;
+  
+  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
+    curbyte = aim_putsnac(newpacket->data, 0x0017, 0x0002, 0x0000, 0x00010000);
+  else {
+    curbyte  = aimutil_put16(newpacket->data, 0x0000);
+    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+  }
+
+  curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(sn), sn);
+  
+  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
+    md5_byte_t digest[16];
+
+    aim_encode_password_md5(password, key, digest);
+    curbyte+= aim_puttlv_str(newpacket->data+curbyte, 0x0025, 16, (char *)digest);
+  } else { 
+    char *password_encoded;
+
+    password_encoded = (char *) malloc(strlen(password));
+    aim_encode_password(password, password_encoded);
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0002, strlen(password), password_encoded);
+    free(password_encoded);
+  }
+
+  /* XXX is clientstring required by oscar? */
+  if (strlen(clientinfo->clientstring))
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring);
+
+  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN) {
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, (unsigned short)clientinfo->major2);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, (unsigned short)clientinfo->major);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, (unsigned short)clientinfo->minor);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, (unsigned short)clientinfo->minor2);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, (unsigned short)clientinfo->build);
+  
+    curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, clientinfo->unknown);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
+    curbyte += aim_puttlv_8(newpacket->data+curbyte, 0x004a, 0x00);
+  } else {
+    /* Use very specific version numbers, to further indicate the hack. */
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, 0x0004);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, 0x003c);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001);
+    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, 0x0cce);
+    curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055);
+  }
+
+  if (strlen(clientinfo->country))
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, strlen(clientinfo->country), clientinfo->country);
+  else
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000e, 2, "us");
+
+  if (strlen(clientinfo->lang))
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, strlen(clientinfo->lang), clientinfo->lang);
+  else
+    curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000f, 2, "en");
+  
+  newpacket->commandlen = curbyte;
+
+  newpacket->lock = 0;
+  return aim_tx_enqueue(sess, newpacket);
+}
+
+static int aim_encode_password_md5(const char *password, const char *key, md5_byte_t *digest)
+{
+  md5_state_t state;
+
+  md5_init(&state);	
+  md5_append(&state, (const md5_byte_t *)key, strlen(key));
+  md5_append(&state, (const md5_byte_t *)password, strlen(password));
+  md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+  md5_finish(&state, (md5_byte_t *)digest);
+
+  return 0;
+}
+
+/**
+ * aim_encode_password - Encode a password using old XOR method
+ * @password: incoming password
+ * @encoded: buffer to put encoded password
+ *
+ * This takes a const pointer to a (null terminated) string
+ * containing the unencoded password.  It also gets passed
+ * an already allocated buffer to store the encoded password.
+ * This buffer should be the exact length of the password without
+ * the null.  The encoded password buffer /is not %NULL terminated/.
+ *
+ * The encoding_table seems to be a fixed set of values.  We'll
+ * hope it doesn't change over time!  
+ *
+ * This is only used for the XOR method, not the better MD5 method.
+ *
+ */
+static int aim_encode_password(const char *password, unsigned char *encoded)
+{
+  u_char encoding_table[] = {
+#if 0 /* old v1 table */
+    0xf3, 0xb3, 0x6c, 0x99,
+    0x95, 0x3f, 0xac, 0xb6,
+    0xc5, 0xfa, 0x6b, 0x63,
+    0x69, 0x6c, 0xc3, 0x9f
+#else /* v2.1 table, also works for ICQ */
+    0xf3, 0x26, 0x81, 0xc4,
+    0x39, 0x86, 0xdb, 0x92,
+    0x71, 0xa3, 0xb9, 0xe6,
+    0x53, 0x7a, 0x95, 0x7c
+#endif
+  };
+
+  int i;
+  
+  for (i = 0; i < strlen(password); i++)
+      encoded[i] = (password[i] ^ encoding_table[i]);
+
+  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 passed as errorcode. If
+ * its nonzero, there was an error.
+ *
+ */
+faim_internal int aim_authparse(struct aim_session_t *sess, 
+				struct command_rx_struct *command)
+{
+  struct aim_tlvlist_t *tlvlist;
+  int ret = 1;
+  rxcallback_t userfunc = NULL;
+  char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
+  unsigned char *cookie = NULL;
+  int errorcode = 0, regstatus = 0;
+  int latestbuild = 0, latestbetabuild = 0;
+  char *latestrelease = NULL, *latestbeta = NULL;
+  char *latestreleaseurl = NULL, *latestbetaurl = NULL;
+  char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
+
+  /*
+   * Read block of TLVs.  All further data is derived
+   * from what is parsed here.
+   *
+   * For SNAC login, there's a 17/3 SNAC header in front.
+   *
+   */
+  if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
+    tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
+  else
+    tlvlist = aim_readtlvchain(command->data, command->commandlen);
+
+  /*
+   * No matter what, we should have a screen name.
+   */
+  memset(sess->sn, 0, sizeof(sess->sn));
+  if (aim_gettlv(tlvlist, 0x0001, 1)) {
+    sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+    strncpy(sess->sn, sn, sizeof(sess->sn));
+  }
+
+  /*
+   * Check for an error code.  If so, we should also
+   * have an error url.
+   */
+  if (aim_gettlv(tlvlist, 0x0008, 1)) 
+    errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
+  if (aim_gettlv(tlvlist, 0x0004, 1))
+    errurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+
+  /*
+   * BOS server address.
+   */
+  if (aim_gettlv(tlvlist, 0x0005, 1))
+    bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
+
+  /*
+   * Authorization cookie.
+   */
+  if (aim_gettlv(tlvlist, 0x0006, 1)) {
+    struct aim_tlv_t *tmptlv;
+
+    tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+
+    if ((cookie = malloc(tmptlv->length)))
+      memcpy(cookie, tmptlv->value, tmptlv->length);
+  }
+
+  /*
+   * The email address attached to this account
+   *   Not available for ICQ logins.
+   */
+  if (aim_gettlv(tlvlist, 0x0011, 1))
+    email = aim_gettlv_str(tlvlist, 0x0011, 1);
+
+  /*
+   * The registration status.  (Not real sure what it means.)
+   *   Not available for ICQ logins.
+   *
+   *   1 = No disclosure
+   *   2 = Limited disclosure
+   *   3 = Full disclosure
+   *
+   * This has to do with whether your email address is available
+   * to other users or not.  AFAIK, this feature is no longer used.
+   *
+   */
+  if (aim_gettlv(tlvlist, 0x0013, 1))
+    regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
+
+  if (aim_gettlv(tlvlist, 0x0040, 1))
+    latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1);
+  if (aim_gettlv(tlvlist, 0x0041, 1))
+    latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1);
+  if (aim_gettlv(tlvlist, 0x0042, 1))
+    latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1);
+  if (aim_gettlv(tlvlist, 0x0043, 1))
+    latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1);
+  if (aim_gettlv(tlvlist, 0x0048, 1))
+    ; /* no idea what this is */
+
+  if (aim_gettlv(tlvlist, 0x0044, 1))
+    latestbuild = aim_gettlv32(tlvlist, 0x0044, 1);
+  if (aim_gettlv(tlvlist, 0x0045, 1))
+    latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1);
+  if (aim_gettlv(tlvlist, 0x0046, 1))
+    latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1);
+  if (aim_gettlv(tlvlist, 0x0047, 1))
+    latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1);
+  if (aim_gettlv(tlvlist, 0x0049, 1))
+    ; /* no idea what this is */
+
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0017, 0x0003)))
+    ret = userfunc(sess, command, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
+
+
+  if (sn)
+    free(sn);
+  if (bosip)
+    free(bosip);
+  if (errurl)
+    free(errurl);
+  if (email)
+    free(email);
+  if (cookie)
+    free(cookie);
+  if (latestrelease)
+    free(latestrelease);
+  if (latestreleaseurl)
+    free(latestreleaseurl);
+  if (latestbeta)
+    free(latestbeta);
+  if (latestbetaurl)
+    free(latestbetaurl);
+  if (latestreleaseinfo)
+    free(latestreleaseinfo);
+  if (latestbetainfo)
+    free(latestbetainfo);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;
+}
+
+/*
+ * Middle handler for 0017/0007 SNACs.  Contains the auth key prefixed
+ * by only its length in a two byte word.
+ *
+ * Calls the client, which should then use the value to call aim_send_login.
+ *
+ */
+faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  unsigned char *key;
+  int keylen;
+  int ret = 1;
+  rxcallback_t userfunc;
+
+  keylen = aimutil_get16(command->data+10);
+  if (!(key = malloc(keylen+1)))
+    return ret;
+  memcpy(key, command->data+12, keylen);
+  key[keylen] = '\0';
+  
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0017, 0x0007)))
+    ret = userfunc(sess, command, (char *)key);
+
+  free(key);  
+
+  return ret;
+}
+
+/*
+ * Generate an authorization response.  
+ *
+ * You probably don't want this unless you're writing an AIM server.
+ *
+ */
+faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess, 
+					   struct aim_conn_t *conn, 
+					   char *sn, int errorcode,
+					   char *errorurl, char *bosip, 
+					   char *cookie, char *email, 
+					   int regstatus)
+{	
+  struct command_tx_struct *tx;
+  struct aim_tlvlist_t *tlvlist = NULL;
+
+  if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0004, 1152)))
+    return -1;
+  
+  tx->lock = 1;
+
+  if (sn)
+    aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn));
+  else
+    aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn));
+
+  if (errorcode) {
+    aim_addtlvtochain16(&tlvlist, 0x0008, errorcode);
+    aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl));
+  } else {
+    aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip));
+    aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
+    aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email));
+    aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus);
+  }
+
+  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)
+ */
+faim_export 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)
+ */
+faim_export 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+0x22)))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++);
+  
+  i += aimutil_put16(tx->data+i, 0x0001);  
+  i += aimutil_put16(tx->data+i, 0x0002);
+  i += aimutil_put16(tx->data+i, 0x0003);
+  i += aimutil_put16(tx->data+i, 0x0004);
+  i += aimutil_put16(tx->data+i, 0x0006);
+  i += aimutil_put16(tx->data+i, 0x0008);
+  i += aimutil_put16(tx->data+i, 0x0009);
+  i += aimutil_put16(tx->data+i, 0x000a);
+  i += aimutil_put16(tx->data+i, 0x000b);
+  i += aimutil_put16(tx->data+i, 0x000c);
+  i += aimutil_put16(tx->data+i, 0x0013);
+  i += aimutil_put16(tx->data+i, 0x0015);
+
+  tx->commandlen = i;
+  tx->lock = 0;
+  return aim_tx_enqueue(sess, tx);
+}
+
+
+/* 
+ * Send service redirect.  (Non-Client)
+ */
+faim_export unsigned long aim_sendredirect(struct aim_session_t *sess, 
+					   struct aim_conn_t *conn, 
+					   unsigned short servid, 
+					   char *ip,
+					   char *cookie)
+{	
+  struct command_tx_struct *tx;
+  struct aim_tlvlist_t *tlvlist = NULL;
+  int i = 0;
+
+  if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+    return -1;
+
+  tx->lock = 1;
+
+  i += aim_putsnac(tx->data+i, 0x0001, 0x0005, 0x0000, 0x00000000);
+  
+  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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/meta.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,48 @@
+/*
+ * Administrative things for libfaim.
+ *
+ *  
+ */
+
+#include <aim.h>
+
+faim_export char *aim_getbuilddate(void)
+{
+  return AIM_BUILDDATE;
+}
+
+faim_export char *aim_getbuildtime(void)
+{
+  return AIM_BUILDTIME;
+}
+
+faim_export char *aim_getbuildstring(void)
+{
+  static char string[100];
+
+  snprintf(string, 99, "%d.%d.%d-%s%s", 
+	   FAIM_VERSION_MAJOR,
+	   FAIM_VERSION_MINOR,
+	   FAIM_VERSION_MINORMINOR,
+	   aim_getbuilddate(),
+	   aim_getbuildtime());
+  return string;
+}
+
+faim_internal void faimdprintf(struct aim_session_t *sess, int dlevel, const char *format, ...)
+{
+  if (!sess) {
+    fprintf(stderr, "faimdprintf: no session! boo! (%d, %s)\n", dlevel, format);
+    return;
+  }
+
+  if ((dlevel <= sess->debug) && sess->debugcb) {
+    va_list ap;
+
+    va_start(ap, format);
+    sess->debugcb(sess, dlevel, format, ap);
+    va_end(ap);
+  }
+
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/misc.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,872 @@
+
+/*
+ * aim_misc.c
+ *
+ * TODO: Seperate a lot of this into an aim_bos.c.
+ *
+ * Other things...
+ *
+ *   - Idle setting 
+ * 
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+/*
+ * aim_bos_setidle()
+ *
+ *  Should set your current idle time in seconds.  Idealy, OSCAR should
+ *  do this for us.  But, it doesn't.  The client must call this to set idle
+ *  time.  
+ *
+ */
+faim_export unsigned long aim_bos_setidle(struct aim_session_t *sess,
+					  struct aim_conn_t *conn, 
+					  u_long idletime)
+{
+  return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
+}
+
+
+/*
+ * aim_bos_changevisibility(conn, changtype, namelist)
+ *
+ * Changes your visibility depending on changetype:
+ *
+ *  AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
+ *  AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
+ *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
+ *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
+ *
+ * list should be a list of 
+ * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
+ *
+ * Equivelents to options in WinAIM:
+ *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
+ *      with only your name on it.
+ *   - Allow only users on my Buddy List: Send an 
+ *      AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
+ *      buddy list
+ *   - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD 
+ *      with everyone listed that you want to see you.
+ *   - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only 
+ *      yourself in the list
+ *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
+ *      the list of users to be blocked
+ *
+ *
+ */
+faim_export unsigned long aim_bos_changevisibility(struct aim_session_t *sess,
+						   struct aim_conn_t *conn, 
+						   int changetype, 
+						   char *denylist)
+{
+  struct command_tx_struct *newpacket;
+  int packlen = 0;
+  u_short subtype;
+
+  char *localcpy = NULL;
+  char *tmpptr = NULL;
+  int i,j;
+  int listcount;
+
+  if (!denylist)
+    return 0;
+
+  localcpy = (char *) malloc(strlen(denylist)+1);
+  memcpy(localcpy, denylist, strlen(denylist)+1);
+  
+  listcount = aimutil_itemcnt(localcpy, '&');
+  packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  switch(changetype)
+    {
+    case AIM_VISIBILITYCHANGE_PERMITADD:    subtype = 0x05; break;
+    case AIM_VISIBILITYCHANGE_PERMITREMOVE: subtype = 0x06; break;
+    case AIM_VISIBILITYCHANGE_DENYADD:      subtype = 0x07; break;
+    case AIM_VISIBILITYCHANGE_DENYREMOVE:   subtype = 0x08; break;
+    default:
+      free(newpacket->data);
+      free(newpacket);
+      return 0;
+    }
+
+  /* We actually DO NOT send a SNAC ID with this one! */
+  aim_putsnac(newpacket->data, 0x0009, subtype, 0x00, 0);
+ 
+  j = 10;  /* the next byte */
+  
+  for (i=0; (i < (listcount - 1)) && (i < 99); i++)
+    {
+      tmpptr = aimutil_itemidx(localcpy, i, '&');
+
+      newpacket->data[j] = strlen(tmpptr);
+      memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
+      j += strlen(tmpptr)+1;
+      free(tmpptr);
+    }
+  free(localcpy);
+
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid); /* dont increment */
+
+}
+
+
+
+/*
+ * aim_bos_setbuddylist(buddylist)
+ *
+ * This just builds the "set buddy list" command then queues it.
+ *
+ * buddy_list = "Screen Name One&ScreenNameTwo&";
+ *
+ * TODO: Clean this up.  
+ *
+ * XXX: I can't stress the TODO enough.
+ *
+ */
+faim_export unsigned 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;
+
+  int len = 0;
+
+  char *localcpy = NULL;
+  char *tmpptr = NULL;
+
+  len = 10; /* 10B SNAC headers */
+
+  if (!buddy_list || !(localcpy = (char *) malloc(strlen(buddy_list)+1))) 
+    return -1;
+  strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
+
+  i = 0;
+  tmpptr = strtok(localcpy, "&");
+  while ((tmpptr != NULL) && (i < 150)) {
+    faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
+    len += 1+strlen(tmpptr);
+    i++;
+    tmpptr = strtok(NULL, "&");
+  }
+  faimdprintf(sess, 2, "*** send buddy list len: %d (%x)\n", len, len);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, len)))
+    return -1;
+
+  newpacket->lock = 1;
+  
+  aim_putsnac(newpacket->data, 0x0003, 0x0004, 0x0000, 0);
+
+  j = 10;  /* the next byte */
+
+  strncpy(localcpy, buddy_list, strlen(buddy_list)+1);
+  i = 0;
+  tmpptr = strtok(localcpy, "&");
+  while ((tmpptr != NULL) & (i < 150)) {
+    faimdprintf(sess, 2, "---adding %d: %s (%d)\n", i, tmpptr, strlen(tmpptr));
+    newpacket->data[j] = strlen(tmpptr);
+    memcpy(&(newpacket->data[j+1]), tmpptr, strlen(tmpptr));
+    j += 1+strlen(tmpptr);
+    i++;
+    tmpptr = strtok(NULL, "&");
+  }
+
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  free(localcpy);
+
+  return (sess->snac_nextid);
+}
+
+/* 
+ * aim_bos_setprofile(profile)
+ *
+ * Gives BOS your profile.
+ *
+ * 
+ */
+faim_export unsigned long aim_bos_setprofile(struct aim_session_t *sess,
+					     struct aim_conn_t *conn, 
+					     char *profile,
+					     char *awaymsg,
+					     unsigned short caps)
+{
+  struct command_tx_struct *newpacket;
+  int i = 0, tmp, caplen;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152+strlen(profile)+1+(awaymsg?strlen(awaymsg):0))))
+    return -1;
+
+  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
+    i += aim_puttlv_str(newpacket->data+i, 0x0004, 0x0000, NULL);
+
+  /* Capability information. */
+ 
+  tmp = (i += aimutil_put16(newpacket->data+i, 0x0005));
+  i += aimutil_put16(newpacket->data+i, 0x0000); /* rewritten later */
+  i += (caplen = aim_putcap(newpacket->data+i, 512, caps));
+  aimutil_put16(newpacket->data+tmp, caplen); /* rewrite TLV size */
+
+  newpacket->commandlen = i;
+  aim_tx_enqueue(sess, newpacket);
+  
+  return (sess->snac_nextid++);
+}
+
+/* 
+ * aim_bos_setgroupperm(mask)
+ * 
+ * Set group permisson mask.  Normally 0x1f (all classes).
+ *
+ * The group permission mask allows you to keep users of a certain
+ * class or classes from talking to you.  The mask should be
+ * a bitwise OR of all the user classes you want to see you.
+ *
+ */
+faim_export unsigned long aim_bos_setgroupperm(struct aim_session_t *sess,
+					       struct aim_conn_t *conn, 
+					       u_long mask)
+{
+  return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
+}
+
+faim_internal int aim_parse_bosrights(struct aim_session_t *sess,
+				      struct command_rx_struct *command, ...)
+{
+  rxcallback_t userfunc = NULL;
+  int ret=1;
+  struct aim_tlvlist_t *tlvlist;
+  unsigned short maxpermits = 0, maxdenies = 0;
+
+  /* 
+   * TLVs follow 
+   */
+  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
+    return ret;
+
+  /*
+   * TLV type 0x0001: Maximum number of buddies on permit list.
+   */
+  if (aim_gettlv(tlvlist, 0x0001, 1))
+    maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
+
+  /*
+   * TLV type 0x0002: Maximum number of buddies on deny list.
+   *
+   */
+  if (aim_gettlv(tlvlist, 0x0002, 1)) 
+    maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
+  
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0009, 0x0003)))
+    ret = userfunc(sess, command, maxpermits, maxdenies);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;  
+}
+
+/*
+ * aim_bos_clientready()
+ * 
+ * Send Client Ready.  
+ *
+ */
+faim_export unsigned long aim_bos_clientready(struct aim_session_t *sess,
+					      struct aim_conn_t *conn)
+{
+  struct aim_tool_version tools[] = {
+    {0x0001, 0x0003,    AIM_TOOL_WIN32, 0x0686},
+    {0x0002, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+    {0x0003, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+    {0x0004, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+    {0x0006, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+    {0x0008, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+    {0x0009, 0x0001,    AIM_TOOL_WIN32, 0x0001}, 
+    {0x000a, 0x0001,    AIM_TOOL_WIN32, 0x0001},
+    {0x000b, 0x0001,    AIM_TOOL_WIN32, 0x0001}
+  };
+  int i,j;
+  struct command_tx_struct *newpacket;
+  int toolcount = sizeof(tools)/sizeof(struct aim_tool_version);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 1152)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+
+  for (j = 0; j < toolcount; j++) {
+    i += aimutil_put16(newpacket->data+i, tools[j].group);
+    i += aimutil_put16(newpacket->data+i, tools[j].version);
+    i += aimutil_put16(newpacket->data+i, tools[j].tool);
+    i += aimutil_put16(newpacket->data+i, tools[j].toolversion);
+  }
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+/* 
+ *  Request Rate Information.
+ * 
+ */
+faim_export unsigned long aim_bos_reqrate(struct aim_session_t *sess,
+					  struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
+}
+
+/* 
+ *  Rate Information Response Acknowledge.
+ *
+ */
+faim_export unsigned long aim_bos_ackrateresp(struct aim_session_t *sess,
+					      struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int packlen = 20, i=0;
+
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen)))
+    return (sess->snac_nextid);
+  
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0008, 0x0000, 0);
+  i += aimutil_put16(newpacket->data+i, 0x0001); 
+  i += aimutil_put16(newpacket->data+i, 0x0002);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+  i += aimutil_put16(newpacket->data+i, 0x0004);
+  i += aimutil_put16(newpacket->data+i, 0x0005);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid);
+}
+
+/* 
+ * aim_bos_setprivacyflags()
+ *
+ * Sets privacy flags. Normally 0x03.
+ *
+ *  Bit 1:  Allows other AIM users to see how long you've been idle.
+ *  Bit 2:  Allows other AIM users to see how long you've been a member.
+ *
+ */
+faim_export unsigned long aim_bos_setprivacyflags(struct aim_session_t *sess,
+						  struct aim_conn_t *conn, 
+						  u_long flags)
+{
+  return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
+}
+
+/*
+ * aim_bos_reqpersonalinfo()
+ *
+ * Requests the current user's information. Can't go generic on this one
+ * because aparently it uses SNAC flags.
+ *
+ */
+faim_export unsigned long aim_bos_reqpersonalinfo(struct aim_session_t *sess,
+						  struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
+}
+
+faim_export unsigned 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(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + (4*12))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x0017, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+
+  i += aimutil_put16(newpacket->data+i, 0x0013);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  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);
+
+  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, 0x0001);
+
+  i += aimutil_put16(newpacket->data+i, 0x000c);
+  i += aimutil_put16(newpacket->data+i, 0x0001);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+
+/*
+ * aim_bos_reqservice(serviceid)
+ *
+ * Service request. 
+ *
+ */
+faim_export unsigned long aim_bos_reqservice(struct aim_session_t *sess,
+			  struct aim_conn_t *conn, 
+			  u_short serviceid)
+{
+  return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
+}
+
+/*
+ * aim_bos_nop()
+ *
+ * No-op.  WinAIM sends these every 4min or so to keep
+ * the connection alive.  Its not real necessary.
+ *
+ */
+faim_export unsigned long aim_bos_nop(struct aim_session_t *sess,
+				      struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
+}
+
+/*
+ * aim_flap_nop()
+ *
+ * No-op.  WinAIM 4.x sends these _every minute_ to keep
+ * the connection alive.  
+ */
+faim_export unsigned long aim_flap_nop(struct aim_session_t *sess,
+				       struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0005, 0)))
+    return sess->snac_nextid;
+
+  newpacket->lock = 1;
+  newpacket->commandlen = 0;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid);
+}
+
+/*
+ * aim_bos_reqrights()
+ *
+ * Request BOS rights.
+ *
+ */
+faim_export unsigned long aim_bos_reqrights(struct aim_session_t *sess,
+					    struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
+}
+
+/*
+ * aim_bos_reqbuddyrights()
+ *
+ * Request Buddy List rights.
+ *
+ */
+faim_export unsigned long aim_bos_reqbuddyrights(struct aim_session_t *sess,
+						 struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
+}
+
+/*
+ * aim_send_warning(struct aim_session_t *sess, 
+ *                  struct aim_conn_t *conn, char *destsn, int anon)
+ * send a warning to destsn.
+ * anon is anonymous or not;
+ *  AIM_WARN_ANON anonymous
+ *
+ * returns -1 on error (couldn't alloc packet), next snacid on success.
+ *
+ */
+faim_export int aim_send_warning(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn, int anon)
+{
+  struct command_tx_struct *newpacket;
+  int curbyte;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, strlen(destsn)+13)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  curbyte  = 0;
+  curbyte += aim_putsnac(newpacket->data+curbyte,
+                        0x0004, 0x0008, 0x0000, sess->snac_nextid);
+
+  curbyte += aimutil_put16(newpacket->data+curbyte, (anon & AIM_WARN_ANON)?1:0);
+
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(destsn));
+
+  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return (sess->snac_nextid++);
+}
+
+/*
+ * aim_debugconn_sendconnect()
+ *
+ * For aimdebugd.  If you don't know what it is, you don't want to.
+ */
+faim_export unsigned long aim_debugconn_sendconnect(struct aim_session_t *sess,
+						    struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT);
+}
+
+/*
+ * Generic routine for sending commands.
+ *
+ *
+ * I know I can do this in a smarter way...but I'm not thinking straight
+ * right now...
+ *
+ * I had one big function that handled all three cases, but then it broke
+ * and I split it up into three.  But then I fixed it.  I just never went
+ * back to the single.  I don't see any advantage to doing it either way.
+ *
+ */
+faim_internal unsigned long aim_genericreq_n(struct aim_session_t *sess,
+					     struct aim_conn_t *conn, 
+					     u_short family, u_short subtype)
+{
+  struct command_tx_struct *newpacket;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10)))
+    return 0;
+
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
+
+  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+  aim_tx_enqueue(sess, newpacket);
+  return sess->snac_nextid;
+}
+
+/*
+ *
+ *
+ */
+faim_internal unsigned long aim_genericreq_l(struct aim_session_t *sess,
+					     struct aim_conn_t *conn, 
+					     u_short family, u_short subtype, 
+					     u_long *longdata)
+{
+  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(sess, conn, family, subtype);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_long))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+  /* copy in data */
+  newlong = htonl(*longdata);
+  memcpy(&(newpacket->data[10]), &newlong, sizeof(u_long));
+
+  aim_tx_enqueue(sess, newpacket);
+  return sess->snac_nextid;
+}
+
+faim_internal unsigned long aim_genericreq_s(struct aim_session_t *sess,
+					     struct aim_conn_t *conn, 
+					     u_short family, u_short subtype, 
+					     u_short *shortdata)
+{
+  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(sess, conn, family, subtype);
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+sizeof(u_short))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, family, subtype, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+  /* copy in data */
+  newshort = htons(*shortdata);
+  memcpy(&(newpacket->data[10]), &newshort, sizeof(u_short));
+
+  aim_tx_enqueue(sess, newpacket);
+  return sess->snac_nextid;
+}
+
+/*
+ * aim_bos_reqlocaterights()
+ *
+ * Request Location services rights.
+ *
+ */
+faim_export unsigned long aim_bos_reqlocaterights(struct aim_session_t *sess,
+						  struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
+}
+
+/*
+* aim_bos_reqicbmparaminfo()
+ *
+ * Request ICBM parameter information.
+ *
+ */
+faim_export unsigned long aim_bos_reqicbmparaminfo(struct aim_session_t *sess,
+						   struct aim_conn_t *conn)
+{
+  return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
+}
+
+/*
+ * Add ICBM parameter? Huh?
+ */
+faim_export unsigned long aim_addicbmparam(struct aim_session_t *sess,
+					   struct aim_conn_t *conn)
+{
+  struct command_tx_struct *newpacket;
+  int packlen = 10+16, i=0;
+
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen)))
+    return (sess->snac_nextid);
+  
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
+  aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+
+  i += aimutil_put16(newpacket->data+i, 0x0000); 
+  i += aimutil_put16(newpacket->data+i, 0x0000);
+  i += aimutil_put16(newpacket->data+i, 0x0003);
+  i += aimutil_put16(newpacket->data+i, 0x1f40);
+  i += aimutil_put16(newpacket->data+i, 0x03e7);
+  i += aimutil_put16(newpacket->data+i, 0x03e7);
+  i += aimutil_put16(newpacket->data+i, 0x0000); 
+  i += aimutil_put16(newpacket->data+i, 0x0000); 
+  
+  aim_tx_enqueue(sess, newpacket);
+
+  return sess->snac_nextid;
+}
+
+/* 
+ * Set directory profile data (not the same as aim_bos_setprofile!)
+ */
+faim_export unsigned long aim_setdirectoryinfo(struct aim_session_t *sess, struct aim_conn_t *conn, char *first, char *middle, char *last, char *maiden, char *nickname, char *street, char *city, char *state, char *zip, int country, unsigned short privacy) 
+{
+  struct command_tx_struct *newpacket;
+  int packlen = 0, i = 0;
+
+  packlen += 2+2+2;
+
+  if(first) /* TLV 0001 */
+    packlen += (strlen(first) + 4);
+  if(middle) 
+    packlen += (strlen(middle) + 4);
+  if(last)
+    packlen += (strlen(last) + 4);
+  if(maiden)
+    packlen += (strlen(maiden) + 4);
+  if(nickname)
+    packlen += (strlen(nickname) + 4);
+  if(street)
+    packlen += (strlen(street) + 4);
+  if(state)
+    packlen += (strlen(state) + 4);
+  if(city)
+    packlen += (strlen(city) + 4);
+  if(zip)
+    packlen += (strlen(zip) + 4);
+    
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0002, 0x0009, 0x0000, 0);
+
+  /* 000a/0002: privacy: 1 to allow search/disp, 0 to disallow */
+  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy);
+
+
+  if (first)
+    i += aim_puttlv_str(newpacket->data+i, 0x0001, strlen(first), first);
+  if (middle)
+    i += aim_puttlv_str(newpacket->data+i, 0x0003, strlen(middle), middle);
+  if (last)
+    i += aim_puttlv_str(newpacket->data+i, 0x0002, strlen(last), last);
+  if (maiden)
+    i += aim_puttlv_str(newpacket->data+i, 0x0004, strlen(maiden), maiden);
+  if (nickname)
+    i += aim_puttlv_str(newpacket->data+i, 0x000c, strlen(nickname), nickname);
+  if (street)
+    i += aim_puttlv_str(newpacket->data+i, 0x0021, strlen(street), street);
+  if (city)
+    i += aim_puttlv_str(newpacket->data+i, 0x0008, strlen(city), city);
+  if (state)
+    i += aim_puttlv_str(newpacket->data+i, 0x0007, strlen(state), state);
+  if (zip)
+    i += aim_puttlv_str(newpacket->data+i, 0x000d, strlen(zip), zip);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+   
+  return(sess->snac_nextid);
+}
+
+faim_export unsigned long aim_setuserinterests(struct aim_session_t *sess, struct aim_conn_t *conn, char *interest1, char *interest2, char *interest3, char *interest4, char *interest5, unsigned short privacy)
+{
+  struct command_tx_struct *newpacket;
+  int packlen = 0, i = 0;
+
+  packlen += 2+2+2;
+
+  if(interest1)
+    packlen += (strlen(interest1) + 4);
+  if(interest2)
+    packlen += (strlen(interest2) + 4);
+  if(interest3)
+    packlen += (strlen(interest3) + 4);
+  if(interest4)
+    packlen += (strlen(interest4) + 4);
+  if(interest5)
+    packlen += (strlen(interest5) + 4) ;
+
+    
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, packlen+10)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0002, 0x000f, 0x0000, 0);
+
+  /* 000a/0002: 0000 ?? ?privacy? */
+  i += aim_puttlv_16(newpacket->data+i, 0x000a, privacy); 
+
+  if(interest1) 
+    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest1);
+  if(interest2) 
+    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest2), interest2);
+  if(interest3) 
+    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest3), interest3);
+  if(interest4) 
+    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest4), interest4);
+  if(interest5) 
+    i += aim_puttlv_str(newpacket->data+i, 0x000b, strlen(interest1), interest5);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+    
+  aim_tx_enqueue(sess, newpacket);
+    
+  return(sess->snac_nextid);
+}
+
+faim_export unsigned long aim_icq_setstatus(struct aim_session_t *sess,
+					    struct aim_conn_t *conn, 
+					    unsigned long status)
+{
+  struct command_tx_struct *newpacket;
+  int i;
+  unsigned long data;
+  
+  data = 0x00030000 | status; /* yay for error checking ;^) */
+
+  if(!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10 + 4)))
+    return -1;
+
+  newpacket->lock = 1;
+
+  i = aim_putsnac(newpacket->data, 0x0001, 0x001e, 0x0000, 0x0000001e);
+  i += aim_puttlv_32(newpacket->data+i, 0x0006, data);
+
+  newpacket->commandlen = i;
+  newpacket->lock = 0;
+
+  aim_tx_enqueue(sess, newpacket);
+
+  return(sess->snac_nextid);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/msgcookie.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,212 @@
+/*
+ * Cookie Caching stuff. Adam wrote this, apparently just some
+ * derivatives of n's SNAC work. I cleaned it up, added comments.
+ * 
+ */
+
+/*
+ * I'm assuming that cookies are type-specific. that is, we can have
+ * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
+ * lose some error checking. if we assume cookies are not type-specific and are
+ * wrong, we get quirky behavior when cookies step on each others' toes.
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/**
+ * aim_cachecookie - appends a cookie to the cookie list
+ * @sess: session to add to
+ * @cookie: pointer to struct to append
+ *
+ * if cookie->cookie for type cookie->type is found, updates the
+ * ->addtime of the found structure; otherwise adds the given cookie
+ * to the cache
+ *
+ * returns -1 on error, 0 on append, 1 on update.  the cookie you pass
+ * in may be free'd, so don't count on its value after calling this!
+ * 
+ */
+faim_internal int aim_cachecookie(struct aim_session_t *sess,
+				  struct aim_msgcookie_t *cookie)
+{
+  struct aim_msgcookie_t *newcook;
+
+  if (!sess || !cookie)
+    return -1;
+
+  if( (newcook = aim_checkcookie(sess, cookie->cookie, cookie->type)) ) {
+    if(newcook != cookie) {
+      aim_cookie_free(sess, newcook);
+    } else {
+      newcook->addtime = time(NULL);
+      return 1;
+    }
+  }
+
+  cookie->addtime = time(NULL);  
+
+  cookie->next = sess->msgcookies;
+  sess->msgcookies = cookie;
+
+  return 0;
+}
+
+/**
+ * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list)
+ * @sess: session to grab cookie from
+ * @cookie: cookie string to look for
+ * @type: cookie type to look for
+ *
+ * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process.
+ *
+ * if found, returns the struct; if none found (or on error), returns NULL:
+ */
+faim_internal struct aim_msgcookie_t *aim_uncachecookie(struct aim_session_t *sess, unsigned char *cookie, int type)
+{
+  struct aim_msgcookie_t *cur, **prev;
+
+  if (!cookie || !sess->msgcookies)
+    return NULL;
+
+  for (prev = &sess->msgcookies; (cur = *prev); ) {
+    if ((cur->type == type) && 
+	(memcmp(cur->cookie, cookie, 8) == 0)) {
+      *prev = cur->next;
+      return cur;
+    }
+    prev = &cur->next;
+  }
+
+  return NULL;
+}
+
+/**
+ * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer.
+ * @c: pointer to the cookie string array
+ * @type: cookie type to use
+ * @data: data to be cached with the cookie
+ *
+ * returns NULL on error, a pointer to the newly-allocated cookie on
+ * success.
+ *
+ */
+faim_internal struct aim_msgcookie_t *aim_mkcookie(unsigned char *c, int type, void *data) 
+{
+  struct aim_msgcookie_t *cookie;
+
+  if (!c)
+    return NULL;
+
+  if (!(cookie = calloc(1, sizeof(struct aim_msgcookie_t))))
+    return NULL;
+  
+  cookie->data = data;
+  cookie->type = type;
+  memcpy(cookie->cookie, c, 8);
+  
+  return cookie;
+}
+
+/**
+ * aim_checkcookie - check to see if a cookietuple has been cached
+ * @sess: session to check for the cookie in
+ * @cookie: pointer to the cookie string array
+ * @type: type of the cookie to look for
+ *
+ * this returns a pointer to the cookie struct (still in the list) on
+ * success; returns NULL on error/not found
+ *
+ */
+
+faim_internal struct aim_msgcookie_t *aim_checkcookie(struct aim_session_t *sess, 
+						      const unsigned char *cookie, 
+						      const int type)
+{
+  struct aim_msgcookie_t *cur;
+
+  for (cur = sess->msgcookies; cur; cur = cur->next) {
+    if ((cur->type == type) && 
+	(memcmp(cur->cookie, cookie, 8) == 0))
+      return cur;   
+  }
+
+  return NULL;
+}
+
+#if 0 /* debugging feature */
+faim_internal int aim_dumpcookie(struct aim_msgcookie_t *cookie) 
+{
+  if(!cookie)
+    return -1;
+  printf("\tCookie at %p: %d/%s with %p, next %p\n", cookie, cookie->type, cookie->cookie, cookie->data, cookie->next);
+  return 0;
+}
+#endif
+
+/**
+ * aim_cookie_free - free an aim_msgcookie_t struct
+ * @sess: session to remove the cookie from
+ * @cookiep: the address of a pointer to the cookie struct to remove
+ *
+ * this function removes the cookie *cookie from teh list of cookies
+ * in sess, and then frees all memory associated with it. including
+ * its data! if you want to use the private data after calling this,
+ * make sure you copy it first.
+ *
+ * returns -1 on error, 0 on success.
+ *
+ */
+
+faim_internal int aim_cookie_free(struct aim_session_t *sess, 
+				  struct aim_msgcookie_t *cookie) 
+{
+  struct aim_msgcookie_t *cur, **prev;
+
+  if (!sess || !cookie)
+    return -1;
+
+  if(!cookie)
+    return 0;
+
+  for (prev = &sess->msgcookies; (cur = *prev); ) {
+    if (cur == cookie) {
+      *prev = cur->next;
+    } else
+      prev = &cur->next;
+  }
+
+  if(cookie->data)
+    free(cookie->data);
+
+  free(cookie);
+
+  return 0;
+} 
+
+faim_internal int aim_msgcookie_gettype(int reqclass) {
+  /* XXX: hokey-assed. needs fixed. */
+  switch(reqclass) {
+  case AIM_CAPS_BUDDYICON:
+    return AIM_COOKIETYPE_OFTICON;
+    break;
+  case AIM_CAPS_VOICE:
+    return AIM_COOKIETYPE_OFTVOICE;
+    break;
+  case AIM_CAPS_IMIMAGE:
+    return AIM_COOKIETYPE_OFTIMAGE;
+    break;
+  case AIM_CAPS_CHAT:
+    return AIM_COOKIETYPE_CHAT;
+    break;
+  case AIM_CAPS_GETFILE:
+    return AIM_COOKIETYPE_OFTGET;
+    break;
+  case AIM_CAPS_SENDFILE:
+    return AIM_COOKIETYPE_OFTSEND;
+    break;
+  default:
+    return AIM_COOKIETYPE_UNKNOWN;
+    break;
+  }           
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/rxhandlers.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,1081 @@
+/*
+ * 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.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/*
+ * Bleck functions get called when there's no non-bleck functions
+ * around to cleanup the mess...
+ */
+faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
+{
+  u_short family;
+  u_short subtype;
+
+  u_short maxf;
+  u_short maxs;
+
+  /* XXX: this is ugly. and big just for debugging. */
+  char *literals[14][25] = {
+    {"Invalid", 
+     NULL
+    },
+    {"General", 
+     "Invalid",
+     "Error",
+     "Client Ready",
+     "Server Ready",
+     "Service Request",
+     "Redirect",
+     "Rate Information Request",
+     "Rate Information",
+     "Rate Information Ack",
+     NULL,
+     "Rate Information Change",
+     "Server Pause",
+     NULL,
+     "Server Resume",
+     "Request Personal User Information",
+     "Personal User Information",
+     "Evil Notification",
+     NULL,
+     "Migration notice",
+     "Message of the Day",
+     "Set Privacy Flags",
+     "Well Known URL",
+     "NOP"
+    },
+    {"Location", 
+      "Invalid",
+      "Error",
+      "Request Rights",
+      "Rights Information", 
+      "Set user information", 
+      "Request User Information", 
+      "User Information", 
+      "Watcher Sub Request",
+      "Watcher Notification"
+    },
+    {"Buddy List Management", 
+      "Invalid", 
+      "Error", 
+      "Request Rights",
+      "Rights Information",
+      "Add Buddy", 
+      "Remove Buddy", 
+      "Watcher List Query", 
+      "Watcher List Response", 
+      "Watcher SubRequest", 
+      "Watcher Notification", 
+      "Reject Notification", 
+      "Oncoming Buddy", 
+      "Offgoing Buddy"
+    },
+    {"Messeging", 
+      "Invalid",
+      "Error", 
+      "Add ICBM Parameter",
+      "Remove ICBM Parameter", 
+      "Request Parameter Information",
+      "Parameter Information",
+      "Outgoing Message", 
+      "Incoming Message",
+      "Evil Request",
+      "Evil Reply", 
+      "Missed Calls",
+      "Message Error", 
+      "Host Ack"
+    },
+    {"Advertisements", 
+      "Invalid", 
+      "Error", 
+      "Request Ad",
+      "Ad Data (GIFs)"
+    },
+    {"Invitation / Client-to-Client", 
+     "Invalid",
+     "Error",
+     "Invite a Friend",
+     "Invitation Ack"
+    },
+    {"Administrative", 
+      "Invalid",
+      "Error",
+      "Information Request",
+      "Information Reply",
+      "Information Change Request",
+      "Information Chat Reply",
+      "Account Confirm Request",
+      "Account Confirm Reply",
+      "Account Delete Request",
+      "Account Delete Reply"
+    },
+    {"Popups", 
+      "Invalid",
+      "Error",
+      "Display Popup"
+    },
+    {"BOS", 
+      "Invalid",
+      "Error",
+      "Request Rights",
+      "Rights Response",
+      "Set group permission mask",
+      "Add permission list entries",
+      "Delete permission list entries",
+      "Add deny list entries",
+      "Delete deny list entries",
+      "Server Error"
+    },
+    {"User Lookup", 
+      "Invalid",
+      "Error",
+      "Search Request",
+      "Search Response"
+    },
+    {"Stats", 
+      "Invalid",
+      "Error",
+      "Set minimum report interval",
+      "Report Events"
+    },
+    {"Translate", 
+      "Invalid",
+      "Error",
+      "Translate Request",
+      "Translate Reply",
+    },
+    {"Chat Navigation", 
+      "Invalid",
+      "Error",
+      "Request rights",
+      "Request Exchange Information",
+      "Request Room Information",
+      "Request Occupant List",
+      "Search for Room",
+      "Outgoing Message", 
+      "Incoming Message",
+      "Evil Request", 
+      "Evil Reply", 
+      "Chat Error",
+    }
+  };
+
+  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))
+    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
+  else
+    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
+
+  return 1;
+}
+
+faim_export 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 *newcb;
+
+  if (!conn)
+    return -1;
+
+  faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
+
+  if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
+    return -1;
+  newcb->family = family;
+  newcb->type = type;
+  newcb->flags = flags;
+  if (!newhandler)
+    newcb->handler = &bleck;
+  else
+    newcb->handler = newhandler;
+  newcb->next = NULL;
+  
+  if (!conn->handlerlist)
+    conn->handlerlist = newcb;
+  else {
+    struct aim_rxcblist_t *cur;
+
+    cur = conn->handlerlist;
+
+    while (cur->next)
+      cur = cur->next;
+    cur->next = newcb;
+  }
+
+  return 0;
+}
+
+faim_export int aim_clearhandlers(struct aim_conn_t *conn)
+{
+ struct aim_rxcblist_t *cur;
+
+ if (!conn)
+   return -1;
+
+ for (cur = conn->handlerlist; cur; ) {
+   struct aim_rxcblist_t *tmp;
+
+   tmp = cur->next;
+   free(cur);
+   cur = tmp;
+ }
+ conn->handlerlist = NULL;
+
+ return 0;
+}
+
+faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess,
+					   struct aim_conn_t *conn,
+					   unsigned short family,
+					   unsigned short type)
+{
+  struct aim_rxcblist_t *cur;
+
+  if (!conn)
+    return NULL;
+
+  faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
+  
+  for (cur = conn->handlerlist; cur; cur = cur->next) {
+    if ((cur->family == family) && (cur->type == type))
+      return cur->handler;
+  }
+
+  if (type == AIM_CB_SPECIAL_DEFAULT) {
+    faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
+    return NULL; /* prevent infinite recursion */
+  }
+
+  faimdprintf(sess, 1, "aim_callhandler: no handler for  0x%04x/0x%04x\n", family, type);
+
+  return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+}
+
+faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
+					  struct aim_conn_t *conn,
+					  u_short family,
+					  u_short type,
+					  struct command_rx_struct *ptr)
+{
+  rxcallback_t userfunc = NULL;
+  userfunc = aim_callhandler(sess, conn, family, type);
+  if (userfunc)
+    return userfunc(sess, ptr);
+  return 1; /* XXX */
+}
+
+/*
+  aim_rxdispatch()
+
+  Basically, heres what this should do:
+    1) Determine correct packet handler for this packet
+    2) Mark the packet handled (so it can be dequeued in purge_queue())
+    3) Send the packet to the packet handler
+    4) Go to next packet in the queue and start over
+    5) When done, run purge_queue() to purge handled commands
+
+  Note that any unhandlable packets should probably be left in the
+  queue.  This is the best way to prevent data loss.  This means
+  that a single packet may get looked at by this function multiple
+  times.  This is more good than bad!  This behavior may change.
+
+  Aren't queue's fun? 
+
+  TODO: Get rid of all the ugly if's.
+  TODO: Clean up.
+  TODO: More support for mid-level handlers.
+  TODO: Allow for NULL handlers.
+  
+ */
+faim_export int aim_rxdispatch(struct aim_session_t *sess)
+{
+  int i = 0;
+  struct command_rx_struct *workingPtr = NULL;
+  
+  if (sess->queue_incoming == NULL) {
+    faimdprintf(sess, 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;
+
+      /*
+       * This is a debugging/sanity check only and probably could/should be removed
+       * for stable code.
+       */
+      if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && 
+	   (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
+	  ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && 
+	   (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
+	faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
+	workingPtr->handled = 1;
+	continue;
+      }
+
+      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: {
+	unsigned long head;
+	
+	head = aimutil_get32(workingPtr->data);
+	if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
+	  faimdprintf(sess, 1, "got connection ack on auth line\n");
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
+	} else if (workingPtr->hdr.oscar.type == 0x04) {
+	  /* Used only by the older login protocol */
+	  workingPtr->handled = aim_authparse(sess, workingPtr);
+        } else {
+	  unsigned short family,subtype;
+	  
+	  family = aimutil_get16(workingPtr->data);
+	  subtype = aimutil_get16(workingPtr->data+2);
+	  
+	  switch (family) {
+	    /* New login protocol */
+	  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_authkeyparse(sess, workingPtr);
+	    else
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
+	    break;
+
+	  case 0x0001:
+	    if (subtype == 0x0003)
+	      workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
+	    else if (subtype == 0x0007)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
+	    else if (subtype == 0x0018)
+	      workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
+	    else
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
+	    break;
+
+	  case 0x0007:
+	    if (subtype == 0x0003)
+	      workingPtr->handled = aim_parse_infochange(sess, workingPtr);
+	    else if (subtype == 0x0005)
+	      workingPtr->handled = aim_parse_infochange(sess, workingPtr);
+	    else if (subtype == 0x0007)
+	      workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
+	    break;
+
+	  case AIM_CB_FAM_SPECIAL:
+	    if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+	      break;
+	    } else
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
+	    break;
+
+	  default:
+	    break;
+	  }
+	}
+	break;
+      }
+      case AIM_CONN_TYPE_BOS: {
+	u_short family;
+	u_short subtype;
+
+	if (workingPtr->hdr.oscar.type == 0x04) {
+	  workingPtr->handled = aim_negchan_middle(sess, workingPtr);
+	  break;
+	}
+
+	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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, 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_parse_hostonline(sess, 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_parse_ratechange_middle(sess, workingPtr);
+	    break;
+	  case 0x000f:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
+	    break;
+	  case 0x0010:
+	    workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
+	    break;
+	  case 0x0013:
+	    workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
+	    break;
+	  case 0x0018:
+	    workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
+	    break;
+	  }
+	  break;
+	case 0x0002: /* Family: Location */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_locateerr(sess, 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;
+	  }
+	  break;
+	case 0x0003: /* Family: Buddy List */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_parse_buddyrights(sess, 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: Messaging */
+	  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 0x0006:
+	    workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
+	    break;
+	  case 0x0007:
+	    workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
+	    break;
+	  case 0x000a:
+	    workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
+	    break;
+	  case 0x000c:
+	    workingPtr->handled = aim_parse_msgack_middle(sess, 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_parse_bosrights(sess, 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_parse_searcherror(sess, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_parse_searchreply(sess, 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;
+	}
+	case 0x0013: {
+	  faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
+	  break;
+	}
+	case AIM_CB_FAM_SPECIAL: 
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+	  break;
+	default:
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	  break;
+	} /* switch(family) */
+	break;
+      } /* AIM_CONN_TYPE_BOS */
+      case AIM_CONN_TYPE_ADS: {
+	unsigned short family;
+	unsigned short 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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
+	} else if ((family == 0x0001) && (subtype == 0x0003)) {
+	  workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
+	} else {
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+	}
+	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 == 0x0000) && (subtype == 0x00001)) {
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
+	} else if ((family == 0x0001) && (subtype == 0x0003)) {
+	  workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
+	} 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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, 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_parse_hostonline(sess, workingPtr);
+	  else if (subtype == 0x0007)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
+	  else if (subtype == 0x000a)
+	    workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
+	  else
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+	} 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	
+	    faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype); 
+	} else {
+	  faimdprintf(sess, 0, "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;
+      }
+      case AIM_CONN_TYPE_RENDEZVOUS: {
+	/* make sure that we only get OFT frames on these connections */
+	if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
+	  faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
+	  workingPtr->handled = 1; /* get rid of it */
+	  break;
+	}
+	
+	/* XXX: implement this */
+	faimdprintf(sess, 0, "faim: OFT frame!\n");
+	
+	break;
+      }
+      case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
+	/* not possible */
+	break;
+      }
+      default:
+	faimdprintf(sess, 0, "internal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
+	workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	break;
+      }	
+    }
+  }
+
+  /* 
+   * 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;
+}
+
+faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  rxcallback_t userfunc = NULL;
+  char sn[MAXSNLEN];
+  unsigned short type;
+  int i = 10+8; /* skip SNAC and cookie */
+  int ret = 1;
+  unsigned char snlen;
+
+  type = aimutil_get16(command->data+i);
+  i += 2;
+
+  snlen = aimutil_get8(command->data+i);
+  i++;
+
+  memset(sn, 0, sizeof(sn));
+  strncpy(sn, (char *)command->data+i, snlen);
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
+    ret =  userfunc(sess, command, type, sn);
+
+  return ret;
+}
+
+/*
+ * The Rate Limiting System, An Abridged Guide to Nonsense.
+ *
+ * OSCAR defines several 'rate classes'.  Each class has seperate
+ * rate limiting properties (limit level, alert level, disconnect
+ * level, etc), and a set of SNAC family/type pairs associated with
+ * it.  The rate classes, their limiting properties, and the definitions
+ * of which SNACs are belong to which class, are defined in the
+ * Rate Response packet at login to each host.  
+ *
+ * Logically, all rate offenses within one class count against further
+ * offenses for other SNACs in the same class (ie, sending messages
+ * too fast will limit the number of user info requests you can send,
+ * since those two SNACs are in the same rate class).
+ *
+ * Since the rate classes are defined dynamically at login, the values
+ * below may change. But they seem to be fairly constant.
+ *
+ * Currently, BOS defines five rate classes, with the commonly used
+ * members as follows...
+ *
+ *  Rate class 0x0001:
+ *  	- Everything thats not in any of the other classes
+ *
+ *  Rate class 0x0002:
+ * 	- Buddy list add/remove
+ *	- Permit list add/remove
+ *	- Deny list add/remove
+ *
+ *  Rate class 0x0003:
+ *	- User information requests
+ *	- Outgoing ICBMs
+ *
+ *  Rate class 0x0004:
+ *	- A few unknowns: 2/9, 2/b, and f/2
+ *
+ *  Rate class 0x0005:
+ *	- Chat room create
+ *	- Outgoing chat ICBMs
+ *
+ * The only other thing of note is that class 5 (chat) has slightly looser
+ * limiting properties than class 3 (normal messages).  But thats just a 
+ * small bit of trivia for you.
+ *
+ * The last thing that needs to be learned about the rate limiting
+ * system is how the actual numbers relate to the passing of time.  This
+ * seems to be a big mystery.
+ * 
+ */
+faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+  int i;
+  int code;
+  unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
+  unsigned long currentavg, maxavg;
+
+  i = 10;
+
+  code = aimutil_get16(command->data+i);
+  i += 2;
+
+  rateclass = aimutil_get16(command->data+i);
+  i += 2;
+
+  windowsize = aimutil_get32(command->data+i);
+  i += 4;
+  clear = aimutil_get32(command->data+i);
+  i += 4;
+  alert = aimutil_get32(command->data+i);
+  i += 4;
+  limit = aimutil_get32(command->data+i);
+  i += 4;
+  disconnect = aimutil_get32(command->data+i);
+  i += 4;
+  currentavg = aimutil_get32(command->data+i);
+  i += 4;
+  maxavg = aimutil_get32(command->data+i);
+  i += 4;
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
+    ret =  userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
+
+  return ret;
+}
+
+faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+  int i;
+  unsigned short newevil;
+  struct aim_userinfo_s userinfo;
+
+  i = 10;
+  newevil = aimutil_get16(command->data+10);
+  i += 2;
+
+  memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
+  if (command->commandlen-i)
+    i += aim_extractuserinfo(sess, command->data+i, &userinfo);
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
+    ret = userfunc(sess, command, newevil, &userinfo);
+  
+  return ret;
+}
+
+faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
+				       struct command_rx_struct *command, ...)
+{
+  rxcallback_t userfunc = NULL;
+  char *msg;
+  int ret=1;
+  struct aim_tlvlist_t *tlvlist;
+  u_short id;
+
+  /*
+   * Code.
+   *
+   * Valid values:
+   *   1 Mandatory upgrade
+   *   2 Advisory upgrade
+   *   3 System bulletin
+   *   4 Nothing's wrong ("top o the world" -- normal)
+   *
+   */
+  id = aimutil_get16(command->data+10);
+
+  /* 
+   * TLVs follow 
+   */
+  if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
+    return ret;
+
+  if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
+    aim_freetlvchain(&tlvlist);
+    return ret;
+  }
+  
+  userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
+  if (userfunc)
+    ret =  userfunc(sess, command, id, msg);
+
+  aim_freetlvchain(&tlvlist);
+  free(msg);
+
+  return ret;  
+}
+
+faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
+				       struct command_rx_struct *command, ...)
+{
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+  unsigned short *families = NULL;
+  int famcount = 0, i;
+
+  famcount = (command->commandlen-10)/2;
+  if (!(families = malloc(command->commandlen-10)))
+    return ret;
+
+  for (i = 0; i < famcount; i++)
+    families[i] = aimutil_get16(command->data+((i*2)+10));
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
+    ret = userfunc(sess, command, famcount, families);
+
+  free(families);
+
+  return ret;  
+}
+
+faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
+					   struct command_rx_struct *command)
+{
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+  int status = -1;
+
+  status = aimutil_get16(command->data+10);
+
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
+    ret = userfunc(sess, command, status);
+
+  return ret;  
+}
+
+faim_internal int aim_parse_infochange(struct aim_session_t *sess,
+				       struct command_rx_struct *command)
+{
+  unsigned short subtype; /* called for both reply and change-reply */
+  int i;
+
+  subtype = aimutil_get16(command->data+2);
+
+  /*
+   * struct {
+   *    unsigned short perms;
+   *    unsigned short tlvcount;
+   *    aim_tlv_t tlvs[tlvcount];
+   *  } admin_info[n];
+   */
+  for (i = 10; i < command->commandlen; ) {
+    int perms, tlvcount;
+
+    perms = aimutil_get16(command->data+i);
+    i += 2;
+
+    tlvcount = aimutil_get16(command->data+i);
+    i += 2;
+
+    while (tlvcount) {
+      rxcallback_t userfunc;
+      struct aim_tlv_t *tlv;
+      int str = 0;
+
+      if ((aimutil_get16(command->data+i) == 0x0011) ||
+	  (aimutil_get16(command->data+i) == 0x0004))
+	str = 1;
+
+      if (str)
+	tlv = aim_grabtlvstr(command->data+i);
+      else
+	tlv = aim_grabtlv(command->data+i);
+
+      /* XXX fix so its only called once for the entire packet */
+      if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
+	userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
+
+      if (tlv)
+	i += 2+2+tlv->length;
+
+      if (tlv && tlv->value)
+	free(tlv->value);
+      if (tlv)
+	free(tlv);
+
+      tlvcount--;
+    }
+  }
+
+  return 1;
+}
+
+faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
+					 struct command_rx_struct *command, ...)
+{
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+  int vercount;
+
+  vercount = (command->commandlen-10)/4;
+  
+  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
+    ret = userfunc(sess, command, vercount, command->data+10);
+
+  return ret;  
+}
+
+faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
+					    struct command_rx_struct *command, ...)
+{
+  int serviceid = 0;
+  unsigned char *cookie = NULL;
+  char *ip = NULL;
+  rxcallback_t userfunc = NULL;
+  struct aim_tlvlist_t *tlvlist;
+  int ret = 1;
+  
+  tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
+
+  if (aim_gettlv(tlvlist, 0x000d, 1))
+    serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
+  if (aim_gettlv(tlvlist, 0x0005, 1))
+    ip = aim_gettlv_str(tlvlist, 0x0005, 1);
+  if (aim_gettlv(tlvlist, 0x0006, 1))
+    cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
+
+  if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
+
+    /*
+     * Chat hack.
+     *
+     */
+    if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
+      ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
+      free(sess->pendingjoin);
+      sess->pendingjoin = NULL;
+      sess->pendingjoinexchange = 0;
+  } else if (!serviceid || !ip || !cookie) { /* yeep! */
+    ret = 1;
+  } else {
+    if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
+      ret =  userfunc(sess, command, serviceid, ip, cookie);
+  }
+
+  if (ip)
+    free(ip);
+  if (cookie)
+    free(cookie);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;
+}
+
+faim_internal int aim_parse_unknown(struct aim_session_t *sess,
+					  struct command_rx_struct *command, ...)
+{
+  u_int i = 0;
+
+  if (!sess || !command)
+    return 1;
+
+  faimdprintf(sess, 1, "\nRecieved unknown packet:");
+
+  for (i = 0; i < command->commandlen; i++)
+    {
+      if ((i % 8) == 0)
+	faimdprintf(sess, 1, "\n\t");
+
+      faimdprintf(sess, 1, "0x%2x ", command->data[i]);
+    }
+  
+  faimdprintf(sess, 1, "\n\n");
+
+  return 1;
+}
+
+
+faim_internal int aim_negchan_middle(struct aim_session_t *sess,
+				     struct command_rx_struct *command)
+{
+  struct aim_tlvlist_t *tlvlist;
+  char *msg = NULL;
+  unsigned short code = 0;
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+
+  tlvlist = aim_readtlvchain(command->data, command->commandlen);
+
+  if (aim_gettlv(tlvlist, 0x0009, 1))
+    code = aim_gettlv16(tlvlist, 0x0009, 1);
+
+  if (aim_gettlv(tlvlist, 0x000b, 1))
+    msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
+    ret =  userfunc(sess, command, code, msg);
+
+  aim_freetlvchain(&tlvlist);
+
+  if (msg)
+    free(msg);
+
+  return ret;
+}
+
+/*
+ * aim_parse_generalerrs()
+ *
+ * Middle handler for 0x0001 snac of each family.
+ *
+ */
+faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
+					struct command_rx_struct *command, ...)
+{
+  unsigned short family;
+  unsigned short subtype;
+  int ret = 1;
+  int error = 0;
+  rxcallback_t userfunc = NULL;
+  
+  family = aimutil_get16(command->data+0);
+  subtype= aimutil_get16(command->data+2);
+  
+  if (command->commandlen > 10)
+    error = aimutil_get16(command->data+10);
+
+  if ((userfunc = aim_callhandler(sess, command->conn, family, subtype))) 
+    ret = userfunc(sess, command, error);
+
+  return ret;
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/rxqueue.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,252 @@
+/*
+ *  aim_rxqueue.c
+ *
+ * This file contains the management routines for the receive
+ * (incoming packet) queue.  The actual packet handlers are in
+ * aim_rxhandlers.c.
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h> 
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ * Since not all implementations support MSG_WAITALL, define
+ * an alternate guarenteed read function...
+ *
+ * We keep recv() for systems that can do it because it means
+ * a single system call for the entire packet, where read may
+ * take more for a badly fragmented packet.
+ *
+ */
+faim_internal int aim_recv(int fd, void *buf, size_t count)
+{
+#ifdef MSG_WAITALL
+  return recv(fd, buf, count, MSG_WAITALL);
+#else
+  int left, ret, cur = 0; 
+
+  left = count;
+
+  while (left) {
+    ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
+    if (ret == -1)
+      return -1;
+    if (ret == 0)
+      return cur;
+    
+    cur += ret;
+    left -= ret;
+  }
+
+  return cur;
+#endif
+}
+
+/*
+ * Grab a single command sequence off the socket, and enqueue
+ * it in the incoming event queue in a seperate struct.
+ */
+faim_export int aim_get_command(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  unsigned char generic[6]; 
+  struct command_rx_struct *newrx = NULL;
+
+  if (!sess || !conn)
+    return 0;
+
+  if (conn->fd == -1)
+    return -1; /* its a aim_conn_close()'d connection */
+
+  if (conn->fd < 3)  /* can happen when people abuse the interface */
+    return 0;
+
+  if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+    return aim_conn_completeconnect(sess, conn);
+
+  /*
+   * Rendezvous (client-client) connections do not speak
+   * FLAP, so this function will break on them.
+   */
+  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) 
+    return aim_get_command_rendezvous(sess, conn);
+  if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+    faimdprintf(sess, 0, "out on fd %d\n", conn->fd);
+    return 0; 
+  }
+
+  /*
+   * Read FLAP header.  Six bytes:
+   *    
+   *   0 char  -- Always 0x2a
+   *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
+   *   2 short -- Sequence number 
+   *   4 short -- Number of data bytes that follow.
+   */
+  faim_mutex_lock(&conn->active);
+  if (aim_recv(conn->fd, generic, 6) < 6){
+    aim_conn_close(conn);
+    faim_mutex_unlock(&conn->active);
+    return -1;
+  }
+
+  /*
+   * This shouldn't happen unless the socket breaks, the server breaks,
+   * or we break.  We must handle it just in case.
+   */
+  if (generic[0] != 0x2a) {
+    faimdprintf(sess, 1, "Bad incoming data!");
+    aim_conn_close(conn);
+    faim_mutex_unlock(&conn->active);
+    return -1;
+  }	
+
+  /* allocate a new struct */
+  if (!(newrx = (struct command_rx_struct *)malloc(sizeof(struct command_rx_struct)))) {
+    faim_mutex_unlock(&conn->active);
+    return -1;
+  }
+  memset(newrx, 0x00, sizeof(struct command_rx_struct));
+
+  newrx->lock = 1;  /* lock the struct */
+
+  /* we're doing OSCAR if we're here */
+  newrx->hdrtype = AIM_FRAMETYPE_OSCAR;
+
+  /* store channel -- byte 2 */
+  newrx->hdr.oscar.type = (char) generic[1];
+
+  /* store seqnum -- bytes 3 and 4 */
+  newrx->hdr.oscar.seqnum = aimutil_get16(generic+2);
+
+  /* store commandlen -- bytes 5 and 6 */
+  newrx->commandlen = aimutil_get16(generic+4);
+
+  newrx->nofree = 0; /* free by default */
+
+  /* malloc for data portion */
+  if (!(newrx->data = (u_char *) malloc(newrx->commandlen))) {
+    free(newrx);
+    faim_mutex_unlock(&conn->active);
+    return -1;
+  }
+
+  /* read the data portion of the packet */
+  if (aim_recv(conn->fd, newrx->data, newrx->commandlen) < newrx->commandlen){
+    free(newrx->data);
+    free(newrx);
+    aim_conn_close(conn);
+    faim_mutex_unlock(&conn->active);
+    return -1;
+  }
+  faim_mutex_unlock(&conn->active);
+
+  newrx->conn = conn;
+
+  newrx->next = NULL;  /* this will always be at the bottom */
+  newrx->lock = 0; /* unlock */
+
+  /* enqueue this packet */
+  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 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.
+ *
+ */
+faim_export void aim_purge_rxqueue(struct aim_session_t *sess)
+{
+  struct command_rx_struct *cur = NULL;
+  struct command_rx_struct *tmp;
+
+  if (sess->queue_incoming == NULL)
+    return;
+  
+  if (sess->queue_incoming->next == NULL) {
+    if (sess->queue_incoming->handled) {
+      tmp = sess->queue_incoming;
+      sess->queue_incoming = NULL;
+
+      if (!tmp->nofree) {
+	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
+	  free(tmp->hdr.oft.hdr2);
+	free(tmp->data);
+	free(tmp);
+      } else
+	tmp->next = NULL;
+    }
+    return;
+  }
+
+  for(cur = sess->queue_incoming; cur->next != NULL; ) {
+    if (cur->next->handled) {
+      tmp = cur->next;
+      cur->next = tmp->next;
+      if (!tmp->nofree) {
+	if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
+	  free(tmp->hdr.oft.hdr2);
+	free(tmp->data);
+	free(tmp);
+      } else
+	tmp->next = NULL;
+    }	
+    cur = cur->next;
+
+    /* 
+     * 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;
+}
+
+/*
+ * Since aim_get_command will aim_conn_kill dead connections, we need
+ * to clean up the rxqueue of unprocessed connections on that socket.
+ *
+ * XXX: this is something that was handled better in the old connection
+ * handling method, but eh.
+ */
+faim_internal void aim_rxqueue_cleanbyconn(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  struct command_rx_struct *currx;
+
+  for (currx = sess->queue_incoming; currx; currx = currx->next) {
+    if ((!currx->handled) && (currx->conn == conn))
+      currx->handled = 1;
+  }	
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/search.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,122 @@
+
+/*
+ * aim_search.c
+ *
+ * TODO: Add aim_usersearch_name()
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+faim_export unsigned long aim_usersearch_address(struct aim_session_t *sess,
+						 struct aim_conn_t *conn, 
+						 char *address)
+{
+  struct command_tx_struct *newpacket;
+  
+  if (!address)
+    return -1;
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+strlen(address))))
+    return -1;
+
+  newpacket->lock = 1;
+
+  aim_putsnac(newpacket->data, 0x000a, 0x0002, 0x0000, sess->snac_nextid);
+
+  aimutil_putstr(newpacket->data+10, address, strlen(address));
+
+  aim_tx_enqueue(sess, newpacket);
+
+  aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, address, strlen(address)+1);
+
+  return sess->snac_nextid;
+}
+
+
+faim_internal unsigned long aim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command) 
+{
+  u_int i, ret;
+  int snacid;
+  rxcallback_t userfunc;
+  struct aim_snac_t *snac;
+
+  i = 6;
+
+  snacid = aimutil_get32(command->data+i);
+  i += 4;
+  
+  if(!(snac = aim_remsnac(sess, snacid))) {
+    faimdprintf(sess, 2, "faim: couldn't get a snac for %d, probably should crash.\n", snacid);
+    return 0;
+  }
+
+  if((userfunc = aim_callhandler(sess, command->conn, 0x000a, 0x0001)))
+    ret = userfunc(sess, command, snac->data /* address */);
+  else
+    ret = 0;
+
+  if(snac) {
+    if(snac->data)
+      free(snac->data);
+    free(snac);
+  }
+
+  return ret;
+}
+	
+  
+faim_internal unsigned long aim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+  u_int i, j, m, ret;
+  int snacid;
+  struct aim_tlvlist_t *tlvlist;
+  char *cur = NULL, *buf = NULL;
+  rxcallback_t userfunc;
+  struct aim_snac_t *snac;
+
+  i = 6;
+
+  snacid = aimutil_get32(command->data+i);
+  i += 4;
+
+  if(!(snac = aim_remsnac(sess, snacid))) {
+    faimdprintf(sess, 2, "faim: couldn't get a snac for %d, probably should crash.\n", snacid);
+    return 0;
+  }
+
+  tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
+
+  j = 0;
+
+  m = aim_counttlvchain(&tlvlist);
+
+  while((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
+    if(!(buf = realloc(buf, (j+1) * (MAXSNLEN+1))))
+      faimdprintf(sess, 2, "faim: couldn't realloc buf. oh well.\n");
+
+    strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
+    free(cur);
+
+    j++; 
+  }
+
+  aim_freetlvchain(&tlvlist);
+
+  if((userfunc = aim_callhandler(sess, command->conn, 0x000a, 0x0003)))
+    ret = userfunc(sess, command, snac->data /* address */, j, buf);
+  else
+    ret = 0;
+
+  if(snac) {
+    if(snac->data)
+      free(snac->data);
+    free(snac);
+  }
+
+  if(buf)
+    free(buf);
+
+  return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/snac.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,177 @@
+/*
+ *
+ * Various SNAC-related dodads... 
+ *
+ * outstanding_snacs is a list of aim_snac_t structs.  A SNAC should be added
+ * whenever a new SNAC is sent and it should remain in the list until the
+ * response for it has been receieved.  
+ *
+ * cleansnacs() should be called periodically by the client in order
+ * to facilitate the aging out of unreplied-to SNACs. This can and does
+ * happen, so it should be handled.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/*
+ * Called from aim_session_init() to initialize the hash.
+ */
+faim_internal void aim_initsnachash(struct aim_session_t *sess)
+{
+  int i;
+
+  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+    sess->snac_hash[i] = NULL;
+    faim_mutex_init(&sess->snac_hash_locks[i]);
+  }
+
+  return;
+}
+
+faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess,
+					  const unsigned short family,
+					  const unsigned short type,
+					  const unsigned short flags,
+					  const void *data, const int datalen)
+{
+  struct aim_snac_t snac;
+
+  snac.id = sess->snac_nextid++;
+  snac.family = family;
+  snac.type = type;
+  snac.flags = flags;
+
+  if (datalen) {
+    if (!(snac.data = malloc(datalen)))
+      return 0; /* er... */
+    memcpy(snac.data, data, datalen);
+  } else
+    snac.data = NULL;
+
+  return aim_newsnac(sess, &snac);
+}
+
+/*
+ * Clones the passed snac structure and caches it in the
+ * list/hash.
+ */
+faim_internal unsigned long aim_newsnac(struct aim_session_t *sess,
+					struct aim_snac_t *newsnac) 
+{
+  struct aim_snac_t *snac = NULL;
+  int index;
+
+  if (!newsnac)
+    return 0;
+
+  if (!(snac = calloc(1, sizeof(struct aim_snac_t))))
+    return 0;
+  memcpy(snac, newsnac, sizeof(struct aim_snac_t));
+  snac->issuetime = time(&snac->issuetime);
+  snac->next = NULL;
+
+  index = snac->id % FAIM_SNAC_HASH_SIZE;
+
+  faim_mutex_lock(&sess->snac_hash_locks[index]);
+  snac->next = sess->snac_hash[index];
+  sess->snac_hash[index] = snac;
+  faim_mutex_unlock(&sess->snac_hash_locks[index]);
+
+  return(snac->id);
+}
+
+/*
+ * Finds a snac structure with the passed SNAC ID, 
+ * removes it from the list/hash, and returns a pointer to it.
+ *
+ * The returned structure must be freed by the caller.
+ *
+ */
+faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, 
+					     u_long id) 
+{
+  struct aim_snac_t *cur = NULL;
+  int index;
+
+  index = id % FAIM_SNAC_HASH_SIZE;
+
+  faim_mutex_lock(&sess->snac_hash_locks[index]);
+  if (!sess->snac_hash[index])
+    ;
+  else if (sess->snac_hash[index]->id == id) {
+    cur = sess->snac_hash[index];
+    sess->snac_hash[index] = cur->next;
+  } else {
+    cur = sess->snac_hash[index];
+    while (cur->next) {
+      if (cur->next->id == id) {
+	struct aim_snac_t *tmp;
+	
+	tmp = cur->next;
+	cur->next = cur->next->next;
+	cur = tmp;
+	break;
+      }
+      cur = cur->next;
+    }
+  }
+  faim_mutex_unlock(&sess->snac_hash_locks[index]);
+
+  return cur;
+}
+
+/*
+ * This is for cleaning up old SNACs that either don't get replies or
+ * a reply was never received for.  Garabage collection. Plain and simple.
+ *
+ * maxage is the _minimum_ age in seconds to keep SNACs.
+ *
+ */
+faim_internal int aim_cleansnacs(struct aim_session_t *sess,
+				 int maxage)
+{
+  int i;
+
+  for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+    struct aim_snac_t *cur, **prev;
+    time_t curtime;
+
+    faim_mutex_lock(&sess->snac_hash_locks[i]);
+    if (!sess->snac_hash[i]) {
+      faim_mutex_unlock(&sess->snac_hash_locks[i]);
+      continue;
+    }
+
+    curtime = time(NULL); /* done here in case we waited for the lock */
+
+    for (prev = &sess->snac_hash[i]; (cur = *prev); ) {
+      if ((curtime - cur->issuetime) > maxage) {
+
+	*prev = cur->next;
+
+	/* XXX should we have destructors here? */
+	if (cur->data)
+	  free(cur->data);
+	free(cur);
+
+      } else
+	prev = &cur->next;
+    }
+
+    faim_mutex_unlock(&sess->snac_hash_locks[i]);
+  }
+
+  return 0;
+}
+
+faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
+{
+  int curbyte = 0;
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff));
+  curbyte += aimutil_put32(buf+curbyte, snacid);
+  return curbyte;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/tlv.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,724 @@
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/**
+ * aim_readtlvchain - Read a TLV chain from a buffer.
+ * @buf: Input buffer
+ * @maxlen: Length of input buffer
+ *
+ * Reads and parses a series of TLV patterns from a data buffer; the
+ * returned structure is manipulatable with the rest of the TLV
+ * routines.  When done with a TLV chain, aim_freetlvchain() should
+ * be called to free the dynamic substructures.
+ *
+ */
+faim_export 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)
+	    {
+	      /*
+	       * Okay, so now AOL has decided that any TLV of
+	       * type 0x0013 can only be two bytes, despite
+	       * what the actual given length is.  So here 
+	       * we dump any invalid TLVs of that sort.  Hopefully
+	       * theres no special cases to this special case.
+	       *   - mid (30jun2000)
+	       */
+	      if ((type == 0x0013) && (length != 0x0002))
+		length = 0x0002;
+	      else {
+		cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+		memset(cur, 0x00, sizeof(struct aim_tlvlist_t));
+
+		cur->tlv = aim_createtlv();	
+		cur->tlv->type = type;
+		cur->tlv->length = length; 
+		if (length) {
+		  cur->tlv->value = (unsigned char *)malloc(length);
+		  memcpy(cur->tlv->value, buf+pos, length);
+		} 
+
+		cur->next = list;
+		list = cur;
+	      }
+	      pos += length;
+	    }
+	}
+    }
+
+  return list;
+}
+
+/**
+ * aim_freetlvchain - Free a TLV chain structure
+ * @list: Chain to be freed
+ *
+ * Walks the list of TLVs in the passed TLV chain and
+ * frees each one. Note that any references to this data
+ * should be removed before calling this.
+ *
+ */
+faim_export 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;
+}
+
+/**
+ * aim_counttlvchain - Count the number of TLVs in a chain
+ * @list: Chain to be counted
+ *
+ * Returns the number of TLVs stored in the passed chain.
+ *
+ */
+faim_export 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;
+}
+
+/**
+ * aim_sizetlvchain - Count the number of bytes in a TLV chain
+ * @list: Chain to be sized
+ *
+ * Returns the number of bytes that would be needed to 
+ * write the passed TLV chain to a data buffer.
+ *
+ */
+faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list)
+{
+  struct aim_tlvlist_t *cur;
+  int size = 0;
+
+  if (!list || !(*list))
+    return 0;
+
+  for (cur = *list; cur; cur = cur->next)
+    size += (4 + cur->tlv->length);
+ 
+  return size;
+}
+
+/**
+ * aim_addtlvtochain_str - Add a string to a TLV chain
+ * @list: Desination chain (%NULL pointer if empty)
+ * @type: TLV type
+ * @str: String to add
+ * @len: Length of string to add (not including %NULL)
+ *
+ * Adds the passed string as a TLV element of the passed type
+ * to the TLV chain.
+ *
+ */
+faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len)
+{
+  struct aim_tlvlist_t *newtlv;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
+
+  newtlv->tlv = aim_createtlv();	
+  newtlv->tlv->type = type;
+  newtlv->tlv->length = len;
+  newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char));
+  memcpy(newtlv->tlv->value, str, newtlv->tlv->length);
+
+  newtlv->next = NULL;
+
+  if (*list == NULL) {
+    *list = newtlv;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = newtlv;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = newtlv;
+  }
+  return newtlv->tlv->length;
+}
+
+/**
+ * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a two-byte unsigned integer to a TLV chain.
+ *
+ */
+faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val)
+{
+  struct aim_tlvlist_t *newtl;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
+
+  newtl->tlv = aim_createtlv();	
+  newtl->tlv->type = type;
+  newtl->tlv->length = 2;
+  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
+  aimutil_put16(newtl->tlv->value, val);
+
+  newtl->next = NULL;
+
+  if (*list == NULL) {
+    *list = newtl;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = newtl;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = newtl;
+  }
+  return 2;
+}
+
+/**
+ * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a four-byte unsigned integer to a TLV chain.
+ *
+ */
+faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val)
+{
+  struct aim_tlvlist_t *newtl;
+  struct aim_tlvlist_t *cur;
+
+  if (!list)
+    return 0;
+
+  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
+
+  newtl->tlv = aim_createtlv();	
+  newtl->tlv->type = type;
+  newtl->tlv->length = 4;
+  newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char));
+  aimutil_put32(newtl->tlv->value, val);
+
+  newtl->next = NULL;
+
+  if (*list == NULL) {
+    *list = newtl;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = newtl;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = newtl;
+  }
+  return 4;
+}
+
+/**
+ * aim_addtlvtochain_caps - Add a capability block to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @caps: Bitfield of capability flags to send
+ *
+ * Adds a block of capability blocks to a TLV chain. The bitfield
+ * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
+ *
+ *      %AIM_CAPS_BUDDYICON   Supports Buddy Icons
+ *
+ *      %AIM_CAPS_VOICE       Supports Voice Chat
+ *
+ *      %AIM_CAPS_IMIMAGE     Supports DirectIM/IMImage
+ *
+ *      %AIM_CAPS_CHAT        Supports Chat
+ *
+ *      %AIM_CAPS_GETFILE     Supports Get File functions
+ *
+ *      %AIM_CAPS_SENDFILE    Supports Send File functions
+ *
+ */
+faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps)
+{
+  unsigned char buf[128]; /* icky fixed length buffer */
+  struct aim_tlvlist_t *newtl;
+  struct aim_tlvlist_t *cur;
+
+  if(!list)
+    return 0;
+
+  newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(newtl, 0x00, sizeof(struct aim_tlvlist_t));
+
+  newtl->tlv = aim_createtlv();	
+  newtl->tlv->type = type;
+
+  newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps);
+  newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length);
+  memcpy(newtl->tlv->value, buf, newtl->tlv->length);
+
+  newtl->next = NULL;
+
+  if (*list == NULL) {
+    *list = newtl;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = newtl;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = newtl;
+  }
+  return newtl->tlv->length;
+}
+
+/**
+ * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ *
+ * Adds a TLV with a zero length to a TLV chain.
+ *
+ */
+faim_internal int aim_addtlvtochain_noval(struct aim_tlvlist_t **list, unsigned short type)
+{
+  struct aim_tlvlist_t *newtlv;
+  struct aim_tlvlist_t *cur;
+
+  newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t));
+  memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t));
+
+  newtlv->tlv = aim_createtlv();	
+  newtlv->tlv->type = type;
+  newtlv->tlv->length = 0;
+  newtlv->tlv->value = NULL;
+
+  newtlv->next = NULL;
+
+  if (*list == NULL) {
+    *list = newtlv;
+  } else if ((*list)->next == NULL) {
+    (*list)->next = newtlv;
+  } else {
+    for(cur = *list; cur->next; cur = cur->next)
+      ;
+    cur->next = newtlv;
+  }
+  return newtlv->tlv->length;
+}
+
+/**
+ * aim_writetlvchain - Write a TLV chain into a data buffer.
+ * @buf: Destination buffer
+ * @buflen: Maximum number of bytes that will be written to buffer
+ * @list: Source TLV chain
+ *
+ * Copies a TLV chain into a raw data buffer, writing only the number
+ * of bytes specified. This operation does not free the chain; 
+ * aim_freetlvchain() must still be called to free up the memory used
+ * by the chain structures.
+ *
+ */
+faim_export 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;
+}
+
+
+/**
+ * aim_gettlv - Grab the Nth TLV of type type in the TLV list list.
+ * @list: Source chain
+ * @type: Requested TLV type
+ * @nth: Index of TLV of type to get
+ *
+ * Returns a pointer to an aim_tlv_t of the specified type; 
+ * %NULL on error.  The @nth parameter is specified starting at %1.
+ * In most cases, there will be no more than one TLV of any type
+ * in a chain.
+ *
+ */
+faim_export 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;
+}
+
+/**
+ * aim_gettlv_str - Retrieve the Nth TLV in chain as a string.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a %NULL-
+ * terminated string instead of an aim_tlv_t.  This is a 
+ * dynamic buffer and must be freed by the caller.
+ *
+ */
+faim_export 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;
+}
+
+/**
+ * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 8bit integer instead of an aim_tlv_t. 
+ *
+ */
+faim_internal unsigned char aim_gettlv8(struct aim_tlvlist_t *list, unsigned short type, int num)
+{
+  struct aim_tlv_t *tlv;
+
+  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
+    return 0; /* erm */
+  return aimutil_get8(tlv->value);
+}
+
+/**
+ * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 16bit integer instead of an aim_tlv_t. 
+ *
+ */
+faim_internal unsigned short aim_gettlv16(struct aim_tlvlist_t *list, unsigned short type, int num)
+{
+  struct aim_tlv_t *tlv;
+
+  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
+    return 0; /* erm */
+  return aimutil_get16(tlv->value);
+}
+
+/**
+ * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 32bit integer instead of an aim_tlv_t. 
+ *
+ */
+faim_internal unsigned long aim_gettlv32(struct aim_tlvlist_t *list, unsigned short type, int num)
+{
+  struct aim_tlv_t *tlv;
+
+  if (!(tlv = aim_gettlv(list, type, num)) || !tlv->value)
+    return 0; /* erm */
+  return aimutil_get32(tlv->value);
+}
+
+/**
+ * aim_grabtlv - Grab a single TLV from a data buffer
+ * @src: Source data buffer (must be at least 4 bytes long)
+ *
+ * Creates a TLV structure aim_tlv_t and returns it
+ * filled with values from a buffer, possibly including a 
+ * dynamically allocated buffer for the value portion.
+ *
+ * Both the aim_tlv_t and the tlv->value pointer
+ * must be freed by the caller if non-%NULL.
+ *
+ */
+faim_export struct aim_tlv_t *aim_grabtlv(u_char *src)
+{
+  struct aim_tlv_t *dest = NULL;
+
+  dest = aim_createtlv();
+
+  dest->type = src[0] << 8;
+  dest->type += src[1];
+
+  dest->length = src[2] << 8;
+  dest->length += src[3];
+
+  dest->value = (u_char *) malloc(dest->length*sizeof(u_char));
+  memset(dest->value, 0, dest->length*sizeof(u_char));
+
+  memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char));
+  
+  return dest;
+}
+
+/**
+ * aim_grabtlvstr - Grab a single TLV from a data buffer as string
+ * @src: Source data buffer (must be at least 4 bytes long)
+ *
+ * Creates a TLV structure aim_tlv_t and returns it
+ * filled with values from a buffer, possibly including a 
+ * dynamically allocated buffer for the value portion, which 
+ * is %NULL-terminated as a string.
+ *
+ * Both the aim_tlv_t and the tlv->value pointer
+ * must be freed by the caller if non-%NULL.
+ *
+ */
+faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src)
+{
+  struct aim_tlv_t *dest = NULL;
+
+  dest = aim_createtlv();
+
+  dest->type = src[0] << 8;
+  dest->type += src[1];
+
+  dest->length = src[2] << 8;
+  dest->length += src[3];
+
+  dest->value = (u_char *) malloc((dest->length+1)*sizeof(u_char));
+  memset(dest->value, 0, (dest->length+1)*sizeof(u_char));
+
+  memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char));
+  dest->value[dest->length] = '\0';
+
+  return dest;
+}
+
+/**
+ * aim_puttlv - Write a aim_tlv_t into a data buffer
+ * @dest: Destination data buffer
+ * @newtlv: Source TLV structure
+ *
+ * Writes out the passed TLV structure into the buffer. No bounds
+ * checking is done on the output buffer.
+ *
+ * The passed aim_tlv_t is not freed. aim_freetlv() should
+ * still be called by the caller to free the structure.
+ *
+ */
+faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv)
+{
+  int i=0;
+
+  dest[i++] = newtlv->type >> 8;
+  dest[i++] = newtlv->type & 0x00FF;
+  dest[i++] = newtlv->length >> 8;
+  dest[i++] = newtlv->length & 0x00FF;
+  memcpy(&(dest[i]), newtlv->value, newtlv->length);
+  i+=newtlv->length;
+  return i;
+}
+
+/**
+ * aim_createtlv - Generate an aim_tlv_t structure.
+ * 
+ * Allocates an empty TLV structure and returns a pointer
+ * to it; %NULL on error.
+ *
+ */
+faim_export struct aim_tlv_t *aim_createtlv(void)
+{
+  struct aim_tlv_t *newtlv;
+
+  if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t))))
+    return NULL;
+  memset(newtlv, 0, sizeof(struct aim_tlv_t));
+  return newtlv;
+}
+
+/**
+ * aim_freetlv - Free a aim_tlv_t structure
+ * @oldtlv: TLV to be destroyed
+ *
+ * Frees both the TLV structure and the value portion.
+ *
+ */
+faim_export int aim_freetlv(struct aim_tlv_t **oldtlv)
+{
+  if (!oldtlv)
+    return -1;
+  if (!*oldtlv)
+    return -1;
+  if ((*oldtlv)->value)
+    free((*oldtlv)->value);
+  free(*(oldtlv));
+  (*oldtlv) = NULL;
+
+  return 0;
+}
+
+/**
+ * aim_puttlv_8 - Write a one-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a one-byte integer value portion.
+ *
+ */
+faim_export int aim_puttlv_8(unsigned char *buf, unsigned short t, unsigned char  v)
+{
+  int curbyte=0;
+
+  curbyte += aimutil_put16(buf+curbyte, (unsigned short)(t&0xffff));
+  curbyte += aimutil_put16(buf+curbyte, (unsigned short)0x0001);
+  curbyte += aimutil_put8(buf+curbyte, (unsigned char)(v&0xff));
+
+  return curbyte;
+}
+
+/**
+ * aim_puttlv_16 - Write a two-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a two-byte integer value portion.
+ *
+ */
+faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v)
+{
+  int curbyte=0;
+  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;
+}
+
+/**
+ * aim_puttlv_32 - Write a four-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a four-byte integer value portion.
+ *
+ */
+faim_export 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;
+}
+
+/**
+ * aim_puttlv_str - Write a string TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @l: Length of string
+ * @v: String to write
+ *
+ * Writes a TLV with a string value portion.  (Only the first @l
+ * bytes of the passed string will be written, which should not
+ * include the terminating NULL.)
+ *
+ */
+faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, 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, (unsigned char *)v, l);
+  curbyte += l;
+  return curbyte;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/txqueue.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,431 @@
+/*
+ *  aim_txqueue.c
+ *
+ * Herein lies all the mangement routines for the transmit (Tx) queue.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ * 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.
+ *
+ * framing = AIM_FRAMETYPE_OFT/OSCAR
+ * chan = channel for OSCAR, hdrtype for OFT
+ *
+ */
+faim_internal struct command_tx_struct *aim_tx_new(struct aim_session_t *sess, struct aim_conn_t *conn, unsigned char framing, int chan, int datalen)
+{
+  struct command_tx_struct *newtx;
+
+  if (!conn) {
+    faimdprintf(sess, 0, "aim_tx_new: ERROR: no connection specified\n");
+    return NULL;
+  }
+
+  /* For sanity... */
+  if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
+    if (framing != AIM_FRAMETYPE_OFT) {
+      faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n");
+      return NULL;
+    }
+  } else {
+    if (framing != AIM_FRAMETYPE_OSCAR) {
+      faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n");
+      return NULL;
+    }
+  }
+
+  newtx = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
+  if (!newtx)
+    return NULL;
+  memset(newtx, 0, sizeof(struct command_tx_struct));
+
+  newtx->conn = conn; 
+
+  if(datalen) {
+    newtx->data = (unsigned char *)malloc(datalen);
+    newtx->commandlen = datalen;
+  } else
+    newtx->data = NULL;
+
+  newtx->hdrtype = framing;
+  if (newtx->hdrtype == AIM_FRAMETYPE_OSCAR) {
+    newtx->hdr.oscar.type = chan;
+  } else if (newtx->hdrtype == AIM_FRAMETYPE_OFT) {
+    newtx->hdr.oft.type = chan;
+    newtx->hdr.oft.hdr2len = 0; /* this will get setup by caller */
+  } else { 
+    faimdprintf(sess, 0, "tx_new: unknown framing\n");
+  }
+
+  return newtx;
+}
+
+/*
+ * aim_tx_enqeue__queuebased()
+ *
+ * The overall purpose here is to enqueue the passed in command struct
+ * into the outgoing (tx) queue.  Basically...
+ *   1) Make a scope-irrelevent copy of the struct
+ *   2) Lock the struct
+ *   3) Mark as not-sent-yet
+ *   4) Enqueue the struct into the list
+ *   5) Unlock the struct once it's linked in
+ *   6) Return
+ *
+ * Note that this is only used when doing queue-based transmitting;
+ * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
+ *
+ */
+static int aim_tx_enqueue__queuebased(struct aim_session_t *sess, struct command_tx_struct *newpacket)
+{
+  struct command_tx_struct *cur;
+
+  if (newpacket->conn == NULL) {
+      faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
+      newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+  }
+ 
+  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR) {
+    /* assign seqnum */
+    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
+  }
+  /* set some more fields */
+  newpacket->lock = 1; /* lock */
+  newpacket->sent = 0; /* not sent yet */
+  newpacket->next = NULL; /* always last */
+
+  /* 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 */
+
+  return 0;
+}
+
+/*
+ * aim_tx_enqueue__immediate()
+ *
+ * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
+ * the whole queue mess when you want immediate writes to happen.
+ *
+ * Basically the same as its __queuebased couterpart, however
+ * instead of doing a list append, it just calls aim_tx_sendframe()
+ * right here. 
+ * 
+ */
+static int aim_tx_enqueue__immediate(struct aim_session_t *sess, struct command_tx_struct *newpacket)
+{
+  if (newpacket->conn == NULL) {
+    faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n");
+    if (newpacket->data)
+      free(newpacket->data);
+    free(newpacket);
+    return -1;
+  }
+
+  if (newpacket->hdrtype == AIM_FRAMETYPE_OSCAR)
+    newpacket->hdr.oscar.seqnum = aim_get_next_txseqnum(newpacket->conn);
+
+  newpacket->lock = 1; /* lock */
+  newpacket->sent = 0; /* not sent yet */
+
+  aim_tx_sendframe(sess, newpacket);
+
+  if (newpacket->data)
+    free(newpacket->data);
+  free(newpacket);
+
+  return 0;
+}
+
+faim_export int aim_tx_setenqueue(struct aim_session_t *sess, int what,  int (*func)(struct aim_session_t *, struct command_tx_struct *))
+{
+  if (!sess)
+    return -1;
+
+  if (what == AIM_TX_QUEUED)
+    sess->tx_enqueue = &aim_tx_enqueue__queuebased;
+  else if (what == AIM_TX_IMMEDIATE) 
+    sess->tx_enqueue = &aim_tx_enqueue__immediate;
+  else if (what == AIM_TX_USER) {
+    if (!func)
+      return -1;
+    sess->tx_enqueue = func;
+  } else
+    return -1; /* unknown action */
+
+  return 0;
+}
+
+faim_internal int aim_tx_enqueue(struct aim_session_t *sess, struct command_tx_struct *command)
+{
+  /*
+   * If we want to send a connection thats inprogress, we have to force
+   * them to use the queue based version. Otherwise, use whatever they
+   * want.
+   */
+  if (command && command->conn && (command->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
+    return aim_tx_enqueue__queuebased(sess, command);
+  }
+  return (*sess->tx_enqueue)(sess, command);
+}
+
+/* 
+ *  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()).
+ *
+ */
+faim_internal unsigned int aim_get_next_txseqnum(struct aim_conn_t *conn)
+{
+  u_int ret;
+  
+  faim_mutex_lock(&conn->seqnum_lock);
+  ret = ++conn->seqnum;
+  faim_mutex_unlock(&conn->seqnum_lock);
+  return ret;
+}
+
+/*
+ *  aim_tx_flushqueue()
+ *
+ *  This the function is responsable for putting the queued commands
+ *  onto the wire.  This function is critical to the operation of 
+ *  the queue and therefore is the most prone to brokenness.  It
+ *  seems to be working quite well at this point.
+ *
+ *  Procedure:
+ *    1) Traverse the list, only operate on commands that are unlocked
+ *       and haven't been sent yet.
+ *    2) Lock the struct
+ *    3) Allocate a temporary buffer to store the finished, fully
+ *       processed packet in.
+ *    4) Build the packet from the command_tx_struct data.
+ *    5) Write the packet to the socket.
+ *    6) If success, mark the packet sent, if fail report failure, do NOT
+ *       mark the packet sent (so it will not get purged and therefore
+ *       be attempted again on next call).
+ *    7) Unlock the struct.
+ *    8) Free the temp buffer
+ *    9) Step to next struct in list and go back to 1.
+ *
+ */
+faim_internal int aim_tx_sendframe(struct aim_session_t *sess, struct command_tx_struct *cur)
+{
+  int buflen = 0;
+  unsigned char *curPacket;
+
+  if (!cur)
+    return -1; /* fatal */
+
+  cur->lock = 1; /* lock the struct */
+
+  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR)
+    buflen = cur->commandlen + 6;
+  else if (cur->hdrtype == AIM_FRAMETYPE_OFT)
+    buflen = cur->hdr.oft.hdr2len + 8;
+  else {
+    cur->lock = 0;
+    return -1;
+  }
+
+  /* allocate full-packet buffer */
+  if (!(curPacket = (unsigned char *) malloc(buflen))) {
+    cur->lock = 0;
+    return -1;
+  }
+      
+  if (cur->hdrtype == AIM_FRAMETYPE_OSCAR) {
+    /* command byte */
+    curPacket[0] = 0x2a;
+      
+    /* type/family byte */
+    curPacket[1] = cur->hdr.oscar.type;
+      
+    /* bytes 3+4: word: FLAP sequence number */
+    aimutil_put16(curPacket+2, cur->hdr.oscar.seqnum);
+
+    /* bytes 5+6: word: SNAC len */
+    aimutil_put16(curPacket+4, cur->commandlen);
+      
+    /* bytes 7 and on: raw: SNAC data */  /* XXX: ye gods! get rid of this! */
+    memcpy(&(curPacket[6]), cur->data, cur->commandlen);
+
+  } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
+    int z = 0;
+
+    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[0]);
+    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[1]);
+    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[2]);
+    z += aimutil_put8(curPacket+z, cur->hdr.oft.magic[3]);
+
+    z += aimutil_put16(curPacket+z, cur->hdr.oft.hdr2len + 8);
+    z += aimutil_put16(curPacket+z, cur->hdr.oft.type);
+
+    memcpy(curPacket+z, cur->hdr.oft.hdr2, cur->hdr.oft.hdr2len);
+  }
+
+  /* 
+   * For OSCAR, a full image of the raw packet data now in curPacket.
+   * For OFT, an image of just the bloated header is in curPacket, 
+   * since OFT allows us to do the data in a different write (yay!).
+   */
+  faim_mutex_lock(&cur->conn->active);
+  if (send(cur->conn->fd, curPacket, buflen, 0) != buflen) {
+    faim_mutex_unlock(&cur->conn->active);
+    cur->sent = 1;
+    aim_conn_close(cur->conn);
+    return 0; /* bail out */
+  }
+
+  if ((cur->hdrtype == AIM_FRAMETYPE_OFT) && cur->commandlen) {
+    int curposi;
+    for(curposi = 0; curposi < cur->commandlen; curposi++)
+      faimdprintf(sess, 0, "%02x ", cur->data[curposi]);
+
+    if (send(cur->conn->fd, cur->data, cur->commandlen, 0) != (int)cur->commandlen) {
+      /* 
+       * Theres nothing we can do about this since we've already sent the 
+       * header!  The connection is unstable.
+       */
+      faim_mutex_unlock(&cur->conn->active);
+      cur->sent = 1;
+      aim_conn_close(cur->conn);
+      return 0; /* bail out */
+    }
+
+  }
+
+  cur->sent = 1; /* mark the struct as sent */
+  cur->conn->lastactivity = time(NULL);
+
+  faim_mutex_unlock(&cur->conn->active);
+
+  if (sess->debug >= 2) {
+    int i;
+
+    faimdprintf(sess, 2, "\nOutgoing packet: (only valid for OSCAR)");
+    for (i = 0; i < buflen; i++) {
+      if (!(i % 8)) 
+	faimdprintf(sess, 2, "\n\t");
+      faimdprintf(sess, 2, "0x%02x ", curPacket[i]);
+    }
+    faimdprintf(sess, 2, "\n");
+  }
+
+  cur->lock = 0; /* unlock the struct */
+
+  free(curPacket); /* free up full-packet buffer */
+
+  return 1; /* success */
+}
+
+faim_export int aim_tx_flushqueue(struct aim_session_t *sess)
+{
+  struct command_tx_struct *cur;
+   
+  if (sess->queue_outgoing == NULL)
+    return 0;
+
+  faimdprintf(sess, 2, "beginning txflush...\n");
+  for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+    /* only process if its unlocked and unsent */
+    if (!cur->lock && !cur->sent) {
+
+      if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
+	continue;
+
+      /*
+       * 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));
+      }
+
+      /* XXX XXX XXX this should call the custom "queuing" function!! */
+      if (aim_tx_sendframe(sess, cur) == -1)
+	break;
+    }
+  }
+
+  /* purge sent commands from queue */
+  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!  
+ *
+ */
+faim_export void aim_tx_purgequeue(struct aim_session_t *sess)
+{
+  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;
+      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
+	free(tmp->hdr.oft.hdr2);
+      free(tmp->data);
+      free(tmp);
+    }
+    return;
+  }
+
+  for(cur = sess->queue_outgoing; cur->next != NULL; ) {
+    if (!cur->next->lock && cur->next->sent) {
+      tmp = cur->next;
+      cur->next = tmp->next;
+      if (tmp->hdrtype == AIM_FRAMETYPE_OFT)
+	free(tmp->hdr.oft.hdr2);
+      free(tmp->data);
+      free(tmp);
+    }	
+    cur = cur->next;
+
+    /* 
+     * 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfaim/util.c	Mon Mar 05 03:59:32 2001 +0000
@@ -0,0 +1,265 @@
+/*
+ *
+ *
+ *
+ */
+
+#include <aim.h>
+#include <ctype.h>
+
+#ifdef AIMUTIL_USEMACROS
+/* macros in faim/aim.h */
+#else
+faim_shortfunc int aimutil_put8(u_char *buf, u_char data)
+{
+  buf[0] = (u_char)data&0xff;
+  return 1;
+}
+
+faim_shortfunc u_char aimutil_get8(u_char *buf)
+{
+  return buf[0];
+}
+
+/*
+ * Endian-ness issues here?
+ */
+faim_shortfunc int aimutil_put16(u_char *buf, u_short data)
+{
+  buf[0] = (u_char)(data>>8)&0xff;
+  buf[1] = (u_char)(data)&0xff;
+  return 2;
+}
+
+faim_shortfunc u_short aimutil_get16(u_char *buf)
+{
+  u_short val;
+  val = (buf[0] << 8) & 0xff00;
+  val+= (buf[1]) & 0xff;
+  return val;
+}
+
+faim_shortfunc int aimutil_put32(u_char *buf, u_long data)
+{
+  buf[0] = (u_char)(data>>24)&0xff;
+  buf[1] = (u_char)(data>>16)&0xff;
+  buf[2] = (u_char)(data>>8)&0xff;
+  buf[3] = (u_char)(data)&0xff;
+  return 4;
+}
+
+faim_shortfunc u_long aimutil_get32(u_char *buf)
+{
+  u_long val;
+  val = (buf[0] << 24) & 0xff000000;
+  val+= (buf[1] << 16) & 0x00ff0000;
+  val+= (buf[2] <<  8) & 0x0000ff00;
+  val+= (buf[3]      ) & 0x000000ff;
+  return val;
+}
+#endif /* AIMUTIL_USEMACROS */
+
+faim_export faim_shortfunc int aimutil_putstr(u_char *dest, const char *src, int len)
+{
+  memcpy(dest, src, len);
+  return len;
+}
+
+/*
+ * Tokenizing functions.  Used to portably replace strtok/sep.
+ *   -- DMP.
+ *
+ */
+faim_export 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;
+}
+
+faim_export int aimutil_itemcnt(char *toSearch, char dl)
+{
+  int curCount;
+  char *next;
+  
+  curCount = 1;
+  
+  next = strchr(toSearch, dl);
+  
+  while(next != NULL)
+    {
+      curCount++;
+      next = strchr(next + 1, dl);
+    }
+  
+  return curCount;
+}
+
+faim_export char *aimutil_itemidx(char *toSearch, int index, char dl)
+{
+  int curCount;
+  char *next;
+  char *last;
+  char *toReturn;
+  
+  curCount = 0;
+  
+  last = toSearch;
+  next = strchr(toSearch, dl);
+  
+  while(curCount < index && next != NULL)
+    {
+      curCount++;
+      last = next + 1;
+      next = strchr(last, dl);
+    }
+  
+  if (curCount < index)
+    {
+      toReturn = malloc(sizeof(char));
+      *toReturn = '\0';
+    }
+  next = strchr(last, dl);
+  
+  if (curCount < index)
+    {
+      toReturn = malloc(sizeof(char));
+      *toReturn = '\0';
+    }
+  else
+    {
+      if (next == NULL)
+	{
+	  toReturn = malloc((strlen(last) + 1) * sizeof(char));
+	  strcpy(toReturn, last);
+	}
+      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().
+ *
+ */
+faim_export 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
+ *
+ */
+
+faim_export 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;
+}
+
+/* strsep Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+   strsep is part of the GNU C Library.
+   
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+   
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+   Cambridge, MA 02139, USA.  */
+
+/* Minor changes by and1000 on 15/1/97 to make it go under Nemesis */
+
+faim_export char *aim_strsep(char **pp, const char *delim)
+{
+  char *p, *q;
+  
+  if (!(p = *pp))
+    return 0;
+  
+  if ((q = strpbrk (p, delim)))
+    {
+      *pp = q + 1;
+      *q = '\0';
+    }
+  else
+    *pp = 0;
+  
+  return p;
+}
--- a/src/dialogs.c	Sun Mar 04 22:37:18 2001 +0000
+++ b/src/dialogs.c	Mon Mar 05 03:59:32 2001 +0000
@@ -141,6 +141,7 @@
 struct findbyemail {
 	GtkWidget *window;
 	GtkWidget *emailentry;
+	struct gaim_connection *gc;
 };
 
 struct findbyinfo {
@@ -2008,8 +2009,9 @@
 
 	email = gtk_entry_get_text(GTK_ENTRY(b->emailentry));
 	
-	/* FIXME : email search. not sure if even works; not important */
-	if (connections)
+	if (b->gc)
+		serv_dir_search(b->gc, "","","","","","","", email);
+	else
 		serv_dir_search(connections->data, "","","","","","","", email);
  
 	destroy_dialog(NULL, b->window);
@@ -2175,7 +2177,7 @@
         gtk_widget_show(b->window);
 }        
 
-void show_find_email()
+void show_find_email(struct gaim_connection *gc)
 {
         GtkWidget *label;
         GtkWidget *bbox;
@@ -2185,6 +2187,8 @@
 	GtkWidget *button;
 
         struct findbyemail *b = g_new0(struct findbyemail, 1);
+	if (g_slist_find(connections, gc))
+		b->gc = gc;
         b->window = gtk_window_new(GTK_WINDOW_DIALOG);
 	gtk_window_set_policy(GTK_WINDOW(b->window), FALSE, TRUE, TRUE);
 	gtk_window_set_wmclass(GTK_WINDOW(b->window), "find_email", "Gaim");
--- a/src/gaim.h	Sun Mar 04 22:37:18 2001 +0000
+++ b/src/gaim.h	Mon Mar 05 03:59:32 2001 +0000
@@ -771,7 +771,7 @@
 extern void show_import_dialog();
 extern void show_new_bp();
 extern void show_log_dialog(struct conversation *);
-extern void show_find_email();
+extern void show_find_email(struct gaim_connection *gc);
 extern void show_find_info();
 extern void g_show_info (struct aim_user *, char *);
 extern void g_show_info_text (char *);
--- a/src/oscar.c	Sun Mar 04 22:37:18 2001 +0000
+++ b/src/oscar.c	Mon Mar 05 03:59:32 2001 +0000
@@ -40,7 +40,7 @@
 #include "multi.h"
 #include "prpl.h"
 #include "gaim.h"
-#include "faim/aim.h"
+#include "aim.h"
 
 #include "pixmaps/cancel.xpm"
 #include "pixmaps/admin_icon.xpm"
@@ -61,12 +61,18 @@
 	struct aim_session_t *sess;
 	struct aim_conn_t *conn;
 
-	int cnpa;
-	int paspa;
+	guint cnpa;
+	guint paspa;
 
 	int create_exchange;
 	char *create_name;
 
+	gboolean conf;
+	gboolean reqemail;
+	gboolean chpass;
+	char *oldp;
+	char *newp;
+
 	GSList *oscar_chats;
 	GSList *direct_ims;
 	GSList *getfiles;
@@ -183,21 +189,6 @@
 	return c;
 }
 
-static struct gaim_connection *find_gaim_conn_by_aim_sess(struct aim_session_t *sess) {
-	GSList *g = connections;
-	struct gaim_connection *gc = NULL;
-
-	while (g) {
-		gc = (struct gaim_connection *)g->data;
-		if (sess == ((struct oscar_data *)gc->proto_data)->sess)
-			break;
-		g = g->next;
-		gc = NULL;
-	}
-
-	return gc;
-}
-
 static struct gaim_connection *find_gaim_conn_by_oscar_conn(struct aim_conn_t *conn) {
 	GSList *g = connections;
 	struct gaim_connection *c = NULL;
@@ -227,6 +218,8 @@
 static int gaim_parse_login      (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_server_ready     (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_handle_redirect  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_info_change      (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_account_confirm  (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_parse_oncoming   (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_parse_offgoing   (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...);
@@ -241,6 +234,8 @@
 static int gaim_parse_msgack     (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_parse_ratechange (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_parse_evilnotify (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_bosrights        (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_rateresp         (struct aim_session_t *, struct command_rx_struct *, ...);
 static int gaim_reportinterval   (struct aim_session_t *, struct command_rx_struct *, ...);
@@ -320,19 +315,25 @@
 					char buf[BUF_LONG];
 					debug_printf("disconnected from chat room %s\n", c->name);
 					c->conn = NULL;
-					if (c->inpa > -1)
+					if (c->inpa > 0)
 						gdk_input_remove(c->inpa);
-					c->inpa = -1;
+					c->inpa = 0;
 					c->fd = -1;
 					aim_conn_kill(odata->sess, &conn);
 					sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
 					do_error_dialog(buf, _("Chat Error!"));
 				} else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
-					if (odata->cnpa > -1)
+					if (odata->cnpa > 0)
 						gdk_input_remove(odata->cnpa);
-					odata->cnpa = -1;
+					odata->cnpa = 0;
 					debug_printf("removing chatnav input watcher\n");
 					aim_conn_kill(odata->sess, &conn);
+				} else if (conn->type == AIM_CONN_TYPE_AUTH) {
+					if (odata->paspa > 0)
+						gdk_input_remove(odata->paspa);
+					odata->paspa = 0;
+					debug_printf("removing authconn input watcher\n");
+					aim_conn_kill(odata->sess, &conn);
 				} else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
 					debug_printf("No handler for rendezvous disconnect (%d).\n",
 							source);
@@ -347,6 +348,21 @@
 	}
 }
 
+static void oscar_debug(struct aim_session_t *sess, int level, const char *format, va_list va) {
+	char *s = g_strdup_vprintf(format, va);
+	char buf[256];
+	char *t;
+	struct gaim_connection *gc = sess->aux_data;
+
+	g_snprintf(buf, sizeof(buf), "%s %d: ", gc->username, level);
+	t = g_strconcat(buf, s, NULL);
+	debug_printf(t);
+	if (t[strlen(t)-1] != '\n')
+		debug_printf("\n");
+	g_free(t);
+	g_free(s);
+}
+
 void oscar_login(struct aim_user *user) {
 	struct aim_session_t *sess;
 	struct aim_conn_t *conn;
@@ -360,7 +376,8 @@
 
 	sess = g_new0(struct aim_session_t, 1);
 
-	aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT);
+	aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
+	aim_setdebuggingcb(sess, oscar_debug);
 
 	if (user->proto_opt[USEROPT_SOCKSHOST][0]) {
 		char *finalproxy;
@@ -383,8 +400,9 @@
 
 	/* we need an immediate queue because we don't use a while-loop to
 	 * see if things need to be sent. */
-	sess->tx_enqueue = &aim_tx_enqueue__immediate;
+	aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
 	odata->sess = sess;
+	sess->aux_data = gc;
 
 	conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, finalauth ? finalauth : FAIM_LOGIN_SERVER);
 
@@ -459,7 +477,7 @@
 	char *latestreleaseurl = NULL, *latestbetaurl = NULL;
 	char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
 
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	sn = va_arg(ap, char *);
@@ -555,11 +573,11 @@
 	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_ratechange, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 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_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_CTN, AIM_CB_CTN_DEFAULT, aim_parse_unknown, 0);
-	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEFAULT, aim_parse_unknown, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0);
 	aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0);
@@ -578,7 +596,7 @@
 	struct client_info_s info = {"AOL Instant Messenger (SM), version 4.1.2010/WIN32", 4, 30, 3141, "us", "en", 0x0004, 0x0001, 0x055};
 	char *key;
 	va_list ap;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	key = va_arg(ap, char *);
@@ -591,9 +609,32 @@
 int gaim_server_ready(struct aim_session_t *sess,
 		      struct command_rx_struct *command, ...) {
 	static int id = 1;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
 	struct chat_connection *chatcon;
 	switch (command->conn->type) {
+	case AIM_CONN_TYPE_AUTH:
+		aim_auth_setversions(sess, command->conn);
+		aim_bos_reqrate(sess, command->conn);
+		debug_printf("done with AUTH ServerReady\n");
+		if (od->chpass) {
+			debug_printf("changing password\n");
+			aim_auth_changepasswd(sess, command->conn, od->newp, od->oldp);
+			g_free(od->oldp);
+			g_free(od->newp);
+			od->chpass = FALSE;
+		}
+		if (od->conf) {
+			debug_printf("confirming account\n");
+			aim_auth_reqconfirm(sess, command->conn);
+			od->conf = FALSE;
+		}
+		if (od->reqemail) {
+			debug_printf("requesting email\n");
+			aim_auth_getinfo(sess, command->conn, 0x0011);
+			od->reqemail = FALSE;
+		}
+		break;
 	case AIM_CONN_TYPE_BOS:
 		aim_setversions(sess, command->conn);
 		aim_bos_reqrate(sess, command->conn); /* request rate info */
@@ -635,7 +676,7 @@
 	int serviceid;
 	char *ip;
 	unsigned char *cookie;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
 
 	va_start(ap, command);
@@ -651,6 +692,11 @@
 		if (tstconn == NULL || tstconn->status & AIM_CONN_STATUS_RESOLVERR)
 			debug_printf("unable to reconnect with authorizer\n");
 		else {
+			aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
+			aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, gaim_rateresp, 0);
+			aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
+			aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
+			aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
 			odata->paspa = gdk_input_add(tstconn->fd,
 					GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 					oscar_callback, tstconn);
@@ -716,7 +762,7 @@
 	struct aim_userinfo_s *info;
 	time_t time_idle;
 	int type = 0;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_list ap;
 	va_start(ap, command);
@@ -750,7 +796,7 @@
 			struct command_rx_struct *command, ...) {
 	char *sn;
 	va_list ap;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	sn = va_arg(ap, char *);
@@ -834,6 +880,7 @@
 	return TRUE;
 }
 
+/*
 static void cancel_getfile(gpointer w, struct ask_getfile *g) {
 	g_free(g->ip);
 	g_free(g->cookie);
@@ -869,7 +916,7 @@
 }
 
 static int gaim_getfile_filereq(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct getfile_transfer *gt;
 	char buf[2048];
@@ -935,7 +982,7 @@
 }
 
 static int gaim_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct getfile_transfer *gt;
 
@@ -971,7 +1018,7 @@
 }
 
 static int gaim_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct getfile_transfer *gt;
 
@@ -994,7 +1041,7 @@
 }
 
 static int gaim_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct getfile_transfer *gt;
 
@@ -1117,12 +1164,13 @@
 
 	return TRUE;
 }
+*/
 
 int gaim_parse_incoming_im(struct aim_session_t *sess,
 			   struct command_rx_struct *command, ...) {
 	int channel;
 	va_list ap;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	channel = va_arg(ap, int);
@@ -1166,6 +1214,7 @@
 					     msg);
 		} else if (rendtype & AIM_CAPS_SENDFILE) {
 		} else if (rendtype & AIM_CAPS_GETFILE) {
+			/*
 			char *ip, *cookie;
 			struct ask_getfile *g = g_new0(struct ask_getfile, 1);
 			char buf[256];
@@ -1185,6 +1234,7 @@
 			g_snprintf(buf, sizeof buf, "%s has just asked to get a file from %s.",
 					userinfo->sn, gc->username);
 			do_ask_dialog(buf, g, accept_getfile, cancel_getfile);
+			*/
 		} else if (rendtype & AIM_CAPS_VOICE) {
 		} else if (rendtype & AIM_CAPS_BUDDYICON) {
 		} else if (rendtype & AIM_CAPS_IMIMAGE) {
@@ -1302,7 +1352,7 @@
 	char *prof_enc = NULL, *prof = NULL;
 	u_short infotype;
 	char buf[BUF_LONG];
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	va_list ap;
 
 	va_start(ap, command);
@@ -1362,7 +1412,7 @@
 		      struct command_rx_struct *command, ...) {
 	va_list ap;
 	u_short type;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
 
 	va_start(ap, command);
@@ -1435,7 +1485,7 @@
 	va_list ap;
 	int count, i = 0;
 	struct aim_userinfo_s *info;
-	struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *g = sess->aux_data;
 
 	GSList *bcs = g->buddy_chats;
 	struct conversation *b = NULL;
@@ -1466,7 +1516,7 @@
 	va_list ap;
 	int count, i = 0;
 	struct aim_userinfo_s *info;
-	struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *g = sess->aux_data;
 
 	GSList *bcs = g->buddy_chats;
 	struct conversation *b = NULL;
@@ -1503,7 +1553,7 @@
 	va_list ap;
 	struct aim_userinfo_s *info;
 	char *msg;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	GSList *bcs = gc->buddy_chats;
 	struct conversation *b = NULL;
@@ -1595,7 +1645,7 @@
 	va_list ap;
 	int newevil;
 	struct aim_userinfo_s *userinfo;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	newevil = va_arg(ap, int);
@@ -1608,7 +1658,7 @@
 }
 
 int gaim_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	switch (command->conn->type) {
 	case AIM_CONN_TYPE_BOS:
 		aim_bos_ackrateresp(sess, command->conn);
@@ -1634,6 +1684,11 @@
 							     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
 
 		break;
+	case AIM_CONN_TYPE_AUTH:
+		aim_bos_ackrateresp(sess, command->conn);
+		aim_auth_clientready(sess, command->conn);
+		debug_printf("connected to auth (admin)\n");
+		break;
 	default:
 		debug_printf("got rate response for unhandled connection type %04x\n",
 				command->conn->type);
@@ -1683,6 +1738,96 @@
 	return 1;
 }
 
+int gaim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *address, *SNs;
+	int i, num;
+	char *buf;
+	int at = 0, len;
+
+	va_start(ap, command);
+	address = va_arg(ap, char *);
+	num = va_arg(ap, int);
+	SNs = va_arg(ap, char *);
+	va_end(ap);
+
+	len = num * (MAXSNLEN + 1) + 1024;
+	buf = g_malloc(len);
+	at += g_snprintf(buf + at, len - at, "<B>%s has the following screen names:</B><BR>", address);
+	for (i = 0; i < num; i++)
+		at += g_snprintf(buf + at, len - at, "%s<BR>", &SNs[i * (MAXSNLEN + 1)]);
+	g_show_info_text(buf);
+	g_free(buf);
+
+	return 1;
+}
+
+int gaim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *address;
+	char buf[BUF_LONG];
+
+	va_start(ap, command);
+	address = va_arg(ap, char *);
+	va_end(ap);
+
+	g_snprintf(buf, sizeof(buf), "No results found for email address %s", address);
+	do_error_dialog(buf, _("Error"));
+
+	return 1;
+}
+
+int gaim_account_confirm(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	int status;
+	va_list ap;
+	char msg[256];
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, command);
+	status = va_arg(ap, int); /* status code of confirmation request */
+	va_end(ap);
+
+	debug_printf("account confirmation returned status 0x%04x (%s)\n", status,
+			status ? "email sent" : "unknown");
+	if (status) {
+		g_snprintf(msg, sizeof(msg), "You should receive an email asking to confirm %s.",
+				gc->username);
+		do_error_dialog(msg, "Confirm");
+	}
+
+	return 1;
+}
+
+int gaim_info_change(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	unsigned short change = 0;
+	int perms, type, length, str;
+	char *val;
+	va_list ap;
+	char buf[BUF_LONG];
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, command);
+	perms = va_arg(ap, int);
+	type = va_arg(ap, int);
+	length = va_arg(ap, int);
+	val = va_arg(ap, char *);
+	str = va_arg(ap, int);
+	va_end(ap);
+
+	if (aimutil_get16(command->data+2) == 0x0005)
+		change = 1;
+
+	debug_printf("info%s: perms = %d, type = %x, length = %d, val = %s\n",
+			change ? " change" : "", perms, type, length, str ? val : "(not string)");
+
+	if ((type == 0x0011) && str) {
+		g_snprintf(buf, sizeof(buf), "The email address for %s is %s", gc->username, val);
+		do_error_dialog(buf, "Email");
+	}
+
+	return 1;
+}
+
 static void oscar_keepalive(struct gaim_connection *gc) {
 	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
 	aim_flap_nop(odata->sess, odata->conn);
@@ -1903,7 +2048,7 @@
 
 static int gaim_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
 	va_list ap;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct aim_directim_priv *priv;
 	struct aim_conn_t *newconn;
@@ -1944,7 +2089,7 @@
 	va_list ap;
 	char *sn = NULL, *msg = NULL;
 	struct aim_conn_t *conn;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 
 	va_start(ap, command);
 	conn = va_arg(ap, struct aim_conn_t *);
@@ -1963,7 +2108,7 @@
 	va_list ap;
 	struct aim_conn_t *conn;
 	char *sn;
-	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	struct gaim_connection *gc = sess->aux_data;
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct direct_im *dim;
 	char buf[256];
@@ -2281,8 +2426,27 @@
 
 static void oscar_do_action(struct gaim_connection *gc, char *act)
 {
+	struct oscar_data *od = gc->proto_data;
+
 	if (!strcmp(act, "Set User Info")) {
 		show_set_info(gc);
+	} else if (!strcmp(act, "Change Password")) {
+		show_change_passwd(gc);
+	} else if (!strcmp(act, "Confirm Account")) {
+		if (od->paspa == 0) {
+			od->conf = TRUE;
+			aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
+		} else
+			aim_auth_reqconfirm(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH));
+	} else if (!strcmp(act, "Change Email")) {
+	} else if (!strcmp(act, "Display Current Registered Address")) {
+		if (od->paspa == 0) {
+			od->reqemail = TRUE;
+			aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
+		} else
+			aim_auth_getinfo(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), 0x11);
+	} else if (!strcmp(act, "Search for Buddy by Email")) {
+		show_find_email(gc);
 	}
 }
 
@@ -2291,10 +2455,33 @@
 	GList *m = NULL;
 
 	m = g_list_append(m, "Set User Info");
+	m = g_list_append(m, NULL);
+	m = g_list_append(m, "Change Password");
+	m = g_list_append(m, "Confirm Account");
+	/*
+	m = g_list_append(m, "Change Email");
+	*/
+	m = g_list_append(m, "Display Current Registered Address");
+	m = g_list_append(m, NULL);
+	m = g_list_append(m, "Search for Buddy by Email");
 
 	return m;
 }
 
+static void oscar_change_passwd(struct gaim_connection *gc, char *old, char *new)
+{
+	struct oscar_data *od = gc->proto_data;
+	if (od->paspa == 0) {
+		od->chpass = TRUE;
+		od->oldp = g_strdup(old);
+		od->newp = g_strdup(new);
+		aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
+	} else {
+		aim_auth_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH),
+				new, old);
+	}
+}
+
 void oscar_init(struct prpl *ret) {
 	ret->protocol = PROTO_OSCAR;
 	ret->options = OPT_PROTO_HTML | OPT_PROTO_CORRECT_TIME;
@@ -2318,7 +2505,7 @@
 	ret->get_dir = NULL; /* Oscar really doesn't have this */
 	ret->dir_search = oscar_dir_search;
 	ret->set_idle = oscar_set_idle;
-	ret->change_passwd = NULL; /* Oscar doesn't have this either */
+	ret->change_passwd = oscar_change_passwd;
 	ret->add_buddy = oscar_add_buddy;
 	ret->add_buddies = oscar_add_buddies;
 	ret->remove_buddy = oscar_remove_buddy;