changeset 960:fa681641643d

[gaim-migrate @ 970] *** MULTIPLE-CONNECTIONS *** committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Tue, 10 Oct 2000 00:02:02 +0000
parents 034d5d1d53eb
children 9189916471f5
files FIXME HACKING STATUS libfaim/AUTHORS libfaim/CHANGES libfaim/CHANGES.gaim libfaim/README.gaim libfaim/aim_ft.c libfaim/aim_im.c libfaim/aim_misc.c libfaim/aim_rxhandlers.c libfaim/aim_txqueue.c libfaim/faim/aim.h libfaim/faim/aim_cbtypes.h plugins/Makefile.am po/POTFILES.in src/Makefile.am src/about.c src/aim.c src/away.c src/buddy.c src/buddy_chat.c src/conversation.c src/convo.h src/dialogs.c src/gaim.h src/gaimrc.c src/multi.c src/multi.h src/oscar.c src/perl.c src/prefs.c src/server.c src/toc.c src/util.c
diffstat 35 files changed, 2926 insertions(+), 1509 deletions(-) [+]
line wrap: on
line diff
--- a/FIXME	Mon Oct 09 23:56:33 2000 +0000
+++ b/FIXME	Tue Oct 10 00:02:02 2000 +0000
@@ -1,2 +1,27 @@
 GAIM: Items to be fixed
 ------------------------
+Holy crap.
+
+Take a deep breath.
+
+Holy Crap.
+
+1. We need to modify the login window to allow for multiple connections. (Account Editor needs to be
+   a button at the very least.) MOSTLY DONE
+2. We need to modify the conversation window to state who you're sending messages as. The current way,
+   with the drop-down menu, is pretty crappy, though effective. (Perhaps just repositioning the menu?)
+3. We need to modify the buddy list to indicate which buddies belong to which connections. We also
+   need to modify the buddy list to vary the right-click menus on a per-conversation basis. (I.e. create
+   the menu based on which options the protocol supports.)
+4. We need to modify nearly every function in server.c to determine which connection to do things on.
+   The ones that still need to be modified have a FIXME comment in them.
+   4.1. We need to modify the UI to let the user indicate which account to do things on.
+   4.2. We need to decide when people are idle. Right now it's possible to have been idle for 10 minutes
+        but only signed on for one. DONE
+5. We need to completely modify the plugin/perl system to account for multiple connections.
+6. We need to modify functions to account for the possibility of protocol plugins.
+7. Get rid of dologin. Signons should be handled differently. The account editor should have final
+   control over this. DONE
+8. I seriously doubt the applet will compile. If it compiles it definitely won't run.
+
+And that's just the large things. There are countless trivial issues that need to be addressed.
--- a/HACKING	Mon Oct 09 23:56:33 2000 +0000
+++ b/HACKING	Tue Oct 10 00:02:02 2000 +0000
@@ -34,53 +34,16 @@
 of the information that's printed is useless anyway though; so the
 --enable-debug option really doesn't do a whole lot.
 
-This file was last modified by $Author: warmenhoven $ on $Date: 2000-10-09 05:18:19 -0400 (Mon, 09 Oct 2000) $.
+This file was last modified by $Author: warmenhoven $ on $Date: 2000-10-09 20:02:02 -0400 (Mon, 09 Oct 2000) $.
 
 
 PROGRAM FLOW
 ============
 
-Before gaim does anything you can see, it initializes itself, which is
-mostly just reading .gaimrc (handled by the functions in gaimrc.c). It
-then draws the login window by calling show_login, and waits for input.
-
-At the login window, when "signon" is clicked, dologin() is called. This
-in turn calls serv_login, which checks to see if you want to use Oscar or
-TOC, and calls oscar_login or toc_login appropriately. We'll assume TOC
-for the rest of this discussion; Oscar has a lot of bad hacks to get it
-working that I don't even want to think about.
-
-After you're signed in (I'll skip that discussion - I doubt many people
-are going to change the login process, since it pretty much just follows
-PROTOCOL), Gaim draws the buddy list by calling show_buddy_list, and
-waits for input from two places: the server and the user. The first place
-it gets input from after signon is invariably the server, when the server
-tells Gaim which buddies are signed on.
-
-When there is information ready to be read from the server, toc_callback
-is called (by GDK) to parse the incoming information. On an UPDATE,
-serv_got_update is called, which takes care of things like notifying
-conversation windows of the update if need be; notifying the plugins;
-and finally, calling set_buddy.
-
-set_buddy is one of the most frequently called functions in gaim, one of
-the largest functions in gaim, and probably one of the buggiest functions
-in gaim. It is responsible for updating the pixmaps in the buddy list;
-notifying plugins of various events; updating the tooltips for buddies;
-making sounds; and updating the ticker. It's also called once per online
-buddy every 20 seconds (by GTK through update_all_buddies).
-
-When the user opens a new conversation window, new_conversation is called.
-That's easy enough. If there isn't a conversation with the person already
-open (checked by calling find_conversation), show_conv is called to
-create the new window. All sorts of neat things happen there, but it's
-mostly drawing the window. show_conv is the best place to edit the UI. Be
-prepared for some incredibly bad GTK programming. (Rob's fixing this as
-we speak no doubt.)
-
-That's pretty much it for the quick tutorial. I know it wasn't much but
-it's enough to get you started. Make sure you know GTK before you get too
-involved. Most of the back-end stuff is pretty basic; most of gaim is GTK.
+This has completely changed since multiple connections were added. Please
+be patient; as soon as the code starts to settle this will get rewritten.
+Most of the source files stayed the same though; only the one that got
+added (multi.c) isn't covered below.
 
 
 SOURCE FILES
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/STATUS	Tue Oct 10 00:02:02 2000 +0000
@@ -0,0 +1,78 @@
+STATUS of GAIM CVS tree. Last modified $Date: 2000-10-09 20:02:02 -0400 (Mon, 09 Oct 2000) $ by $Author: warmenhoven $.
+
+This file is meant to provide gaim users who use the CVS version to see whether
+the actually want to compile what they just checked out. Gaim CVS is usually
+relatively stable (we use it all the time), but has tendencies to be quirky at
+times.
+
+
+OVERALL
+=======
+
+The current CVS tree is completely broken. You can still use it to sign in and
+converse with users as normal. If you only have one account signed in then all
+of the old functions still work perfectly (or as well as they did before The
+Change (multiple connections)).
+
+There are a lot of things that are probably going to be very buggy over the
+next month or so. Please bear with us.
+
+
+MULTIPLE CONNECTIONS
+====================
+
+I'm impressed that this is working. It took a major restructuring of the
+internals of gaim. Everything had been based around one connection, and that
+connection used one protocol or the other. Now, there are many connections, and
+each connection may be using a different protocol. Most of the functionality is
+complete but the UI hasn't been updated to reflect all of the different users.
+This change isn't going to be complete for quite some time so just bear with us
+as we try to get things operational again.
+
+
+BUDDY LIST
+==========
+
+The buddy list is completely fucked up. It needs to be heavily modified to be
+able to support The Change. But logging in as only one user makes it work as
+it always did.
+
+This may be changed, but here's how the buddy list cache currently works. When
+you sign on it imports the list from the name you sign on as. When the list is
+saved, it is saved to all of the signed on users. This means that when you sign
+on two or more names at once their lists will be merged.
+
+
+TOC
+===
+
+TOC is working reasonably well.
+
+
+Oscar
+=====
+
+Oscar is working reasonably well. Please see gaim/libfaim/README.gaim for more
+details on Oscar.
+
+
+Applet
+======
+
+I'll be amazed if the applet compiles. If it compiles, I'll be amazed if it
+doesn't segfault. But technically nothing drastic changed with the applet in
+The Change, so it may end up working just fine. I don't have the time nor the
+energy to test it right now though; I'll come back to it as soon as the app is
+stabilized.
+
+
+Plugins/Perl
+============
+
+The current plugin/perl system is in complete disarray as things are being
+totally restructured inside gaim, in hopes of adding a more powerful plugin
+system (one that can hopefully support Protocol Plugins (see PRPL comments)).
+While The Change is taking place, most plugins won't compile (and we have set
+all plugins in gaim/plugins not to compile by default). However, ones that have
+been previously compiled may or may not work. The only way to know is to load
+the plugin and test it.
--- a/libfaim/AUTHORS	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/AUTHORS	Tue Oct 10 00:02:02 2000 +0000
@@ -11,7 +11,7 @@
 
 N: Josh Myer
 E: josh@joshisanerd.com
-D: [I'm sure he did something -mid]
+D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings.
 
 N: Daniel Reed
 H: n, linuxkitty
--- a/libfaim/CHANGES	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/CHANGES	Tue Oct 10 00:02:02 2000 +0000
@@ -1,6 +1,25 @@
 
 No release numbers
 ------------------
+ - Fri Sep 22 22:47:49 UTC 2000
+  - Add aim_icq_setstatus() (jbm)
+
+ - Fri Sep 22 22:35:51 UTC 2000
+  - Apply jbm's patch below
+  - Minor cleanup in cb handler stuff
+
+ - Thu Sep 21 20:28:20 CDT 2000 (jbm)
+  - DirectIM shouldn't kill us any more.
+  - Eliminated a potential segfault in aim_send_im_direct().
+  - make tags (yay!).
+  - Added an aim_tx_destroy(); need to move the lib over to using it.
+  - Got rid of some #if 0 BS code in a couple of places 
+      (aim_ft::establish() and aim_im::aim_parse_incoming_im_middle()).
+  - Added some file transfer stuff (so experimental, i haven't 
+      had a chance to see if it causes cancer in lab animals).
+  - Added to faimtest for all of the above.
+  - Added myself to AUTHORS with Real Info.
+
  - Thu Sep 21 00:24:36 UTC 2000
    - Add socks5 proxy support (not tested real well, worked the 
        few times a tried).
--- a/libfaim/CHANGES.gaim	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/CHANGES.gaim	Tue Oct 10 00:02:02 2000 +0000
@@ -1,3 +1,11 @@
+
+Mon Oct  9 22:40:56 UTC 2000 EWarmenhoven
+	- added multiple connections to gaim. This actually happened over
+	  the course of about 3 or 4 days. It is now somewhat functional;
+	  enough for me to commit.
+	- libfaim had some updates which make Direct IM not quite work.
+	  Please don't use Direct IM for a short time. There is a fix that
+	  has been submitted but has not been put into CVS yet.
 
 Fri Sep 22 10:12:37 UTC 2000 EWarmenhoven
 	- yeah, libfaim does socks 5 proxying now. it may be fragile though,
--- a/libfaim/README.gaim	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/README.gaim	Tue Oct 10 00:02:02 2000 +0000
@@ -80,6 +80,9 @@
 - What *is* protocol-dependent about the RVOUS stuff is that only Oscar can
 request RVOUS actions, though both can receive them.
 
+- Direct IM has problems. Don't accept a Direct IM if you're offered one, it
+may cause you to segfault.
+
 - Getting Dir Info is not in libfaim yet, and so is not in Gaim/Faim yet.
 
 - Warnings are there now. Yes, I know it says the wrong value.
--- a/libfaim/aim_ft.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/aim_ft.c	Tue Oct 10 00:02:02 2000 +0000
@@ -7,9 +7,6 @@
 #include <sys/utsname.h> /* for aim_directim_initiate */
 #include <arpa/inet.h> /* for inet_ntoa */
 #endif
-#ifdef NEED_SOCKLEN_T
-typedef unsigned int socklen_t;
-#endif
 
 /* aim_msgcookies.c is mostly new. just look at the diff and replace yours, easiest. */
 
@@ -105,14 +102,14 @@
   struct aim_directim_priv *priv = NULL;
   int i;
 
-  if (strlen(msg) >= MAXMSGLEN)
-    return -1;
-
-  if (!sess || !conn || !(conn->type) || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv) {
+  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 */
@@ -409,11 +406,6 @@
    * allocate and set up our connection
    */
 
-#if 0
-  i = fcntl(listenfd, F_GETFL, 0);
-  fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
-#endif
-
   newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
   if (!newconn) { 
     perror("aim_newconn");
@@ -452,6 +444,14 @@
   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,
@@ -474,140 +474,260 @@
   return newconn;
 }
 
-faim_export unsigned long aim_accepttransfer(struct aim_session_t *sess,
-					     struct aim_conn_t *conn, 
-					     struct aim_conn_t *oftconn,
-					     char *sn,
-					     char *cookie,
-					     unsigned short rendid)
-{
-  struct command_tx_struct *newpacket, *newoft;
-  struct aim_fileheader_t *listingfh;
-  int curbyte, i;
-  /* now for the oft bits */
+/*
+ * 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.
+ */
 
-  if(rendid == AIM_CAPS_GETFILE) {
-    printf("jbm: getfile request accept\n");
-    if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1108, oftconn, 0))) {
-      printf("faim: accept_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))) {
-      free(newoft);
-      return -1;
-    }  
-
-    listingfh = aim_getlisting(sess);
-
-    memcpy(listingfh->bcookie, cookie, 8);
-
-    curbyte = 0;
+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;
     
-    for(i = 0; i < 8; i++)
-      curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, cookie[i]);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->encrypt);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->compress);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->totfiles);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->filesleft);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->totparts);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->partsleft);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->totsize);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->size);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->modtime);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->checksum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfrcsum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfsize);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->cretime);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfcsum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->nrecvd);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->recvcsum);
-
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->idstring, 32);
-    curbyte += 32;
-
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->flags);
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->lnameoffset);
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->lsizeoffset);
-
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->dummy, 69);
-    curbyte += 69;
-    
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->macfileinfo, 16);
-    curbyte += 16;
-
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->nencode);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->nlanguage);
+    priv = cur->priv;
+    if (aim_sncmp(priv->sn, name) == 0)
+      break;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
 
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->name, 64);
-    curbyte += 64;
-
-    free(listingfh);
-
-    newoft->lock = 0;
-    aim_tx_enqueue(sess, newoft);
-    printf("faim: getfile: OFT listing enqueued.\n");
-    
-  }
-
-
-  if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sn)+4+2+8+16)))
-    return -1;
-  
-  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 (sess->snac_nextid++);
+  return cur;
 }
 
 /*
- * aim_getlisting()
- * 
- * Get file listing.txt info. where else to put it? i
- * dunno. client-side issue for sure tho. for now we just side-step
- * the issue with a nice default. =)
- *  
+ * 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_internal struct aim_fileheader_t *aim_getlisting(struct aim_session_t *sess) 
+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    = 0x0001;
-  fh->filesleft   = 0x0001;
+  fh->compress    = 0x0000; 
+  fh->totfiles    = totfiles;
+  fh->filesleft   = totfiles; /* is this right ?*/
   fh->totparts    = 0x0001;
   fh->partsleft   = 0x0001;
-  fh->totsize     = 0x00000064;
-  fh->size        = 0x00000024; /* ls -l listing.txt */
-  fh->modtime     = (int)time(NULL); /*0x39441fb4; */
-  fh->checksum    = 0xb8350000;
+  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;
@@ -615,28 +735,25 @@
   fh->nrecvd      = 0x00000000;
   fh->recvcsum    = 0x00000000;
 
-  memset(fh->idstring, 0, 32/*sizeof(fh->idstring)*/);
-  memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", 32/*sizeof(fh->idstring)*/);
-  memset(fh->idstring+strlen(fh->idstring), 0, 32-strlen(fh->idstring)); /* jbm hack */ 
+  /*  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, 69/*sizeof(fh->dummy)*/);
-  /*  fh->dummy = ;*/
+  /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
+  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
 
-  memset(fh->macfileinfo, 0, 16/*sizeof(fh->macfileinfo)*/);
-  /*  fh->macfileinfo = ; */
-
-  fh->nencode     = 0x0000;
+  fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
   fh->nlanguage   = 0x0000;
 
-  memset(fh->name, 0, 64/*sizeof(fh->name)*/);
-  memcpy(fh->name, "listing.txt", 64 /*sizeof(fh->name)*/);
-  memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name)); /* jbm hack */
+  /*  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));
 
-  printf("jbm: fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
+  faimdprintf(2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
   return fh;
 }
 
@@ -715,9 +832,6 @@
 
 faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn)
 {
-
-  /* XXX: NOT THREAD SAFE RIGHT NOW. the locks are acting up. deal. -- jbm */
-
   unsigned char hdrbuf1[6];
   unsigned char *hdr = NULL;
   int hdrlen, hdrtype;
@@ -730,27 +844,74 @@
   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);
+    aim_conn_close(conn);
+    
     if(hdrlen < 0)
       perror("read");
-    printf("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);
-    aim_conn_close(conn);
-    return -1;
+    else { /* disconnected */
+      int i = -1;
+      switch(conn->subtype) {
+      case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { /* XXX: clean up cookies here ? */
+	struct aim_directim_priv *priv;
+	if(!(priv = (struct aim_directim_priv *)conn->priv) )
+	  return -1; /* not much we can do */
+
+	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) )
+	  i = userfunc(sess, NULL, conn, priv->sn);
+	else
+	  aim_conn_kill(sess, &conn);
+
+	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM);
+      }
+      case AIM_CONN_SUBTYPE_OFT_GETFILE: {
+	struct aim_filetransfer_priv *priv;
+	if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
+	  return -1;
+
+	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) )
+	  i = userfunc(sess, NULL, conn, priv->sn);
+	else
+	  aim_conn_kill(sess, &conn);
+
+	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
+      }
+      case AIM_CONN_SUBTYPE_OFT_SENDFILE: {
+	struct aim_filetransfer_priv *priv;
+	if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
+	  return -1;
+
+	if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) )
+	  i = userfunc(sess, NULL, conn, priv->sn);
+	else
+	  aim_conn_kill(sess, &conn);
+
+
+	aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
+      }
+      }
+
+    return i;
+    }
   }
 
   hdrlen = aimutil_get16(hdrbuf1+4);
 
   hdrlen -= 6;
-  if (!(hdr = malloc(hdrlen)))
+  if (!(hdr = malloc(hdrlen))) {
+    faim_mutex_unlock(&conn->active);
     return -1;
+  }
 
   if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
     perror("read");
-    printf("faim: rend: read2 error\n");
+    faimdprintf(2,"faim: rend: read2 error\n");
     free(hdr);
     faim_mutex_unlock(&conn->active);
     aim_conn_close(conn);
-    return 0; /* see comment on previous read check */
+    return -1;
   }
 
   hdrtype = aimutil_get16(hdr);  
@@ -770,9 +931,7 @@
 
     strncpy(priv->sn, snptr, MAXSNLEN);
 
-#if 0
-    printf("faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); 
-#endif
+    faimdprintf(2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); 
 
     if (flags == 0x000e) {
       faim_mutex_unlock(&conn->active);
@@ -783,7 +942,7 @@
 
       if(! (msg = calloc(1, payloadlength+1)) ) {
 	faim_mutex_unlock(&conn->active);
-	return 0;
+	return -1;
       }
       
       if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
@@ -796,9 +955,8 @@
       }
       faim_mutex_unlock(&conn->active);
       msg[payloadlength] = '\0';
-#if 0     
-      printf("faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg);
-#endif
+      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);
@@ -807,8 +965,58 @@
       return i;
     }
     break;
-  } 
-  case 0x1209: { /* get file first */
+  }
+#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;
@@ -816,22 +1024,23 @@
     int commandlen;
     char *data;
 
-    printf("faim: rend: fileget 0x1209\n");
+    faimdprintf(2,"faim: rend: fileget 0x1209\n");
 
-    if(hdrlen != 0x100)
-      printf("faim: fileget_command(1209): um. hdrlen != 0x100.. 0x%x\n", hdrlen);
-    
     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");
+      faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
       faim_mutex_unlock(&conn->active);
-      return 0;
+      return -1;
     }
 
     fh = aim_oft_getfh(hdr);
 
     memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
     
-    cook = aim_checkcookie(sess, (unsigned char *)ft->fh.bcookie, AIM_COOKIETYPE_OFTGET);
+    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 */
@@ -840,17 +1049,36 @@
     
     aim_cachecookie(sess, cook);
 
-    commandlen = 36;
+    /* 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;
 
-    data = calloc(1, commandlen);
-    memcpy(data, "01/01/1999 00:00      100 file.txt\r\n", commandlen);
+    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;
+
+    }
 
-    if (send(conn->fd, data, commandlen, 0) != commandlen) {
+    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);
 
-    printf("jbm: hit end of 1209\n");
+    faimdprintf(2, "faim: get_command_rendezvous: hit end of 1209\n");
+
+    free(data);
 
     break;
   }
@@ -860,22 +1088,22 @@
 
     struct aim_fileheader_t *fh;
 
-    printf("faim: rend: fileget 120b\n");
+    faimdprintf(2, "faim: rend: fileget 120b\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");
+      faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
       faim_mutex_unlock(&conn->active);
-      return 0;
+      return -1;
     }
 
-    if(hdrlen != 0x100)
-      printf("faim: fileget_command(120b): um. hdrlen != 0x100..\n");
-    
     fh = aim_oft_getfh(hdr);
 
     memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
-    
-    cook = aim_checkcookie(sess, (unsigned char *)ft->fh.bcookie, AIM_COOKIETYPE_OFTGET);
+ 
+    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 */
@@ -885,7 +1113,9 @@
     aim_cachecookie(sess, cook);
 
     faim_mutex_unlock(&conn->active);
-    
+
+    /* call listing.txt rx confirm */    
+
     break;
   }
   case 0x120c: { /* yet more get file */
@@ -893,24 +1123,30 @@
     struct aim_msgcookie_t *cook;
     struct aim_fileheader_t *listingfh;
     struct command_tx_struct *newoft;
-    int curbyte, i;
+
+    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 0;
+      return -1;
     }
 
     if(hdrlen != 0x100)
       printf("faim: fileget_command(120c): um. hdrlen != 0x100..\n");
 
-    listingfh = aim_oft_getfh(hdr);
+    listingfh = aim_oft_getfh((char *)hdr);
 
     memcpy(&(ft->fh), listingfh, sizeof(struct aim_fileheader_t));
     
-    cook = aim_checkcookie(sess, (unsigned char *)ft->fh.bcookie, AIM_COOKIETYPE_OFTGET);
+    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 */
@@ -921,121 +1157,89 @@
 
     faim_mutex_unlock(&conn->active);
 
-    printf("faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
- 
-    if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0101, conn, 0/*listingfh->size*/))) {
-      printf("faim: send_final_transfer: tx_new OFT failed\n");
-      return 0;
+    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;
     }
     
-    /* XXX: actually implement Real Handling of all this */
-
-    printf("jbm: listingfh->size: 0x%lx\n", listingfh->size);
-
     newoft->lock = 1;
-
-    /*    if(!(newoft->data = calloc(1, listingfh->size))) {
-      printf("newoft data malloc failed. bombing.\n");
-      return 0;
-      }*/
-
-    if(newoft->commandlen > 0) {
-      int i;
-      memset(newoft->data, 0, newoft->commandlen);
-      for(i = 0; i < (signed)newoft->commandlen; i++)
-	newoft->data[i] = 0x30 + (i%10);
-
-      //      memcpy(newoft->data, "This has been a Test\r\n-josh\r\n", newoft->commandlen);
-    }
-
+    
     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))) {
-      if(newoft->data)
-	free(newoft->data); /* XXX: make this into a destructor function */
-      free(newoft);
-      return 0;
+      newoft->lock = 0;
+      aim_tx_destroy(newoft);
+      return -1;
     }  
-
-    memcpy(listingfh->bcookie, ft->fh.bcookie, 8);
-
-    curbyte = 0;
+    
+    /*  memcpy(listingfh->bcookie, ft->fh.bcookie, 8); */
     
-    for(i = 0; i < 8; i++)
-      curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->bcookie[i]);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->encrypt);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->compress);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->totfiles);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->filesleft);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->totparts);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->partsleft);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->totsize);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->size);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->modtime);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->checksum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfrcsum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfsize);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->cretime);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, listingfh->rfcsum);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, 0 /*listingfh->nrecvd*/);
-    curbyte += aimutil_put32(newoft->hdr.oft.hdr2+curbyte, 0/*listingfh->recvcsum*/);
+    listingfh->nrecvd = 0; /* these need reset for protocol-correctness */
+    listingfh->recvcsum = 0;
 
-    strncpy((char *)newoft->hdr.oft.hdr2+curbyte, listingfh->idstring, 32);
-    curbyte += 32;
-
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, 0x20 /*listingfh->flags */);
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->lnameoffset);
-    curbyte += aimutil_put8(newoft->hdr.oft.hdr2+curbyte, listingfh->lsizeoffset);
-
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->dummy, 69);
-    curbyte += 69;
+    aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh);
     
-    memcpy(newoft->hdr.oft.hdr2+curbyte, listingfh->macfileinfo, 16);
-    curbyte += 16;
-
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->nencode);
-    curbyte += aimutil_put16(newoft->hdr.oft.hdr2+curbyte, listingfh->nlanguage);
-
-    strncpy((char *)newoft->hdr.oft.hdr2+curbyte, listingfh->name, 64);
-    curbyte += 64;
+    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);
 
-    newoft->lock = 0;
-    aim_tx_enqueue(sess, newoft);
-    printf("jbm: OFT listing enqueued.\n");
+    return i;
 
     break;
   }
   case 0x0202: { /* get file: ready to recieve data */
-    char *c;
-    int i;
-
     struct aim_fileheader_t *fh;    
-    fh = aim_oft_getfh(hdr);
-
-    c = (char *)calloc(1, fh->size);
+    fh = aim_oft_getfh((char *)hdr);
 
-    printf("looks like we're ready to send data.(oft 0x0202)\n");
-
-
-    
-    for(i = 0; i < fh->size; i++)
-      c[i] = 0x30 + (i%10);
-
-    if ( (i = send(conn->fd, c, fh->size, 0)) != fh->size ) {
-      printf("whoopsy, didn't write it all...\n");
-    }
+    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 */
-    printf("looks like we're done with a transfer (oft 0x0204)\n");
+    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);
-    aim_conn_close(conn);
+    
+    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: {
@@ -1052,9 +1256,43 @@
 }
 
 /*
- * this currently feeds totally bogus data
+ * 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;
@@ -1128,3 +1366,511 @@
 
   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
+   */
+
+#ifdef USE_SNAC_FOR_IMS
+ {
+    struct aim_snac_t snac;
+
+    snac.id = sess->snac_nextid;
+    snac.family = 0x0004;
+    snac.type = 0x0006;
+    snac.flags = 0x0000;
+
+    snac.data = malloc(strlen(destsn)+1);
+    memcpy(snac.data, destsn, strlen(destsn)+1);
+
+    aim_newsnac(sess, &snac);
+
+    aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
+  }
+#endif
+  
+  return newconn;
+}
+
+#endif
--- a/libfaim/aim_im.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/aim_im.c	Tue Oct 10 00:02:02 2000 +0000
@@ -505,13 +505,17 @@
 	/*
 	 * Call client.
 	 */
+#if 0
 	userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
 	if (userfunc || (i = 0))
 	  i = userfunc(sess, 
 		       command, 
 		       channel, 
 		       reqclass,
-		       &userinfo);
+		       &userinfo,
+		       ip,
+		       cookie);
+#endif
 
       } else if (reqclass & AIM_CAPS_VOICE) {
 	struct aim_msgcookie_t *cachedcook;
@@ -541,7 +545,7 @@
 	char ip[30];
 	struct aim_directim_priv *priv;
 
-	memset(ip, 0, sizeof(ip));
+	memset(ip, 0, 30);
 	
 	if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
 	  struct aim_tlv_t *iptlv, *porttlv;
@@ -549,7 +553,7 @@
 	  iptlv = aim_gettlv(list2, 0x0003, 1);
 	  porttlv = aim_gettlv(list2, 0x0005, 1);
 
-	  snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d", 
+	  snprintf(ip, 30, "%d.%d.%d.%d:%d", 
 		  aimutil_get8(iptlv->value+0),
 		  aimutil_get8(iptlv->value+1),
 		  aimutil_get8(iptlv->value+2),
@@ -623,7 +627,7 @@
 	if (!(cachedcook = calloc(1, sizeof(struct aim_msgcookie_t))))
 	  return 0;
 
-	memset(ip, 0, sizeof(ip));
+	memset(ip, 0, 30);
 
 	if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) {
 	  free(cachedcook);
@@ -638,7 +642,7 @@
 	    return 0;
 	  }
 
-	  snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d",
+	  snprintf(ip, 30, "%d.%d.%d.%d:%d",
 		   aimutil_get8(iptlv->value+0),
 		   aimutil_get8(iptlv->value+1),
 		   aimutil_get8(iptlv->value+2),
@@ -648,40 +652,6 @@
 
 	printf("faim: rend: file get request from %s (%s)\n", userinfo.sn, ip);
 
-#if 0 /* XXX finish this */
-	newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
-	if (!newconn || (newconn->fd == -1)) {
-	  printf("could not connect to %s\n", ip);
-	  perror("aim_newconn");
-	  aim_conn_kill(sess, &newconn);
-	} else {
-	  struct aim_filetransfer_priv *priv;
-	  priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
-	  memcpy(priv->cookie, cookie, 8);
-	  strncpy(priv->sn, userinfo.sn, MAXSNLEN);
-	  newconn->priv = priv;
-	  printf("faim: connected to peer (fd = %d)\n", newconn->fd);
-	}
-
-	memcpy(cachedcook->cookie, cookie, 8);
-
-	ft = malloc(sizeof(struct aim_filetransfer_priv));
-	ft->state = 1;
-	strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
-	strncpy(ft->ip, ip, sizeof(ft->ip));
-#if 0
-	strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
-#endif
-	cachedcook->type = AIM_COOKIETYPE_OFTGET;
-	cachedcook->data = ft;
-
-	if (aim_cachecookie(sess, cachedcook) != 0)
-	  printf("faim: ERROR caching message cookie\n");
-
-	aim_accepttransfer(sess, command->conn, newconn, ft->sn, cookie, AIM_CAPS_GETFILE);
-
-	free(desc);
-#endif
 	/*
 	 * Call client.
 	 */
@@ -691,7 +661,9 @@
 		       command, 
 		       channel, 
 		       reqclass,
-		       &userinfo);
+		       &userinfo,
+		       ip,
+		       cookie);
 
       } else if (reqclass & AIM_CAPS_SENDFILE) {
 #if 0
--- a/libfaim/aim_misc.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/aim_misc.c	Tue Oct 10 00:02:02 2000 +0000
@@ -889,3 +889,29 @@
     
   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_rxhandlers.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/aim_rxhandlers.c	Tue Oct 10 00:02:02 2000 +0000
@@ -188,14 +188,15 @@
 			rxcallback_t newhandler,
 			u_short flags)
 {
-  struct aim_rxcblist_t *newcb,*cur;
+  struct aim_rxcblist_t *newcb;
 
   if (!conn)
     return -1;
 
   faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
 
-  newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
+  if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
+    return -1;
   newcb->family = family;
   newcb->type = type;
   newcb->flags = flags;
@@ -205,32 +206,37 @@
     newcb->handler = newhandler;
   newcb->next = NULL;
   
-  cur = conn->handlerlist;
-  if (!cur)
+  if (!conn->handlerlist)
     conn->handlerlist = newcb;
-  else 
-    {
-      while (cur->next)
-	cur = cur->next;
-      cur->next = 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,*tmp;
+ struct aim_rxcblist_t *cur;
+
  if (!conn)
    return -1;
 
  cur = conn->handlerlist;
- while(cur)
-   {
-     tmp = cur->next;
-     free(cur);
-     cur = tmp;
-   }
+ while(cur) {
+   struct aim_rxcblist_t *tmp;
+
+   tmp = cur->next;
+   free(cur);
+   cur = tmp;
+ }
+
  return 0;
 }
 
@@ -255,6 +261,7 @@
 
   if (type==0xffff)
     return NULL;
+
   return aim_callhandler(conn, family, 0xffff);
 }
 
--- a/libfaim/aim_txqueue.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/aim_txqueue.c	Tue Oct 10 00:02:02 2000 +0000
@@ -273,10 +273,10 @@
   } else if (cur->hdrtype == AIM_FRAMETYPE_OFT) {
     int z = 0;
 
-    z += aimutil_put8(curPacket+z, 0x4f);
-    z += aimutil_put8(curPacket+z, 0x44);
-    z += aimutil_put8(curPacket+z, 0x43);
-    z += aimutil_put8(curPacket+z, 0x32);
+    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);
--- a/libfaim/faim/aim.h	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/faim/aim.h	Tue Oct 10 00:02:02 2000 +0000
@@ -276,6 +276,14 @@
   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.  
@@ -296,6 +304,11 @@
    */
   void *aux_data;
 
+  /* 
+   * OFT Data 
+   */
+
+  struct aim_oft_session_t oft;
 
   /* ---- Internal Use Only ------------------------ */
   /* 
@@ -534,9 +547,12 @@
 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(struct aim_session_t*);
+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 *);
@@ -671,7 +687,8 @@
 #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 unsigned long aim_accepttransfer(struct aim_session_t *sess, struct aim_conn_t *conn,struct aim_conn_t *oftconn, char *sender, char *cookie, unsigned short rendid);
+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 *);
--- a/libfaim/faim/aim_cbtypes.h	Mon Oct 09 23:56:33 2000 +0000
+++ b/libfaim/faim/aim_cbtypes.h	Tue Oct 10 00:02:02 2000 +0000
@@ -186,15 +186,22 @@
  *
  * See non-SNAC note below.
  */
-#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001
+#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 0x0006
-#define AIM_CB_OFT_DIRECTIMINITIATE 0x0007
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
 
-#define AIM_CB_OFT_GETFILECONNECT 0x0004
-#define AIM_CB_OFT_GETFILECOMPLETE 0x0005
-#define AIM_CB_OFT_GETFILEINITIATE 0x0007
+#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
--- a/plugins/Makefile.am	Mon Oct 09 23:56:33 2000 +0000
+++ b/plugins/Makefile.am	Tue Oct 10 00:02:02 2000 +0000
@@ -5,13 +5,13 @@
 
 
 
-if PLUGINS
-plugin_DATA = autorecon.so iconaway.so notify.so spellchk.so lagmeter.so
-plugindir = $(libdir)/gaim
+#if PLUGINS
+#plugin_DATA = autorecon.so iconaway.so notify.so spellchk.so lagmeter.so
+#plugindir = $(libdir)/gaim
 
-clean distclean clean-recursive distclean-recursive:
-	$(RM) $(plugin_DATA)
-endif
+#clean distclean clean-recursive distclean-recursive:
+#	$(RM) $(plugin_DATA)
+#endif
 
 
 
--- a/po/POTFILES.in	Mon Oct 09 23:56:33 2000 +0000
+++ b/po/POTFILES.in	Tue Oct 10 00:02:02 2000 +0000
@@ -8,7 +8,10 @@
 src/gnome_applet_mgr.c
 src/gtkhtml.c
 src/html.c
+src/multi.c
 src/oscar.c
+src/perl.c
 src/plugins.c
 src/prefs.c
 src/rvous.c
+src/toc.c
--- a/src/Makefile.am	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/Makefile.am	Tue Oct 10 00:02:02 2000 +0000
@@ -14,6 +14,7 @@
 			gtkticker.c \
 			html.c \
 			idle.c \
+			multi.c \
 			network.c \
 			oscar.c \
 			perl.c \
@@ -43,6 +44,7 @@
 		gtkticker.c \
 		html.c \
 		idle.c \
+		multi.c \
 		network.c \
 		oscar.c \
 		perl.c \
@@ -58,6 +60,7 @@
 gaim_DEPENDENCIES = ../libfaim/libfaim.a
 endif
 
-CFLAGS += -DLOCALEDIR=\"$(datadir)/locale\"
+CFLAGS += -DLOCALEDIR=\"$(datadir)/locale\" $(DEBUG_CFLAGS)
+LDFLAGS += $(DEBUG_LDFLAGS)
 
-EXTRA_DIST = gaim.h proxy.h gnome_applet_mgr.h gtkhtml.h gtkticker.h convo.h
+EXTRA_DIST = gaim.h proxy.h gnome_applet_mgr.h gtkhtml.h gtkticker.h convo.h multi.h
--- a/src/about.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/about.c	Tue Oct 10 00:02:02 2000 +0000
@@ -30,7 +30,6 @@
 #include <gtk/gtk.h>
 #include "gaim.h"
 #include "pixmaps/logo.xpm"
-#include "pixmaps/gnome_close.xpm"
 
 static GtkWidget *about=NULL;
 
--- a/src/aim.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/aim.c	Tue Oct 10 00:02:02 2000 +0000
@@ -55,13 +55,6 @@
 #include "locale.h"
 #include "gtkticker.h"
 
-static GtkWidget *name;
-static GtkWidget *pass;
-static GtkWidget *signon;
-static GtkWidget *cancel;
-static GtkWidget *progress;
-static GtkWidget *notice;
-
 GList *permit = NULL;
 GList *deny = NULL;
 GList *log_conversations = NULL;
@@ -73,22 +66,16 @@
 GList *chat_rooms = NULL;
 
 GtkWidget *mainwindow = NULL;
-GtkWidget *remember = NULL;
 
 void BuddyTickerCreateWindow( void );
 
 char toc_addy[16];
 char *quad_addr = NULL;
 
-gboolean running = FALSE; /* whether or not we're currently trying to sign on */
-
 void cancel_logon(void)
 {
 #ifdef USE_APPLET
 	applet_buddy_show = FALSE;
-	if (running)
-		serv_close();
-	running = FALSE;
 	if (mainwindow)
 		gtk_widget_hide(mainwindow);
 #else
@@ -126,6 +113,7 @@
 
 void set_login_progress(int howfar, char *whattosay)
 {
+	/* FIXME: we should do this on a per-connection basis
 	gtk_progress_bar_update(GTK_PROGRESS_BAR(progress),
 				((float)howfar / (float)LOGIN_STEPS));
 	gtk_statusbar_pop(GTK_STATUSBAR(notice), 1);
@@ -133,10 +121,12 @@
 
         while (gtk_events_pending())
                gtk_main_iteration();
+	*/
 }
 
 void hide_login_progress(char *why)
 {
+	/* FIXME: we should do this on a per-connection basis
 	gtk_progress_bar_update(GTK_PROGRESS_BAR(progress),
 				0);
 	gtk_statusbar_pop(GTK_STATUSBAR(notice), 1);
@@ -144,6 +134,7 @@
 
         while (gtk_events_pending())
                gtk_main_iteration();
+	*/
 }
 
 static int snd_tmout;
@@ -153,60 +144,10 @@
 	gtk_timeout_remove(snd_tmout);
 }
 
-char g_screenname[ 64 ];	/* gotta be enough */
-
-void dologin(GtkWidget *widget, GtkWidget *w)
-{
-	char *username = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(name)->entry));
-        char *password = gtk_entry_get_text(GTK_ENTRY(pass));
-	int i;
-
-        if (query_state() != STATE_OFFLINE)
-                return;
-        
-	if (!strlen(username)) {
-		hide_login_progress(_("Please enter your logon"));
-		return;
-	}
-	if (!strlen(password)) {
-		hide_login_progress(_("You must give your password"));
-		return;
-	}
-
-	/* save screenname away for cache file use */
-
-	strcpy( g_screenname, username );
-
-	/* fold cache screen name file to upper case to avoid problems
-	   finding file later if user uses different case at login time */
-
-	for ( i = 0; i < strlen( g_screenname ); i++ )
-		g_screenname[i] = toupper( g_screenname[i] );
-
-#ifdef USE_APPLET
-	set_user_state(signing_on);
-#endif /* USE_APPLET */
-
-	if (running) return;
-	running = TRUE;
-
-        if (serv_login(username, password) < 0) {
-		running = FALSE;
-                return;
-	}
-
-	if (!USE_OSCAR) /* serv_login will set up USE_OSCAR */
-		gaim_setup();
-}
-
-void auth_failed() {
-	running = FALSE;
-}
-
 /* we need to do this for Oscar because serv_login only starts the login
  * process, it doesn't end there. gaim_setup will be called later from
  * oscar.c, after the buddy list is made and serv_finish_login is called */
-void gaim_setup() {
+void gaim_setup(struct gaim_connection *gc) {
 	if (sound_options & OPT_SOUND_LOGIN &&
 		sound_options & OPT_SOUND_SILENT_SIGNON) {
 		logins_not_muted = 0;
@@ -224,76 +165,24 @@
 			NULL);
 #endif /* USE_APPLET */
 
+	account_online(gc);
+
 	plugin_event(event_signon, 0, 0, 0);
 
-	 running = FALSE;
 	 return;
 }
 
 
 
 
-void doenter(GtkWidget *widget, GtkWidget *w)
-{
-	if (widget == name) {
-		gtk_entry_set_text(GTK_ENTRY(pass),"");
-		gtk_entry_select_region(GTK_ENTRY(GTK_COMBO(name)->entry), 0, 0);
-		gtk_widget_grab_focus(pass);
-	} else if (widget == pass) {
-		dologin(widget, w);
-	}
-
-}
-
-
-static void combo_changed(GtkWidget *w, GtkWidget *combo)
-{
-        char *txt = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));
-        struct aim_user *u;
-        
-        if (!(general_options & OPT_GEN_REMEMBER_PASS)) {
-                return;
-        }
-
-        u = find_user(txt);
-
-        if (u != NULL) {
-                gtk_entry_set_text(GTK_ENTRY(pass), u->password);
-        } else {
-                gtk_entry_set_text(GTK_ENTRY(pass), "");
-        }
-       
-        return;
-}
-
-static GList *combo_user_names()
-{
-        GList *usr = aim_users;
-        GList *tmp = NULL;
-        struct aim_user *u;
-
-        
-        if (!usr)
-                return g_list_append(NULL, "<unknown>");
-        
-        while(usr) {
-                u = (struct aim_user *)usr->data;
-                tmp = g_list_append(tmp, g_strdup(u->username));
-                usr = usr->next;
-
-        }
-
-        return tmp;
-}
-
-
-
 void show_login()
 {
 	GtkWidget *options;
 #ifdef GAIM_PLUGINS
 	GtkWidget *plugs;
 #endif
+	GtkWidget *accts;
+	GtkWidget *cancel;
 	GtkWidget *reg;
 	GtkWidget *bbox;
 	GtkWidget *hbox;
@@ -310,8 +199,6 @@
 
         if (mainwindow) {
                 gtk_widget_show(mainwindow);
-		if (!(general_options & OPT_GEN_REMEMBER_PASS))
-			gtk_entry_set_text(GTK_ENTRY(pass), "");
                 return;
         }
        
@@ -322,7 +209,7 @@
         /* Disallow resizing */
         gtk_window_set_policy(GTK_WINDOW(mainwindow), FALSE, FALSE, TRUE);
 	gtk_widget_realize(mainwindow);
-	signon   = gtk_button_new_with_label(_("Signon"));
+	accts    = gtk_button_new_with_label(_("Accounts"));
 	cancel   = gtk_button_new_with_label(_("Cancel"));
 	reg      = gtk_button_new_with_label(_("Register"));
 	options  = gtk_button_new_with_label(_("Options"));
@@ -330,16 +217,10 @@
 	plugs    = gtk_button_new_with_label(_("Plugins")); 
 #endif
 	table    = gtk_table_new(8, 2, FALSE);
-	name     = gtk_combo_new();
-	pass     = gtk_entry_new();
-	notice   = gtk_statusbar_new();
-	progress = gtk_progress_bar_new();
-
-	gtk_combo_set_popdown_strings(GTK_COMBO(name), combo_user_names());
 
 	if (display_options & OPT_DISP_COOL_LOOK)
 	{
-		gtk_button_set_relief(GTK_BUTTON(signon), GTK_RELIEF_NONE);
+		gtk_button_set_relief(GTK_BUTTON(accts), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(cancel), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(reg), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(options), GTK_RELIEF_NONE);
@@ -350,8 +231,8 @@
 
 	/* Make the buttons do stuff */
 	/* Clicking the button initiates a login */
-	gtk_signal_connect(GTK_OBJECT(signon), "clicked",
-			   GTK_SIGNAL_FUNC(dologin), mainwindow);
+	gtk_signal_connect(GTK_OBJECT(accts), "clicked",
+			   GTK_SIGNAL_FUNC(account_editor), mainwindow);
 	gtk_signal_connect(GTK_OBJECT(cancel), "clicked",
 			   GTK_SIGNAL_FUNC(cancel_logon), mainwindow);
 	/* Allow user to change prefs before logging in */
@@ -366,16 +247,6 @@
 	/* Register opens the right URL */
 	gtk_signal_connect(GTK_OBJECT(reg), "clicked",
 			   GTK_SIGNAL_FUNC(open_url), "http://aim.aol.com/aimnew/Aim/register.adp?promo=106723&pageset=Aim&client=no");
-	/* Enter in the username clears the password and sets
-	   the pointer in the password field */
-	gtk_signal_connect(GTK_OBJECT(GTK_COMBO(name)->entry), "activate",
-                           GTK_SIGNAL_FUNC(doenter), mainwindow);
-
-        gtk_signal_connect(GTK_OBJECT(GTK_COMBO(name)->entry), "changed",
-                           GTK_SIGNAL_FUNC(combo_changed), name);
-			   
-	gtk_signal_connect(GTK_OBJECT(pass), "activate",
-			   GTK_SIGNAL_FUNC(doenter), mainwindow);
 	gtk_signal_connect(GTK_OBJECT(mainwindow), "delete_event",
 			   GTK_SIGNAL_FUNC(cancel_logon), mainwindow);
 	/* Homogenous spacing, 10 padding */
@@ -385,70 +256,34 @@
 	
 	gtk_box_pack_start(GTK_BOX(bbox), reg, TRUE, TRUE, 0);
 	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(bbox), signon, TRUE, TRUE, 0);
 
 	gtk_box_pack_start(GTK_BOX(hbox), options, TRUE, TRUE, 0);
 #ifdef GAIM_PLUGINS
 	gtk_box_pack_start(GTK_BOX(hbox), plugs, TRUE, TRUE, 0);
 #endif
 
+	gtk_box_pack_start(GTK_BOX(sbox), hbox, TRUE, TRUE, 0);
 	gtk_box_pack_start(GTK_BOX(sbox), bbox, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(sbox), hbox, TRUE, TRUE, 0);
 
 	/* Labels for selectors and text boxes */
-#if 0	
-	label = gtk_label_new("TOC: ");
-	gtk_table_attach(GTK_TABLE(table), label, 0,1,1,2,0,0, 5, 5);
-	gtk_widget_show(label);
-#endif
-	label = gtk_label_new(_("Screen Name: "));
-	gtk_table_attach(GTK_TABLE(table), label, 0,1,2,3,0,0, 5, 5);
-	gtk_widget_show(label);
-	label = gtk_label_new(_("Password: "));
-	gtk_table_attach(GTK_TABLE(table), label, 0,1,3,4,0,0, 5, 5);
-	gtk_widget_show(label);
-	remember = gtk_check_button_new_with_label(_("Remember Password"));
-	gtk_table_attach(GTK_TABLE(table), remember, 0,2,4,5,0,0, 5, 5);
-	gtk_widget_show(remember);
 
 	gtk_widget_show(options);
 #ifdef GAIM_PLUGINS
 	gtk_widget_show(plugs);
 #endif
 	
-        /* Adjust sizes of inputs */
-	gtk_widget_set_usize(name,100,0);
-	gtk_widget_set_usize(pass,100,0);
-
-
-	/* Status and label */
-	gtk_widget_show(notice);
-
-	gtk_widget_set_usize(progress,150,0);
-
-        gtk_widget_show(progress);
-
-        gtk_table_attach(GTK_TABLE(table), progress, 0, 2, 6, 7, 0, 0, 5, 5);
-	gtk_widget_set_usize(GTK_STATUSBAR(notice)->label, 150, 0);
-	gtk_table_attach(GTK_TABLE(table), notice, 0, 2, 8, 9, 0, 0, 5, 5);
-
 	/* Attach the buttons at the bottom */
-	gtk_widget_show(signon);
 	gtk_widget_show(cancel);
 	gtk_widget_show(reg);
+	gtk_widget_show(accts);
 	gtk_widget_show(bbox);
 	gtk_widget_show(hbox);
 	gtk_widget_show(sbox);
+	gtk_table_attach(GTK_TABLE(table), accts, 0,2,6,7,0,0, 5, 5);
 	gtk_table_attach(GTK_TABLE(table), sbox, 0,2,7,8,0,0, 5, 5);
 	
 	/* Text fields */
 	
-	gtk_table_attach(GTK_TABLE(table),name,1,2,2,3,0,0,5,5);
-	gtk_widget_show(name);
-	gtk_table_attach(GTK_TABLE(table),pass,1,2,3,4,0,0,5,5);
-	gtk_entry_set_visibility(GTK_ENTRY(pass), FALSE);
-	gtk_widget_show(pass);
-	
 	gtk_container_border_width(GTK_CONTAINER(sbox), 10);	 
 	
 	gtk_container_add(GTK_CONTAINER(mainwindow),table );
@@ -456,25 +291,6 @@
 	gtk_widget_show(table);
         gtk_window_set_title(GTK_WINDOW(mainwindow),_("Gaim - Login"));
 
-        gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(remember), (general_options & OPT_GEN_REMEMBER_PASS));
-
-	if (current_user) {
-		sprintf(debug_buff, "Current user is %s\n", current_user->username);
-		debug_print(debug_buff);
-		gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(name)->entry), current_user->username);
-		if ((general_options & OPT_GEN_REMEMBER_PASS)) {
-			combo_changed(NULL, name);
-			gtk_widget_grab_focus(signon);
-		} else {
-			gtk_widget_grab_focus(pass);
-		}
-	} else {
-		gtk_widget_grab_focus(name);
-	}
-
-
-	gtk_signal_connect(GTK_OBJECT(remember), "clicked", GTK_SIGNAL_FUNC(set_general_option), (int *)OPT_GEN_REMEMBER_PASS);
-
 
         gtk_widget_realize(mainwindow);
 
@@ -501,10 +317,12 @@
 
 	SetTickerPrefs();
 	
+	/*
         if((general_options & OPT_GEN_AUTO_LOGIN) &&
            (general_options & OPT_GEN_REMEMBER_PASS)) {
 		dologin(signon, NULL);
 	}
+	*/
 }
 
 extern void show_debug(GtkObject *);
@@ -591,6 +409,7 @@
         
 
         show_login();
+	auto_login();
         gtk_main();
         
 #endif /* USE_APPLET */
--- a/src/away.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/away.c	Tue Oct 10 00:02:02 2000 +0000
@@ -159,7 +159,6 @@
 
         buf2 = g_malloc(strlen(awaymessage->message)*4 + 1);
 	strcpy(buf2, awaymessage->message);
-        escape_text(buf2);
         serv_set_away(buf2);
         g_free(buf2);
 	gtk_widget_show(imaway);
--- a/src/buddy.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/buddy.c	Tue Oct 10 00:02:02 2000 +0000
@@ -48,20 +48,9 @@
 #include "pixmaps/login_icon.xpm"
 #include "pixmaps/logout_icon.xpm"
 
-#include "pixmaps/buddyadd.xpm"
-#include "pixmaps/buddydel.xpm"
-#include "pixmaps/buddychat.xpm"
-#include "pixmaps/im.xpm"
-#include "pixmaps/info.xpm"
 #include "pixmaps/away_icon.xpm"
 #include "pixmaps/away_small.xpm"
 
-#include "pixmaps/daemon-buddyadd.xpm"
-#include "pixmaps/daemon-buddydel.xpm"
-#include "pixmaps/daemon-buddychat.xpm"
-#include "pixmaps/daemon-im.xpm"
-#include "pixmaps/daemon-info.xpm"
-
 #include "pixmaps/add_small.xpm"
 #include "pixmaps/import_small.xpm"
 #include "pixmaps/export_small.xpm"
@@ -286,12 +275,26 @@
 #endif
 
 
-void signoff()
+static void signoff_all(GtkWidget *w, gpointer d)
+{
+	GSList *c = connections;
+	struct gaim_connection *g = NULL;
+
+	while (c) {
+		g = (struct gaim_connection *)c->data;
+		signoff(g);
+		c = connections;
+	}
+}
+
+void signoff(struct gaim_connection *gc)
 {
 	GList *mem;
 
-	plugin_event(event_signoff, 0, 0, 0);
+	plugin_event(event_signoff, gc, 0, 0);
+	serv_close(gc);
 
+	if (connections) return;
         while(groups) {
 		mem = ((struct group *)groups->data)->members;
 		while(mem) {
@@ -304,8 +307,7 @@
 
 	sprintf(debug_buff, "date: %s\n", full_date());
 	debug_print(debug_buff);
-	update_keepalive(FALSE);
-	serv_close();
+	update_keepalive(gc, FALSE);
         destroy_all_dialogs();
         destroy_buddy();
         hide_login_progress("");
@@ -421,13 +423,11 @@
 		gtk_menu_append(GTK_MENU(menu), button);
 		gtk_widget_show(button);
 
-	if (!USE_OSCAR) {
 		button = gtk_menu_item_new_with_label(_("Dir Info"));
 		gtk_signal_connect(GTK_OBJECT(button), "activate",
 				   GTK_SIGNAL_FUNC(pressed_dir_info), b);
 		gtk_menu_append(GTK_MENU(menu), button);
 		gtk_widget_show(button);
-	} else {
 
 		button = gtk_menu_item_new_with_label(_("Direct IM"));
 		gtk_signal_connect(GTK_OBJECT(button), "activate",
@@ -440,7 +440,6 @@
 				   GTK_SIGNAL_FUNC(pressed_away_msg), b);
 		gtk_menu_append(GTK_MENU(menu), button);
 		gtk_widget_show(button);
-	}
 
 		button = gtk_menu_item_new_with_label(_("Toggle Logging"));
 		gtk_signal_connect(GTK_OBJECT(button), "activate",
@@ -968,11 +967,6 @@
 }
 
 
-void gaimreg_callback(GtkWidget *widget)
-{
-	show_register_dialog(); 
-}
-
 void import_callback(GtkWidget *widget, void *null)
 {
         show_import_dialog();
@@ -1207,8 +1201,6 @@
 
                         	write_to_conv(c, b->message, WFLAG_SEND, NULL);
 
-                        	escape_text(b->message);
-
                                 serv_send_im(name, b->message, 0);
 			}
                         
@@ -1835,6 +1827,11 @@
         GtkWidget *bbox;
         GtkWidget *tbox;
 
+	if (blist) {
+		gtk_widget_show(blist);
+		return;
+	}
+
 
 #ifdef USE_APPLET
         blist = gtk_window_new(GTK_WINDOW_DIALOG);
@@ -1864,13 +1861,8 @@
         gaim_seperator(menu);
         gaim_new_item_with_pixmap(menu, _("Import Buddy List"), import_small_xpm, GTK_SIGNAL_FUNC(import_callback));
         gaim_new_item_with_pixmap(menu, _("Export Buddy List"), export_small_xpm,GTK_SIGNAL_FUNC(export_callback));
-	if (!(general_options & OPT_GEN_REGISTERED))
-	{
-        	gaim_seperator(menu);
-		gaim_new_item_with_pixmap(menu, _("Register"), add_small_xpm, GTK_SIGNAL_FUNC(gaimreg_callback));
-	}
 	gaim_seperator(menu);
-	gaim_new_item_with_pixmap(menu, _("Signoff"), logout_icon_xpm, GTK_SIGNAL_FUNC(signoff));
+	gaim_new_item_with_pixmap(menu, _("Signoff"), logout_icon_xpm, GTK_SIGNAL_FUNC(signoff_all));
 
 #ifndef USE_APPLET
 	gaim_new_item_with_pixmap(menu, _("Quit"), exit_small_xpm, GTK_SIGNAL_FUNC(do_quit));
@@ -1928,6 +1920,7 @@
 	gtk_menu_append(GTK_MENU(setmenu), menuitem);
 	gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(show_change_passwd), NULL);
 	gtk_widget_show(menuitem);
+        gaim_new_item_with_pixmap(menu, _("Accounts"), add_small_xpm, GTK_SIGNAL_FUNC(account_editor));
 	gaim_seperator(menu);
 
         gaim_new_item_with_pixmap(menu, _("Preferences"), prefs_small_xpm, GTK_SIGNAL_FUNC(show_prefs));
--- a/src/buddy_chat.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/buddy_chat.c	Tue Oct 10 00:02:02 2000 +0000
@@ -315,8 +315,8 @@
         
         if (!(flag & WFLAG_WHISPER)) {
 		str = g_strdup(normalize(who));
-		if (!strcasecmp(str, normalize(current_user->username))) {
-			sprintf(debug_buff, "%s %s\n", normalize(who), normalize(current_user->username));
+		if (!strcasecmp(str, normalize(b->gc->username))) {
+			sprintf(debug_buff, "%s %s\n", normalize(who), normalize(b->gc->username));
 			debug_print(debug_buff);
 			if (b->makesound && (sound_options & OPT_SOUND_CHAT_SAY))
 				play_sound(SEND);
@@ -358,10 +358,10 @@
 
 	gtk_editable_delete_text(GTK_EDITABLE(b->entry), 0, -1);
 
-        escape_text(buf);
+        escape_text(buf); /* it's ok to leave this here because oscar can't whisper */
         serv_chat_whisper(b->id, who, buf);
                           
-	g_snprintf(buf2, sizeof(buf2), "%s->%s", current_user->username, who);
+	g_snprintf(buf2, sizeof(buf2), "%s->%s", b->gc->username, who);
 
 	chat_write(b, buf2, WFLAG_WHISPER, buf);
 
--- a/src/conversation.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/conversation.c	Tue Oct 10 00:02:02 2000 +0000
@@ -47,6 +47,8 @@
 #include "pixmaps/wood.xpm"
 #include "pixmaps/link.xpm"
 #include "pixmaps/strike.xpm"
+#include "pixmaps/fgcolor.xpm"
+#include "pixmaps/bgcolor.xpm"
 
 #include "pixmaps/angel.xpm"
 #include "pixmaps/bigsmile.xpm"
@@ -134,6 +136,8 @@
 	}
 
         show_conv(c);
+	if (connections)
+		c->gc = (struct gaim_connection *)connections->data;
         conversations = g_list_append(conversations, c);
 	plugin_event(event_new_conversation, name, 0, 0);
         return c;
@@ -348,7 +352,7 @@
 		debug_print("chat clicked close button\n");
 		c->window = NULL;
 		gtk_widget_destroy(tmp);
-		return;
+		return FALSE;
 	}
 
 	debug_print("conversation close callback\n");
@@ -380,13 +384,13 @@
 		serv_chat_leave(c->id);
 	} else {
 		if (c->is_direct) {
-			if (!USE_OSCAR) {
-				/* Direct IM TOC FIXME */
-			} else {
+			if (c->gc->protocol == PROTO_OSCAR) {
 				gdk_input_remove(c->watcher);
 				sprintf(debug_buff, "Closing DirectIM conversation (%p)\n", c->conn);
 				debug_print(debug_buff);
-				aim_conn_kill(gaim_sess, &c->conn);
+				aim_conn_kill(c->gc->oscar_sess, &c->conn);
+			} else {
+				/* Direct IM TOC FIXME */
 			}
 		}
 	        delete_conversation(c);
@@ -600,9 +604,10 @@
 	char *buf, *buf2, *buf3;
 	int hdrlen, limit;
 
+	if (!c->gc) return;
 	if (c->is_direct) limit = 0x8000; /* 32 k */
-	else if (c->is_chat && USE_OSCAR) limit = MAXCHATMSGLEN;
-	else if (USE_OSCAR) limit = MAXMSGLEN;
+	else if (c->is_chat && c->gc->protocol == PROTO_OSCAR) limit = MAXCHATMSGLEN;
+	else if (c->gc->protocol == PROTO_OSCAR) limit = MAXMSGLEN;
 	else limit = MSG_LEN;
 	limit <<= 2;
 
@@ -626,7 +631,7 @@
          * toc_send_im is 11 chars long + 2 quotes.
          * + 2 spaces + 6 for the header + 2 for good
          * measure = 23 bytes + the length of normalize c->name */
-	if (!USE_OSCAR)
+	if (c->gc->protocol == PROTO_TOC)
 		hdrlen = 23 + strlen(normalize(c->name));
 	else
 		hdrlen = 0;
@@ -683,16 +688,13 @@
 		buf3 = g_strdup(buf);
 		write_to_conv(c, buf3, WFLAG_SEND, NULL);
 		g_free(buf3);
-		escape_text(buf);
-		if (escape_message(buf) > limit/4 - hdrlen)
-			do_error_dialog(_("Message too long, some data truncated."), _("Error"));
 
 	        serv_send_im(c->name, buf, 0);
 
 		if (c->makesound && (sound_options & OPT_SOUND_SEND))
 			play_sound(SEND);
 	} else {
-		serv_chat_send(c->id, buf); /* this does escape_text for us */
+		serv_chat_send(c->id, buf);
 
 		/* no sound because we do that when we receive our message */
 	}
@@ -1204,11 +1206,11 @@
 
 	if (!who) {
 		if (flags & WFLAG_SEND) {
-			b = find_buddy(current_user->username);
+			b = find_buddy(c->gc->username);
 			if (b)
 				who = b->show;
 			else
-				who = current_user->username;
+				who = c->gc->username;
 		} else {
 			b = find_buddy(c->name);
 			if (b)
@@ -1615,6 +1617,54 @@
 	return toolbar;
 }
 
+static void convo_sel_send(GtkObject *m, struct gaim_connection *c)
+{
+	struct conversation *cnv = gtk_object_get_user_data(m);
+	cnv->gc = c;
+}
+
+static void create_convo_menu(struct conversation *cnv)
+{
+	GtkWidget *menu, *opt;
+	GSList *g = connections;
+	struct gaim_connection *c;
+
+	if (g_slist_length(g) < 2)
+		gtk_widget_hide(cnv->menu->parent);
+	else {
+		menu = gtk_menu_new();
+
+		while (g) {
+			c = (struct gaim_connection *)g->data;
+			opt = gtk_menu_item_new_with_label(c->username);
+			gtk_object_set_user_data(GTK_OBJECT(opt), cnv);
+			gtk_signal_connect(GTK_OBJECT(opt), "activate",
+					   GTK_SIGNAL_FUNC(convo_sel_send), c);
+			gtk_widget_show(opt);
+			gtk_menu_append(GTK_MENU(menu), opt);
+			g = g->next;
+		}
+
+		gtk_option_menu_remove_menu(GTK_OPTION_MENU(cnv->menu));
+		gtk_option_menu_set_menu(GTK_OPTION_MENU(cnv->menu), menu);
+		gtk_option_menu_set_history(GTK_OPTION_MENU(cnv->menu), 0);
+
+		gtk_widget_show(cnv->menu);
+		gtk_widget_show(cnv->menu->parent);
+	}
+}
+
+void redo_convo_menus()
+{
+	GList *c = conversations;
+	struct conversation *C;
+
+	while (c) {
+		C = (struct conversation *)c->data;
+		create_convo_menu(C);
+		c = c->next;
+	}
+}
 
 void show_conv(struct conversation *c)
 {
@@ -1634,7 +1684,8 @@
 	GtkWidget *paned;
 	GtkWidget *add;
 	GtkWidget *toolbar;
-	GtkWidget *sep;
+	GtkWidget *hbox;
+	GtkWidget *label;
 	int dispstyle;
 	
 	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -1678,6 +1729,20 @@
 	gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, FALSE);
 	gtk_widget_show(vbox2);
 	gtk_widget_show(paned);
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
+	gtk_widget_show(hbox);
+
+	label = gtk_label_new(_("Send message as: "));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_widget_show(label);
+
+	c->menu = gtk_option_menu_new();
+	gtk_box_pack_start(GTK_BOX(hbox), c->menu, FALSE, FALSE, 5);
+	gtk_widget_show(c->menu);
+
+	create_convo_menu(c);
 	
 	entry = gtk_text_new(NULL, NULL);
 	gtk_text_set_editable(GTK_TEXT(entry), TRUE);
--- a/src/convo.h	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/convo.h	Tue Oct 10 00:02:02 2000 +0000
@@ -28,8 +28,6 @@
 #include "pixmaps/tmp_send.xpm"
 #include "pixmaps/gnome_remove.xpm"
 #include "pixmaps/gnome_add.xpm"
-#include "pixmaps/fgcolor.xpm"
-#include "pixmaps/bgcolor.xpm"
 #include "pixmaps/cancel.xpm"
 #include "pixmaps/warn.xpm"
 #include "pixmaps/tb_search.xpm"
@@ -59,7 +57,6 @@
 extern void do_normal(GtkWidget *, GtkWidget *);
 extern void do_big(GtkWidget *, GtkWidget *);
 extern void toggle_font(GtkWidget *, struct conversation *);
-extern void toggle_link(GtkWidget *, struct conversation *);
 extern void toggle_color(GtkWidget *, struct conversation *);
 extern void toggle_loggle(GtkWidget *, struct conversation *);
 extern void insert_smiley(GtkWidget *, struct conversation *);
--- a/src/dialogs.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/dialogs.c	Tue Oct 10 00:02:02 2000 +0000
@@ -129,15 +129,6 @@
 	GtkWidget *countryentry;
 };
 
-struct registerdlg {
-	GtkWidget *window;
-	GtkWidget *name;
-	GtkWidget *email;
-	GtkWidget *uname;
-	GtkWidget *sname;
-	GtkWidget *country;
-};
-
 struct info_dlg {
 	GtkWidget *window;
 	GtkWidget *text;
@@ -147,6 +138,8 @@
 
 struct set_info_dlg {
 	GtkWidget *window;
+	GtkWidget *menu;
+	struct aim_user *user;
 	GtkWidget *text;
 	GtkWidget *save;
 	GtkWidget *cancel;
@@ -186,93 +179,6 @@
 };
 
 /*------------------------------------------------------------------------*/
-/*  Function to Send an Email                                             */
-/*------------------------------------------------------------------------*/
-
-static int g_sendemail(char *name, char *email, int uname, int sname, char *country)
-{
-	static char email_data[2000];
-	int sock;
-	struct in_addr *host;
-/*	char data[3]; */
-	FILE *sockfile;
-	char uname_output;
-        FILE *tmpfile;
-        char filename[128];
-        char buf[256];
-        int i=0, tmpfd=-1;
-
-        while (i<10000 && tmpfd < 0) {
-                g_snprintf(filename, 128, "/tmp/gaim_%s%d.tmp", current_user->username, i++);
-
-                tmpfd = open(filename, O_RDWR|O_CREAT|O_EXCL, 0600);
-        }
-
-        if(tmpfd < 0) {
-                return -1;
-        }
-
-        
-	if (uname)
-        {
-                g_snprintf(buf, sizeof(buf), "uname -a > %s", filename);
-		system(buf);
-	}
-	
-	host = (struct in_addr *)get_address(REG_SRVR);
-	if (!host) 
-	{
-		printf(_("Error Resolving Mail Server.\n"));
-		return -1;
-	}
-
-	if ((sock = connect_address(host->s_addr, REG_PORT)) < 0)
-	{
-		printf(_("Error Connecting to Socket.\n"));
-		return -1;
-	}	 
-
-	sockfile = fdopen(sock, "r+");
-
-	g_snprintf(email_data, sizeof(email_data), "mail from: %s\n", REG_EMAIL_ADDR);
-	fputs(email_data, sockfile);
-	
-	g_snprintf(email_data, sizeof(email_data), "rcpt to: %s\n", REG_EMAIL_ADDR);
-	fputs(email_data, sockfile);
-
-	g_snprintf(email_data, sizeof(email_data), "data\n");
-	fputs(email_data, sockfile);
-	g_snprintf(email_data, sizeof(email_data), "Subject: Registration Information\n\nBelow is the submitted Registration Information\n----------------------------------\nName: %s\nEmail: %s\nCountry: %s\nSName: %s\nGAIM: v%s\nUname: ", name, email, country, sname ? current_user->username : "N/A", VERSION);
-	fputs(email_data, sockfile);
-
-	if (uname)
-	{
-		tmpfile = fopen(filename, "r");
-		while (!feof(tmpfile))
-		{
-			uname_output = fgetc(tmpfile);
-			if (!feof(tmpfile))
-				fputc(uname_output, sockfile);
-		}
-		fclose(tmpfile);
-        }
-
-        unlink(filename);
-	
-	g_snprintf(email_data, sizeof(email_data), "\n.\nquit\n\n");
-	fputs(email_data, sockfile);
-
-/*	while (fgets(data, 2, sockfile)) {
-	}
-        */
-        /* I don't think the above is necessary... */
-        
-	close(sock);
-
-	return 1;
-}
-
-/*------------------------------------------------------------------------*/
 /*  Destroys                                                              */
 /*------------------------------------------------------------------------*/
 
@@ -530,10 +436,7 @@
 
 	plugin_event(event_error, (void *)no, 0, 0);
 
-	if (USE_OSCAR)
-		w = d + 4;
-	else
-		w = strtok(NULL, ":");
+	w = strtok(NULL, ":");
  	
 	
         switch(no) {
@@ -1235,22 +1138,27 @@
 {
 	gchar *junk;
 	char *buf;
+	struct gaim_connection *gc;
 
 	junk = gtk_editable_get_chars(GTK_EDITABLE(b->text), 0, -1);
 
-	g_snprintf(current_user->user_info, sizeof(current_user->user_info), "%s", junk);
-		
-	save_prefs();
-
-        buf = g_malloc(strlen(current_user->user_info) * 4);
-	if (!buf) {
-		buf = g_malloc(1);
-		buf[0] = 0;
+	if (b->user) {
+		g_snprintf(b->user->user_info, sizeof(b->user->user_info), "%s", junk);
+		gc = find_gaim_conn_by_name(b->user->username);
+			
+		save_prefs();
+
+		if (gc) {
+			buf = g_malloc(strlen(junk) * 4);
+			if (!buf) {
+				buf = g_malloc(1);
+				buf[0] = 0;
+			}
+			g_snprintf(buf, MIN(strlen(junk) * 2, 4096), "%s", junk);
+			serv_set_info(gc, buf);
+			g_free(buf);
+		}
 	}
-        g_snprintf(buf, strlen(current_user->user_info) * 2, "%s", current_user->user_info);
-        escape_text(buf);
-        serv_set_info(buf);
-        g_free(buf);
 	g_free(junk);
 	destroy_dialog(NULL, b->window);
 	g_free(b);
@@ -1576,6 +1484,57 @@
 
 }
 
+static void info_choose(GtkWidget *opt, struct set_info_dlg *b)
+{
+	int text_len;
+	struct aim_user *u = gtk_object_get_user_data(GTK_OBJECT(opt));
+	b->user = u;
+	text_len = gtk_text_get_length(GTK_TEXT(b->text));
+	gtk_text_set_point(GTK_TEXT(b->text), 0);
+	gtk_text_forward_delete(GTK_TEXT(b->text), text_len);
+	gtk_text_insert(GTK_TEXT(b->text), NULL, NULL, NULL, u->user_info, -1);
+}
+
+static void info_user_menu(struct set_info_dlg *b, GtkWidget *box)
+{
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *optmenu;
+	GtkWidget *menu;
+	GtkWidget *opt;
+	GList *u = aim_users;
+	struct aim_user *a;
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	label = gtk_label_new(_("Set info for:"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_widget_show(label);
+
+	optmenu = gtk_option_menu_new();
+	gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 5);
+	gtk_widget_show(optmenu);
+
+	menu = gtk_menu_new();
+
+	while (u) {
+		a = (struct aim_user *)u->data;
+		opt = gtk_menu_item_new_with_label(a->username);
+		gtk_object_set_user_data(GTK_OBJECT(opt), a);
+		gtk_signal_connect(GTK_OBJECT(opt), "activate", GTK_SIGNAL_FUNC(info_choose), b);
+		gtk_menu_append(GTK_MENU(menu), opt);
+		gtk_widget_show(opt);
+		u = u->next;
+	}
+
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
+	gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), 0);
+
+	b->menu = optmenu;
+}
+
 void show_set_info()
 {
 	GtkWidget *bot;
@@ -1586,7 +1545,7 @@
 	b->window = gtk_window_new(GTK_WINDOW_DIALOG);
         gtk_window_set_wmclass(GTK_WINDOW(b->window), "set_info", "Gaim");
 	dialogwindows = g_list_prepend(dialogwindows, b->window);
-	gtk_widget_show(b->window);
+	gtk_widget_realize(b->window);
 
 	bot = gtk_hbox_new(TRUE, 10);
 	top = gtk_vbox_new(FALSE, 10);
@@ -1609,11 +1568,17 @@
 	gtk_widget_show(bot);
 
 
+	info_user_menu(b, top);
+
 	b->text = gtk_text_new(NULL, NULL);
 	gtk_text_set_word_wrap(GTK_TEXT(b->text), TRUE);
 	gtk_text_set_editable(GTK_TEXT(b->text), TRUE);
 	gtk_widget_set_usize(b->text, 350, 100);
-	gtk_text_insert(GTK_TEXT(b->text), NULL, NULL, NULL, current_user->user_info, -1);
+	/* is this necessary?
+	if (users)
+		gtk_text_insert(GTK_TEXT(b->text), NULL, NULL, NULL,
+				((struct aim_user *)users->data)->user_info, -1);
+	*/
 
 	gtk_widget_show(b->text);
 
@@ -1633,125 +1598,6 @@
 }
 
 /*------------------------------------------------------------------------*/
-/*  The dialog for registration information                               */
-/*------------------------------------------------------------------------*/
-
-void do_register_dialog(GtkWidget *widget, struct registerdlg *b)
-{
-	char *email = gtk_entry_get_text(GTK_ENTRY(b->email));
-	char *name = gtk_entry_get_text(GTK_ENTRY(b->name));
-	int uname = GTK_TOGGLE_BUTTON(b->uname)->active;
-	int sname = GTK_TOGGLE_BUTTON(b->sname)->active;
-	char *country = gtk_entry_get_text(GTK_ENTRY(b->country));
-
-	general_options |= OPT_GEN_REGISTERED;
-	save_prefs();
-	
-	destroy_dialog(NULL, b->window);
-
-	g_free(b);
-
-        g_sendemail(name, email, uname, sname, country);
-}
-
-void set_reg_flag(GtkWidget *widget, struct registerdlg *b)
-{
-	general_options |= OPT_GEN_REGISTERED;
-	save_prefs();
-	destroy_dialog(NULL, b->window);
-	g_free(b);
-}
- 
-void show_register_dialog()
-{
-	GtkWidget *ok;
-	GtkWidget *cancel;
-	GtkWidget *label;
-	GtkWidget *table;
-	GtkWidget *vbox;
-	GtkWidget *bbox;
-
-	struct registerdlg *b = g_new0(struct registerdlg, 1);
-	b->window = gtk_window_new(GTK_WINDOW_DIALOG);
-	dialogwindows = g_list_prepend(dialogwindows, b->window);
-
-	cancel = gtk_button_new_with_label(_("Cancel"));
-	ok = gtk_button_new_with_label(_("Send"));
-
-	bbox = gtk_hbox_new(TRUE, 10);
-	table = gtk_table_new(6, 2, TRUE);
-	vbox = gtk_vbox_new(FALSE, 5);
-
-	b->name = gtk_entry_new();
-	b->email = gtk_entry_new();
-	b->uname = gtk_check_button_new_with_label("Send the output of uname -a with registration");
-	gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(b->uname), TRUE);
-	b->sname = gtk_check_button_new_with_label("Send my screenname with registration");
-	gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(b->sname), TRUE);
-	gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 10);
-	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 10);
-
-	label = gtk_label_new("This list will not, in any way, be distributed and\nwill be used for internal census purposes only.\nAll fields are completely optional.");
-	gtk_widget_show(label);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
-
-	label = gtk_label_new("Name");
-	gtk_widget_show(label);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
-	gtk_table_attach_defaults(GTK_TABLE(table), b->name, 1, 2, 1, 2);
-
-	label = gtk_label_new("Email");
-	gtk_widget_show(label);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
-	gtk_table_attach_defaults(GTK_TABLE(table), b->email, 1, 2, 2, 3);
-	
-	label = gtk_label_new("Country");
-	b->country = gtk_entry_new();
-	gtk_widget_show(label);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
-	gtk_table_attach_defaults(GTK_TABLE(table), b->country, 1, 2, 3, 4);
-
-	gtk_table_attach_defaults(GTK_TABLE(table), b->sname, 0, 2, 4, 5);
-	gtk_table_attach_defaults(GTK_TABLE(table), b->uname, 0, 2, 5, 6);
-
-	gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
-
-	if (display_options & OPT_DISP_COOL_LOOK)
-	{
-		gtk_button_set_relief(GTK_BUTTON(ok), GTK_RELIEF_NONE);
-		gtk_button_set_relief(GTK_BUTTON(cancel), GTK_RELIEF_NONE);
-	}
-	
-	gtk_signal_connect(GTK_OBJECT(b->window), "destroy",
-			   GTK_SIGNAL_FUNC(destroy_dialog), b->window);
-	gtk_signal_connect(GTK_OBJECT(cancel), "clicked",
-			   GTK_SIGNAL_FUNC(set_reg_flag), b);
-	gtk_signal_connect(GTK_OBJECT(ok), "clicked",
-			   GTK_SIGNAL_FUNC(do_register_dialog), b);
-
-	gtk_widget_show(ok);
-	gtk_widget_show(cancel);
-	gtk_widget_show(b->name);
-	gtk_widget_show(b->email);
-	gtk_widget_show(b->uname);
-	gtk_widget_show(b->sname);
-	gtk_widget_show(b->country);
-	gtk_widget_show(table);
-	gtk_widget_show(bbox);
-	gtk_widget_show(vbox);
-	gtk_window_set_title(GTK_WINDOW(b->window), "Gaim - Registration");
-	gtk_window_set_focus(GTK_WINDOW(b->window), b->name);
-	gtk_container_add(GTK_CONTAINER(b->window), vbox);
-        gtk_container_border_width(GTK_CONTAINER(b->window), 10);
-        gtk_widget_realize(b->window);
-	aol_icon(b->window->window);
-
-	gtk_widget_show(b->window);
-}
-
-
-/*------------------------------------------------------------------------*/
 /*  The dialog for the info requests                                      */
 /*------------------------------------------------------------------------*/
 
@@ -1816,7 +1662,11 @@
 
 void g_show_info(char *url) {
 	char *url_text = grab_url(url);
-	g_show_info_text(away_subs(url_text, current_user->username));
+	if (connections)
+		g_show_info_text(away_subs(url_text,
+					((struct gaim_connection *)connections->data)->username));
+	else
+		g_show_info_text(url_text);
 	g_free(url_text);
 }
 
@@ -2769,24 +2619,36 @@
 /* see if a buddy list cache file for this user exists */
 
 gboolean
-bud_list_cache_exists( void )
+bud_list_cache_exists(struct gaim_connection *gc)
 {
 	gboolean ret = FALSE;
 	char path[PATHSIZE];
 	char *file;
 	struct stat sbuf;
-	extern char g_screenname[];
+	char g_screenname[64];
+	int i;
+
+	for (i = 0; i < strlen(gc->username); i++)
+		g_screenname[i] = toupper(gc->username[i]);
+	g_screenname[i] = '\0';
 
 	file = getenv( "HOME" );
 	if ( file != (char *) NULL ) {
-	       	sprintf( path, "%s/.gaim/%s.blist", file, g_screenname );
-		if ( !stat(path, &sbuf) ) 
+	       	sprintf( path, "%s/.gaim/%s.blist", file, g_screenname);
+		if ( !stat(path, &sbuf) ) {
+			sprintf(debug_buff, "%s exists.\n", path);
+			debug_print(debug_buff);
 			ret = TRUE;
+		} else {
+			sprintf(debug_buff, "%s does not exist.\n", path);
+			debug_print(debug_buff);
+		}
 	}
 	return ret;
 }
 
-/* if dummy is 0, save to ~/.gaim/screenname.blist. Else, let user choose */
+/* if dummy is 0, save to ~/.gaim/screenname.blist, where screenname is each
+ * signed in user. Else, let user choose */
 
 void do_export(GtkWidget *w, void *dummy)
 {
@@ -2795,13 +2657,27 @@
         char *buf = g_malloc(BUF_LONG);
         char *file;
 	char path[PATHSIZE];
-	extern char g_screenname[];
 
 	if ( show_dialog == 1 ) {
 		file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(exportdialog));
 		strncpy( path, file, PATHSIZE - 1 );
-	}
-	else {
+		if ((f = fopen(path,"w"))) {
+			serv_build_config(buf, 8192 - 1, TRUE);
+			fprintf(f, "%s\n", buf);
+			fclose(f);
+			chmod(buf, S_IRUSR | S_IWUSR);
+		} else {
+			g_snprintf(buf, BUF_LONG / 2, _("Error writing file %s"), file);
+			do_error_dialog(buf, _("Error"));
+		}
+        	destroy_dialog(NULL, exportdialog);
+        	exportdialog = NULL;
+	} else {
+		GSList *c = connections;
+		struct gaim_connection *g;
+		char g_screenname[64];
+		int i;
+
 		file = getenv( "HOME" );
 		if ( file != (char *) NULL ) {
 			FILE *dir;
@@ -2811,24 +2687,31 @@
 				mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR);
 			else
 				fclose(dir);
-                        sprintf( path, "%s/.gaim/%s.blist", file, g_screenname );
-		} else
-			return;
+
+			while (c) {
+				g = (struct gaim_connection *)c->data;
+
+				for (i = 0; i < strlen(g->username); i++)
+					g_screenname[i] = toupper(g->username[i]);
+				g_screenname[i] = '\0';
+				sprintf( path, "%s/.gaim/%s.blist", file, g_screenname);
+				if ((f = fopen(path,"w"))) {
+					sprintf(debug_buff, "writing %s\n", path);
+					debug_print(debug_buff);
+					serv_build_config(buf, 8192 - 1, TRUE);
+					fprintf(f, "%s\n", buf);
+					fclose(f);
+					chmod(buf, S_IRUSR | S_IWUSR);
+				} else {
+					sprintf(debug_buff, "unable to write %s\n", path);
+					debug_print(debug_buff);
+				}
+
+				c = c->next;
+			}
+		} else return;
 	}
-        if ((f = fopen(path,"w"))) {
-                serv_build_config(buf, 8192 - 1, TRUE);
-                fprintf(f, "%s\n", buf);
-                fclose(f);
-                chmod(buf, S_IRUSR | S_IWUSR);
-        } else if ( show_dialog == 1 ) {
-                g_snprintf(buf, BUF_LONG / 2, _("Error writing file %s"), file);
-                do_error_dialog(buf, _("Error"));
-        }
-	if ( show_dialog == 1 ) {
-        	destroy_dialog(NULL, exportdialog);
-        	exportdialog = NULL;
-	}
-        
+
         g_free(buf);
         
 }
@@ -2863,39 +2746,45 @@
 
 }
 
-/* if dummy is 0, then import from ~/.gaim/screenname.blist, else let user
+/* if gc is non-NULL, then import from ~/.gaim/gc->username.blist, else let user
    choose */
 
-void do_import(GtkWidget *w, void *dummy)
+void do_import(GtkWidget *w, struct gaim_connection *gc)
 {
-	gint show_dialog = (int) dummy;
         char *buf = g_malloc(BUF_LONG);
         char *buf2;
         char *first = g_malloc(64);
 	char *file;
 	char path[PATHSIZE];
+	char g_screenname[64];
+	int i;
         FILE *f;
-	extern char g_screenname[];
-
-        if ( show_dialog == 1 ) {
+
+        if ( !gc ) {
         	file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(importdialog));
                 strncpy( path, file, PATHSIZE - 1 );
         }
         else {
+		for (i = 0; i < strlen(gc->username); i++)
+			g_screenname[i] = toupper(gc->username[i]);
+		g_screenname[i] = '\0';
+
                 file = getenv( "HOME" );
                 if ( file != (char *) NULL )
-                        sprintf( path, "%s/.gaim/%s.blist", file, g_screenname );
+                        sprintf( path, "%s/.gaim/%s.blist", file, g_screenname);
                 else
 			return;
         }
 
         if (!(f = fopen(path,"r"))) {
-		if ( show_dialog == 1 ) {
+		if ( !gc ) {
                 	g_snprintf(buf, BUF_LONG / 2, _("Error reading file %s"), file);
                 	do_error_dialog(buf, _("Error"));
                 	destroy_dialog(NULL, importdialog);
                 	importdialog = NULL;
 		}
+		sprintf(debug_buff, "Unable to open %s.\n", path);
+		debug_print(debug_buff);
                 g_free(buf);
 		g_free(first);
                 return;
@@ -2935,7 +2824,7 @@
                 g_free(buf2);
 	/* Something else */
         } else {
-		if ( show_dialog == 1 ) {
+		if ( !gc ) {
                 	destroy_dialog(NULL, importdialog);
                 	importdialog = NULL;
 		}
@@ -2945,7 +2834,7 @@
                 return;
 	}
 
-        parse_toc_buddy_list(buf, 1);
+        parse_toc_buddy_list(gc, buf, 1);
 
         serv_save_config();
 
@@ -2954,7 +2843,7 @@
 
 	fclose( f );
 
-	if ( show_dialog == 1 ) {
+	if ( !gc ) {
 		/* save what we just did to cache */
 
 		do_export( (GtkWidget *) NULL, 0 );
@@ -2981,7 +2870,7 @@
                                    GTK_SIGNAL_FUNC(destroy_dialog), importdialog);
                 
                 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(importdialog)->ok_button),
-                                   "clicked", GTK_SIGNAL_FUNC(do_import), (void*)1);
+                                   "clicked", GTK_SIGNAL_FUNC(do_import), NULL);
                 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(importdialog)->cancel_button),
                                    "clicked", GTK_SIGNAL_FUNC(destroy_dialog), importdialog);
                 
--- a/src/gaim.h	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/gaim.h	Tue Oct 10 00:02:02 2000 +0000
@@ -19,19 +19,24 @@
  *
  */
 
+#ifndef _GAIM_GAIM_H_
+#define _GAIM_GAIM_H_
+
 #ifdef HAVE_CONFIG_H
 #include "../config.h"
 #endif
+
 #include <gtk/gtk.h>
 #include <time.h>
 #include <stdio.h>
-#include <aim.h>
 #ifdef USE_APPLET
 #include <applet-widget.h>
 #endif /* USE_APPLET */
 #ifdef USE_GNOME
 #include <gnome.h>
 #endif
+#include "aim.h"
+#include "multi.h"
 
 
 /*
@@ -60,8 +65,6 @@
 #define PERMIT_NONE	2
 #define PERMIT_SOME	3
 #define DENY_SOME	4
-#define PERMIT_BUDDY	5 /* TOC doesn't have this,
-			     but we can fake it */
 
 #define UC_AOL		1
 #define UC_ADMIN 	2
@@ -125,8 +128,21 @@
 
 struct aim_user {
 	char username[64];
-        char password[32];
-        char user_info[2048];
+	char password[32];
+	char user_info[2048];
+	int options;
+	int protocol;
+
+	/* stuff for modify window */
+	GtkWidget *mod;
+	GtkWidget *name;
+	GtkWidget *pass;
+	int tmp_options;
+	int tmp_protocol;
+
+	/* stuff for password prompt */
+	GtkWidget *passprmt;
+	GtkWidget *passentry;
 };
 
 struct save_pos {
@@ -257,6 +273,8 @@
 
 /* struct buddy_chat went away and got merged with this. */
 struct conversation {
+	struct gaim_connection *gc;
+
 	/* stuff used for both IM and chat */
 	GtkWidget *window;
 	char name[80];
@@ -297,6 +315,7 @@
 	GtkWidget *sep1;
 	GtkWidget *sep2;
  	time_t sent_away;
+	GtkWidget *menu;
 
 	/* stuff used just for chat */
         GList *in_room;
@@ -414,7 +433,6 @@
 #define TYPE_SIGNOFF   4
 #define TYPE_KEEPALIVE 5
 
-#define REVISION "gaim:$Revision: 950 $"
 #define FLAPON "FLAPON\r\n\r\n"
 
 #define ROAST "Tic/Toc"
@@ -432,14 +450,6 @@
 extern GtkWidget *applet;
 #endif /* USE_APPLET */
 
-/* Globals in oscar.c */
-extern struct aim_session_t *gaim_sess;
-extern struct aim_conn_t    *gaim_conn;
-extern GList *oscar_chats;
-extern int create_exchange;
-extern char *create_name;
-extern int keepalv;
-
 /* Globals in server.c */
 extern int correction_time;
 
@@ -464,7 +474,6 @@
 extern GList *conversations;
 extern GList *chat_rooms;
 extern GtkWidget *mainwindow;
-extern GtkWidget *remember;
 extern char *quad_addr;
 extern char toc_addy[16];
 
@@ -479,29 +488,28 @@
 extern GtkWidget *blist;
 
 extern int general_options;
-#define OPT_GEN_ENTER_SENDS      0x00000001
-#define OPT_GEN_AUTO_LOGIN       0x00000002
-#define OPT_GEN_LOG_ALL          0x00000004
-#define OPT_GEN_STRIP_HTML       0x00000008
-#define OPT_GEN_APP_BUDDY_SHOW   0x00000010
-#define OPT_GEN_POPUP_WINDOWS    0x00000020
-#define OPT_GEN_SEND_LINKS       0x00000040
-#define OPT_GEN_DEBUG            0x00000100
-#define OPT_GEN_REMEMBER_PASS    0x00000200
-#define OPT_GEN_REGISTERED       0x00000400
-#define OPT_GEN_BROWSER_POPUP    0x00000800
-#define OPT_GEN_SAVED_WINDOWS    0x00001000
+#define OPT_GEN_ENTER_SENDS       0x00000001
+/* #define OPT_GEN_AUTO_LOGIN        0x00000002 now OPT_USR_AUTO */
+#define OPT_GEN_LOG_ALL           0x00000004
+#define OPT_GEN_STRIP_HTML        0x00000008
+#define OPT_GEN_APP_BUDDY_SHOW    0x00000010
+#define OPT_GEN_POPUP_WINDOWS     0x00000020
+#define OPT_GEN_SEND_LINKS        0x00000040
+#define OPT_GEN_DEBUG             0x00000100
+/* #define OPT_GEN_REMEMBER_PASS     0x00000200 now OPT_USR_REM_PASS */
+#define OPT_GEN_REGISTERED        0x00000400
+#define OPT_GEN_BROWSER_POPUP     0x00000800
+#define OPT_GEN_SAVED_WINDOWS     0x00001000
 #define OPT_GEN_DISCARD_WHEN_AWAY 0x00002000
-#define OPT_GEN_NEAR_APPLET	0x00004000
-#define OPT_GEN_CHECK_SPELLING	0x00008000
-#define OPT_GEN_POPUP_CHAT	0x00010000
-#define OPT_GEN_BACK_ON_IM	0x00020000
-#define OPT_GEN_USE_OSCAR	0x00040000
-#define OPT_GEN_CTL_CHARS	0x00080000
-#define OPT_GEN_TIK_HACK        0x00100000
-#define OPT_GEN_CTL_SMILEYS     0x00200000
-#define OPT_GEN_KEEPALIVE       0x00400000
-extern int USE_OSCAR;
+#define OPT_GEN_NEAR_APPLET       0x00004000
+#define OPT_GEN_CHECK_SPELLING    0x00008000
+#define OPT_GEN_POPUP_CHAT        0x00010000
+#define OPT_GEN_BACK_ON_IM        0x00020000
+/* #define OPT_GEN_USE_OSCAR         0x00040000 now PROTO_OSCAR */
+#define OPT_GEN_CTL_CHARS         0x00080000
+#define OPT_GEN_TIK_HACK          0x00100000
+#define OPT_GEN_CTL_SMILEYS       0x00200000
+/* #define OPT_GEN_KEEPALIVE         0x00400000 now OPT_USR_KEEPALV */
 
 extern int display_options;
 #define OPT_DISP_SHOW_TIME        0x00000001
@@ -548,11 +556,14 @@
 #define OPT_FONT_FGCOL           0x00000040
 #define OPT_FONT_BGCOL           0x00000080
 
+#define OPT_USR_AUTO		0x00000001
+#define OPT_USR_KEEPALV		0x00000002
+#define OPT_USR_REM_PASS	0x00000004
+
 #define DEFAULT_INFO "Visit the GAIM website at <A HREF=\"http://www.marko.net/gaim\">http://www.marko.net/gaim</A>."
 
 extern int report_idle;
 extern int web_browser;
-extern struct aim_user *current_user;
 extern GList *aim_users;
 extern char web_command[2048];
 extern char debug_buff[BUF_LONG];
@@ -598,8 +609,6 @@
 extern char *date();
 extern gint linkify_text(char *);
 extern void aol_icon(GdkWindow *);
-extern int query_state();
-extern void set_state(int);
 extern FILE *open_log_file (char *);
 extern char *sec_to_text(int);
 extern struct aim_user *find_user(const char *);
@@ -616,16 +625,16 @@
 
 /* Functions in server.c */
 /* input to serv */
-extern int serv_login(char *, char *);
-extern void serv_close();
-extern void serv_touch_idle();
+extern struct gaim_connection *serv_login(char *, char *);
+extern void serv_close(struct gaim_connection *);
+extern void serv_touch_idle(struct gaim_connection *);
 extern void serv_finish_login();
 extern void serv_send_im(char *, char *, int);
 extern void serv_get_info(char *);
 extern void serv_get_away_msg(char *);
 extern void serv_get_dir(char *);
-extern void serv_set_idle(int);
-extern void serv_set_info(char *);
+extern void serv_set_idle(struct gaim_connection *, int);
+extern void serv_set_info(struct gaim_connection *, char *);
 extern void serv_set_away(char *);
 extern void serv_change_passwd(char *, char *);
 extern void serv_add_buddy(char *);
@@ -646,11 +655,11 @@
 extern void serv_chat_whisper(int, char *, char *);
 extern void serv_chat_send(int, char *);
 extern void serv_do_imimage(GtkWidget *, char *);
-extern void serv_got_imimage(char *, char *, char *, struct aim_conn_t *, int);
+extern void serv_got_imimage(struct gaim_connection *, char *, char *, char *, struct aim_conn_t *, int);
 
 /* output from serv */
 extern void serv_got_update(char *, int, int, time_t, time_t, int, u_short);
-extern void serv_got_im(char *, char *, int);
+extern void serv_got_im(struct gaim_connection *, char *, char *, int);
 extern void serv_got_eviled(char *, int);
 extern void serv_got_chat_invite(char *, int, char *, char *);
 extern void serv_got_joined_chat(int, char *);
@@ -684,30 +693,27 @@
 extern void do_small(GtkWidget *, GtkWidget *);
 extern void do_normal(GtkWidget *, GtkWidget *);
 extern void do_big(GtkWidget *, GtkWidget *);
-extern void toggle_link(GtkWidget *, struct conversation *);
-extern int invert_tags(GtkWidget *, char *, char *, int);
-extern void quiet_set(GtkWidget *, int);
-extern int count_tag(GtkWidget *, char *, char *);
 extern void set_font_face(char *, struct conversation *);
+extern void redo_convo_menus();
 
 /* Functions in network.c */
 extern unsigned int *get_address(char *);
 extern int connect_address(unsigned int, unsigned short);
 
 /* Functions in oscar.c */
-extern int oscar_login(char *, char *);
-extern void oscar_close();
-extern struct chat_connection *find_oscar_chat(char *name);
-extern void oscar_do_directim(char *);
-extern void update_keepalive(gboolean);
+extern struct gaim_connection *oscar_login(char *, char *);
+extern void oscar_close(struct gaim_connection *);
+extern struct chat_connection *find_oscar_chat(struct gaim_connection *, char *name);
+extern void oscar_do_directim(struct gaim_connection *, char *);
+extern void update_keepalive(struct gaim_connection *, gboolean);
 
 /* Functions in toc.c */
 extern void toc_close();
-extern int toc_login(char *, char *);
-extern int toc_wait_signon(void);
-extern char *toc_wait_config(void);
-extern int sflap_send(char *, int , int );
-extern void parse_toc_buddy_list(char *, int);
+extern struct gaim_connection *toc_login(char *, char *);
+extern int toc_wait_signon(struct gaim_connection *);
+extern char *toc_wait_config(struct gaim_connection *);
+extern int sflap_send(struct gaim_connection *, char *, int , int );
+extern void parse_toc_buddy_list(struct gaim_connection *, char *, int);
 
 
 /* Functions in buddy.c */
@@ -719,7 +725,7 @@
 extern void show_buddy_list();
 extern void refresh_buddy_window();
 extern void toc_build_config(char *, int len, gboolean);
-extern void signoff();
+extern void signoff(struct gaim_connection *);
 extern void do_im_back();
 extern void set_buddy(struct buddy *);
 extern struct person *add_person(char *, char *);
@@ -749,7 +755,7 @@
 extern void hide_login_progress(char *);
 extern void set_login_progress(int, char *);
 extern void show_login();
-extern void gaim_setup();
+extern void gaim_setup(struct gaim_connection *gc);
 #ifdef USE_APPLET
 extern void createOnlinePopup();
 extern void applet_show_login(AppletWidget *, gpointer);
@@ -822,7 +828,6 @@
 extern void show_find_info();
 extern void g_show_info (char *);
 extern void g_show_info_text (char *);
-extern void show_register_dialog();
 extern void show_set_info();
 extern void show_set_dir();
 extern void show_fgcolor_dialog(struct conversation *c, GtkWidget *color);
@@ -833,8 +838,8 @@
 extern void show_ee_dialog(int);
 extern void show_add_link(GtkWidget *,struct conversation *);
 extern void show_change_passwd();
-extern void do_import(GtkWidget *, void *);
-extern int bud_list_cache_exists();
+extern void do_import(GtkWidget *, struct gaim_connection *);
+extern int bud_list_cache_exists(struct gaim_connection *);
 extern void show_smiley_dialog(struct conversation *, GtkWidget *);
 extern void close_smiley_dialog(GtkWidget *widget, struct conversation *c);
 extern void set_smiley_array(GtkWidget *widget, int smiley_type);
@@ -863,3 +868,5 @@
 void BuddyTickerAddUser(char *, GdkPixmap *, GdkBitmap *);
 void BuddyTickerSetPixmap(char *, GdkPixmap *, GdkBitmap *);
 void BuddyTickerSignoff();
+
+#endif /* _GAIM_GAIM_H_ */
--- a/src/gaimrc.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/gaimrc.c	Tue Oct 10 00:02:02 2000 +0000
@@ -38,7 +38,6 @@
 /* for people like myself, who are too lazy to add an away msg :) */
 #define BORING_DEFAULT_AWAY_MSG "sorry, i ran out for a while. bbl"
 
-struct aim_user *current_user = NULL;
 GList *aim_users = NULL;
 int general_options;
 int display_options;
@@ -442,6 +441,8 @@
         strcpy(u->password, p->value[1]);
 
         u->user_info[0] = 0;
+	u->options = OPT_USR_REM_PASS;
+	u->protocol = PROTO_TOC;
 
         if (!fgets(buf, sizeof(buf), f))
                 return u;
@@ -462,6 +463,22 @@
                 }
         }
 
+	if (!fgets(buf, sizeof(buf), f)) {
+		return u;
+	}
+
+	if (!strcmp(buf, "\t}")) {
+		return u;
+	}
+
+	p = parse_line(buf);
+
+	if (strcmp(p->option, "user_opts"))
+		return u;
+
+	u->options = atoi(p->value[0]);
+	u->protocol = atoi(p->value[1]);
+
         return u;
         
 }
@@ -470,7 +487,7 @@
 {
         char *c;
         int nl = 1;;
-	if (general_options & OPT_GEN_REMEMBER_PASS)
+	if (u->options & OPT_USR_REM_PASS)
 	        fprintf(f, "\t\tident { %s } { %s }\n", u->username, u->password);
 	else
 		fprintf(f, "\t\tident { %s } {  }\n", u->username);
@@ -492,6 +509,7 @@
                 c++;
         }
         fprintf(f, "\n\t\t}\n");
+	fprintf(f, "\t\tuser_opts { %d } { %d }\n", u->options, u->protocol);
         
 }
 
@@ -501,7 +519,6 @@
 	char buf[2048];
         struct aim_user *u;
         struct parse *p;
-        int cur = 0;
 
 	buf[0] = 0;
 
@@ -517,19 +534,13 @@
                 p = parse_line(buf);
 
                 if (!strcmp(p->option, "current_user")) {
-                        cur = 1;
                 } else if (strcmp(p->option, "user")) {
-			cur = 0;
                         continue;
                 } else {
-			cur = 0;
 		}
 
                 u = gaimrc_read_user(f);
 
-                if (cur)
-                        current_user = u;
-                
                 aim_users = g_list_append(aim_users, u);
 	}
 }
@@ -543,11 +554,7 @@
 	
 	while(usr) {
                 u = (struct aim_user *)usr->data;
-                if (current_user == u) {
-                        fprintf(f, "\tcurrent_user {\n");
-                } else {
-                        fprintf(f, "\tuser {\n");
-                }
+                fprintf(f, "\tuser {\n");
                 gaimrc_write_user(f, u);
 
                 fprintf(f, "\t}\n");
@@ -679,7 +686,7 @@
                 OPT_GEN_SEND_LINKS |
                 OPT_GEN_ENTER_SENDS |
                 OPT_GEN_SAVED_WINDOWS |
-                OPT_GEN_REMEMBER_PASS |
+                /* OPT_GEN_REMEMBER_PASS | */
 		OPT_GEN_REGISTERED |
 		OPT_GEN_NEAR_APPLET |
 		OPT_GEN_CTL_SMILEYS |
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/multi.c	Tue Oct 10 00:02:02 2000 +0000
@@ -0,0 +1,678 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <gtk/gtk.h>
+#include "multi.h"
+#include "gaim.h"
+#include "aim.h"
+
+#include "pixmaps/gnome_add.xpm"
+#include "pixmaps/gnome_preferences.xpm"
+#include "pixmaps/join.xpm"
+#include "pixmaps/gnome_remove.xpm"
+#include "pixmaps/gnome_close.xpm"
+#include "pixmaps/cancel.xpm"
+#include "pixmaps/ok.xpm"
+
+GSList *connections;
+
+static GtkWidget *acctedit = NULL;
+static GtkWidget *list = NULL; /* the clist of names in the accteditor */
+static GtkWidget *newmod = NULL; /* the dialog for creating a new account */
+static struct aim_user tmpusr;
+
+struct mod_usr_opt {
+	struct aim_user *user;
+	int opt;
+};
+
+struct gaim_connection *new_gaim_conn(int proto, char *username, char *password)
+{
+	struct gaim_connection *gc = g_new0(struct gaim_connection, 1);
+	gc->protocol = proto;
+	g_snprintf(gc->username, sizeof(gc->username), "%s", username);
+	g_snprintf(gc->password, sizeof(gc->password), "%s", password);
+	gc->keepalive = -1;
+
+	switch(proto) {
+		case PROTO_TOC:
+			gc->toc_fd = -1;
+			gc->seqno = 0;
+			gc->state = 0;
+			gc->inpa = -1;
+			break;
+		case PROTO_OSCAR:
+			gc->oscar_sess = NULL;
+			gc->oscar_conn = NULL;
+			gc->inpa = -1;
+			gc->cnpa = -1;
+			gc->paspa = -1;
+			gc->create_exchange = 0;
+			gc->create_name = NULL;
+			gc->oscar_chats = NULL;
+			break;
+		default: /* damn plugins */
+			/* PRPL */
+			break;
+	}
+
+	connections = g_slist_append(connections, gc);
+
+	return gc;
+}
+
+void destroy_gaim_conn(struct gaim_connection *gc)
+{
+	switch (gc->protocol) {
+		case PROTO_TOC:
+			break;
+		case PROTO_OSCAR:
+			break;
+		default:
+			/* PRPL */
+			break;
+	}
+	connections = g_slist_remove(connections, gc);
+	g_free(gc);
+	redo_convo_menus();
+}
+
+struct gaim_connection *find_gaim_conn_by_name(char *name) {
+	char *who = g_strdup(normalize(name));
+	GSList *c = connections;
+	struct gaim_connection *g = NULL;
+
+	while (c) {
+		g = (struct gaim_connection *)c->data;
+		if (!strcmp(normalize(g->username), who)) {
+			g_free(who);
+			return g;
+		}
+		c = c->next;
+	}
+
+	g_free(who);
+	return NULL;
+}
+
+static void delete_acctedit(GtkWidget *w, gpointer d)
+{
+	if (acctedit) {
+		save_prefs();
+		gtk_widget_destroy(acctedit);
+	}
+	acctedit = NULL;
+}
+
+static gint acctedit_close(GtkWidget *w, gpointer d)
+{
+	gtk_widget_destroy(acctedit);
+	return FALSE;
+}
+
+static char *proto_name(int proto)
+{
+	switch (proto) {
+		case PROTO_TOC:
+			return "TOC";
+		case PROTO_OSCAR:
+			return "Oscar";
+		default:
+			/* PRPL */
+			return "Other";
+	}
+}
+
+static GtkWidget *generate_list()
+{
+	GtkWidget *win;
+	char *titles[4] = {"Screenname", "Currently Online", "Auto-login", "Protocol"};
+	GList *u = aim_users;
+	struct aim_user *a;
+	int i;
+
+	win = gtk_scrolled_window_new(0, 0);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win), GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_ALWAYS);
+
+	list = gtk_clist_new_with_titles(4, titles);
+	gtk_clist_set_column_width(GTK_CLIST(list), 0, 90);
+	gtk_clist_set_selection_mode(GTK_CLIST(list), GTK_SELECTION_BROWSE);
+	gtk_clist_column_titles_passive(GTK_CLIST(list));
+	gtk_container_add(GTK_CONTAINER(win), list);
+	gtk_widget_show(list);
+
+	while (u) {
+		a = (struct aim_user *)u->data;
+		titles[0] = a->username;
+		titles[1] = find_gaim_conn_by_name(a->username) ? "True" : "False";
+		titles[2] = (a->options & OPT_USR_AUTO) ? "True" : "False";
+		titles[3] = proto_name(a->protocol);
+		i = gtk_clist_append(GTK_CLIST(list), titles);
+		gtk_clist_set_row_data(GTK_CLIST(list), i, a);
+		u = u->next;
+	}
+
+	gtk_widget_show(win);
+	return win;
+}
+
+static void delmod(GtkWidget *w, struct aim_user *u)
+{
+	gtk_widget_destroy(w);
+	if (u) {
+		u->mod = NULL;
+	} else {
+		newmod = NULL;
+	}
+}
+
+static void mod_opt(GtkWidget *b, struct mod_usr_opt *m)
+{
+	if (m->user) {
+		m->user->tmp_options = m->user->tmp_options ^ m->opt;
+	} else {
+		tmpusr.options = tmpusr.options ^ m->opt;
+	}
+}
+
+static GtkWidget *acct_button(const char *text, struct aim_user *u, int option, GtkWidget *box)
+{
+	GtkWidget *button;
+	struct mod_usr_opt *muo = g_new0(struct mod_usr_opt, 1);
+	button = gtk_check_button_new_with_label(text);
+	if (u) {
+		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (u->options & option));
+	} else {
+		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (tmpusr.options & option));
+	}
+	gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
+	muo->user = u; muo->opt = option;
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(mod_opt), muo);
+	gtk_widget_show(button);
+	return button;
+}
+
+static void ok_mod(GtkWidget *w, struct aim_user *u)
+{
+	char *txt;
+	int i;
+	if (u) {
+		u->options = u->tmp_options;
+		u->protocol = u->tmp_protocol;
+		txt = gtk_entry_get_text(GTK_ENTRY(u->pass));
+		if (u->options & OPT_USR_REM_PASS)
+			g_snprintf(u->password, sizeof(u->password), "%s", txt);
+		else
+			u->password[0] = '\0';
+		gtk_widget_destroy(u->mod);
+		i = gtk_clist_find_row_from_data(GTK_CLIST(list), u);
+		gtk_clist_set_text(GTK_CLIST(list), i, 2, (u->options & OPT_USR_AUTO) ? "True" : "False");
+		gtk_clist_set_text(GTK_CLIST(list), i, 3, proto_name(u->protocol));
+	} else {
+		char *titles[4];
+		txt = gtk_entry_get_text(GTK_ENTRY(tmpusr.name));
+		if (!find_user(txt)) {
+			/* PRPL: also need to check protocol. remember TOC and Oscar are both AIM */
+			gtk_widget_destroy(newmod);
+			return;
+		}
+		u = g_new0(struct aim_user, 1);
+		u->protocol = PROTO_TOC;
+		g_snprintf(u->username, sizeof(u->username), "%s", txt);
+		txt = gtk_entry_get_text(GTK_ENTRY(tmpusr.pass));
+		g_snprintf(u->password, sizeof(u->password), "%s", txt);
+		u->options = tmpusr.options;
+		u->protocol = tmpusr.protocol;
+		gtk_widget_destroy(newmod);
+		titles[0] = u->username;
+		titles[1] = find_gaim_conn_by_name(u->username) ? "True" : "False";
+		titles[2] = (u->options & OPT_USR_AUTO) ? "True" : "False";
+		titles[3] = proto_name(u->protocol);
+		i = gtk_clist_append(GTK_CLIST(list), titles);
+		gtk_clist_set_row_data(GTK_CLIST(list), i, u);
+	}
+	save_prefs();
+}
+
+static void cancel_mod(GtkWidget *w, struct aim_user *u)
+{
+	if (u) {
+		gtk_widget_destroy(u->mod);
+	} else {
+		gtk_widget_destroy(newmod);
+	}
+}
+
+static void set_prot(GtkWidget *opt, int proto)
+{
+	struct aim_user *u = gtk_object_get_user_data(GTK_OBJECT(opt));
+	if (u) {
+		u->tmp_protocol = proto;
+	} else {
+		tmpusr.protocol = proto;
+	}
+}
+
+static GtkWidget *make_protocol_menu(GtkWidget *box, struct aim_user *u)
+{
+	GtkWidget *optmenu;
+	GtkWidget *menu;
+	GtkWidget *opt;
+
+	/* PRPL: should we set some way to update these when new protocols get added? */
+	optmenu = gtk_option_menu_new();
+	gtk_box_pack_start(GTK_BOX(box), optmenu, FALSE, FALSE, 5);
+	gtk_widget_show(optmenu);
+
+	menu = gtk_menu_new();
+
+	/* PRPL: we need to have some way of getting all the plugin names, etc */
+	opt = gtk_menu_item_new_with_label("TOC");
+	gtk_object_set_user_data(GTK_OBJECT(opt), u);
+	gtk_signal_connect(GTK_OBJECT(opt), "activate", GTK_SIGNAL_FUNC(set_prot), (void *)PROTO_TOC);
+	gtk_menu_append(GTK_MENU(menu), opt);
+	gtk_widget_show(opt);
+
+	opt = gtk_menu_item_new_with_label("Oscar");
+	gtk_object_set_user_data(GTK_OBJECT(opt), u);
+	gtk_signal_connect(GTK_OBJECT(opt), "activate", GTK_SIGNAL_FUNC(set_prot), (void *)PROTO_OSCAR);
+	gtk_menu_append(GTK_MENU(menu), opt);
+	gtk_widget_show(opt);
+
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
+	if (u) {
+		gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), u->protocol);
+	} else {
+		gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), PROTO_TOC);
+	}
+
+	return optmenu;
+}
+
+static void show_acct_mod(struct aim_user *u)
+{
+	/* here we can have all the aim_user options, including ones not shown in the main acctedit
+	 * window. this can keep the size of the acctedit window small and readable, and make this
+	 * one the powerful editor. this is where things like name/password are edited, but can
+	 * also have toggles (and even more complex options) like whether to autologin or whether
+	 * to send keepalives or whatever. this would be the perfect place to specify which protocol
+	 * to use. make sure to account for the possibility of protocol plugins. */
+	GtkWidget *mod;
+	GtkWidget *frame;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *name;
+	GtkWidget *pass;
+	GtkWidget *button;
+
+	if (!u && newmod) {
+		gtk_widget_show(newmod);
+		return;
+	}
+	if (u && u->mod) {
+		gtk_widget_show(u->mod);
+		return;
+	}
+
+	mod = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_wmclass(GTK_WINDOW(mod), "account", "Gaim");
+	gtk_widget_realize(mod);
+	aol_icon(mod->window);
+	gtk_container_border_width(GTK_CONTAINER(mod), 10);
+	gtk_window_set_title(GTK_WINDOW(mod), _("Gaim - Modify Account"));
+	gtk_signal_connect(GTK_OBJECT(mod), "destroy",
+			   GTK_SIGNAL_FUNC(delmod), u);
+
+	frame = gtk_frame_new(_("Modify Account"));
+	gtk_container_add(GTK_CONTAINER(mod), frame);
+	gtk_widget_show(frame);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	gtk_widget_show(vbox);
+
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	label = gtk_label_new(_("Screenname:"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_widget_show(label);
+
+	name = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(hbox), name, FALSE, FALSE, 5);
+	gtk_widget_show(name);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	label = gtk_label_new(_("Password:"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_widget_show(label);
+
+	pass = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(hbox), pass, FALSE, FALSE, 5);
+	gtk_entry_set_visibility(GTK_ENTRY(pass), FALSE);
+	gtk_widget_show(pass);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	make_protocol_menu(hbox, u);
+
+	acct_button(_("Remember Password"), u, OPT_USR_REM_PASS, vbox);
+	acct_button(_("Auto-Login"), u, OPT_USR_AUTO, vbox);
+	acct_button(_("Send KeepAlive packet (6 bytes/second)"), u, OPT_USR_KEEPALV, vbox);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	button = picture_button(mod, _("Cancel"), cancel_xpm);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(cancel_mod), u);
+	gtk_widget_show(button);
+
+	button = picture_button(mod, _("OK"), ok_xpm);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(ok_mod), u);
+	gtk_widget_show(button);
+
+	if (u) {
+		u->mod = mod;
+		u->name = name;
+		u->pass = pass;
+		u->tmp_options = u->options;
+		gtk_entry_set_text(GTK_ENTRY(name), u->username);
+		gtk_entry_set_text(GTK_ENTRY(pass), u->password);
+		gtk_entry_set_editable(GTK_ENTRY(name), FALSE);
+	} else {
+		newmod = mod;
+		tmpusr.name = name;
+		tmpusr.pass = pass;
+	}
+
+	gtk_widget_show(mod);
+}
+
+static void add_acct(GtkWidget *w, gpointer d)
+{
+	show_acct_mod(NULL);
+}
+
+static void mod_acct(GtkWidget *w, gpointer d)
+{
+	int row = -1;
+	char *name;
+	struct aim_user *u;
+	if (GTK_CLIST(list)->selection)
+		row = (int)GTK_CLIST(list)->selection->data;
+	if (row != -1) {
+		gtk_clist_get_text(GTK_CLIST(list), row, 0, &name);
+		u = find_user(name);
+		if (u)
+			show_acct_mod(u);
+	}
+}
+
+static void pass_des(GtkWidget *w, struct aim_user *u)
+{
+	gtk_widget_destroy(w);
+	u->passprmt = NULL;
+}
+
+static void pass_cancel(GtkWidget *w, struct aim_user *u)
+{
+	gtk_widget_destroy(u->passprmt);
+	u->passprmt = NULL;
+}
+
+static void pass_signon(GtkWidget *w, struct aim_user *u)
+{
+	char *txt = gtk_entry_get_text(GTK_ENTRY(u->passentry));
+	char *un, *ps;
+#ifdef USE_APPLET
+	set_user_state(signing_on);
+#endif
+	un = g_strdup(u->username);
+	ps = g_strdup(txt);
+	gtk_widget_destroy(u->passprmt);
+	u->passprmt = NULL;
+	serv_login(un, ps);
+	g_free(un);
+	g_free(ps);
+}
+
+static void do_pass_dlg(struct aim_user *u)
+{
+	/* we can safely assume that u is not NULL */
+	GtkWidget *frame;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	char buf[96];
+	GtkWidget *label;
+	GtkWidget *button;
+
+	if (u->passprmt) { gtk_widget_show(u->passprmt); return; }
+	u->passprmt = gtk_window_new(GTK_WINDOW_DIALOG);
+	gtk_window_set_wmclass(GTK_WINDOW(u->passprmt), "password", "Gaim");
+	gtk_container_border_width(GTK_CONTAINER(u->passprmt), 5);
+	gtk_signal_connect(GTK_OBJECT(u->passprmt), "destroy", GTK_SIGNAL_FUNC(pass_des), u);
+	gtk_widget_realize(u->passprmt);
+	aol_icon(u->passprmt->window);
+
+	frame = gtk_frame_new(_("Enter Password"));
+	gtk_container_add(GTK_CONTAINER(u->passprmt), frame);
+	gtk_widget_show(frame);
+
+	vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	gtk_widget_show(vbox);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	g_snprintf(buf, sizeof(buf), "Password for %s:", u->username);
+	label = gtk_label_new(buf);
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+	gtk_widget_show(label);
+
+	u->passentry = gtk_entry_new();
+	gtk_entry_set_visibility(GTK_ENTRY(u->passentry), FALSE);
+	gtk_box_pack_start(GTK_BOX(hbox), u->passentry, FALSE, FALSE, 5);
+	gtk_signal_connect(GTK_OBJECT(u->passentry), "activate", GTK_SIGNAL_FUNC(pass_signon), u);
+	gtk_widget_grab_focus(u->passentry);
+	gtk_widget_show(u->passentry);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	button = picture_button(u->passprmt, _("Cancel"), cancel_xpm);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(pass_cancel), u);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+	button = picture_button(u->passprmt, _("Signon"), ok_xpm);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(pass_signon), u);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
+
+	gtk_widget_show(u->passprmt);
+}
+
+static void acct_signin(GtkWidget *w, gpointer d)
+{
+	int row = -1;
+	char *name;
+	struct aim_user *u;
+	struct gaim_connection *gc;
+	if (GTK_CLIST(list)->selection)
+		row = (int)GTK_CLIST(list)->selection->data;
+	if (row != -1) {
+		gtk_clist_get_text(GTK_CLIST(list), row, 0, &name);
+		u = find_user(name);
+		gc = find_gaim_conn_by_name(name);
+		if (!gc) {
+			char *un, *ps;
+			if (!u->password[0]) {
+				do_pass_dlg(u);
+			} else {
+#ifdef USE_APPLET
+				set_user_state(signing_on);
+#endif /* USE_APPLET */
+
+				un = g_strdup(u->username);
+				ps = g_strdup(u->password);
+				gc = serv_login(un, ps);
+				g_free(un);
+				g_free(ps);
+			}
+		} else {
+			signoff(gc);
+		}
+	}
+}
+		
+static void del_acct(GtkWidget *w, gpointer d)
+{
+	int row = -1;
+	char *name;
+	struct aim_user *u;
+	if (GTK_CLIST(list)->selection)
+		row = (int)GTK_CLIST(list)->selection->data;
+	if (row != -1) {
+		gtk_clist_get_text(GTK_CLIST(list), row, 0, &name);
+		u = find_user(name);
+		if (u) {
+			aim_users = g_list_remove(aim_users, u);
+			save_prefs();
+		}
+		gtk_clist_remove(GTK_CLIST(list), row);
+	}
+}
+
+void account_editor(GtkWidget *w, GtkWidget *W)
+{
+	/* please kill me */
+	GtkWidget *frame;
+	GtkWidget *box;
+	GtkWidget *list;
+	GtkWidget *hbox;
+	GtkWidget *button; /* used for many things */
+
+	if (acctedit) { gtk_widget_show(acctedit); return; }
+
+	acctedit = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(acctedit), _("Gaim - Account Editor"));
+	gtk_window_set_wmclass(GTK_WINDOW(acctedit), "accounteditor", "Gaim");
+	gtk_widget_realize(acctedit);
+	aol_icon(acctedit->window);
+	gtk_container_border_width(GTK_CONTAINER(acctedit), 10);
+	gtk_widget_set_usize(acctedit, -1, 200);
+	gtk_signal_connect(GTK_OBJECT(acctedit), "destroy",
+			   GTK_SIGNAL_FUNC(delete_acctedit), NULL);
+
+	frame = gtk_frame_new(_("Account Editor"));
+	gtk_container_add(GTK_CONTAINER(acctedit), frame);
+	gtk_widget_show(frame);
+
+	box = gtk_vbox_new(FALSE, 5);
+	gtk_container_add(GTK_CONTAINER(frame), box);
+	gtk_widget_show(box);
+
+	list = generate_list();
+	gtk_box_pack_start(GTK_BOX(box), list, TRUE, TRUE, 5);
+
+	hbox = gtk_hbox_new(TRUE, 5);
+	gtk_box_pack_end(GTK_BOX(box), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	button = picture_button(acctedit, _("Add"), gnome_add_xpm);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(add_acct), NULL);
+
+	button = picture_button(acctedit, _("Modify"), gnome_preferences_xpm);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(mod_acct), NULL);
+
+	button = picture_button(acctedit, _("Sign On/Off"), join_xpm);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(acct_signin), NULL);
+
+	button = picture_button(acctedit, _("Delete"), gnome_remove_xpm);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(del_acct), NULL);
+
+	button = picture_button(acctedit, _("Close"), gnome_close_xpm);
+	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
+	gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(acctedit_close), NULL);
+
+	gtk_widget_show(acctedit);
+}
+
+void account_online(struct gaim_connection *gc)
+{
+	struct aim_user *u;
+	int i;
+	if (!acctedit) return;
+	u = find_user(gc->username);
+	i = gtk_clist_find_row_from_data(GTK_CLIST(list), u);
+	gtk_clist_set_text(GTK_CLIST(list), i, 1, "True");
+	gtk_clist_set_text(GTK_CLIST(list), i, 3, proto_name(gc->protocol));
+	redo_convo_menus();
+}
+
+void account_offline(struct gaim_connection *gc)
+{
+	struct aim_user *u;
+	int i;
+	if (!acctedit) return;
+	u = find_user(gc->username);
+	i = gtk_clist_find_row_from_data(GTK_CLIST(list), u);
+	gtk_clist_set_text(GTK_CLIST(list), i, 1, "False");
+}
+
+void auto_login()
+{
+	GList *u = aim_users;
+	struct aim_user *a = NULL;
+	char *un, *ps;
+
+	while (u) {
+		a = (struct aim_user *)u->data;
+		if ((a->options & OPT_USR_AUTO) && (a->options & OPT_USR_REM_PASS)) {
+#ifdef USE_APPLET
+			set_user_state(signing_on);
+#endif /* USE_APPLET */
+
+			un = g_strdup(a->username);
+			ps = g_strdup(a->password);
+			serv_login(un, ps);
+			g_free(un);
+			g_free(ps);
+		}
+		u = u->next;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/multi.h	Tue Oct 10 00:02:02 2000 +0000
@@ -0,0 +1,87 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _GAIMMULTI_H_
+#define _GAIMMULTI_H_
+
+#include <gtk/gtk.h>
+#include "gaim.h"
+#include "aim.h"
+
+#define PROTO_TOC 0
+#define PROTO_OSCAR 1
+
+/* ok. now the fun begins. first we create a connection structure */
+struct gaim_connection {
+	/* we need to do either oscar or TOC */
+	/* we make this as an int in case if we want to add more protocols later */
+	int protocol;
+
+	/* let's do the oscar-specific stuff first since i know it better */
+	struct aim_session_t *oscar_sess;
+	struct aim_conn_t *oscar_conn; /* we don't particularly need this since it
+					  will be in oscar_sess, but it's useful to
+					  still keep our own reference to it */
+	int inpa; /* do we really need this? it's for the BOS conn */
+	int cnpa; /* chat nav input watcher */
+	int paspa; /* for changing passwords, which doesn't work yet */
+
+	int create_exchange;
+	char *create_name;
+
+	GSList *oscar_chats;
+
+	/* that's all we need for oscar. now then, on to TOC.... */
+	int toc_fd;
+	int seqno;
+	int state;
+	/* int inpa; input watcher, dual-declared for oscar as well */
+
+	/* now we'll do stuff that both of them need */
+	char username[64];
+	char password[32];
+	char user_info[2048];
+	char g_screenname[64];
+	int options; /* same as aim_user options */
+	int keepalive;
+	/* stuff needed for per-connection idle times */
+	int idle_timer;
+	time_t login_time;
+	time_t lastsent;
+	int is_idle;
+};
+
+/* now that we have our struct, we're going to need lots of them. Maybe even a list of them. */
+extern GSList *connections;
+
+struct gaim_connection *new_gaim_conn(int, char *, char *);
+void destroy_gaim_conn(struct gaim_connection *);
+
+struct gaim_connection *find_gaim_conn_by_name(char *);
+
+void account_editor(GtkWidget *, GtkWidget *);
+
+void account_online(struct gaim_connection *);
+void account_offline(struct gaim_connection *);
+
+void auto_login();
+
+#endif /* _GAIMMULTI_H_ */
--- a/src/oscar.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/oscar.c	Tue Oct 10 00:02:02 2000 +0000
@@ -37,30 +37,22 @@
 #include <time.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include "multi.h"
 #include "gaim.h"
-#include <aim.h>
+#include "aim.h"
 #include "gnome_applet_mgr.h"
 
 #include "pixmaps/cancel.xpm"
 #include "pixmaps/ok.xpm"
 
-static int inpa = -1;
-static int paspa = -1;
-static int cnpa = -1;
-struct aim_session_t *gaim_sess = NULL;
-struct aim_conn_t    *gaim_conn;
 int gaim_caps = AIM_CAPS_CHAT | AIM_CAPS_SENDFILE | AIM_CAPS_GETFILE |
 		AIM_CAPS_VOICE | AIM_CAPS_IMIMAGE | AIM_CAPS_BUDDYICON;
-int USE_OSCAR = 0;
 int keepalv = -1;
-int create_exchange = 0;
-char *create_name = NULL;
 
-GList *oscar_chats = NULL;
-
-struct chat_connection *find_oscar_chat(char *name) {
-	GList *g = oscar_chats;
+struct chat_connection *find_oscar_chat(struct gaim_connection *gc, char *name) {
+	GSList *g = gc->oscar_chats;
 	struct chat_connection *c = NULL;
+	if (gc->protocol != PROTO_OSCAR) return NULL;
 
 	while (g) {
 		c = (struct chat_connection *)g->data;
@@ -73,8 +65,9 @@
 	return c;
 }
 
-static struct chat_connection *find_oscar_chat_by_conn(struct aim_conn_t *conn) {
-	GList *g = oscar_chats;
+static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
+							struct aim_conn_t *conn) {
+	GSList *g = gc->oscar_chats;
 	struct chat_connection *c = NULL;
 
 	while (g) {
@@ -88,6 +81,41 @@
 	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 == gc->oscar_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;
+	struct aim_conn_t *s;
+	while (g) {
+		c = (struct gaim_connection *)g->data;
+		s = c->oscar_sess->connlist;
+		while (s) {
+			if (conn == s)
+				break;
+			s = s->next;
+		}
+		if (s) break;
+		g = g->next;
+		c = NULL;
+	}
+
+	return c;
+}
+
 static int gaim_parse_auth_resp  (struct aim_session_t *, struct command_rx_struct *, ...);
 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 *, ...);
@@ -146,27 +174,31 @@
 };
 static int msgerrreasonlen = 25;
 
-extern void auth_failed();
-
 static void oscar_callback(gpointer data, gint source,
 				GdkInputCondition condition) {
 	struct aim_conn_t *conn = (struct aim_conn_t *)data;
+	struct gaim_connection *gc = find_gaim_conn_by_oscar_conn(conn);
+	if (!gc) {
+		/* oh boy. this is probably bad. i guess the only thing we can really do
+		 * is return? */
+		debug_print("oscar callback for closed connection.\n");
+		return;
+	}
 
 	if (condition & GDK_INPUT_EXCEPTION) {
-		signoff();
+		signoff(gc);
 		hide_login_progress(_("Disconnected."));
-		auth_failed();
 		return;
 	}
 	if (condition & GDK_INPUT_READ) {
 		if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
 			debug_print("got information on rendezvous\n");
-			if (aim_handlerendconnect(gaim_sess, conn) < 0) {
+			if (aim_handlerendconnect(gc->oscar_sess, conn) < 0) {
 				debug_print(_("connection error (rend)\n"));
 			}
 		} else {
-			if (aim_get_command(gaim_sess, conn) >= 0) {
-				aim_rxdispatch(gaim_sess);
+			if (aim_get_command(gc->oscar_sess, conn) >= 0) {
+				aim_rxdispatch(gc->oscar_sess);
 			} else {
 				if (conn->type == AIM_CONN_TYPE_RENDEZVOUS &&
 				    conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
@@ -176,56 +208,59 @@
 					if (cnv) {
 						make_direct(cnv, FALSE, NULL, 0);
 					}
-					aim_conn_kill(gaim_sess, &conn);
+					aim_conn_kill(gc->oscar_sess, &conn);
 				} else if ((conn->type == AIM_CONN_TYPE_BOS) ||
-					   !(aim_getconn_type(gaim_sess, AIM_CONN_TYPE_BOS))) {
+					   !(aim_getconn_type(gc->oscar_sess, AIM_CONN_TYPE_BOS))) {
 					debug_print(_("major connection error\n"));
-					signoff();
+					signoff(gc);
 					hide_login_progress(_("Disconnected."));
-					auth_failed();
 				} else if (conn->type == AIM_CONN_TYPE_CHAT) {
-					struct chat_connection *c = find_oscar_chat_by_conn(conn);
+					struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
 					char buf[BUF_LONG];
 					sprintf(debug_buff, "disconnected from chat room %s\n", c->name);
 					debug_print(debug_buff);
 					c->conn = NULL;
-					gdk_input_remove(c->inpa);
+					if (c->inpa > -1)
+						gdk_input_remove(c->inpa);
 					c->inpa = -1;
 					c->fd = -1;
-					aim_conn_kill(gaim_sess, &conn);
+					aim_conn_kill(gc->oscar_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) {
-					gdk_input_remove(cnpa);
-					cnpa = -1;
+					if (gc->cnpa > -1)
+						gdk_input_remove(gc->cnpa);
+					gc->cnpa = -1;
 					debug_print("removing chatnav input watcher\n");
-					aim_conn_kill(gaim_sess, &conn);
+					aim_conn_kill(gc->oscar_sess, &conn);
 				} else {
 					sprintf(debug_buff, "holy crap! generic connection error! %d\n",
 							conn->type);
 					debug_print(debug_buff);
-					aim_conn_kill(gaim_sess, &conn);
+					aim_conn_kill(gc->oscar_sess, &conn);
 				}
 			}
 		}
 	}
 }
 
-int oscar_login(char *username, char *password) {
+struct gaim_connection *oscar_login(char *username, char *password) {
 	struct aim_session_t *sess;
 	struct aim_conn_t *conn;
 	struct aim_user *u;
 	char buf[256];
+	struct gaim_connection *gc;
 
 	sprintf(debug_buff, _("Logging in %s\n"), username);
 	debug_print(debug_buff);
 
+	gc = new_gaim_conn(PROTO_OSCAR, username, password);
 	sess = g_new0(struct aim_session_t, 1);
 	aim_session_init(sess);
 	/* 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;
-	gaim_sess = sess;
+	gc->oscar_sess = sess;
 
 	sprintf(buf, _("Looking up %s"), FAIM_LOGIN_SERVER);
 	set_login_progress(1, buf);
@@ -236,14 +271,14 @@
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif
-		set_state(STATE_OFFLINE);
+		destroy_gaim_conn(gc);
 		hide_login_progress(_("Unable to login to AIM"));
-		return -1;
+		return NULL;
 	} else if (conn->fd == -1) {
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif
-		set_state(STATE_OFFLINE);
+		destroy_gaim_conn(gc);
 		if (conn->status & AIM_CONN_STATUS_RESOLVERR) {
 			sprintf(debug_buff, _("couldn't resolve host\n"));
 			debug_print(debug_buff);
@@ -253,7 +288,7 @@
 			debug_print(debug_buff);
 			hide_login_progress(debug_buff);
 		}
-		return -1;
+		return NULL;
 	}
 	g_snprintf(buf, sizeof(buf), _("Signon: %s"), username);
 	set_login_progress(2, buf);
@@ -263,45 +298,42 @@
 	aim_sendconnack(sess, conn);
 	aim_request_login(sess, conn, username);
 
-	inpa = gdk_input_add(conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+	gc->inpa = gdk_input_add(conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 			oscar_callback, conn);
 
 	u = find_user(username);
-
-	if (!u) {
-		u = g_new0(struct aim_user, 1);
-		g_snprintf(u->username, sizeof(u->username), DEFAULT_INFO);
-		aim_users = g_list_append(aim_users, u);
-	}
-	current_user = u;
-	g_snprintf(current_user->username, sizeof(current_user->username),
-				"%s", username);
-	g_snprintf(current_user->password, sizeof(current_user->password),
-				"%s", password);
+	sprintf(gc->user_info, "%s", u->user_info);
+	gc->options = u->options;
 	save_prefs();
 
 	debug_print(_("Password sent, waiting for response\n"));
 
-	return 0;
+	return gc;
 }
 
-void oscar_close() {
+void oscar_close(struct gaim_connection *gc) {
+	if (gc->protocol != PROTO_OSCAR) return;
 #ifdef USE_APPLET
 	set_user_state(offline);
 #endif
-	set_state(STATE_OFFLINE);
-	if (inpa > 0)
-		gdk_input_remove(inpa);
-	inpa = -1;
-	aim_logoff(gaim_sess);
-	g_free(gaim_sess);
-	gaim_sess = NULL;
+	if (gc->inpa > 0)
+		gdk_input_remove(gc->inpa);
+	gc->inpa = -1;
+	if (gc->cnpa > 0)
+		gdk_input_remove(gc->cnpa);
+	gc->cnpa = -1;
+	if (gc->paspa > 0)
+		gdk_input_remove(gc->paspa);
+	gc->paspa = -1;
+	aim_logoff(gc->oscar_sess);
+	g_free(gc->oscar_sess);
 	debug_print(_("Signed off.\n"));
 }
 
 int gaim_parse_auth_resp(struct aim_session_t *sess,
 			 struct command_rx_struct *command, ...) {
 	struct aim_conn_t *bosconn = NULL;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 	sprintf(debug_buff, "inside auth_resp (Screen name: %s)\n",
 			sess->logininfo.screen_name);
 	debug_print(debug_buff);
@@ -333,11 +365,9 @@
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif
-		set_state(STATE_OFFLINE);
-		gdk_input_remove(inpa);
+		gdk_input_remove(gc->inpa);
 		hide_login_progress(_("Authentication Failed"));
-		signoff();
-		auth_failed();
+		signoff(gc);
 		return 0;
 	}
 
@@ -350,7 +380,7 @@
 	}
 	sprintf(debug_buff, "Closing auth connection...\n");
 	debug_print(debug_buff);
-	gdk_input_remove(inpa);
+	gdk_input_remove(gc->inpa);
 	aim_conn_kill(sess, &command->conn);
 
 	bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, sess->logininfo.BOSIP);
@@ -358,17 +388,15 @@
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif
-		set_state(STATE_OFFLINE);
+		destroy_gaim_conn(gc);
 		hide_login_progress(_("Internal Error"));
-		auth_failed();
 		return -1;
 	} else if (bosconn->status != 0) {
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif
-		set_state(STATE_OFFLINE);
+		destroy_gaim_conn(gc);
 		hide_login_progress(_("Could Not Connect"));
-		auth_failed();
 		return -1;
 	}
 
@@ -395,8 +423,8 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
 
 	aim_auth_sendcookie(sess, bosconn, sess->logininfo.cookie);
-	gaim_conn = bosconn;
-	inpa = gdk_input_add(bosconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+	gc->oscar_conn = bosconn;
+	gc->inpa = gdk_input_add(bosconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 			oscar_callback, bosconn);
 	set_login_progress(4, _("Connection established, cookie sent"));
 	return 1;
@@ -407,13 +435,13 @@
 	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);
 
 	va_start(ap, command);
 	key = va_arg(ap, char *);
 	va_end(ap);
 
-	aim_send_login(sess, command->conn,
-			current_user->username, current_user->password, &info, key);
+	aim_send_login(sess, command->conn, gc->username, gc->password, &info, key);
 	return 1;
 }
 
@@ -462,6 +490,7 @@
 	int serviceid;
 	char *ip;
 	unsigned char *cookie;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 
 	va_start(ap, command);
 	serviceid = va_arg(ap, int);
@@ -476,7 +505,7 @@
 		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR)
 			debug_print("unable to reconnect with authorizer\n");
 		else {
-			paspa = gdk_input_add(tstconn->fd,
+			gc->paspa = gdk_input_add(tstconn->fd,
 					GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 					oscar_callback, tstconn);
 			aim_auth_sendcookie(sess, tstconn, cookie);
@@ -492,7 +521,7 @@
 		}
 		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
 		aim_auth_sendcookie(sess, tstconn, cookie);
-		cnpa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+		gc->cnpa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 					oscar_callback, tstconn);
 		}
 		debug_print("chatnav: connected\n");
@@ -518,7 +547,7 @@
 				GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 				oscar_callback, tstconn);
 
-		oscar_chats = g_list_append(oscar_chats, ccon);
+		gc->oscar_chats = g_slist_append(gc->oscar_chats, ccon);
 		
 		aim_chat_attachname(tstconn, roomname);
 		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
@@ -589,18 +618,20 @@
 {
 	struct aim_conn_t *newconn;
 	struct aim_directim_priv *priv;
+	struct gaim_connection *gc;
 	int watcher;
 
 	priv = (struct aim_directim_priv *)gtk_object_get_user_data(GTK_OBJECT(m));
+	gc = (struct gaim_connection *)gtk_object_get_user_data(GTK_OBJECT(w));
 	gtk_widget_destroy(m);
 
-	if (!(newconn = aim_directim_connect(gaim_sess, gaim_conn, priv))) {
+	if (!(newconn = aim_directim_connect(gc->oscar_sess, gc->oscar_conn, priv))) {
 		debug_print("imimage: could not connect\n");
 		return;
 	}
 
-	aim_conn_addhandler(gaim_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_directim_incoming, 0);
-	aim_conn_addhandler(gaim_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_directim_typing, 0);
+	aim_conn_addhandler(gc->oscar_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_directim_incoming, 0);
+	aim_conn_addhandler(gc->oscar_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_directim_typing, 0);
 
 	watcher = gdk_input_add(newconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
 				oscar_callback, newconn);
@@ -608,7 +639,7 @@
 	sprintf(debug_buff, "DirectIM: connected to %s\n", priv->sn);
 	debug_print(debug_buff);
 
-	serv_got_imimage(priv->sn, priv->cookie, priv->ip, newconn, watcher);
+	serv_got_imimage(gc, priv->sn, priv->cookie, priv->ip, newconn, watcher);
 
 	g_free(priv);
 }
@@ -618,7 +649,7 @@
 	gtk_widget_destroy(m);
 }
 
-static void directim_dialog(struct aim_directim_priv *priv)
+static void directim_dialog(struct gaim_connection *gc, struct aim_directim_priv *priv)
 {
 	GtkWidget *window;
 	GtkWidget *vbox;
@@ -651,6 +682,7 @@
 
 	yes = picture_button(window, _("Accept"), ok_xpm);
 	gtk_box_pack_start(GTK_BOX(hbox), yes, FALSE, FALSE, 5);
+	gtk_object_set_user_data(GTK_OBJECT(yes), gc);
 
 	no = picture_button(window, _("Cancel"), cancel_xpm);
 	gtk_box_pack_end(GTK_BOX(hbox), no, FALSE, FALSE, 5);
@@ -667,6 +699,7 @@
 			   struct command_rx_struct *command, ...) {
 	int channel;
 	va_list ap;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 
 	va_start(ap, command);
 	channel = va_arg(ap, int);
@@ -687,7 +720,7 @@
 		va_end(ap);
 
 		g_snprintf(tmp, BUF_LONG, "%s", msg);
-		serv_got_im(userinfo->sn, tmp, icbmflags & AIM_IMFLAGS_AWAY);
+		serv_got_im(gc, userinfo->sn, tmp, icbmflags & AIM_IMFLAGS_AWAY);
 		g_free(tmp);
 	} else if (channel == 2) {
 		struct aim_userinfo_s *userinfo;
@@ -730,7 +763,7 @@
 			strcpy(priv2->cookie, priv->cookie);
 			strcpy(priv2->sn, priv->sn);
 			strcpy(priv2->ip, priv->ip);
-			directim_dialog(priv2);
+			directim_dialog(gc, priv2);
 		} else {
 			sprintf(debug_buff, "Unknown rendtype %d\n", rendtype);
 			debug_print(debug_buff);
@@ -815,6 +848,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);
 	va_list ap;
 
 	va_start(ap, command);
@@ -843,8 +877,8 @@
 				  asctime(localtime(&info->onlinesince)),
 				  info->idletime,
 				  infotype == AIM_GETINFO_GENERALINFO ? prof :
-		  		   away_subs(prof, current_user->username));
-	g_show_info_text(away_subs(buf, current_user->username));
+ 				  away_subs(prof, gc->username));
+	g_show_info_text(away_subs(buf, gc->username));
 
 	return 1;
 }
@@ -854,6 +888,7 @@
 	char *msg;
 	u_short id;
 	va_list ap;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 
 	va_start(ap, command);
 	id  = (u_short)va_arg(ap, u_int);
@@ -869,8 +904,8 @@
 		do_error_dialog(_("Your connection may be lost."),
 				_("AOL error"));
 
-	if (keepalv < 0)
-		update_keepalive(general_options & OPT_GEN_KEEPALIVE);
+	if (gc->keepalive < 0)
+		update_keepalive(gc, gc->keepalive);
 
 	return 1;
 }
@@ -879,6 +914,7 @@
 		      struct command_rx_struct *command, ...) {
 	va_list ap;
 	u_short type;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 
 	va_start(ap, command);
 	type = (u_short)va_arg(ap, u_int);
@@ -908,14 +944,14 @@
 				debug_print(debug_buff);
 				i++;
 			}
-			if (create_exchange) {
+			if (gc->create_exchange) {
 				sprintf(debug_buff, "creating room %s\n",
-						create_name);
+						gc->create_name);
 				debug_print(debug_buff);
-				aim_chatnav_createroom(sess, command->conn, create_name, create_exchange);
-				create_exchange = 0;
-				g_free(create_name);
-				create_name = NULL;
+				aim_chatnav_createroom(sess, command->conn, gc->create_name, gc->create_exchange);
+				gc->create_exchange = 0;
+				g_free(gc->create_name);
+				gc->create_name = NULL;
 			}
 			}
 			break;
@@ -942,11 +978,11 @@
 			if (flags & 0x4) {
 				sprintf(debug_buff, "joining %s on exchange 5\n", name);
 				debug_print(debug_buff);
-				aim_chat_join(gaim_sess, gaim_conn, 5, ck);
+				aim_chat_join(gc->oscar_sess, gc->oscar_conn, 5, ck);
 			} else 
 				sprintf(debug_buff, "joining %s on exchange 4\n", name);{
 				debug_print(debug_buff);
-				aim_chat_join(gaim_sess, gaim_conn, 4, ck);
+				aim_chat_join(gc->oscar_sess, gc->oscar_conn, 4, ck);
 			}
 			}
 			break;
@@ -1100,13 +1136,13 @@
 }
 
 int gaim_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
 	switch (command->conn->type) {
 	case AIM_CONN_TYPE_BOS:
 		aim_bos_ackrateresp(sess, command->conn);
 		aim_bos_reqpersonalinfo(sess, command->conn);
 		aim_bos_reqlocaterights(sess, command->conn);
-		aim_bos_setprofile(sess, command->conn, current_user->user_info,
-					NULL, gaim_caps);
+		aim_bos_setprofile(sess, command->conn, gc->user_info, NULL, gaim_caps);
 		aim_bos_reqbuddyrights(sess, command->conn);
 
 		gtk_widget_hide(mainwindow);
@@ -1126,11 +1162,11 @@
 		refresh_buddy_window();
 #endif
 
-		serv_finish_login();
-		gaim_setup();
+		serv_finish_login(gc);
+		gaim_setup(gc);
 
-		if (bud_list_cache_exists())
-			do_import(NULL, 0);
+		if (bud_list_cache_exists(gc))
+			do_import(NULL, gc);
 
 		debug_print("buddy list loaded\n");
 
@@ -1202,6 +1238,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);
 
 	va_start(ap, command);
 	conn = va_arg(ap, struct aim_conn_t *);
@@ -1212,7 +1249,7 @@
 	sprintf(debug_buff, "Got DirectIM message from %s\n", sn);
 	debug_print(debug_buff);
 
-	serv_got_im(sn, msg, 0);
+	serv_got_im(gc, sn, msg, 0);
 
 	return 1;
 }
@@ -1261,30 +1298,31 @@
 	return 1;
 }
 
-void oscar_do_directim(char *name) {
-	struct aim_conn_t *newconn = aim_directim_initiate(gaim_sess, gaim_conn, NULL, name);
+void oscar_do_directim(struct gaim_connection *gc, char *name) {
+	struct aim_conn_t *newconn = aim_directim_initiate(gc->oscar_sess, gc->oscar_conn, NULL, name);
 	struct conversation *cnv = find_conversation(name); /* this will never be null because it just got set up */
 	cnv->conn = newconn;
 	cnv->watcher = gdk_input_add(newconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, oscar_callback, newconn);
-	aim_conn_addhandler(gaim_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, gaim_directim_initiate, 0);
+	aim_conn_addhandler(gc->oscar_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, gaim_directim_initiate, 0);
 }
 
 void send_keepalive(gpointer d) {
+	struct gaim_connection *gc = (struct gaim_connection *)d;
 	debug_print("sending oscar NOP\n");
-	if (USE_OSCAR) { /* keeping it open for TOC */
-		aim_flap_nop(gaim_sess, gaim_conn);
-	} else {
-		sflap_send("", 0, TYPE_KEEPALIVE);
+	if (gc->protocol == PROTO_OSCAR) { /* keeping it open for TOC */
+		aim_flap_nop(gc->oscar_sess, gc->oscar_conn);
+	} else if (gc->protocol == PROTO_TOC) {
+		sflap_send(gc, "", 0, TYPE_KEEPALIVE);
 	}
 }
 
-void update_keepalive(gboolean on) {
-	if (on && keepalv < 0 && blist) {
+void update_keepalive(struct gaim_connection *gc, gboolean on) {
+	if (on && gc->keepalive < 0 && blist) {
 		debug_print("allowing NOP\n");
-		keepalv = gtk_timeout_add(60000, (GtkFunction)send_keepalive, 0);
-	} else if (!on && keepalv > -1) {
+		gc->keepalive = gtk_timeout_add(60000, (GtkFunction)send_keepalive, gc);
+	} else if (!on && gc->keepalive > -1) {
 		debug_print("removing NOP\n");
-		gtk_timeout_remove(keepalv);
-		keepalv = -1;
+		gtk_timeout_remove(gc->keepalive);
+		gc->keepalive = -1;
 	}
 }
--- a/src/perl.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/perl.c	Tue Oct 10 00:02:02 2000 +0000
@@ -298,15 +298,19 @@
 		XST_mPV(0, VERSION);
 		break;
 	case 1:
+		/* FIXME: no more current_user
 		XST_mPV(0, current_user->username);
+		*/
 		break;
 	case 2:
+		/* FIXME: more per-connection issues
 		if (!blist)
 			XST_mPV(0, "Offline");
 		else if (!USE_OSCAR)
 			XST_mPV(0, "TOC");
 		else
 			XST_mPV(0, "Oscar");
+		*/
 		break;
 	default:
 		XST_mPV(0, "Error2");
@@ -401,12 +405,15 @@
 	command = SvPV(ST(0), junk);
 	if (!command) XSRETURN(0);
 	if        (!strncasecmp(command, "signon", 6)) {
+		/* FIXME
 		if (!blist) {
 			show_login();
 			dologin(0, 0);
 		}
+		*/
 	} else if (!strncasecmp(command, "signoff", 7)) {
-		signoff();
+		/* FIXME: we need to figure out how this works for multiple connections
+		 * signoff(); */
 	} else if (!strncasecmp(command, "away", 4)) {
 		char *message = SvPV(ST(1), junk);
 		struct away_message a;
@@ -415,7 +422,9 @@
 	} else if (!strncasecmp(command, "back", 4)) {
 		do_im_back();
 	} else if (!strncasecmp(command, "idle", 4)) {
+		/* FIXME
 		serv_set_idle(atoi(SvPV(ST(1), junk)));
+		*/
 	} else if (!strncasecmp(command, "warn", 4)) {
 		char *name = SvPV(ST(1), junk);
 		serv_warn(name, 0);
--- a/src/prefs.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/prefs.c	Tue Oct 10 00:02:02 2000 +0000
@@ -50,7 +50,7 @@
 
 static GtkWidget *gaim_button(const char *, int *, int, GtkWidget *);
 static void prefs_build_general(GtkWidget *);
-static void prefs_build_connect(GtkWidget *);
+static void prefs_build_proxy(GtkWidget *);
 #ifdef USE_APPLET
 static void prefs_build_applet(GtkWidget *);
 #endif
@@ -101,9 +101,11 @@
 	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5);
 	gtk_widget_show(label);
 
+	/*
 	prefrem = gaim_button(_("Remember password"), &general_options, OPT_GEN_REMEMBER_PASS, box);
 	gtk_signal_connect(GTK_OBJECT(prefrem), "destroy", GTK_SIGNAL_FUNC(remdes), 0);
-	gaim_button(_("Auto-login"), &general_options, OPT_GEN_AUTO_LOGIN, box);
+	*/
+	/* gaim_button(_("Auto-login"), &general_options, OPT_GEN_AUTO_LOGIN, box); */
 
 	sep = gtk_hseparator_new();
 	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 5);
@@ -189,42 +191,7 @@
 	proxy_port_entry = NULL;
 }
 
-static void connect_page()
-{
-	GtkWidget *parent;
-	GtkWidget *box;
-	GtkWidget *label;
-	GtkWidget *sep;
-
-	parent = prefdialog->parent;
-	gtk_widget_destroy(prefdialog);
-
-	prefdialog = gtk_frame_new(_("Connection Options"));
-	gtk_container_add(GTK_CONTAINER(parent), prefdialog);
-	gtk_signal_connect(GTK_OBJECT(prefdialog), "destroy", GTK_SIGNAL_FUNC(connect_destroy), 0);
-
-	box = gtk_vbox_new(FALSE, 5);
-	gtk_container_add(GTK_CONTAINER(prefdialog), box);
-	gtk_widget_show(box);
-
-	label = gtk_label_new(_("AOL has two protocols for connecting to AIM. One of them is Oscar and the other is TOC.\n\nTOC is a published protocol; AOL allows people to use the TOC protocol in their clients to connect. It is a simplified version of Oscar; it is capable of most tasks, but cannot perform all of the functions of Oscar. Because TOC is published, using TOC in gaim tends to be more stable and reliable.\n\nOscar is a proprietary protocol. AOL has not published any information about it. Gaim is able to use Oscar thanks to libfaim, which reverse-engineered the Oscar protocol and is able to emulate it. While libfaim has not decoded or implemented all of the functions of Oscar, it is still able to perform most functions TOC provides as well as several others. However, using Oscar in gaim tends to be less reliable, though more usable.\n\nChanging this option takes effect at signon time."));
-	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
-	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5);
-	gtk_widget_show(label);
-
-	gaim_button(_("Use Oscar Protocol"), &general_options, OPT_GEN_USE_OSCAR, box);
-
-	sep = gtk_hseparator_new();
-	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 5);
-	gtk_widget_show(sep);
-
-	gaim_button(_("Send Keep-Alive Packet (6 bytes/minute)"), &general_options, OPT_GEN_KEEPALIVE, box);
-
-	gtk_widget_show(prefdialog);
-}
-
-static void toc_page()
+static void proxy_page()
 {
 	GtkWidget *parent;
 	GtkWidget *box;
@@ -236,7 +203,7 @@
 	parent = prefdialog->parent;
 	gtk_widget_destroy(prefdialog);
 
-	prefdialog = gtk_frame_new(_("TOC Options"));
+	prefdialog = gtk_frame_new(_("Proxy Options"));
 	gtk_container_add(GTK_CONTAINER(parent), prefdialog);
 	gtk_signal_connect(GTK_OBJECT(prefdialog), "destroy", GTK_SIGNAL_FUNC(connect_destroy), 0);
 
@@ -339,33 +306,6 @@
 	gtk_widget_show(prefdialog);
 }
 
-static void oscar_page()
-{
-	GtkWidget *parent;
-	GtkWidget *box;
-	GtkWidget *label;
-
-	parent = prefdialog->parent;
-	gtk_widget_destroy(prefdialog);
-
-	prefdialog = gtk_frame_new(_("Oscar Options"));
-	gtk_container_add(GTK_CONTAINER(parent), prefdialog);
-
-	box = gtk_vbox_new(FALSE, 5);
-	gtk_container_add(GTK_CONTAINER(prefdialog), box);
-	gtk_widget_show(box);
-
-	label = gtk_label_new(_("All options take effect immediately unless otherwise noted."));
-	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5);
-	gtk_widget_show(label);
-
-	label = gtk_label_new(_("No options currently (Isn't that sad)"));
-	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5);
-	gtk_widget_show(label);
-
-	gtk_widget_show(prefdialog);
-}
-
 #ifdef USE_APPLET
 static void applet_page()
 {
@@ -1226,12 +1166,6 @@
 GtkWidget *pref_fg_picture = NULL;
 GtkWidget *pref_bg_picture = NULL;
 
-static fgbgdes(GtkWidget *w, gpointer d)
-{
-	pref_fg_picture = NULL;
-	pref_bg_picture = NULL;
-}
-
 void update_color(GtkWidget *w, GtkWidget *pic)
 {
 	GdkColor c;
@@ -1791,7 +1725,7 @@
 	gtk_widget_show(prefdialog);
 
 	prefs_build_general(preftree);
-	prefs_build_connect(preftree);
+	prefs_build_proxy(preftree);
 #ifdef USE_APPLET
 	prefs_build_applet(preftree);
 #endif
@@ -1917,9 +1851,7 @@
        	if ((int)option == OPT_GEN_LOG_ALL)
        		update_log_convs();
 
-	if ((int)option == OPT_GEN_KEEPALIVE)
-		update_keepalive(general_options & OPT_GEN_KEEPALIVE);
-
+	/*
 	if (prefrem)
 		gtk_signal_handler_block_by_data(GTK_OBJECT(prefrem), (int *)OPT_GEN_REMEMBER_PASS);
 	if (remember)
@@ -1934,6 +1866,7 @@
 		gtk_signal_handler_unblock_by_data(GTK_OBJECT(prefrem), (int *)OPT_GEN_REMEMBER_PASS);
 	if (remember)
 		gtk_signal_handler_unblock_by_data(GTK_OBJECT(remember), (int *)OPT_GEN_REMEMBER_PASS);
+	*/
 
 	save_prefs();
 }
@@ -2003,25 +1936,15 @@
 	gtk_ctree_select(GTK_CTREE(preftree), parent);
 }
 
-void prefs_build_connect(GtkWidget *preftree)
+void prefs_build_proxy(GtkWidget *preftree)
 {
-	GtkCTreeNode *parent, *node;
+	GtkCTreeNode *parent;
 	char *text[1];
 
-	text[0] = _("Connection");
+	text[0] = _("Proxy");
 	parent = gtk_ctree_insert_node(GTK_CTREE(preftree), NULL, NULL,
 					text, 5, NULL, NULL, NULL, NULL, 0, 1);
-	gtk_ctree_node_set_row_data(GTK_CTREE(preftree), parent, connect_page);
-
-	text[0] = _("TOC Options");
-	node = gtk_ctree_insert_node(GTK_CTREE(preftree), parent, NULL,
-					text, 5, NULL, NULL, NULL, NULL, 0, 1);
-	gtk_ctree_node_set_row_data(GTK_CTREE(preftree), node, toc_page);
-
-	text[0] = _("Oscar Options");
-	node = gtk_ctree_insert_node(GTK_CTREE(preftree), parent, NULL,
-					text, 5, NULL, NULL, NULL, NULL, 0, 1);
-	gtk_ctree_node_set_row_data(GTK_CTREE(preftree), node, oscar_page);
+	gtk_ctree_node_set_row_data(GTK_CTREE(preftree), parent, proxy_page);
 }
 
 #ifdef USE_APPLET
--- a/src/server.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/server.c	Tue Oct 10 00:02:02 2000 +0000
@@ -32,51 +32,57 @@
 #include <gtk/gtk.h>
 #include <aim.h>
 extern int gaim_caps;
+#include "multi.h"
 #include "gaim.h"
 
-static int idle_timer = -1;
-static time_t lastsent = 0;
-static time_t login_time = 0;
-static int is_idle = 0;
-
 int correction_time = 0;
 
-int serv_login(char *username, char *password)
+struct gaim_connection *serv_login(char *username, char *password)
 {
-	if (!(general_options & OPT_GEN_USE_OSCAR)) {
-		USE_OSCAR = 0;
+	struct aim_user *u = find_user(username);
+
+	if (u->protocol == PROTO_TOC) {
 	        return toc_login(username, password);
-	} else {
-		USE_OSCAR = 1;
+	} else if (u->protocol == PROTO_OSCAR) {
 		debug_print("Logging in using Oscar. Expect problems.\n");
 		return oscar_login(username, password);
+	} else {
+		/* PRPL */
+		return NULL;
 	}
 }
 
-void serv_close()
+void serv_close(struct gaim_connection *gc)
 {
-	if (!USE_OSCAR)
-		toc_close();
-	else
-		oscar_close();
+	if (gc->protocol == PROTO_TOC)
+		toc_close(gc);
+	else if (gc->protocol == PROTO_OSCAR)
+		oscar_close(gc);
+	else /* PRPL */ ;
 
-        gtk_timeout_remove(idle_timer);
-        idle_timer = -1;
+	account_offline(gc);
+	destroy_gaim_conn(gc);
+
+	if (connections) return;
+
+	if (gc->idle_timer > 0)
+		gtk_timeout_remove(gc->idle_timer);
+        gc->idle_timer = -1;
 }
 
 
-void serv_touch_idle()
+void serv_touch_idle(struct gaim_connection *gc)
 {
 	/* Are we idle?  If so, not anymore */
-	if (is_idle > 0) {
-		is_idle = 0;
-                serv_set_idle(0);
+	if (gc->is_idle > 0) {
+		gc->is_idle = 0;
+                serv_set_idle(gc, 0);
         }
-        time(&lastsent);
+        time(&gc->lastsent);
 }
 
 
-static gint check_idle()
+static gint check_idle(struct gaim_connection *gc)
 {
 	time_t t;
 
@@ -91,12 +97,12 @@
                 return TRUE;
 
 	
-	if (is_idle)
+	if (gc->is_idle)
 		return TRUE;
 
-	if ((t - lastsent) > 600) { /* 15 minutes! */
-		serv_set_idle((int)t - lastsent);
-		is_idle = 1;
+	if ((t - gc->lastsent) > 600) { /* 15 minutes! */
+		serv_set_idle(gc, (int)t - gc->lastsent);
+		gc->is_idle = 1;
         }
 
 	return TRUE;
@@ -104,33 +110,26 @@
 }
 
 
-void serv_finish_login()
+void serv_finish_login(struct gaim_connection *gc)
 {
         char *buf;
 
-	if (strlen(current_user->user_info)) {
-		buf = g_malloc(strlen(current_user->user_info) * 4);
-		strcpy(buf, current_user->user_info);
-		escape_text(buf);
-		serv_set_info(buf);
+	if (strlen(gc->user_info)) {
+		buf = g_malloc(strlen(gc->user_info) * 4);
+		strcpy(buf, gc->user_info);
+		serv_set_info(gc, buf);
 		g_free(buf);
 	}
 
-        if (idle_timer != -1)
-                gtk_timeout_remove(idle_timer);
+        if (gc->idle_timer > 0)
+                gtk_timeout_remove(gc->idle_timer);
         
-        idle_timer = gtk_timeout_add(20000, (GtkFunction)check_idle, NULL);
-        serv_touch_idle();
-
-        time(&login_time);
+        gc->idle_timer = gtk_timeout_add(20000, (GtkFunction)check_idle, gc);
+        serv_touch_idle(gc);
 
-        serv_add_buddy(current_user->username);
-        
-	if (!(general_options & OPT_GEN_REGISTERED))
-	{
-		show_register_dialog();
-		save_prefs();
-	}
+        time(&gc->login_time);
+
+        serv_add_buddy(gc->username);
 }
 
 
@@ -139,74 +138,85 @@
 {
 	struct conversation *cnv = find_conversation(name);
 	if (cnv && cnv->is_direct) {
-		if (!USE_OSCAR) {
-			/* Direct IM TOC FIXME */
-		} else {
+		if (cnv->gc->protocol == PROTO_OSCAR) {
 			sprintf(debug_buff, "Sending DirectIM to %s\n", name);
 			debug_print(debug_buff);
-			aim_send_im_direct(gaim_sess, cnv->conn, message);
+			aim_send_im_direct(cnv->gc->oscar_sess, cnv->conn, message);
+		} else {
+			/* Direct IM TOC FIXME */
 		}
 	} else {
-		if (!USE_OSCAR) {
+		if (cnv->gc->protocol == PROTO_TOC) {
 			char buf[MSG_LEN - 7];
 
+			escape_text(message);
 		        g_snprintf(buf, MSG_LEN - 8, "toc_send_im %s \"%s\"%s", normalize(name),
 		                   message, ((away) ? " auto" : ""));
-			sflap_send(buf, strlen(buf), TYPE_DATA);
-		} else {
+			sflap_send(cnv->gc, buf, -1, TYPE_DATA);
+		} else if (cnv->gc->protocol == PROTO_OSCAR) {
 			if (away)
-				aim_send_im(gaim_sess, gaim_conn, name, AIM_IMFLAGS_AWAY, message);
+				aim_send_im(cnv->gc->oscar_sess, cnv->gc->oscar_conn,
+						name, AIM_IMFLAGS_AWAY, message);
 			else
-				aim_send_im(gaim_sess, gaim_conn, name, AIM_IMFLAGS_ACK, message);
+				aim_send_im(cnv->gc->oscar_sess, cnv->gc->oscar_conn,
+						name, AIM_IMFLAGS_ACK, message);
 		}
 	}
         if (!away)
-                serv_touch_idle();
+                serv_touch_idle(cnv->gc);
 }
 
 void serv_get_info(char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
         	char buf[MSG_LEN];
 	        g_snprintf(buf, MSG_LEN, "toc_get_info %s", normalize(name));
-	        sflap_send(buf, -1, TYPE_DATA);
-	} else {
-		aim_getinfo(gaim_sess, gaim_conn, name, AIM_GETINFO_GENERALINFO);
+	        sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_getinfo(g->oscar_sess, g->oscar_conn, name, AIM_GETINFO_GENERALINFO);
 	}
 }
 
 void serv_get_away_msg(char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		/* HAHA! TOC doesn't have this yet */
-	} else {
-		aim_getinfo(gaim_sess, gaim_conn, name, AIM_GETINFO_AWAYMESSAGE);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_getinfo(g->oscar_sess, g->oscar_conn, name, AIM_GETINFO_AWAYMESSAGE);
 	}
 }
 
 void serv_get_dir(char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[MSG_LEN];
 		g_snprintf(buf, MSG_LEN, "toc_get_dir %s", normalize(name));
-		sflap_send(buf, -1, TYPE_DATA);
+		sflap_send(g, buf, -1, TYPE_DATA);
 	}
 }
 
 void serv_set_dir(char *first, char *middle, char *last, char *maiden,
 		  char *city, char *state, char *country, int web)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf2[BUF_LEN*4], buf[BUF_LEN];
 		g_snprintf(buf2, sizeof(buf2), "%s:%s:%s:%s:%s:%s:%s:%s", first,
 			   middle, last, maiden, city, state, country,
 			   (web == 1) ? "Y" : "");
 		escape_text(buf2);
 		g_snprintf(buf, sizeof(buf), "toc_set_dir %s", buf2);
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		/* FIXME : some of these things are wrong, but i'm lazy */
-		aim_setdirectoryinfo(gaim_sess, gaim_conn, first, middle, last,
+		aim_setdirectoryinfo(g->oscar_sess, g->oscar_conn, first, middle, last,
 				maiden, NULL, NULL, city, state, NULL, 0, web);
 	}
 }
@@ -214,75 +224,86 @@
 void serv_dir_search(char *first, char *middle, char *last, char *maiden,
 		     char *city, char *state, char *country, char *email)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[BUF_LONG];
 		g_snprintf(buf, sizeof(buf)/2, "toc_dir_search %s:%s:%s:%s:%s:%s:%s:%s", first, middle, last, maiden, city, state, country, email);
 		sprintf(debug_buff,"Searching for: %s,%s,%s,%s,%s,%s,%s\n", first, middle, last, maiden, city, state, country);
 		debug_print(debug_buff);
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		if (strlen(email))
-			aim_usersearch_address(gaim_sess, gaim_conn, email);
+			aim_usersearch_address(g->oscar_sess, g->oscar_conn, email);
 	}
 }
 
 
 void serv_set_away(char *message)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char buf[MSG_LEN];
-	        if (message)
+	        if (message) {
+			escape_text(message);
 	                g_snprintf(buf, MSG_LEN, "toc_set_away \"%s\"", message);
-	        else
+		} else
 	                g_snprintf(buf, MSG_LEN, "toc_set_away \"\"");
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
-		aim_bos_setprofile(gaim_sess, gaim_conn, current_user->user_info,
-					message, gaim_caps);
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_bos_setprofile(g->oscar_sess, g->oscar_conn, g->user_info, message, gaim_caps);
 	}
 }
 
-void serv_set_info(char *info)
+void serv_set_info(struct gaim_connection *g, char *info)
 {
-	if (!USE_OSCAR) {
+	if (g->protocol == PROTO_TOC) {
 		char buf[MSG_LEN];
+		escape_text(info);
 		g_snprintf(buf, sizeof(buf), "toc_set_info \"%s\n\"", info);
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		if (awaymessage)
-			aim_bos_setprofile(gaim_sess, gaim_conn, info,
+			aim_bos_setprofile(g->oscar_sess, g->oscar_conn, info,
 						awaymessage->message, gaim_caps);
 		else
-			aim_bos_setprofile(gaim_sess, gaim_conn, info,
+			aim_bos_setprofile(g->oscar_sess, g->oscar_conn, info,
 						NULL, gaim_caps);
 	}
 }
 
 void serv_change_passwd(char *orig, char *new) {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char *buf = g_malloc(BUF_LONG); 
 		g_snprintf(buf, BUF_LONG, "toc_change_passwd %s %s", orig, new);
-		sflap_send(buf, strlen(buf), TYPE_DATA);
+		sflap_send(g, buf, strlen(buf), TYPE_DATA);
 		g_free(buf);
-	} else {
+	} else if (g->protocol == PROTO_OSCAR) {
 		/* Oscar change_passwd FIXME */
 	}
 }
 
 void serv_add_buddy(char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[1024];
 		g_snprintf(buf, sizeof(buf), "toc_add_buddy %s", normalize(name));
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
-		aim_add_buddy(gaim_sess, gaim_conn, name);
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_add_buddy(g->oscar_sess, g->oscar_conn, name);
 	}
 }
 
 void serv_add_buddies(GList *buddies)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[MSG_LEN];
 	        int n, num = 0;
 
@@ -290,7 +311,7 @@
 	        while(buddies) {
 			/* i don't know why we choose 8, it just seems good */
 	                if (strlen(normalize(buddies->data)) > MSG_LEN - n - 8) {
-	                        sflap_send(buf, -1, TYPE_DATA);
+	                        sflap_send(g, buf, -1, TYPE_DATA);
 	                        n = g_snprintf(buf, sizeof(buf), "toc_add_buddy");
 	                        num = 0;
 	                }
@@ -298,32 +319,34 @@
 	                n += g_snprintf(buf + n, sizeof(buf) - n, " %s", normalize(buddies->data));
 	                buddies = buddies->next;
 	        }
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		char buf[MSG_LEN];
 		int n = 0;
 		while(buddies) {
 			if (n > MSG_LEN - 18) {
-				aim_bos_setbuddylist(gaim_sess, gaim_conn, buf);
+				aim_bos_setbuddylist(g->oscar_sess, g->oscar_conn, buf);
 				n = 0;
 			}
 			n += g_snprintf(buf + n, sizeof(buf) - n, "%s&",
 					(char *)buddies->data);
 			buddies = buddies->next;
 		}
-		aim_bos_setbuddylist(gaim_sess, gaim_conn, buf);
+		aim_bos_setbuddylist(g->oscar_sess, g->oscar_conn, buf);
 	}
 }
 
 
 void serv_remove_buddy(char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[1024];
 		g_snprintf(buf, sizeof(buf), "toc_remove_buddy %s", normalize(name));
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
-		aim_remove_buddy(gaim_sess, gaim_conn, name);
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_remove_buddy(g->oscar_sess, g->oscar_conn, name);
 	}
 }
 
@@ -347,23 +370,25 @@
 
 void serv_set_permit_deny()
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char buf[MSG_LEN];
 		int at;
 		GList *list;
 
 		switch (permdeny) {
 		case PERMIT_ALL:
-			sprintf(buf, "toc_add_permit %s", current_user->username);
-			sflap_send(buf, -1, TYPE_DATA);
+			sprintf(buf, "toc_add_permit %s", g->username);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			sprintf(buf, "toc_add_deny");
-			sflap_send(buf, -1, TYPE_DATA);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			break;
 		case PERMIT_NONE:
-			sprintf(buf, "toc_add_deny %s", current_user->username);
-			sflap_send(buf, -1, TYPE_DATA);
+			sprintf(buf, "toc_add_deny %s", g->username);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			sprintf(buf, "toc_add_permit");
-			sflap_send(buf, -1, TYPE_DATA);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			break;
 		case PERMIT_SOME:
 			at = g_snprintf(buf, sizeof(buf), "toc_add_permit");
@@ -373,7 +398,7 @@
 				list = list->next;
 			}
 			buf[at] = 0; /* is this necessary? */
-			sflap_send(buf, -1, TYPE_DATA);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			break;
 		case DENY_SOME:
 			/* you'll still see people as being online, but they won't see you, and you
@@ -385,10 +410,10 @@
 				list = list->next;
 			}
 			buf[at] = 0; /* is this necessary? */
-			sflap_send(buf, -1, TYPE_DATA);
+			sflap_send(g, buf, -1, TYPE_DATA);
 			break;
 		}
-	} else {
+	} else if (g->protocol == PROTO_OSCAR) {
 /*
 		int at;
 		GList *list;
@@ -438,28 +463,30 @@
 	}
 }
 
-void serv_set_idle(int time)
+void serv_set_idle(struct gaim_connection *g, int time)
 {
-	if (!USE_OSCAR) {
+	if (g->protocol == PROTO_TOC) {
 		char buf[256];
 		g_snprintf(buf, sizeof(buf), "toc_set_idle %d", time);
-		sflap_send(buf, -1, TYPE_DATA);
-	} else {
-		aim_bos_setidle(gaim_sess, gaim_conn, time);
+		sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_bos_setidle(g->oscar_sess, g->oscar_conn, time);
 	}
 }
 
 
 void serv_warn(char *name, int anon)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char *send = g_malloc(256);
 		g_snprintf(send, 255, "toc_evil %s %s", name,
 			   ((anon) ? "anon" : "norm"));
-		sflap_send(send, -1, TYPE_DATA);
+		sflap_send(g, send, -1, TYPE_DATA);
 		g_free(send);
-	} else {
-		aim_send_warning(gaim_sess, gaim_conn, name, anon);
+	} else if (g->protocol == PROTO_OSCAR) {
+		aim_send_warning(g->oscar_sess, g->oscar_conn, name, anon);
 	}
 }
 
@@ -470,12 +497,14 @@
 
 void serv_save_config()
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 		char *buf = g_malloc(BUF_LONG);
 		char *buf2 = g_malloc(MSG_LEN);
 		serv_build_config(buf, BUF_LONG / 2, FALSE);
 		g_snprintf(buf2, MSG_LEN, "toc_set_config {%s}", buf);
-	        sflap_send(buf2, -1, TYPE_DATA);
+	        sflap_send(g, buf2, -1, TYPE_DATA);
 		g_free(buf2);
 		g_free(buf);
 	}
@@ -484,12 +513,14 @@
 
 void serv_accept_chat(int i)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char *buf = g_malloc(256);
 	        g_snprintf(buf, 255, "toc_chat_accept %d",  i);
-	        sflap_send(buf, -1, TYPE_DATA);
+	        sflap_send(g, buf, -1, TYPE_DATA);
 	        g_free(buf);
-	} else {
+	} else if (g->protocol == PROTO_OSCAR) {
 	/* this should never get called because libfaim doesn't use the id
 	 * (i'm not even sure Oscar does). go through serv_join_chat instead */
 	}
@@ -497,34 +528,38 @@
 
 void serv_join_chat(int exchange, char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char buf[BUF_LONG];
 	        g_snprintf(buf, sizeof(buf)/2, "toc_chat_join %d \"%s\"", exchange, name);
-	        sflap_send(buf, -1, TYPE_DATA);
-	} else {
+	        sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		struct aim_conn_t *cur = NULL;
 		sprintf(debug_buff, "Attempting to join chat room %s.\n", name);
 		debug_print(debug_buff);
-		if ((cur = aim_getconn_type(gaim_sess, AIM_CONN_TYPE_CHATNAV))) {
+		if ((cur = aim_getconn_type(g->oscar_sess, AIM_CONN_TYPE_CHATNAV))) {
 			debug_print("chatnav exists, creating room\n");
-			aim_chatnav_createroom(gaim_sess, cur, name, exchange);
+			aim_chatnav_createroom(g->oscar_sess, cur, name, exchange);
 		} else {
 			/* this gets tricky */
 			debug_print("chatnav does not exist, opening chatnav\n");
-			create_exchange = exchange;
-			create_name = g_strdup(name);
-			aim_bos_reqservice(gaim_sess, gaim_conn, AIM_CONN_TYPE_CHATNAV);
+			g->create_exchange = exchange;
+			g->create_name = g_strdup(name);
+			aim_bos_reqservice(g->oscar_sess, g->oscar_conn, AIM_CONN_TYPE_CHATNAV);
 		}
 	}
 }
 
 void serv_chat_invite(int id, char *message, char *name)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char buf[BUF_LONG];
 	        g_snprintf(buf, sizeof(buf)/2, "toc_chat_invite %d \"%s\" %s", id, message, normalize(name));
-	        sflap_send(buf, -1, TYPE_DATA);
-	} else {
+	        sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		GList *bcs = buddy_chats;
 		struct conversation *b = NULL;
 
@@ -539,18 +574,20 @@
 		if (!b)
 			return;
 		
-		aim_chat_invite(gaim_sess, gaim_conn, name, message, 0x4, b->name, 0x1);
+		aim_chat_invite(g->oscar_sess, g->oscar_conn, name, message, 0x4, b->name, 0x1);
 	}
 }
 
 void serv_chat_leave(int id)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char *buf = g_malloc(256);
 	        g_snprintf(buf, 255, "toc_chat_leave %d",  id);
-	        sflap_send(buf, -1, TYPE_DATA);
+	        sflap_send(g, buf, -1, TYPE_DATA);
 	        g_free(buf);
-	} else {
+	} else if (g->protocol == PROTO_OSCAR) {
 		GList *bcs = buddy_chats;
 		struct conversation *b = NULL;
 		struct chat_connection *c = NULL;
@@ -572,12 +609,12 @@
 					b->name, count);
 		debug_print(debug_buff);
 
-		c = find_oscar_chat(b->name);
+		c = find_oscar_chat(g, b->name);
 		if (c != NULL) {
-			oscar_chats = g_list_remove(oscar_chats, c);
+			g->oscar_chats = g_slist_remove(g->oscar_chats, c);
 			gdk_input_remove(c->inpa);
-			if (gaim_sess)
-				aim_conn_kill(gaim_sess, &c->conn);
+			if (g && g->oscar_sess)
+				aim_conn_kill(g->oscar_sess, &c->conn);
 			g_free(c->name);
 			g_free(c);
 		}
@@ -588,11 +625,13 @@
 
 void serv_chat_whisper(int id, char *who, char *message)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char buf2[MSG_LEN];
 	        g_snprintf(buf2, sizeof(buf2), "toc_chat_whisper %d %s \"%s\"", id, who, message);
-	        sflap_send(buf2, -1, TYPE_DATA);
-	} else {
+	        sflap_send(g, buf2, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		do_error_dialog("Sorry, Oscar doesn't whisper. Send an IM. (The last message was not received.)",
 				"Gaim - Chat");
 	}
@@ -600,12 +639,14 @@
 
 void serv_chat_send(int id, char *message)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        char buf[MSG_LEN];
 		escape_text(message);
 	        g_snprintf(buf, sizeof(buf), "toc_chat_send %d \"%s\"",id, message);
-	        sflap_send(buf, -1, TYPE_DATA);
-	} else {
+	        sflap_send(g, buf, -1, TYPE_DATA);
+	} else if (g->protocol == PROTO_OSCAR) {
 		struct aim_conn_t *cn;
 		GList *bcs = buddy_chats;
 		struct conversation *b = NULL;
@@ -620,15 +661,15 @@
 		if (!b)
 			return;
 
-		cn = aim_chat_getconn(gaim_sess, b->name);
-		aim_chat_send_im(gaim_sess, cn, message);
+		cn = aim_chat_getconn(g->oscar_sess, b->name);
+		aim_chat_send_im(g->oscar_sess, cn, message);
 	}
-	serv_touch_idle();
+	serv_touch_idle(g);
 }
 
 
 
-void serv_got_im(char *name, char *message, int away)
+void serv_got_im(struct gaim_connection *gc, char *name, char *message, int away)
 {
 	struct conversation *cnv;
 	int is_idle = -1;
@@ -690,11 +731,15 @@
 	}
 
 
+	cnv->gc = gc;
+	gtk_option_menu_set_history(GTK_OPTION_MENU(cnv->menu), g_slist_index(connections, gc));
 
 
 	if (awaymessage != NULL) {
 		time_t t;
 		char *tmpmsg;
+		struct buddy *b = find_buddy(name);
+		char *alias = b ? b->show : name;
 
 		time(&t);
 
@@ -712,7 +757,7 @@
 		
 		escape_text(tmpmsg);
 		escape_message(tmpmsg);
-		serv_send_im(name, away_subs(tmpmsg, name), 1);
+		serv_send_im(name, away_subs(tmpmsg, alias), 1);
 		g_free(tmpmsg);
 		tmpmsg = stylize(awaymessage->message, MSG_LEN);
 
@@ -720,7 +765,7 @@
 			is_idle = 1;
 		
 		if (cnv != NULL)
-			write_to_conv(cnv, away_subs(tmpmsg, name), WFLAG_SEND | WFLAG_AUTO, NULL);
+			write_to_conv(cnv, away_subs(tmpmsg, alias), WFLAG_SEND | WFLAG_AUTO, NULL);
 		g_free(tmpmsg);
 	}
 }
@@ -729,22 +774,16 @@
 
 void serv_got_update(char *name, int loggedin, int evil, time_t signon, time_t idle, int type, u_short caps)
 {
-        struct buddy *b;
-        char *nname;
+        struct buddy *b = find_buddy(name);
+	struct gaim_connection *gc = find_gaim_conn_by_name(name);
                      
-        b = find_buddy(name);
-
-        nname = g_strdup(normalize(name));
-        if (!strcasecmp(nname, normalize(current_user->username))) {
-                correction_time = (int)(signon - login_time);
+        if (gc) {
+                correction_time = (int)(signon - gc->login_time);
                 update_all_buddies();
                 if (!b) {
-			g_free(nname);
                         return;
 		}
         }
-
-	g_free(nname);
         
         if (!b) {
                 sprintf(debug_buff,"Error, no such person\n");
@@ -860,11 +899,13 @@
 
 static void chat_invite_callback(GtkWidget *w, GtkWidget *w2)
 {
-	if (!USE_OSCAR) {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
+	if (g->protocol == PROTO_TOC) {
 	        int i = (int)gtk_object_get_user_data(GTK_OBJECT(w2));
 	        serv_accept_chat(i);
 		gtk_widget_destroy(w2);
-	} else {
+	} else if (g->protocol == PROTO_OSCAR) {
 		char *i = (char *)gtk_object_get_user_data(GTK_OBJECT(w2));
 		int id = (int)gtk_object_get_user_data(GTK_OBJECT(w));
 		serv_join_chat(id, i);
@@ -883,6 +924,8 @@
         GtkWidget *nobtn;
 
         char buf2[BUF_LONG];
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
 
 
 	plugin_event(event_chat_invited, who, name, message);
@@ -917,11 +960,13 @@
 
         /*		gtk_widget_set_usize(d, 200, 110); */
 
-	if (!USE_OSCAR)
+	if (g->protocol == PROTO_TOC)
 	        gtk_object_set_user_data(GTK_OBJECT(d), (void *)id);
-	else {
+	else if (g->protocol == PROTO_OSCAR) {
 		gtk_object_set_user_data(GTK_OBJECT(d), (void *)g_strdup(name));
 		gtk_object_set_user_data(GTK_OBJECT(yesbtn), (void *)id);
+	} else {
+		/* PRPL */
 	}
 
 
@@ -1026,36 +1071,41 @@
 void serv_rvous_accept(char *name, char *cookie, char *uid)
 {
 	/* Oscar doesn't matter here because this won't ever be called for it */
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
 	char buf[MSG_LEN];
 	g_snprintf(buf, MSG_LEN, "toc_rvous_accept %s %s %s", normalize(name),
 			cookie, uid);
-	sflap_send(buf, strlen(buf), TYPE_DATA);
+	sflap_send(g, buf, -1, TYPE_DATA);
 }
 
 void serv_rvous_cancel(char *name, char *cookie, char *uid)
 {
+	/* FIXME */
+	struct gaim_connection *g = connections->data;
 	char buf[MSG_LEN];
 	g_snprintf(buf, MSG_LEN, "toc_rvous_cancel %s %s %s", normalize(name),
 			cookie, uid);
-	sflap_send(buf, strlen(buf), TYPE_DATA);
+	sflap_send(g, buf, -1, TYPE_DATA);
 }
 
 void serv_do_imimage(GtkWidget *w, char *name) {
 	struct conversation *cnv = find_conversation(name);
 	if (!cnv) cnv = new_conversation(name);
 
-	if (!USE_OSCAR) {
+	if (cnv->gc->protocol == PROTO_TOC) {
 		/* Direct IM TOC FIXME */
-	} else {
-		oscar_do_directim(name);
+	} else if (cnv->gc->protocol == PROTO_OSCAR) {
+		oscar_do_directim(cnv->gc, name);
 	}
 }
 
-void serv_got_imimage(char *name, char *cookie, char *ip, struct aim_conn_t *conn, int watcher)
+void serv_got_imimage(struct gaim_connection *gc, char *name, char *cookie, char *ip,
+			struct aim_conn_t *conn, int watcher)
 {
-	if (!USE_OSCAR) {
+	if (gc->protocol == PROTO_TOC) {
 		/* Direct IM TOC FIXME */
-	} else {
+	} else if (gc->protocol == PROTO_OSCAR) {
 		struct conversation *cnv = find_conversation(name);
 		if (!cnv) cnv = new_conversation(name);
 		make_direct(cnv, TRUE, conn, watcher);
--- a/src/toc.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/toc.c	Tue Oct 10 00:02:02 2000 +0000
@@ -35,30 +35,31 @@
 #include <stdio.h>
 #include <time.h>
 #include <sys/socket.h>
+#include "multi.h"
 #include "gaim.h"
 #include "gnome_applet_mgr.h"
 
+#define REVISION "gaim:$Revision: 970 $"
 
 
-/* descriptor for talking to TOC */
-static int toc_fd;
-static int seqno;
 static unsigned int peer_ver=0;
-static int state;
-static int inpa=-1;
 #ifdef _WIN32
 static int win32_r;
 #endif
 
-int toc_signon(char *username, char *password);
+static int toc_signon(struct gaim_connection *);
 
 
 
-int toc_login(char *username, char *password)
+/* ok. this function used to take username/password, and return 0 on success.
+ * now, it takes username/password, and returns NULL on error or a new gaim_connection
+ * on success. FIXME: should this modify the UI? or just sign in? */
+struct gaim_connection *toc_login(char *username, char *password)
 {
 	char *config;
         struct in_addr *sin;
         struct aim_user *u;
+	struct gaim_connection *gc;
 	char buf[80];
 	char buf2[2048];
 
@@ -71,10 +72,9 @@
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
 		g_snprintf(buf, sizeof(buf), "Unable to lookup %s", aim_host);
 		hide_login_progress(buf);
-		return -1;
+		return NULL;
 	}
 	
 	g_snprintf(toc_addy, sizeof(toc_addy), "%s", inet_ntoa(*sin));
@@ -83,18 +83,19 @@
 	set_login_progress(2, buf);
 
 
+	gc = new_gaim_conn(PROTO_TOC, username, password);
 	
-	toc_fd = connect_address(sin->s_addr, aim_port);
+	gc->toc_fd = connect_address(sin->s_addr, aim_port);
 
-        if (toc_fd < 0) {
+        if (gc->toc_fd < 0) {
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
 		g_snprintf(buf, sizeof(buf), "Connect to %s failed",
 			 inet_ntoa(*sin));
 		hide_login_progress(buf);
-		return -1;
+		destroy_gaim_conn(gc);
+		return NULL;
         }
 
         g_free(sin);
@@ -103,45 +104,35 @@
 	
 	set_login_progress(3, buf);
 	
-	if (toc_signon(username, password) < 0) {
+	if (toc_signon(gc) < 0) {
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
 		hide_login_progress("Disconnected.");
-		return -1;
+		destroy_gaim_conn(gc);
+		return NULL;
 	}
 
 	g_snprintf(buf, sizeof(buf), "Waiting for reply...");
 	set_login_progress(4, buf);
-	if (toc_wait_signon() < 0) {
+	if (toc_wait_signon(gc) < 0) {
 #ifdef USE_APPLET
 		set_user_state(offline);
 #endif /* USE_APPLET */
-                set_state(STATE_OFFLINE);
 		hide_login_progress("Authentication Failed");
-		return -1;
+		destroy_gaim_conn(gc);
+		return NULL;
 	}
 
         u = find_user(username);
-
-        if (!u) {
-                u = g_new0(struct aim_user, 1);
-                g_snprintf(u->user_info, sizeof(u->user_info), DEFAULT_INFO);
-                aim_users = g_list_append(aim_users, u);
-        }
-
-        current_user = u;
-        
-	g_snprintf(current_user->username, sizeof(current_user->username), "%s", username);
-	g_snprintf(current_user->password, sizeof(current_user->password), "%s", password);
-
+	sprintf(gc->user_info, "%s", u->user_info);
+	gc->options = u->options;
 	save_prefs();
 
 	g_snprintf(buf, sizeof(buf), "Retrieving config...");
 	set_login_progress(5, buf);
-	config = toc_wait_config();
-	state = STATE_ONLINE;
+	config = toc_wait_config(gc);
+	gc->state = STATE_ONLINE;
 
         gtk_widget_hide(mainwindow);
 	show_buddy_list();
@@ -160,39 +151,38 @@
 	refresh_buddy_window();
 #endif
 	if (config != NULL)
-		parse_toc_buddy_list(config, 0);
+		parse_toc_buddy_list(gc, config, 0);
 	else
-		do_import(0, 0);
+		do_import(0, gc);
         
 	setup_buddy_chats();
 
 	g_snprintf(buf2, sizeof(buf2), "toc_init_done");
-	sflap_send(buf2, -1, TYPE_DATA);
+	sflap_send(gc, buf2, -1, TYPE_DATA);
 
 	g_snprintf(buf2, sizeof(buf2), "toc_set_caps %s %s %s %s %s",
 		   FILE_SEND_UID, FILE_GET_UID, B_ICON_UID, IMAGE_UID,
 		   VOICE_UID);
-	sflap_send(buf2, -1, TYPE_DATA);
+	sflap_send(gc, buf2, -1, TYPE_DATA);
 
-	if (keepalv < 0)
-		update_keepalive(general_options & OPT_GEN_KEEPALIVE);
+	if (gc->keepalive < 0)
+		update_keepalive(gc, gc->options & OPT_USR_KEEPALV);
 
-        serv_finish_login();
+        serv_finish_login(gc);
+	gaim_setup(gc);
 	return 0;
 }
 
-void toc_close()
+void toc_close(struct gaim_connection *gc)
 {
+	if (gc->protocol != PROTO_TOC) return; /* how did this happen? */
 #ifdef USE_APPLET
 	set_user_state(offline);
 #endif /* USE_APPLET */
-        seqno = 0;
-        state = STATE_OFFLINE;
-        if (inpa > 0)
-		gdk_input_remove(inpa);
-	close(toc_fd);
-	toc_fd=-1;
-	inpa=-1;
+        if (gc->inpa > 0)
+		gdk_input_remove(gc->inpa);
+	gc->inpa = -1;
+	close(gc->toc_fd);
 }
 
 unsigned char *roast_password(char *pass)
@@ -234,7 +224,7 @@
 #endif
 }
 
-int sflap_send(char *buf, int olen, int type)
+int sflap_send(struct gaim_connection *gc, char *buf, int olen, int type)
 {
 	int len;
 	int slen=0;
@@ -261,7 +251,7 @@
 		len = olen;
 	hdr.ast = '*';
 	hdr.type = type;
-	hdr.seqno = htons(seqno++ & 0xffff);
+	hdr.seqno = htons(gc->seqno++ & 0xffff);
         hdr.len = htons(len + (type == TYPE_SIGNON ? 0 : 1));
 
     sprintf(debug_buff,"Escaped message is '%s'\n",buf);
@@ -277,11 +267,11 @@
 	}
 	print_buffer(obuf, slen);
 
-	return write(toc_fd, obuf, slen);
+	return write(gc->toc_fd, obuf, slen);
 }
 
 
-int wait_reply(char *buffer, size_t buflen)
+static int wait_reply(struct gaim_connection *gc, char *buffer, size_t buflen)
 {
         size_t res=-1;
 	int read_rv = -1;
@@ -289,11 +279,12 @@
         char *c;
 
 	if(buflen < sizeof(struct sflap_hdr)) {
-	    do_error_dialog("Buffer too small", "Gaim - Error (internal)");
+	    do_error_dialog(_("Unable to read from server: Buffer too small"),
+			    _("Gaim - Error (internal)"));
 	    return -1;
 	}
 
-        while((read_rv = read(toc_fd, buffer, 1))) {
+        while((read_rv = read(gc->toc_fd, buffer, 1))) {
 		if (read_rv < 0 || read_rv > 1)
 			return -1;
 		if (buffer[0] == '*')
@@ -301,7 +292,7 @@
 
 	}
 
-	read_rv = read(toc_fd, buffer+1, sizeof(struct sflap_hdr) - 1);
+	read_rv = read(gc->toc_fd, buffer+1, sizeof(struct sflap_hdr) - 1);
 
         if (read_rv < 0)
 		return read_rv;
@@ -314,16 +305,21 @@
 
 
 	if(buflen < sizeof(struct sflap_hdr) + ntohs(hdr->len) + 1) {
-	    do_error_dialog("Buffer too small", "Gaim - Error (internal)");
+	    do_error_dialog(_("Unable to read from server: Too much information"),
+			    _("Gaim - Error (internal)"));
 	    return -1;
 	}
 
         while (res < (sizeof(struct sflap_hdr) + ntohs(hdr->len))) {
-		read_rv = read(toc_fd, buffer + res, (ntohs(hdr->len) + sizeof(struct sflap_hdr)) - res);
+		read_rv = read(gc->toc_fd, buffer + res, (ntohs(hdr->len) + sizeof(struct sflap_hdr)) - res);
 		if(read_rv < 0) return read_rv;
 		res += read_rv;
+		/* my feeling is this will kill us. if there's data pending then we'll come right back
+		 * to where we are now. possible workarounds are to remove the input watcher until
+		 * we're done with this part
 		while(gtk_events_pending())
 			gtk_main_iteration();
+		 */
 	}
         
         if (res >= sizeof(struct sflap_hdr)) 
@@ -335,14 +331,14 @@
 	case TYPE_SIGNON:
 		memcpy(&peer_ver, buffer + sizeof(struct sflap_hdr), 4);
 		peer_ver = ntohl(peer_ver);
-		seqno = ntohs(hdr->seqno);
-		state = STATE_SIGNON_REQUEST;
+		gc->seqno = ntohs(hdr->seqno);
+		gc->state = STATE_SIGNON_REQUEST;
 		break;
 	case TYPE_DATA:
 		if (!strncasecmp(buffer + sizeof(struct sflap_hdr), "SIGN_ON:", strlen("SIGN_ON:")))
-			state = STATE_SIGNON_ACK;
+			gc->state = STATE_SIGNON_ACK;
 		else if (!strncasecmp(buffer + sizeof(struct sflap_hdr), "CONFIG:", strlen("CONFIG:"))) {
-			state = STATE_CONFIG;
+			gc->state = STATE_CONFIG;
 		} else if (!strncasecmp(buffer + sizeof(struct sflap_hdr), "ERROR:", strlen("ERROR:"))) {
 			c = strtok(buffer + sizeof(struct sflap_hdr) + strlen("ERROR:"), ":");
 			show_error_dialog(c);
@@ -368,10 +364,11 @@
         char *buf;
 	char *c;
         char *l;
+	struct gaim_connection *gc = (struct gaim_connection *)data;
 
         buf = g_malloc(2 * BUF_LONG);
-        if (wait_reply(buf, 2 * BUF_LONG) < 0) {
-                signoff();
+        if (wait_reply(gc, buf, 2 * BUF_LONG) < 0) {
+                signoff(gc); /* this will free gc for us */
                 hide_login_progress("Connection Closed");
                 g_free(buf);
 		return;
@@ -440,7 +437,7 @@
 	} else if (!strcasecmp(c, "CONFIG")) {
 		/* do we want to load the buddy list again here? */
 		c = strtok(NULL,":");
-		parse_toc_buddy_list(c, 0);
+		parse_toc_buddy_list(gc, c, 0);
 	} else if (!strcasecmp(c, "ERROR")) {
 		/* This should be handled by wait_reply
 		c = strtok(NULL,":");
@@ -448,7 +445,7 @@
 		*/
 	} else if (!strcasecmp(c, "NICK")) {
 		c = strtok(NULL,":");
-		g_snprintf(current_user->username, sizeof(current_user->username), "%s", c);
+		g_snprintf(gc->username, sizeof(gc->username), "%s", c);
 	} else if (!strcasecmp(c, "IM_IN")) {
 		char *away, *message;
                 int a = 0;
@@ -465,7 +462,7 @@
 
 		if (!strncasecmp(away, "T", 1))
 			a = 1;
-                serv_got_im(c, message, a);
+                serv_got_im(gc, c, message, a);
 		
 	} else if (!strcasecmp(c, "GOTO_URL")) {
 		char *name;
@@ -695,7 +692,7 @@
 			name = frombase64(cookie);
 			snprintf(tmp, BUF_LEN, "toc_rvous_cancel %s %s %s",
 					user, name, uuid);
-			sflap_send(tmp, strlen(tmp), TYPE_DATA);
+			sflap_send(gc, tmp, strlen(tmp), TYPE_DATA);
 			free(name);
 			free(tmp);
 		}
@@ -707,59 +704,59 @@
 }
 
 
-int toc_signon(char *username, char *password)
+int toc_signon(struct gaim_connection *gc)
 {
 	char buf[BUF_LONG];
 	int res;
 	struct signon so;
 
-        sprintf(debug_buff,"State = %d\n", state);
+        sprintf(debug_buff,"State = %d\n", gc->state);
 	debug_print(debug_buff);
 
-	if ((res = write(toc_fd, FLAPON, strlen(FLAPON))) < 0)
+	if ((res = write(gc->toc_fd, FLAPON, strlen(FLAPON))) < 0)
 		return res;
 	/* Wait for signon packet */
 
-	state = STATE_FLAPON;
+	gc->state = STATE_FLAPON;
 
-	if ((res = wait_reply(buf, sizeof(buf)) < 0))
+	if ((res = wait_reply(gc, buf, sizeof(buf)) < 0))
 		return res;
 	
-	if (state != STATE_SIGNON_REQUEST) {
-			sprintf(debug_buff, "State should be %d, but is %d instead\n", STATE_SIGNON_REQUEST, state);
+	if (gc->state != STATE_SIGNON_REQUEST) {
+			sprintf(debug_buff, "State should be %d, but is %d instead\n", STATE_SIGNON_REQUEST, gc->state);
 			debug_print(debug_buff);
 			return -1;
 	}
 	
 	/* Compose a response */
 	
-	g_snprintf(so.username, sizeof(so.username), "%s", username);
+	g_snprintf(so.username, sizeof(so.username), "%s", gc->username);
 	so.ver = ntohl(1);
 	so.tag = ntohs(1);
 	so.namelen = htons(strlen(so.username));	
 	
-	sflap_send((char *)&so, ntohs(so.namelen) + 8, TYPE_SIGNON);
+	sflap_send(gc, (char *)&so, ntohs(so.namelen) + 8, TYPE_SIGNON);
 	
 	g_snprintf(buf, sizeof(buf), 
 		"toc_signon %s %d %s %s %s \"%s\"",
-		login_host, login_port, normalize(username), roast_password(password), LANGUAGE, REVISION);
+		login_host, login_port, normalize(gc->username), roast_password(gc->password), LANGUAGE, REVISION);
 
         sprintf(debug_buff,"Send: %s\n", buf);
 		debug_print(debug_buff);
 
-	return sflap_send(buf, -1, TYPE_DATA);
+	return sflap_send(gc, buf, -1, TYPE_DATA);
 }
 
-int toc_wait_signon()
+int toc_wait_signon(struct gaim_connection *gc)
 {
 	/* Wait for the SIGNON to be approved */
 	char buf[BUF_LONG];
 	int res;
-	res = wait_reply(buf, sizeof(buf));
+	res = wait_reply(gc, buf, sizeof(buf));
 	if (res < 0)
 		return res;
-	if (state != STATE_SIGNON_ACK) {
-			sprintf(debug_buff, "State should be %d, but is %d instead\n",STATE_SIGNON_ACK, state);
+	if (gc->state != STATE_SIGNON_ACK) {
+			sprintf(debug_buff, "State should be %d, but is %d instead\n",STATE_SIGNON_ACK, gc->state);
 			debug_print(debug_buff);
 		return -1;
 	}
@@ -792,27 +789,27 @@
 #endif
 
 
-char *toc_wait_config()
+char *toc_wait_config(struct gaim_connection *gc)
 {
 	/* Waits for configuration packet, returning the contents of the packet */
 	static char buf[BUF_LONG];
 	int res;
-	res = wait_reply(buf, sizeof(buf));
+	res = wait_reply(gc, buf, sizeof(buf));
 	if (res < 0)
 		return NULL;
 /* Apparently, the toc_config is optional.  *VERY* Optional
 */
-	if (state != STATE_CONFIG) {
+	if (gc->state != STATE_CONFIG) {
 		res = 0;
 	} else {
 		res = 1;
 	}
 	/* At this point, it's time to setup automatic handling of incoming packets */
-	state = STATE_ONLINE;
+	gc->state = STATE_ONLINE;
 #ifdef _WIN32
 	win32_r = gtk_timeout_add(1000, (GtkFunction)win32_read, NULL);
 #else
-	inpa = gdk_input_add(toc_fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, toc_callback, NULL);
+	gc->inpa = gdk_input_add(gc->toc_fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, toc_callback, gc);
 #endif
 	if (res)
 		return buf;
@@ -859,7 +856,7 @@
 	}
 }
 
-void parse_toc_buddy_list(char *config, int from_do_import)
+void parse_toc_buddy_list(struct gaim_connection *gc, char *config, int from_do_import)
 {
 	char *c;
 	char current[256];
@@ -869,10 +866,9 @@
 
 	bud = NULL;
         
-/* skip "CONFIG:" (if it exists)*/
-
 	if (config != NULL) {
 
+		/* skip "CONFIG:" (if it exists)*/
 		c = strncmp(config + sizeof(struct sflap_hdr),"CONFIG:",strlen("CONFIG:"))?
 			strtok(config, "\n"):
 			strtok(config + sizeof(struct sflap_hdr)+strlen("CONFIG:"), "\n");
@@ -956,8 +952,8 @@
            cache */
 
 	if ( how_many == 0 && !from_do_import ) {
-		do_import( (GtkWidget *) NULL, 0 );
-	} else if ( bud_list_cache_exists() == FALSE ) {
+		do_import( (GtkWidget *) NULL, gc );
+	} else if ( gc && (bud_list_cache_exists(gc) == FALSE) ) {
 		do_export( (GtkWidget *) NULL, 0 );	
 	}
  }
--- a/src/util.c	Mon Oct 09 23:56:33 2000 +0000
+++ b/src/util.c	Tue Oct 10 00:02:02 2000 +0000
@@ -38,7 +38,6 @@
 
 static GdkPixmap *icon_pm = NULL;
 static GdkBitmap *icon_bm = NULL;
-static int state;
 
 char *full_date() {
 	char * date;
@@ -382,7 +381,9 @@
         } else
                 fclose(fd);
 
-        g_snprintf(log_all_file, 256, "%s/.gaim/%s", getenv("HOME"), current_user->username);
+	/* FIXME: we need to figure out which directory to log things to. for now, it's just going
+	 * to have to be ~/.gaim/logs :-P */
+        g_snprintf(log_all_file, 256, "%s/.gaim/logs", getenv("HOME"));
 
         if (stat(log_all_file, &st) < 0)
                 flag = 1;
@@ -403,7 +404,8 @@
                 fclose(fd);
 
         
-        g_snprintf(log_all_file, 256, "%s/.gaim/%s/%s.log", getenv("HOME"), current_user->username, normalize(name));
+	/* same FIXME as above; need to find better dir than ~/.gaim/logs */
+        g_snprintf(log_all_file, 256, "%s/.gaim/logs/%s.log", getenv("HOME"), normalize(name));
 
         if (stat(log_all_file, &st) < 0)
                 flag = 1;
@@ -413,7 +415,7 @@
 
         fd = fopen(log_all_file, "a");
 
-        if (flag) { /* is a new file */
+        if (fd && flag) { /* is a new file */
 		fprintf(fd, "<HTML><HEAD><TITLE>" );
 		fprintf(fd, "IM Sessions with %s", name );
 		fprintf(fd, "</TITLE></HEAD><BODY BGCOLOR=\"ffffff\">\n" );
@@ -428,7 +430,6 @@
 /* we only need this for TOC, because messages must be escaped */
 int escape_message(char *msg)
 {
-if (!USE_OSCAR) {
 	char *c, *cpy;
 	int cnt=0;
 	/* Assumes you have a buffer able to cary at least BUF_LEN * 2 bytes */
@@ -458,15 +459,11 @@
 	msg[cnt]='\0';
 	g_free(cpy);
 	return cnt;
-} else {
-	return strlen(msg);
-}
 }
 
 /* we don't need this for oscar either */
 int escape_text(char *msg)
 {
-if (!USE_OSCAR) {
 	char *c, *cpy;
 	int cnt=0;
 	/* Assumes you have a buffer able to cary at least BUF_LEN * 4 bytes */
@@ -478,13 +475,13 @@
 	cpy = g_strdup(msg);
 	c = cpy;
 	while(*c) {
-                switch(*c) {
-                case '\n':
-                        msg[cnt++] = '<';
-                        msg[cnt++] = 'B';
-                        msg[cnt++] = 'R';
-                        msg[cnt++] = '>';
-                        break;
+		switch(*c) {
+		case '\n':
+			msg[cnt++] = '<';
+			msg[cnt++] = 'B';
+			msg[cnt++] = 'R';
+			msg[cnt++] = '>';
+			break;
 		case '{':
 		case '}':
 		case '\\':
@@ -499,9 +496,6 @@
 	msg[cnt]='\0';
 	g_free(cpy);
 	return cnt;
-} else {
-	return strlen(msg);
-}
 }
 
 char * escape_text2(char *msg)
@@ -684,16 +678,6 @@
 	return buf;
 }
 
-int query_state()
-{
-        return state;
-}
-
-void set_state(int i)
-{
-        state = i;
-}
-
 char *date()
 {
 	static char date[80];