diff plugins/oscar.c @ 981:7e231bc0018a

[gaim-migrate @ 991] I think I need a Pepsi. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 13 Oct 2000 07:24:40 +0000
parents
children 0b5db8cdd30f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/oscar.c	Fri Oct 13 07:24:40 2000 +0000
@@ -0,0 +1,1530 @@
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+
+#include <netdb.h>
+#include <gtk/gtk.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include "multi.h"
+#include "prpl.h"
+#include "gaim.h"
+#include "aim.h"
+#include "gnome_applet_mgr.h"
+
+#include "pixmaps/cancel.xpm"
+#include "pixmaps/ok.xpm"
+
+int gaim_caps = AIM_CAPS_CHAT | AIM_CAPS_SENDFILE | AIM_CAPS_GETFILE |
+		AIM_CAPS_VOICE | AIM_CAPS_IMIMAGE | AIM_CAPS_BUDDYICON;
+int keepalv = -1;
+
+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;
+		if (!strcmp(name, c->name))
+			break;
+		g = g->next;
+		c = NULL;
+	}
+
+	return c;
+}
+
+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) {
+		c = (struct chat_connection *)g->data;
+		if (c->conn == conn)
+			break;
+		g = g->next;
+		c = NULL;
+	}
+
+	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;
+		if (c->protocol != PROTO_OSCAR) {
+			c = NULL;
+			g = g->next;
+			continue;
+		}
+		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 *, ...);
+static int gaim_handle_redirect  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_oncoming   (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_offgoing   (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_misses     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_user_info  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_motd       (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_chatnav_info     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_chat_join        (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_chat_leave       (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_chat_info_update (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_chat_incoming_msg(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_msgack     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_ratechange (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_evilnotify (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_bosrights        (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_rateresp         (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_reportinterval   (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_msgerr     (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_buddyrights(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_parse_locerr     (struct aim_session_t *, struct command_rx_struct *, ...);
+
+static int gaim_directim_incoming(struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_directim_typing  (struct aim_session_t *, struct command_rx_struct *, ...);
+static int gaim_directim_initiate(struct aim_session_t *, struct command_rx_struct *, ...);
+
+static char *msgerrreason[] = {
+	"Invalid error",
+	"Invalid SNAC",
+	"Rate to host",
+	"Rate to client",
+	"Not logged in",
+	"Service unavailable",
+	"Service not defined",
+	"Obsolete SNAC",
+	"Not supported by host",
+	"Not supported by client",
+	"Refused by client",
+	"Reply too big",
+	"Responses lost",
+	"Request denied",
+	"Busted SNAC payload",
+	"Insufficient rights",
+	"In local permit/deny",
+	"Too evil (sender)",
+	"Too evil (receiver)",
+	"User temporarily unavailable",
+	"No match",
+	"List overflow",
+	"Request ambiguous",
+	"Queue full",
+	"Not while on AOL"
+};
+static int msgerrreasonlen = 25;
+
+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) {
+		hide_login_progress(gc->username, _("Disconnected."));
+		signoff(gc);
+		return;
+	}
+	if (condition & GDK_INPUT_READ) {
+		if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+			debug_print("got information on rendezvous\n");
+			if (aim_handlerendconnect(gc->oscar_sess, conn) < 0) {
+				debug_print(_("connection error (rend)\n"));
+			}
+		} else {
+			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) {
+					struct conversation *cnv =
+						find_conversation(((struct aim_directim_priv *)conn->priv)->sn);
+					debug_print("connection error for directim\n");
+					if (cnv) {
+						make_direct(cnv, FALSE, NULL, 0);
+					}
+					aim_conn_kill(gc->oscar_sess, &conn);
+				} else if ((conn->type == AIM_CONN_TYPE_BOS) ||
+					   !(aim_getconn_type(gc->oscar_sess, AIM_CONN_TYPE_BOS))) {
+					debug_print(_("major connection error\n"));
+					hide_login_progress(gc->username, _("Disconnected."));
+					signoff(gc);
+				} else if (conn->type == AIM_CONN_TYPE_CHAT) {
+					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;
+					if (c->inpa > -1)
+						gdk_input_remove(c->inpa);
+					c->inpa = -1;
+					c->fd = -1;
+					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) {
+					if (gc->cnpa > -1)
+						gdk_input_remove(gc->cnpa);
+					gc->cnpa = -1;
+					debug_print("removing chatnav input watcher\n");
+					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(gc->oscar_sess, &conn);
+				}
+			}
+		}
+	}
+}
+
+void oscar_login(struct aim_user *user) {
+	struct aim_session_t *sess;
+	struct aim_conn_t *conn;
+	char buf[256];
+	struct gaim_connection *gc;
+
+	sprintf(debug_buff, _("Logging in %s\n"), user->username);
+	debug_print(debug_buff);
+
+	gc = new_gaim_conn(PROTO_OSCAR, user->username, user->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;
+	gc->oscar_sess = sess;
+
+	sprintf(buf, _("Looking up %s"), FAIM_LOGIN_SERVER);
+	conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, FAIM_LOGIN_SERVER);
+
+	if (conn == NULL) {
+		debug_print(_("internal connection error\n"));
+		hide_login_progress(gc->username, _("Unable to login to AIM"));
+		destroy_gaim_conn(gc);
+		return;
+	} else if (conn->fd == -1) {
+		if (conn->status & AIM_CONN_STATUS_RESOLVERR) {
+			sprintf(debug_buff, _("couldn't resolve host"));
+			debug_print(debug_buff); debug_print("\n");
+			hide_login_progress(gc->username, debug_buff);
+		} else if (conn->status & AIM_CONN_STATUS_CONNERR) {
+			sprintf(debug_buff, _("couldn't connect to host"));
+			debug_print(debug_buff); debug_print("\n");
+			hide_login_progress(gc->username, debug_buff);
+		}
+		destroy_gaim_conn(gc);
+		return;
+	}
+	g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
+
+	aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
+	aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
+	aim_sendconnack(sess, conn);
+	aim_request_login(sess, conn, gc->username);
+
+	gc->inpa = gdk_input_add(conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+			oscar_callback, conn);
+
+	sprintf(gc->user_info, "%s", user->user_info);
+	gc->options = user->options;
+	save_prefs(); /* is this necessary anymore? */
+
+	debug_print(_("Password sent, waiting for response\n"));
+}
+
+void oscar_close(struct gaim_connection *gc) {
+	if (gc->protocol != PROTO_OSCAR) return;
+	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);
+
+	if (sess->logininfo.errorcode) {
+		switch (sess->logininfo.errorcode) {
+		case 0x18:
+			/* connecting too frequently */
+			do_error_dialog(_("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."), _("Gaim - Error"));
+			plugin_event(event_error, (void *)983, 0, 0);
+			break;
+		case 0x05:
+			/* Incorrect nick/password */
+			do_error_dialog(_("Incorrect nickname or password."), _("Gaim - Error"));
+			plugin_event(event_error, (void *)980, 0, 0);
+			break;
+		case 0x1c:
+			/* client too old */
+			do_error_dialog(_("The client version you are using is too old. Please upgrade at http://www.marko.net/gaim/"), _("Gaim - Error"));
+			plugin_event(event_error, (void *)989, 0, 0);
+			break;
+		}
+		sprintf(debug_buff, "Login Error Code 0x%04x\n",
+				sess->logininfo.errorcode);
+		debug_print(debug_buff);
+		sprintf(debug_buff, "Error URL: %s\n",
+				sess->logininfo.errorurl);
+		debug_print(debug_buff);
+#ifdef USE_APPLET
+		set_user_state(offline);
+#endif
+		gdk_input_remove(gc->inpa);
+		hide_login_progress(gc->username, _("Authentication Failed"));
+		signoff(gc);
+		return 0;
+	}
+
+
+	if (sess->logininfo.email) {
+		sprintf(debug_buff, "Email: %s\n", sess->logininfo.email);
+		debug_print(debug_buff);
+	} else {
+		debug_print("Email is NULL\n");
+	}
+	sprintf(debug_buff, "Closing auth connection...\n");
+	debug_print(debug_buff);
+	gdk_input_remove(gc->inpa);
+	aim_conn_kill(sess, &command->conn);
+
+	bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, sess->logininfo.BOSIP);
+	if (bosconn == NULL) {
+#ifdef USE_APPLET
+		set_user_state(offline);
+#endif
+		hide_login_progress(gc->username, _("Internal Error"));
+		destroy_gaim_conn(gc);
+		return -1;
+	} else if (bosconn->status != 0) {
+#ifdef USE_APPLET
+		set_user_state(offline);
+#endif
+		hide_login_progress(gc->username, _("Could Not Connect"));
+		destroy_gaim_conn(gc);
+		return -1;
+	}
+
+	aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0);
+	aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, gaim_rateresp, 0); /* rate info */
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, gaim_server_ready, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, gaim_reportinterval, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_CTN, AIM_CB_CTN_DEFAULT, aim_parse_unknown, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_DEFAULT, aim_parse_unknown, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
+
+	aim_auth_sendcookie(sess, bosconn, sess->logininfo.cookie);
+	gc->oscar_conn = bosconn;
+	gc->inpa = gdk_input_add(bosconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+			oscar_callback, bosconn);
+	return 1;
+}
+
+int gaim_parse_login(struct aim_session_t *sess,
+		     struct command_rx_struct *command, ...) {
+	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, gc->username, gc->password, &info, key);
+	return 1;
+}
+
+int gaim_server_ready(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	static int id = 1;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+	switch (command->conn->type) {
+	case AIM_CONN_TYPE_BOS:
+		aim_setversions(sess, command->conn);
+		aim_bos_reqrate(sess, command->conn); /* request rate info */
+		debug_print("done with BOS ServerReady\n");
+		break;
+	case AIM_CONN_TYPE_CHATNAV:
+		debug_print("chatnav: got server ready\n");
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
+		aim_bos_reqrate(sess, command->conn);
+		aim_bos_ackrateresp(sess, command->conn);
+		aim_chatnav_clientready(sess, command->conn);
+		aim_chatnav_reqrights(sess, command->conn);
+		break;
+	case AIM_CONN_TYPE_CHAT:
+		debug_print("chat: got server ready\n");
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
+		aim_bos_reqrate(sess, command->conn);
+		aim_bos_ackrateresp(sess, command->conn);
+		aim_chat_clientready(sess, command->conn);
+		serv_got_joined_chat(gc, id++, aim_chat_getname(command->conn));
+		break;
+	case AIM_CONN_TYPE_RENDEZVOUS:
+		aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_directim_incoming, 0);
+		break;
+	default: /* huh? */
+		sprintf(debug_buff, "server ready: got unexpected connection type %04x\n", command->conn->type);
+		debug_print(debug_buff);
+		break;
+	}
+	return 1;
+}
+
+int gaim_handle_redirect(struct aim_session_t *sess,
+			 struct command_rx_struct *command, ...) {
+	va_list ap;
+	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);
+	ip        = va_arg(ap, char *);
+	cookie    = va_arg(ap, unsigned char *);
+
+	switch(serviceid) {
+	case 0x7: /* Authorizer */
+		debug_print("Reconnecting with authorizor...\n");
+		{
+		struct aim_conn_t *tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
+		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR)
+			debug_print("unable to reconnect with authorizer\n");
+		else {
+			gc->paspa = gdk_input_add(tstconn->fd,
+					GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+					oscar_callback, tstconn);
+			aim_auth_sendcookie(sess, tstconn, cookie);
+		}
+		}
+		break;
+	case 0xd: /* ChatNav */
+		{
+		struct aim_conn_t *tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, ip);
+		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR) {
+			debug_print("unable to connect to chatnav server\n");
+			return 1;
+		}
+		aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0);
+		aim_auth_sendcookie(sess, tstconn, cookie);
+		gc->cnpa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+					oscar_callback, tstconn);
+		}
+		debug_print("chatnav: connected\n");
+		break;
+	case 0xe: /* Chat */
+		{
+		struct aim_conn_t *tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, ip);
+		char *roomname = va_arg(ap, char *);
+		struct chat_connection *ccon;
+		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR) {
+			debug_print("unable to connect to chat server\n");
+			return 1;
+		}
+		sprintf(debug_buff, "Connected to chat room %s\n", roomname);
+		debug_print(debug_buff);
+
+		ccon = g_new0(struct chat_connection, 1);
+		ccon->conn = tstconn;
+		ccon->fd = tstconn->fd;
+		ccon->name = g_strdup(roomname);
+		
+		ccon->inpa = gdk_input_add(tstconn->fd,
+				GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+				oscar_callback, tstconn);
+
+		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);
+		aim_auth_sendcookie(sess, tstconn, cookie);
+		}
+		break;
+	default: /* huh? */
+		sprintf(debug_buff, "got redirect for unknown service 0x%04x\n",
+				serviceid);
+		debug_print(debug_buff);
+		break;
+	}
+
+	va_end(ap);
+
+	return 1;
+}
+
+int gaim_parse_oncoming(struct aim_session_t *sess,
+			struct command_rx_struct *command, ...) {
+	struct aim_userinfo_s *info;
+	time_t time_idle;
+	int type = 0;
+
+	va_list ap;
+	va_start(ap, command);
+	info = va_arg(ap, struct aim_userinfo_s *);
+	va_end(ap);
+
+	if (info->flags & AIM_FLAG_UNCONFIRMED)
+		type |= UC_UNCONFIRMED;
+	else if (info->flags & AIM_FLAG_ADMINISTRATOR)
+		type |= UC_ADMIN;
+	else if (info->flags & AIM_FLAG_AOL)
+		type |= UC_AOL;
+	else if (info->flags & AIM_FLAG_FREE)
+		type |= UC_NORMAL;
+	if (info->flags & AIM_FLAG_AWAY)
+		type |= UC_UNAVAILABLE;
+
+	if (info->idletime) {
+		time(&time_idle);
+		time_idle -= info->idletime*60;
+	} else
+		time_idle = 0;
+
+	serv_got_update(info->sn, 1, info->warnlevel/10, info->onlinesince,
+			time_idle, type, info->capabilities);
+
+	return 1;
+}
+
+int gaim_parse_offgoing(struct aim_session_t *sess,
+			struct command_rx_struct *command, ...) {
+	char *sn;
+	va_list ap;
+
+	va_start(ap, command);
+	sn = va_arg(ap, char *);
+	va_end(ap);
+
+	serv_got_update(sn, 0, 0, 0, 0, 0, 0);
+
+	return 1;
+}
+
+static void accept_directim(GtkWidget *w, GtkWidget *m)
+{
+	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(gc->oscar_sess, gc->oscar_conn, priv))) {
+		debug_print("imimage: could not connect\n");
+		return;
+	}
+
+	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);
+
+	sprintf(debug_buff, "DirectIM: connected to %s\n", priv->sn);
+	debug_print(debug_buff);
+
+	serv_got_imimage(gc, priv->sn, priv->cookie, priv->ip, newconn, watcher);
+
+	g_free(priv);
+}
+
+static void cancel_directim(GtkWidget *w, GtkWidget *m)
+{
+	gtk_widget_destroy(m);
+}
+
+static void directim_dialog(struct gaim_connection *gc, struct aim_directim_priv *priv)
+{
+	GtkWidget *window;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *yes;
+	GtkWidget *no;
+	char buf[BUF_LONG];
+
+	window = gtk_window_new(GTK_WINDOW_DIALOG);
+	gtk_window_set_title(GTK_WINDOW(window), _("Accept Direct IM?"));
+	gtk_window_set_wmclass(GTK_WINDOW(window), "directim", "Gaim");
+	gtk_widget_realize(window);
+	aol_icon(window->window);
+	gtk_object_set_user_data(GTK_OBJECT(window), (void *)priv);
+
+	vbox = gtk_vbox_new(TRUE, 5);
+	gtk_container_add(GTK_CONTAINER(window), vbox);
+	gtk_widget_show(vbox);
+
+	sprintf(buf,  _("%s has requested to directly connect to your computer. "
+			"Do you accept?"), priv->sn);
+	label = gtk_label_new(buf);
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5);
+	gtk_widget_show(label);
+
+	hbox = gtk_hbox_new(TRUE, 10);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+	gtk_widget_show(hbox);
+
+	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);
+
+	gtk_signal_connect(GTK_OBJECT(yes), "clicked",
+			   GTK_SIGNAL_FUNC(accept_directim), window);
+	gtk_signal_connect(GTK_OBJECT(no), "clicked",
+			   GTK_SIGNAL_FUNC(cancel_directim), window);
+
+	gtk_widget_show(window);
+}
+
+int gaim_parse_incoming_im(struct aim_session_t *sess,
+			   struct command_rx_struct *command, ...) {
+	int channel;
+	va_list ap;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+
+	va_start(ap, command);
+	channel = va_arg(ap, int);
+
+	/* channel 1: standard message */
+	if (channel == 1) {
+		struct aim_userinfo_s *userinfo;
+		char *msg = NULL;
+		char *tmp = g_malloc(BUF_LONG);
+		u_int icbmflags = 0;
+		u_short flag1, flag2;
+
+		userinfo  = va_arg(ap, struct aim_userinfo_s *);
+		msg       = va_arg(ap, char *);
+		icbmflags = va_arg(ap, u_int);
+		flag1     = (u_short)va_arg(ap, u_int);
+		flag2     = (u_short)va_arg(ap, u_int);
+		va_end(ap);
+
+		g_snprintf(tmp, BUF_LONG, "%s", msg);
+		serv_got_im(gc, userinfo->sn, tmp, icbmflags & AIM_IMFLAGS_AWAY);
+		g_free(tmp);
+	} else if (channel == 2) {
+		struct aim_userinfo_s *userinfo;
+		int rendtype = va_arg(ap, int);
+		if (rendtype & AIM_CAPS_CHAT) {
+			char *msg, *encoding, *lang;
+			struct aim_chat_roominfo *roominfo;
+
+			userinfo = va_arg(ap, struct aim_userinfo_s *);
+			roominfo = va_arg(ap, struct aim_chat_roominfo *);
+			msg      = va_arg(ap, char *);
+			encoding = va_arg(ap, char *);
+			lang     = va_arg(ap, char *);
+			va_end(ap);
+
+			serv_got_chat_invite(gc,
+					     roominfo->name,
+					     roominfo->exchange,
+					     userinfo->sn,
+					     msg);
+		} else if (rendtype & AIM_CAPS_SENDFILE) {
+			/* libfaim won't tell us that we got this just yet */
+		} else if (rendtype & AIM_CAPS_GETFILE) {
+			/* nor will it tell us this. but it's still there */
+		} else if (rendtype & AIM_CAPS_VOICE) {
+			/* this one libfaim tells us unuseful info about  */
+		} else if (rendtype & AIM_CAPS_BUDDYICON) {
+			/* bah */
+		} else if (rendtype & AIM_CAPS_IMIMAGE) {
+			/* DirectIM stuff */
+			struct aim_directim_priv *priv, *priv2;
+
+			userinfo = va_arg(ap, struct aim_userinfo_s *);
+			priv = va_arg(ap, struct aim_directim_priv *);
+			va_end(ap);
+
+			sprintf(debug_buff, "DirectIM request from %s (%s)\n", userinfo->sn, priv->ip);
+			debug_print(debug_buff);
+
+			priv2 = g_new0(struct aim_directim_priv, 1);
+			strcpy(priv2->cookie, priv->cookie);
+			strcpy(priv2->sn, priv->sn);
+			strcpy(priv2->ip, priv->ip);
+			directim_dialog(gc, priv2);
+		} else {
+			sprintf(debug_buff, "Unknown rendtype %d\n", rendtype);
+			debug_print(debug_buff);
+		}
+	}
+
+	return 1;
+}
+
+int gaim_parse_misses(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	va_list ap;
+	u_short chan, nummissed, reason;
+	struct aim_userinfo_s *userinfo;
+	char buf[1024];
+
+	va_start(ap, command);
+	chan = (u_short)va_arg(ap, u_int);
+	userinfo = va_arg(ap, struct aim_userinfo_s *);
+	nummissed = (u_short)va_arg(ap, u_int);
+	reason = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	switch(reason) {
+		case 1:
+			/* message too large */
+			sprintf(buf, _("You missed a message from %s because it was too large."), userinfo->sn);
+			do_error_dialog(buf, _("Gaim - Error"));
+			plugin_event(event_error, (void *)961, 0, 0);
+			break;
+		default:
+			sprintf(buf, _("You missed a message from %s for unknown reasons."), userinfo->sn);
+			do_error_dialog(buf, _("Gaim - Error"));
+			plugin_event(event_error, (void *)970, 0, 0);
+			break;
+	}
+
+	return 1;
+}
+
+int gaim_parse_msgerr(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *destn;
+	u_short reason;
+	char buf[1024];
+
+	va_start(ap, command);
+	destn = va_arg(ap, char *);
+	reason = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
+			(reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+	do_error_dialog(buf, _("Gaim - Error"));
+
+	return 1;
+}
+
+int gaim_parse_locerr(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *destn;
+	u_short reason;
+	char buf[1024];
+
+	va_start(ap, command);
+	destn = va_arg(ap, char *);
+	reason = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	sprintf(buf, _("User information for %s unavailable: %s"), destn,
+			(reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+	do_error_dialog(buf, _("Gaim - Error"));
+
+	return 1;
+}
+
+int gaim_parse_user_info(struct aim_session_t *sess,
+			 struct command_rx_struct *command, ...) {
+	struct aim_userinfo_s *info;
+	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);
+	info = va_arg(ap, struct aim_userinfo_s *);
+	prof_enc = va_arg(ap, char *);
+	prof = va_arg(ap, char *);
+	infotype = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	if (prof == NULL || !strlen(prof)) {
+		/* no info/away message */
+		char buf[1024];
+		sprintf(buf, _("%s has no info/away message."), info->sn);
+		do_error_dialog(buf, _("Gaim - Error"));
+		plugin_event(event_error, (void *)977, 0, 0);
+		return 1;
+	}
+
+	snprintf(buf, sizeof buf, _("Username : <B>%s</B>\n<BR>"
+				  "Warning Level : <B>%d %%</B>\n<BR>"
+				  "Online Since : <B>%s</B><BR>"
+				  "Idle Minutes : <B>%d</B>\n<BR><HR><BR>"
+				  "%s\n"),
+				  info->sn,
+				  info->warnlevel/10,
+				  asctime(localtime(&info->onlinesince)),
+				  info->idletime,
+				  infotype == AIM_GETINFO_GENERALINFO ? prof :
+ 				  away_subs(prof, gc->username));
+	g_show_info_text(away_subs(buf, gc->username));
+
+	return 1;
+}
+
+int gaim_parse_motd(struct aim_session_t *sess,
+		    struct command_rx_struct *command, ...) {
+	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);
+	msg = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(debug_buff, "MOTD: %s (%d)\n", msg, id);
+	debug_print(debug_buff);
+	sprintf(debug_buff, "Gaim %s / Libfaim %s\n",
+			VERSION, aim_getbuildstring());
+	debug_print(debug_buff);
+	if (id != 4)
+		do_error_dialog(_("Your connection may be lost."),
+				_("AOL error"));
+
+	if (gc->keepalive < 0)
+		update_keepalive(gc, gc->keepalive);
+
+	return 1;
+}
+
+int gaim_chatnav_info(struct aim_session_t *sess,
+		      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);
+
+	switch(type) {
+		case 0x0002: {
+			int maxrooms;
+			struct aim_chat_exchangeinfo *exchanges;
+			int exchangecount, i = 0;
+
+			maxrooms = (u_char)va_arg(ap, u_int);
+			exchangecount = va_arg(ap, int);
+			exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
+			va_end(ap);
+
+			debug_print("chat info: Chat Rights:\n");
+			sprintf(debug_buff, "chat info: \tMax Concurrent Rooms: %d\n", maxrooms);
+			debug_print(debug_buff);
+			sprintf(debug_buff, "chat info: \tExchange List: (%d total)\n", exchangecount);
+			debug_print(debug_buff);
+			while (i < exchangecount) {
+				sprintf(debug_buff, "chat info: \t\t%x: %s (%s/%s)\n",
+						exchanges[i].number,
+						exchanges[i].name,
+						exchanges[i].charset1,
+						exchanges[i].lang1);
+				debug_print(debug_buff);
+				i++;
+			}
+			if (gc->create_exchange) {
+				sprintf(debug_buff, "creating room %s\n",
+						gc->create_name);
+				debug_print(debug_buff);
+				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;
+		case 0x0008: {
+			char *fqcn, *name, *ck;
+			u_short instance, flags, maxmsglen, maxoccupancy, unknown;
+			unsigned char createperms;
+			unsigned long createtime;
+
+			fqcn = va_arg(ap, char *);
+			instance = (u_short)va_arg(ap, u_int);
+			flags = (u_short)va_arg(ap, u_int);
+			createtime = va_arg(ap, unsigned long);
+			maxmsglen = (u_short)va_arg(ap, u_int);
+			maxoccupancy = (u_short)va_arg(ap, u_int);
+			createperms = (unsigned char)va_arg(ap, int);
+			unknown = (u_short)va_arg(ap, u_int);
+			name = va_arg(ap, char *);
+			ck = va_arg(ap, char *);
+			va_end(ap);
+
+			sprintf(debug_buff, "created room: %s %d %d %lu %d %d %d %d %s %s\n", fqcn, instance, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
+			debug_print(debug_buff);
+			if (flags & 0x4) {
+				sprintf(debug_buff, "joining %s on exchange 5\n", name);
+				debug_print(debug_buff);
+				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(gc->oscar_sess, gc->oscar_conn, 4, ck);
+			}
+			}
+			break;
+		default:
+			va_end(ap);
+			sprintf(debug_buff, "chatnav info: unknown type (%04x)\n", type);
+			debug_print(debug_buff);
+			break;
+	}
+	return 1;
+}
+
+int gaim_chat_join(struct aim_session_t *sess,
+		   struct command_rx_struct *command, ...) {
+	va_list ap;
+	int count, i = 0;
+	struct aim_userinfo_s *info;
+	struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess);
+
+	GSList *bcs = g->buddy_chats;
+	struct conversation *b = NULL;
+
+	va_start(ap, command);
+	count = va_arg(ap, int);
+	info  = va_arg(ap, struct aim_userinfo_s *);
+	va_end(ap);
+
+	while(bcs) {
+		b = (struct conversation *)bcs->data;
+		if (!strcasecmp(b->name, (char *)command->conn->priv))
+			break;	
+		bcs = bcs->next;
+		b = NULL;
+	}
+	if (!b)
+		return 1;
+		
+	while (i < count)
+		add_chat_buddy(b, info[i++].sn);
+
+	return 1;
+}
+
+int gaim_chat_leave(struct aim_session_t *sess,
+		    struct command_rx_struct *command, ...) {
+	va_list ap;
+	int count, i = 0;
+	struct aim_userinfo_s *info;
+	struct gaim_connection *g = find_gaim_conn_by_aim_sess(sess);
+
+	GSList *bcs = g->buddy_chats;
+	struct conversation *b = NULL;
+
+	va_start(ap, command);
+	count = va_arg(ap, int);
+	info  = va_arg(ap, struct aim_userinfo_s *);
+	va_end(ap);
+
+	while(bcs) {
+		b = (struct conversation *)bcs->data;
+		if (!strcasecmp(b->name, (char *)command->conn->priv))
+			break;	
+		bcs = bcs->next;
+		b = NULL;
+	}
+	if (!b)
+		return 1;
+		
+	while (i < count)
+		remove_chat_buddy(b, info[i++].sn);
+
+	return 1;
+}
+
+int gaim_chat_info_update(struct aim_session_t *sess,
+			  struct command_rx_struct *command, ...) {
+	debug_print("inside chat_info_update\n");
+	return 1;
+}
+
+int gaim_chat_incoming_msg(struct aim_session_t *sess,
+			   struct command_rx_struct *command, ...) {
+	va_list ap;
+	struct aim_userinfo_s *info;
+	char *msg;
+	struct gaim_connection *gc = find_gaim_conn_by_aim_sess(sess);
+
+	GSList *bcs = gc->buddy_chats;
+	struct conversation *b = NULL;
+
+	va_start(ap, command);
+	info = va_arg(ap, struct aim_userinfo_s *);
+	msg  = va_arg(ap, char *);
+
+	while(bcs) {
+		b = (struct conversation *)bcs->data;
+		if (!strcasecmp(b->name, (char *)command->conn->priv))
+			break;
+		bcs = bcs->next;
+		b = NULL;
+	}
+	if (!b)
+		return 0;
+
+	serv_got_chat_in(gc, b->id, info->sn, 0, msg);
+
+	return 1;
+}
+
+ /*
+  * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option.
+  */
+int gaim_parse_msgack(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	u_short type;
+	char *sn = NULL;
+
+	va_start(ap, command);
+	type = (u_short)va_arg(ap, u_int);
+	sn = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(debug_buff, "Sent message to %s.\n", sn);
+	debug_print(debug_buff);
+
+	return 1;
+}
+
+int gaim_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	unsigned long newrate;
+
+	va_start(ap, command); 
+	newrate = va_arg(ap, unsigned long);
+	va_end(ap);
+
+	sprintf(debug_buff, "ratechange: %lu\n", newrate);
+	debug_print(debug_buff);
+
+	return 1;
+}
+
+int gaim_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *sn;
+
+	va_start(ap, command);
+	sn = va_arg(ap, char *);
+	va_end(ap);
+
+	serv_got_eviled(sn, 0);
+
+	return 1;
+}
+
+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, gc->user_info, NULL, gaim_caps);
+		aim_bos_reqbuddyrights(sess, command->conn);
+
+		if (mainwindow)
+			gtk_widget_hide(mainwindow);
+		show_buddy_list();
+
+#ifdef USE_APPLET
+		if (general_options & OPT_GEN_APP_BUDDY_SHOW) {
+			refresh_buddy_window();
+			createOnlinePopup();
+			applet_buddy_show = TRUE;
+		} else {
+			gtk_widget_hide(blist);
+			applet_buddy_show = FALSE;
+		}
+		set_user_state(online);
+#else
+		refresh_buddy_window();
+#endif
+
+		serv_finish_login(gc);
+		gaim_setup(gc);
+
+		if (bud_list_cache_exists(gc))
+			do_import(NULL, gc);
+
+		debug_print("buddy list loaded\n");
+
+		setup_buddy_chats();
+
+		aim_addicbmparam(sess, command->conn);
+		aim_bos_reqicbmparaminfo(sess, command->conn);
+
+		aim_bos_reqrights(sess, command->conn);
+		aim_bos_setgroupperm(sess, command->conn, AIM_FLAG_ALLUSERS);
+		aim_bos_setprivacyflags(sess, command->conn, AIM_PRIVFLAGS_ALLOWIDLE |
+							     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
+
+		break;
+	default:
+		sprintf(debug_buff, "got rate response for unhandled connection type %04x\n",
+				command->conn->type);
+		debug_print(debug_buff);
+		break;
+	}
+
+	return 1;
+}
+
+int gaim_reportinterval(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	if (command->data) {
+		sprintf(debug_buff, "minimum report interval: %d (seconds?)\n", aimutil_get16(command->data+10));
+		debug_print(debug_buff);
+	} else
+		debug_print("NULL minimum report interval!\n");
+	return 1;
+}
+
+int gaim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	u_short maxbuddies, maxwatchers;
+
+	va_start(ap, command);
+	maxbuddies = (u_short)va_arg(ap, u_int);
+	maxwatchers = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	sprintf(debug_buff, "buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
+	debug_print(debug_buff);
+
+	return 1;
+}
+
+int gaim_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	u_short maxpermits, maxdenies;
+	va_list ap;
+
+	va_start(ap, command);
+	maxpermits = (u_short)va_arg(ap, u_int);
+	maxdenies = (u_short)va_arg(ap, u_int);
+	va_end(ap);
+
+	sprintf(debug_buff, "BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
+	debug_print(debug_buff);
+
+	aim_bos_clientready(sess, command->conn);
+
+	aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_CHATNAV);
+
+	return 1;
+}
+
+int gaim_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	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 *);
+	sn = va_arg(ap, char *);
+	msg = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(debug_buff, "Got DirectIM message from %s\n", sn);
+	debug_print(debug_buff);
+
+	serv_got_im(gc, sn, msg, 0);
+
+	return 1;
+}
+
+/* this is such a f*cked up function */
+int gaim_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	struct aim_directim_priv *priv;
+	struct aim_conn_t *newconn;
+	struct conversation *cnv;
+	int watcher;
+
+	va_start(ap, command);
+	newconn = va_arg(ap, struct aim_conn_t *);
+	va_end(ap);
+
+	priv = (struct aim_directim_priv *)newconn->priv;
+
+	sprintf(debug_buff, "DirectIM: initiate success to %s\n", priv->sn);
+	debug_print(debug_buff);
+
+	cnv = find_conversation(priv->sn);
+	gdk_input_remove(cnv->watcher);
+	watcher = gdk_input_add(newconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+					oscar_callback, newconn);
+	make_direct(cnv, TRUE, newconn, watcher);
+
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_directim_incoming, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_directim_typing, 0);
+
+	return 1;
+}
+
+int gaim_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...) {
+	va_list ap;
+	char *sn;
+
+	va_start(ap, command);
+	sn = va_arg(ap, char *);
+	va_end(ap);
+
+	/* I had to leave this. It's just too funny. It reminds me of my sister. */
+	sprintf(debug_buff, "ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn);
+	debug_print(debug_buff);
+
+	return 1;
+}
+
+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(gc->oscar_sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, gaim_directim_initiate, 0);
+}
+
+static void oscar_keepalive(struct gaim_connection *gc) {
+	aim_flap_nop(gc->oscar_sess, gc->oscar_conn);
+}
+
+static char *oscar_name() {
+	return "Oscar";
+}
+
+char *name() {
+	return "Oscar";
+}
+
+char *description() {
+	return "Allows gaim to use the Oscar protocol";
+}
+
+static void oscar_send_im(struct gaim_connection *gc, char *name, char *message, int away) {
+	struct conversation *cnv = find_conversation(name);
+	if (cnv && cnv->is_direct) {
+		debug_printf("Sending DirectIM to %s\n", name);
+		aim_send_im_direct(gc->oscar_sess, cnv->conn, message);
+	} else {
+		if (away)
+			aim_send_im(gc->oscar_sess, gc->oscar_conn, name, AIM_IMFLAGS_AWAY, message);
+		else
+			aim_send_im(gc->oscar_sess, gc->oscar_conn, name, AIM_IMFLAGS_ACK, message);
+	}
+}
+
+static void oscar_get_info(struct gaim_connection *g, char *name) {
+	aim_getinfo(g->oscar_sess, g->oscar_conn, name, AIM_GETINFO_GENERALINFO);
+}
+
+static void oscar_get_away_msg(struct gaim_connection *g, char *name) {
+	aim_getinfo(g->oscar_sess, g->oscar_conn, name, AIM_GETINFO_AWAYMESSAGE);
+}
+
+static void oscar_set_dir(struct gaim_connection *g, char *first, char *middle, char *last,
+			  char *maiden, char *city, char *state, char *country, int web) {
+	/* FIXME : some of these things are wrong, but i'm lazy */
+	aim_setdirectoryinfo(g->oscar_sess, g->oscar_conn, first, middle, last,
+				maiden, NULL, NULL, city, state, NULL, 0, web);
+}
+
+
+static void oscar_set_idle(struct gaim_connection *g, int time) {
+	aim_bos_setidle(g->oscar_sess, g->oscar_conn, time);
+}
+
+static void oscar_set_info(struct gaim_connection *g, char *info) {
+	if (awaymessage)
+		aim_bos_setprofile(g->oscar_sess, g->oscar_conn, info,
+					awaymessage->message, gaim_caps);
+	else
+		aim_bos_setprofile(g->oscar_sess, g->oscar_conn, info,
+					NULL, gaim_caps);
+}
+
+static void oscar_set_away(struct gaim_connection *g, char *message) {
+	aim_bos_setprofile(g->oscar_sess, g->oscar_conn, g->user_info, message, gaim_caps);
+}
+
+static void oscar_warn(struct gaim_connection *g, char *name, int anon) {
+	aim_send_warning(g->oscar_sess, g->oscar_conn, name, anon);
+}
+
+static void oscar_dir_search(struct gaim_connection *g, char *first, char *middle, char *last,
+			     char *maiden, char *city, char *state, char *country, char *email) {
+	if (strlen(email))
+		aim_usersearch_address(g->oscar_sess, g->oscar_conn, email);
+}
+
+static void oscar_add_buddy(struct gaim_connection *g, char *name) {
+	aim_add_buddy(g->oscar_sess, g->oscar_conn, name);
+}
+
+static void oscar_add_buddies(struct gaim_connection *g, GList *buddies) {
+	char buf[MSG_LEN];
+	int n = 0;
+	while (buddies) {
+		if (n > MSG_LEN - 18) {
+			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(g->oscar_sess, g->oscar_conn, buf);
+}
+
+static void oscar_remove_buddy(struct gaim_connection *g, char *name) {
+	aim_remove_buddy(g->oscar_sess, g->oscar_conn, name);
+}
+
+static void oscar_join_chat(struct gaim_connection *g, int exchange, char *name) {
+	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(g->oscar_sess, AIM_CONN_TYPE_CHATNAV))) {
+		debug_print("chatnav exists, creating room\n");
+		aim_chatnav_createroom(g->oscar_sess, cur, name, exchange);
+	} else {
+		/* this gets tricky */
+		debug_print("chatnav does not exist, opening chatnav\n");
+		g->create_exchange = exchange;
+		g->create_name = g_strdup(name);
+		aim_bos_reqservice(g->oscar_sess, g->oscar_conn, AIM_CONN_TYPE_CHATNAV);
+	}
+}
+
+static void oscar_chat_invite(struct gaim_connection *g, int id, char *message, char *name) {
+	GSList *bcs = g->buddy_chats;
+	struct conversation *b = NULL;
+
+	while (bcs) {
+		b = (struct conversation *)bcs->data;
+		if (id == b->id)
+			break;
+		bcs = bcs->next;
+		b = NULL;
+	}
+
+	if (!b)
+		return;
+
+	aim_chat_invite(g->oscar_sess, g->oscar_conn, name,
+			message ? message : "", 0x4, b->name, 0x0);
+}
+
+static void oscar_chat_leave(struct gaim_connection *g, int id) {
+	GSList *bcs = g->buddy_chats;
+	struct conversation *b = NULL;
+	struct chat_connection *c = NULL;
+	int count = 0;
+
+	while (bcs) {
+		count++;
+		b = (struct conversation *)bcs->data;
+		if (id == b->id)
+			break;
+		bcs = bcs->next;
+		b = NULL;
+	}
+
+	if (!b)
+		return;
+
+	sprintf(debug_buff, "Attempting to leave room %s (currently in %d rooms)\n",
+				b->name, count);
+	debug_print(debug_buff);
+	
+	c = find_oscar_chat(g, b->name);
+	if (c != NULL) {
+		g->oscar_chats = g_slist_remove(g->oscar_chats, c);
+		gdk_input_remove(c->inpa);
+		if (g && g->oscar_sess)
+			aim_conn_kill(g->oscar_sess, &c->conn);
+		g_free(c->name);
+		g_free(c);
+	}
+	/* we do this because with Oscar it doesn't tell us we left */
+	serv_got_chat_left(g, b->id);
+}
+
+static void oscar_chat_whisper(struct gaim_connection *g, int id, char *who, char *message) {
+	do_error_dialog("Sorry, Oscar doesn't whisper. Send an IM. (The last message was not received.)",
+			"Gaim - Chat");
+}
+
+static void oscar_chat_send(struct gaim_connection *g, int id, char *message) {
+	struct aim_conn_t *cn; 
+	GSList *bcs = g->buddy_chats;
+	struct conversation *b = NULL;
+
+	while (bcs) {
+		b = (struct conversation *)bcs->data;
+		if (id == b->id)
+			break;
+		bcs = bcs->next;
+		b = NULL;
+	}
+	if (!b)
+		return;
+
+	cn = aim_chat_getconn(g->oscar_sess, b->name);
+	aim_chat_send_im(g->oscar_sess, cn, message);
+}
+
+struct prpl *oscar_init() {
+	struct prpl *ret = g_new0(struct prpl, 1);
+
+	ret->protocol = PROTO_OSCAR;
+	ret->name = oscar_name;
+	ret->login = oscar_login;
+	ret->close = oscar_close;
+	ret->send_im = oscar_send_im;
+	ret->set_info = oscar_set_info;
+	ret->get_info = oscar_get_info;
+	ret->set_away = oscar_set_away;
+	ret->get_away_msg = oscar_get_away_msg;
+	ret->set_dir = oscar_set_dir;
+	ret->get_dir = NULL; /* Oscar really doesn't have this */
+	ret->dir_search = oscar_dir_search;
+	ret->set_idle = oscar_set_idle;
+	ret->change_passwd = NULL; /* Oscar doesn't have this either */
+	ret->add_buddy = oscar_add_buddy;
+	ret->add_buddies = oscar_add_buddies;
+	ret->remove_buddy = oscar_remove_buddy;
+	ret->add_permit = NULL; /* Oscar's permit/deny stuff is messed up */
+	ret->add_deny = NULL; /* at least, i can't figure it out :-P */
+	ret->warn = oscar_warn;
+	ret->accept_chat = NULL; /* oscar doesn't have accept, it just joins */
+	ret->join_chat = oscar_join_chat;
+	ret->chat_invite = oscar_chat_invite;
+	ret->chat_leave = oscar_chat_leave;
+	ret->chat_whisper = oscar_chat_whisper;
+	ret->chat_send = oscar_chat_send;
+	ret->keepalive = oscar_keepalive;
+	
+	return ret;
+}
+
+int gaim_plugin_init(void *handle) {
+	protocols = g_slist_append(protocols, oscar_init());
+	return 0;
+}