changeset 2821:9467e4ee81be

[gaim-migrate @ 2834] new libfaim stuff. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sat, 01 Dec 2001 00:56:31 +0000
parents b917845dad3c
children 744df95bf123
files src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/aim_internal.h src/protocols/oscar/auth.c src/protocols/oscar/chat.c src/protocols/oscar/chatnav.c src/protocols/oscar/conn.c src/protocols/oscar/misc.c src/protocols/oscar/oscar.c src/protocols/oscar/service.c src/protocols/oscar/tlv.c
diffstat 11 files changed, 264 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/oscar/aim.h	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/aim.h	Sat Dec 01 00:56:31 2001 +0000
@@ -130,48 +130,42 @@
 #define AIM_MD5_STRING "AOL Instant Messenger (SM)"
 
 /*
- * Client info.  Filled in by the client and passed
- * in to aim_login().  The information ends up
- * getting passed to OSCAR through the initial
- * login command.
- *
- * XXX: Should this be per-session? -mid
+ * Client info.  Filled in by the client and passed in to 
+ * aim_send_login().  The information ends up getting passed to OSCAR
+ * through the initial login command.
  *
  */
 struct client_info_s {
-	char clientstring[100]; /* arbitrary size */
+	const char *clientstring;
+	fu16_t clientid;
 	int major;
 	int minor;
+	int point;
 	int build;
-	char country[3];
-	char lang[3];
-	int major2;
-	int minor2;
-	long unknown;
+	const char *country; /* two-letter abbrev */
+	const char *lang; /* two-letter abbrev */
 };
 
 #define AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 { \
 	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
+	0x0004, \
 	0x0003, \
 	0x0005, \
+	0x0000, \
 	0x0686, \
 	"us", \
 	"en", \
-	0x0004, \
-	0x0000, \
-	0x0000002a, \
 }
 
 #define AIM_CLIENTINFO_KNOWNGOOD_4_1_2010 { \
 	  "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
 	  0x0004, \
+	  0x0004, \
 	  0x0001, \
+	  0x0000, \
 	  0x07da, \
 	  "us", \
 	  "en", \
-	  0x0004, \
-	  0x0000, \
-	  0x0000004b, \
 }
 
 /*
@@ -336,14 +330,6 @@
 	int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *);
 
 	/*
-	 * This is a dreadful solution to the what-room-are-we-joining
-	 * problem.  (There's no connection between the service
-	 * request and the resulting redirect.)
-	 */ 
-	char *pendingjoin;
-	fu16_t pendingjoinexchange;
-
-	/*
 	 * Outstanding snac handling 
 	 *
 	 * XXX: Should these be per-connection? -mid
@@ -465,6 +451,7 @@
 faim_internal fu16_t aim_gettlv16(aim_tlvlist_t *list, const fu16_t t, const int n);
 faim_internal fu32_t aim_gettlv32(aim_tlvlist_t *list, const fu16_t t, const int n);
 faim_internal int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
+faim_internal int aim_addtlvtochain8(aim_tlvlist_t **list, const fu16_t t, const fu8_t v);
 faim_internal int aim_addtlvtochain16(aim_tlvlist_t **list, const fu16_t t, const fu16_t v);
 faim_internal int aim_addtlvtochain32(aim_tlvlist_t **list, const fu16_t type, const fu32_t v);
 faim_internal int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t t, const fu16_t l, const fu8_t *v);
@@ -520,6 +507,18 @@
 	struct aim_clientrelease latestbeta;
 };
 
+/* Callback data for redirect. */
+struct aim_redirect_data {
+	fu16_t group;
+	const char *ip;
+	const fu8_t *cookie;
+	struct { /* group == AIM_CONN_TYPE_CHAT */
+		fu16_t exchange;
+		const char *room;
+		fu16_t instance;
+	} chat;
+};
+
 faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn);
 faim_export int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn);
 faim_export int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn);
@@ -926,8 +925,15 @@
 /* aim_search.c */
 faim_export int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
 
+/* These apply to exchanges as well. */
+#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
+#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
+#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
+#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
+
 struct aim_chat_exchangeinfo {
 	fu16_t number;
+	fu16_t flags;
 	char *name;
 	char *charset1;
 	char *lang1;
@@ -939,7 +945,7 @@
 #define AIM_CHATFLAGS_AWAY      0x0002
 faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen);
 faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance);
-faim_export int aim_chat_attachname(aim_conn_t *conn, const char *roomname);
+faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance);
 faim_export char *aim_chat_getname(aim_conn_t *conn);
 faim_export aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name);
 
--- a/src/protocols/oscar/aim_cbtypes.h	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/aim_cbtypes.h	Sat Dec 01 00:56:31 2001 +0000
@@ -236,7 +236,6 @@
 #define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
 #define AIM_CB_SPECIAL_FLAPVER 0x0005
 #define AIM_CB_SPECIAL_CONNINITDONE 0x0006
-#define AIM_CB_SPECIAL_DEBUGCONN_CONNECT 0xe001
 #define AIM_CB_SPECIAL_UNKNOWN 0xffff
 #define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
 
--- a/src/protocols/oscar/aim_internal.h	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/aim_internal.h	Sat Dec 01 00:56:31 2001 +0000
@@ -127,6 +127,13 @@
 
 faim_internal int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
 
+/* Stored in ->priv of the service request SNAC for chats. */
+struct chatsnacinfo {
+	fu16_t exchange;
+	char name[128];
+	fu16_t instance;
+};
+
 /* these are used by aim_*_clientready */
 #define AIM_TOOL_JAVA   0x0001
 #define AIM_TOOL_MAC    0x0002
@@ -210,6 +217,8 @@
 faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn);
 faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn);
 
+faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
+
 /* These are all handled internally now. */
 faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
 faim_internal int aim_reqrates(aim_session_t *, aim_conn_t *);
--- a/src/protocols/oscar/auth.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/auth.c	Sat Dec 01 00:56:31 2001 +0000
@@ -172,12 +172,12 @@
 	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), sn);
 	aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded);
 	aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), clientstr);
-	aim_addtlvtochain16(&tl, 0x0016, 0x010a);
-	aim_addtlvtochain16(&tl, 0x0017, 0x0004);
-	aim_addtlvtochain16(&tl, 0x0018, 0x0041);
-	aim_addtlvtochain16(&tl, 0x0019, 0x0001);
-	aim_addtlvtochain16(&tl, 0x001a, 0x0cd1);
-	aim_addtlvtochain32(&tl, 0x0014, 0x00000055);
+	aim_addtlvtochain16(&tl, 0x0016, 0x010a); /* cliend ID */
+	aim_addtlvtochain16(&tl, 0x0017, 0x0004); /* major version */
+	aim_addtlvtochain16(&tl, 0x0018, 0x0041); /* minor version */
+	aim_addtlvtochain16(&tl, 0x0019, 0x0001); /* point version */
+	aim_addtlvtochain16(&tl, 0x001a, 0x0cd1); /* build */
+	aim_addtlvtochain32(&tl, 0x0014, 0x00000055); /* distribution chan */
 	aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), lang);
 	aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), country);
 
@@ -200,12 +200,22 @@
  * then the client information you send here must exactly match the
  * executable that you're pulling the data from.
  *
- * Latest WinAIM:
+ * WinAIM 4.8.2540
+ *   clientstring = "AOL Instant Messenger (SM), version 4.8.2540/WIN32"
+ *   clientid = 0x0109
+ *   major = 0x0004
+ *   minor = 0x0008
+ *   point = 0x0000
+ *   build = 0x09ec
+ *   t(0x0014) = 0x000000af
+ *   t(0x004a) = 0x01
+ *
+ * WinAIM 4.3.2188:
  *   clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32"
- *   major2 = 0x0109
+ *   clientid = 0x0109
  *   major = 0x0400
  *   minor = 0x0003
- *   minor2 = 0x0000
+ *   point = 0x0000
  *   build = 0x088c
  *   unknown = 0x00000086
  *   lang = "en"
@@ -214,70 +224,64 @@
  *
  * Latest WinAIM that libfaim can emulate without server-side buddylists:
  *   clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32"
- *   major2 = 0x0004
+ *   clientid = 0x0004
  *   major  = 0x0004
  *   minor  = 0x0001
- *   minor2 = 0x0000
+ *   point = 0x0000
  *   build  = 0x07da
  *   unknown= 0x0000004b
  *
  * WinAIM 3.5.1670:
  *   clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32"
- *   major2 = 0x0004
+ *   clientid = 0x0004
  *   major =  0x0003
  *   minor =  0x0005
- *   minor2 = 0x0000
+ *   point = 0x0000
  *   build =  0x0686
  *   unknown =0x0000002a
  *
  * Java AIM 1.1.19:
  *   clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
- *   major2 = 0x0001
+ *   clientid = 0x0001
  *   major  = 0x0001
  *   minor  = 0x0001
- *   minor2 = (not sent)
+ *   point = (not sent)
  *   build  = 0x0013
  *   unknown= (not sent)
  *   
  * AIM for Linux 1.1.112:
  *   clientstring = "AOL Instant Messenger (SM)"
- *   major2 = 0x1d09
+ *   clientid = 0x1d09
  *   major  = 0x0001
  *   minor  = 0x0001
- *   minor2 = 0x0001
+ *   point = 0x0001
  *   build  = 0x0070
  *   unknown= 0x0000008b
  *   serverstore = 0x01
  *
  */
-faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *clientinfo, const char *key)
+faim_export int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key)
 {
 	aim_frame_t *fr;
 	aim_tlvlist_t *tl = NULL;
 	fu8_t digest[16];
 	aim_snacid_t snacid;
 
-	if (!clientinfo || !sn || !password)
+	if (!ci || !sn || !password)
 		return -EINVAL;
 
+	/*
+	 * What the XORLOGIN flag _really_ means is that its an ICQ login,
+	 * which is really stupid and painful, so its not done here.
+	 *
+	 */
 	if (sess->flags & AIM_SESS_FLAGS_XORLOGIN)
 		return goddamnicq2(sess, conn, sn, password);
 
+
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
 		return -ENOMEM;
 
-	if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) {
-		fr->hdr.flap.type = 0x01;
-
-		/* Use very specific version numbers to further indicate hack */
-		clientinfo->major2 = 0x010a;
-		clientinfo->major = 0x0004;
-		clientinfo->minor = 0x003c;
-		clientinfo->minor2 = 0x0001;
-		clientinfo->build = 0x0cce;
-		clientinfo->unknown = 0x00000055;
-	}
-
 	snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
 	aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
 
@@ -286,15 +290,22 @@
 	aim_encode_password_md5(password, key, digest);
 	aim_addtlvtochain_raw(&tl, 0x0025, 16, digest);
 
-	aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientinfo->clientstring), clientinfo->clientstring);
-	aim_addtlvtochain16(&tl, 0x0016, (fu16_t)clientinfo->major2);
-	aim_addtlvtochain16(&tl, 0x0017, (fu16_t)clientinfo->major);
-	aim_addtlvtochain16(&tl, 0x0018, (fu16_t)clientinfo->minor);
-	aim_addtlvtochain16(&tl, 0x0019, (fu16_t)clientinfo->minor2);
-	aim_addtlvtochain16(&tl, 0x001a, (fu16_t)clientinfo->build);
-	aim_addtlvtochain_raw(&tl, 0x000e, strlen(clientinfo->country), clientinfo->country);
-	aim_addtlvtochain_raw(&tl, 0x000f, strlen(clientinfo->lang), clientinfo->lang);
-	aim_addtlvtochain16(&tl, 0x0009, 0x0015);
+	if (ci->clientstring)
+		aim_addtlvtochain_raw(&tl, 0x0003, strlen(ci->clientstring), ci->clientstring);
+	aim_addtlvtochain16(&tl, 0x0016, (fu16_t)ci->clientid);
+	aim_addtlvtochain16(&tl, 0x0017, (fu16_t)ci->major);
+	aim_addtlvtochain16(&tl, 0x0018, (fu16_t)ci->minor);
+	aim_addtlvtochain16(&tl, 0x0019, (fu16_t)ci->point);
+	aim_addtlvtochain16(&tl, 0x001a, (fu16_t)ci->build);
+	aim_addtlvtochain_raw(&tl, 0x000e, strlen(ci->country), ci->country);
+	aim_addtlvtochain_raw(&tl, 0x000f, strlen(ci->lang), ci->lang);
+
+	/*
+	 * If set, old-fashioned buddy lists will not work. You will need
+	 * to use SSI.
+	 */
+	if (0)
+		aim_addtlvtochain8(&tl, 0x004a, 0x01);
 
 	aim_writetlvchain(&fr->data, &tl);
 
--- a/src/protocols/oscar/chat.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/chat.c	Sat Dec 01 00:56:31 2001 +0000
@@ -8,16 +8,37 @@
 #define FAIM_INTERNAL
 #include <aim.h> 
 
+/* Stored in the ->priv of chat connections */
+struct chatconnpriv {
+	fu16_t exchange;
+	char *name;
+	fu16_t instance;
+};
+
+faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv;
+
+	if (ccp)
+		free(ccp->name);
+	free(ccp);
+
+	return;
+}
+
 faim_export char *aim_chat_getname(aim_conn_t *conn)
 {
-	
+	struct chatconnpriv *ccp;
+
 	if (!conn)
 		return NULL;
-	
+
 	if (conn->type != AIM_CONN_TYPE_CHAT)
 		return NULL;
 
-	return (char *)conn->priv; /* yuck ! */
+	ccp = (struct chatconnpriv *)conn->priv;
+
+	return ccp->name;
 }
 
 /* XXX get this into conn.c -- evil!! */
@@ -26,21 +47,25 @@
 	aim_conn_t *cur;
 
 	for (cur = sess->connlist; cur; cur = cur->next) {
+		struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv;
+
 		if (cur->type != AIM_CONN_TYPE_CHAT)
 			continue;
 		if (!cur->priv) {
 			faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
 			continue;
 		}
-		if (strcmp((char *)cur->priv, name) == 0)
+
+		if (strcmp(ccp->name, name) == 0)
 			break;
 	}
 
 	return cur;
 }
 
-faim_export int aim_chat_attachname(aim_conn_t *conn, const char *roomname)
+faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
 {
+	struct chatconnpriv *ccp;
 
 	if (!conn || !roomname)
 		return -EINVAL;
@@ -48,7 +73,14 @@
 	if (conn->priv)
 		free(conn->priv);
 
-	conn->priv = strdup(roomname);
+	if (!(ccp = malloc(sizeof(struct chatconnpriv))))
+		return -ENOMEM;
+
+	ccp->exchange = exchange;
+	ccp->name = strdup(roomname);
+	ccp->instance = instance;
+
+	conn->priv = (void *)ccp;
 
 	return 0;
 }
@@ -95,7 +127,7 @@
 		aimutil_put8(ckstr+i, (fu8_t) rand());
 
 	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
-	cookie->data = strdup(conn->priv); /* chat hack dependent */
+	cookie->data = NULL; /* XXX store something useful here */
 
 	aim_cachecookie(sess, cookie);
 
@@ -186,15 +218,20 @@
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	aim_tlvlist_t *tl = NULL;
+	struct chatsnacinfo csi;
 	
 	if (!sess || !conn || !roomname || !strlen(roomname))
 		return -EINVAL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+9+strlen(roomname)+2)))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
 		return -ENOMEM;
 
+	memset(&csi, 0, sizeof(csi));
+	csi.exchange = exchange;
+	strncpy(csi.name, roomname, sizeof(csi.name));
+	csi.instance = instance;
 
-	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, roomname, strlen(roomname)+1);
+	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
 	aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
 
 	/*
@@ -206,18 +243,6 @@
 	aim_writetlvchain(&fr->data, &tl);
 	aim_freetlvchain(&tl);
 
-	/*
-	 * Chat hack.
-	 *
-	 * XXX: A problem occurs here if we request a channel
-	 *      join but it fails....pendingjoin will be nonnull
-	 *      even though the channel is never going to get a
-	 *      redirect!
-	 *
-	 */
-	sess->pendingjoin = strdup(roomname);
-	sess->pendingjoinexchange = exchange;
-
 	aim_tx_enqueue(sess, fr);
 
 	return 0; 
@@ -371,7 +396,7 @@
 	fu16_t tlvcount = 0;
 	aim_tlvlist_t *tlvlist;
 	char *roomdesc = NULL;
-	fu16_t unknown_c9 = 0;
+	fu16_t flags = 0;
 	fu32_t creationtime = 0;
 	fu16_t maxmsglen = 0, maxvisiblemsglen = 0;
 	fu16_t unknown_d2 = 0, unknown_d5 = 0;
@@ -424,10 +449,10 @@
 	}
 
 	/* 
-	 * Type 0x00c9: Unknown. (2 bytes)
+	 * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
 	 */
 	if (aim_gettlv(tlvlist, 0x00c9, 1))
-		unknown_c9 = aim_gettlv16(tlvlist, 0x00c9, 1);
+		flags = aim_gettlv16(tlvlist, 0x00c9, 1);
 
 	/* 
 	 * Type 0x00ca: Creation time (4 bytes)
@@ -504,7 +529,7 @@
 				usercount,
 				userinfo,	
 				roomdesc,
-				unknown_c9,
+				flags,
 				creationtime,
 				maxmsglen,
 				unknown_d2,
--- a/src/protocols/oscar/chatnav.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/chatnav.c	Sat Dec 01 00:56:31 2001 +0000
@@ -20,11 +20,14 @@
 
 faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange)
 {
+	static const char ck[] = {"create"};
+	static const char lang[] = {"en"};
+	static const char charset[] = {"us-ascii"};
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	aim_tlvlist_t *tl = NULL;
 
-	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+12+strlen("invite")+strlen(name))))
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
 		return -ENOMEM;
 
 	snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
@@ -33,9 +36,9 @@
 	/* exchange */
 	aimbs_put16(&fr->data, exchange);
 
-	/* room cookie */
-	aimbs_put8(&fr->data, strlen("invite"));
-	aimbs_putraw(&fr->data, "invite", strlen("invite"));
+	/* action cookie */
+	aimbs_put8(&fr->data, strlen(ck));
+	aimbs_putraw(&fr->data, ck, strlen(ck));
 
 	/* 
 	 * instance
@@ -48,8 +51,9 @@
 	/* detail level */
 	aimbs_put8(&fr->data, 0x01);
 
-	/* room name */
 	aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), name);
+	aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), charset);
+	aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), lang);
 
 	/* tlvcount */
 	aimbs_put16(&fr->data, aim_counttlvchain(&tl));
@@ -93,7 +97,7 @@
 		aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
 
 		curexchange++;
-	
+
 		exchanges = realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
 
 		/* exchange number */
@@ -101,6 +105,15 @@
 		innerlist = aim_readtlvchain(&tbs);
 
 		/* 
+		 * Type 0x000a: Unknown.
+		 *
+		 * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
+		 *
+		 */
+		if (aim_gettlv(innerlist, 0x000a, 1))
+			;
+
+		/* 
 		 * Type 0x000d: Unknown.
 		 */
 		if (aim_gettlv(innerlist, 0x000d, 1))
@@ -124,10 +137,16 @@
 		}
 
 		/*
-		 * Type 0x00c9: Unknown
+		 * Type 0x00c9: Flags
+		 *
+		 * 1 Evilable
+		 * 2 Nav Only
+		 * 4 Instancing Allowed
+		 * 8 Occupant Peek Allowed
+		 *
 		 */ 
 		if (aim_gettlv(innerlist, 0x00c9, 1))
-			;
+			exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1);
 		      
 		/*
 		 * Type 0x00ca: Creation Date 
@@ -154,7 +173,7 @@
 			;
 
 		/*
-		 * Type 0x00d3: Exchange Name
+		 * Type 0x00d3: Exchange Description
 		 */
 		if (aim_gettlv(innerlist, 0x00d3, 1))	
 			exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
@@ -162,6 +181,12 @@
 			exchanges[curexchange-1].name = NULL;
 
 		/*
+		 * Type 0x00d4: Exchange Description URL
+		 */
+		if (aim_gettlv(innerlist, 0x00d4, 1))	
+			;
+
+		/*
 		 * Type 0x00d5: Creation Permissions
 		 *
 		 * 0  Creation not allowed
@@ -207,6 +232,12 @@
 		else
 			exchanges[curexchange-1].lang2 = NULL;
 		      
+		/*
+		 * Type 0x00da: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x00da, 1))	
+			;
+
 		aim_freetlvchain(&innerlist);
 	}
 
@@ -306,6 +337,18 @@
 /*
  * Since multiple things can trigger this callback, we must lookup the 
  * snacid to determine the original snac subtype that was called.
+ *
+ * XXX This isn't really how this works.  But this is:  Every d/9 response
+ * has a 16bit value at the beginning. That matches to:
+ *    Short Desc = 1
+ *    Full Desc = 2
+ *    Instance Info = 4
+ *    Nav Short Desc = 8
+ *    Nav Instance Info = 16
+ * And then everything is really asynchronous.  There is no specific 
+ * attachment of a response to a create room request, for example.  Creating
+ * the room yields no different a response than requesting the room's info.
+ *
  */
 static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
@@ -363,8 +406,8 @@
 
 	mod->family = 0x000d;
 	mod->version = 0x0001;
-	mod->toolid = 0x0004; /* XXX this doesn't look right */
-	mod->toolversion = 0x0001; /* XXX nor does this */
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x047c;
 	mod->flags = 0;
 	strncpy(mod->name, "chatnav", sizeof(mod->name));
 	mod->snachandler = snachandler;
--- a/src/protocols/oscar/conn.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/conn.c	Sat Dec 01 00:56:31 2001 +0000
@@ -171,6 +171,8 @@
 	 */
 	if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
 		aim_conn_kill_rend(sess, *deadconn);
+	else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
+		aim_conn_kill_chat(sess, *deadconn);
 
 	if ((*deadconn)->inside) {
 		aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;
@@ -864,8 +866,6 @@
 	aim_connrst(sess);
 	sess->queue_outgoing = NULL;
 	sess->queue_incoming = NULL;
-	sess->pendingjoin = NULL;
-	sess->pendingjoinexchange = 0;
 	aim_initsnachash(sess);
 	sess->msgcookies = NULL;
 	sess->snacid_next = 0x00000001;
--- a/src/protocols/oscar/misc.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/misc.c	Sat Dec 01 00:56:31 2001 +0000
@@ -169,16 +169,6 @@
 }
 
 /*
- * aim_debugconn_sendconnect()
- *
- * For aimdebugd.  If you don't know what it is, you don't want to.
- */
-faim_export int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn)
-{
-	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEBUGCONN_CONNECT);
-}
-
-/*
  * Generic routine for sending commands.
  *
  *
--- a/src/protocols/oscar/oscar.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/oscar.c	Sat Dec 01 00:56:31 2001 +0000
@@ -105,7 +105,8 @@
 struct chat_connection {
 	char *name;
 	char *show; /* AOL did something funny to us */
-	fu16_t exchange; /* XXX should have instance here too */
+	fu16_t exchange;
+	fu16_t instance;
 	int fd; /* this is redundant since we have the conn below */
 	aim_conn_t *conn;
 	int inpa;
@@ -155,7 +156,7 @@
 	return m;
 }
 
-static char *extract_name(char *name) {
+static char *extract_name(const char *name) {
 	char *tmp;
 	int i, j;
 	char *x = strchr(name, '-');
@@ -858,6 +859,7 @@
 	struct chat_connection *chatcon;
 	static int id = 1;
 
+	aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
 	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
 	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
 	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
@@ -874,6 +876,7 @@
 
 static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
 
+	aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0);
 	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
 
 	aim_clientready(sess, fr->conn);
@@ -971,15 +974,12 @@
 			GAIM_INPUT_READ,
 			oscar_callback, tstconn);
 	odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
-	aim_chat_attachname(tstconn, ccon->name);
 }
 
 /* Hrmph. I don't know how to make this look better. --mid */
 static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
-	fu16_t serviceid;
-	char *ip;
-	fu8_t *cookie;
+	struct aim_redirect_data *redir;
 	struct gaim_connection *gc = sess->aux_data;
 	struct aim_user *user = gc->user;
 	aim_conn_t *tstconn;
@@ -991,19 +991,18 @@
 		atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT,
 
 	va_start(ap, fr);
-	serviceid = (fu16_t)va_arg(ap, unsigned int);
-	ip = va_arg(ap, char *);
-	cookie = (fu8_t *)va_arg(ap, unsigned char *);
-
-	for (i = 0; i < (int)strlen(ip); i++) {
-		if (ip[i] == ':') {
-			port = atoi(&(ip[i+1]));
+	redir = va_arg(ap, struct aim_redirect_data *);
+	va_end(ap);
+
+	for (i = 0; i < (int)strlen(redir->ip); i++) {
+		if (redir->ip[i] == ':') {
+			port = atoi(&(redir->ip[i+1]));
 			break;
 		}
 	}
-	host = g_strndup(ip, i);
-
-	switch(serviceid) {
+	host = g_strndup(redir->ip, i);
+
+	switch(redir->group) {
 	case 0x7: /* Authorizer */
 		debug_printf("Reconnecting with authorizor...\n");
 		tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
@@ -1025,7 +1024,7 @@
 			g_free(host);
 			return 1;
 		}
-		aim_sendcookie(sess, tstconn, cookie);
+		aim_sendcookie(sess, tstconn, redir->cookie);
 		break;
 	case 0xd: /* ChatNav */
 		tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
@@ -1044,17 +1043,12 @@
 			g_free(host);
 			return 1;
 		}
-		aim_sendcookie(sess, tstconn, cookie);
+		aim_sendcookie(sess, tstconn, redir->cookie);
 		break;
 	case 0xe: /* Chat */
 		{
-		char *roomname;
-		fu16_t exchange;
 		struct chat_connection *ccon;
 
-		roomname = va_arg(ap, char *);
-		exchange = (fu16_t)va_arg(ap, unsigned int);
-
 		tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
 		if (tstconn == NULL) {
 			debug_printf("unable to connect to chat server\n");
@@ -1068,9 +1062,10 @@
 		ccon->conn = tstconn;
 		ccon->gc = gc;
 		ccon->fd = -1;
-		ccon->name = g_strdup(roomname);
-		ccon->exchange = exchange;
-		ccon->show = extract_name(roomname);
+		ccon->name = g_strdup(redir->chat.room);
+		ccon->exchange = redir->chat.exchange;
+		ccon->instance = redir->chat.instance;
+		ccon->show = extract_name(redir->chat.room);
 		
 		ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
 		ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
@@ -1083,17 +1078,15 @@
 			g_free(ccon);
 			return 1;
 		}
-		aim_sendcookie(sess, tstconn, cookie);
-		debug_printf("Connected to chat room %s exchange %d\n", roomname, exchange);
+		aim_sendcookie(sess, tstconn, redir->cookie);
+		debug_printf("Connected to chat room %s exchange %d\n", ccon->name, ccon->exchange);
 		}
 		break;
 	default: /* huh? */
-		debug_printf("got redirect for unknown service 0x%04x\n", serviceid);
+		debug_printf("got redirect for unknown service 0x%04x\n", redir->group);
 		break;
 	}
 
-	va_end(ap);
-
 	g_free(host);
 	return 1;
 }
--- a/src/protocols/oscar/service.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/service.c	Sat Dec 01 00:56:31 2001 +0000
@@ -100,15 +100,14 @@
 /* Redirect (group 1, type 5) */
 static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
-	int serviceid;
-	fu8_t *cookie;
-	char *ip;
+	struct aim_redirect_data redir;
 	aim_rxcallback_t userfunc;
 	aim_tlvlist_t *tlvlist;
-	char *chathack = NULL;
-	int chathackex = 0;
+	aim_snac_t *origsnac = NULL;
 	int ret = 0;
 
+	memset(&redir, 0, sizeof(redir));
+
 	tlvlist = aim_readtlvchain(bs);
 
 	if (!aim_gettlv(tlvlist, 0x000d, 1) ||
@@ -118,26 +117,30 @@
 		return 0;
 	}
 
-	serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
-	ip = aim_gettlv_str(tlvlist, 0x0005, 1);
-	cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
+	redir.group = aim_gettlv16(tlvlist, 0x000d, 1);
+	redir.ip = aim_gettlv_str(tlvlist, 0x0005, 1);
+	redir.cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
+
+	/* Fetch original SNAC so we can get csi if needed */
+	origsnac = aim_remsnac(sess, snac->id);
 
-	/*
-	 * Chat hack.
-	 */
-	if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
-		chathack = sess->pendingjoin;
-		chathackex = sess->pendingjoinexchange;
-		sess->pendingjoin = NULL;
-		sess->pendingjoinexchange = 0;
+	if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
+		struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
+
+		redir.chat.exchange = csi->exchange;
+		redir.chat.room = csi->name;
+		redir.chat.instance = csi->instance;
 	}
 
 	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		ret = userfunc(sess, rx, serviceid, ip, cookie, chathack, chathackex);
+		ret = userfunc(sess, rx, &redir);
 
-	free(ip);
-	free(cookie);
-	free(chathack);
+	free((void *)redir.ip);
+	free((void *)redir.cookie);
+
+	if (origsnac)
+		free(origsnac->data);
+	free(origsnac);
 
 	aim_freetlvchain(&tlvlist);
 
--- a/src/protocols/oscar/tlv.c	Fri Nov 30 21:16:28 2001 +0000
+++ b/src/protocols/oscar/tlv.c	Sat Dec 01 00:56:31 2001 +0000
@@ -204,6 +204,24 @@
 }
 
 /**
+ * aim_addtlvtochain8 - Add a 8bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a one-byte unsigned integer to a TLV chain.
+ *
+ */
+faim_internal int aim_addtlvtochain8(aim_tlvlist_t **list, const fu16_t t, const fu8_t v)
+{
+	fu8_t v8[1];
+
+	aimutil_put8(v8, v);
+
+	return aim_addtlvtochain_raw(list, t, 1, v8);
+}
+
+/**
  * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
  * @list: Destination chain
  * @type: TLV type to add