changeset 8808:bbd8cdaf0ad5

[gaim-migrate @ 9570] A massive patch by shx to reorganize MSN some more and add command processor support. This allows us to do cool things like produce more detailed error messages. For example, the Invalid Username dialog now shows the username of the invalid user. I modified the aforementioned dialog so it'll look a little nicer looking, and also mention the account this happened on. It also removes the user from your blist, as there's no point to keeping the user on there. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Sun, 25 Apr 2004 22:02:06 +0000
parents 482fc53c969d
children fde4101fa183
files ChangeLog src/protocols/msn/Makefile.am src/protocols/msn/Makefile.mingw src/protocols/msn/error.h src/protocols/msn/msn.c src/protocols/msn/nexus.c src/protocols/msn/notification.c src/protocols/msn/notification.h src/protocols/msn/page.h src/protocols/msn/servconn.c src/protocols/msn/servconn.h src/protocols/msn/session.c src/protocols/msn/session.h src/protocols/msn/switchboard.c src/protocols/msn/switchboard.h
diffstat 15 files changed, 1322 insertions(+), 1577 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Apr 25 17:01:38 2004 +0000
+++ b/ChangeLog	Sun Apr 25 22:02:06 2004 +0000
@@ -6,6 +6,8 @@
 	  the autoreconnect plugin (Christopher (siege) O'Brien)
 	* Added support for dragging buddies from the buddy list into the
 	  Add Buddy Pounce dialog.
+	* Massive rewrite of MSN support, which should fix a number of issues
+	  and make errors easier to interpret. (Felipe Contreras)
 
 	Bug Fixes:
 	* Gadu-Gadu updates (Andrew Wellington)
--- a/src/protocols/msn/Makefile.am	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/Makefile.am	Sun Apr 25 22:02:06 2004 +0000
@@ -4,10 +4,16 @@
 pkgdir = $(libdir)/gaim
 
 MSNSOURCES = \
+	cmdproc.c \
+	cmdproc.h \
+	command.c \
+	command.h \
 	error.c \
 	error.h \
 	group.c \
 	group.h \
+	history.c \
+	history.h \
 	httpmethod.c \
 	httpmethod.h \
 	msg.c \
@@ -32,6 +38,10 @@
 	state.h \
 	switchboard.c \
 	switchboard.h \
+	table.c \
+	table.h \
+	transaction.c \
+	transaction.h \
 	user.c \
 	user.h \
 	utils.c \
--- a/src/protocols/msn/Makefile.mingw	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/Makefile.mingw	Sun Apr 25 22:02:06 2004 +0000
@@ -68,8 +68,11 @@
 ##  SOURCES, OBJECTS
 ##
 
-C_SRC =			error.c \
+C_SRC =			cmdproc.c \
+			command.c \
+			error.c \
 			group.c \
+			history.c \
 			httpmethod.c \
 			msg.c \
 			msn.c \
@@ -82,6 +85,8 @@
 			session.c \
 			state.c \
 			switchboard.c \
+			table.c \
+			transaction.c \
 			user.c \
 			utils.c
 
--- a/src/protocols/msn/error.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/error.h	Sun Apr 25 22:02:06 2004 +0000
@@ -22,6 +22,18 @@
 #ifndef _MSN_ERROR_H_
 #define _MSN_ERROR_H_
 
+typedef enum
+{
+	MSN_ERROR_NONE,
+	MSN_ERROR_MISC,
+	MSN_ERROR_CONNECT,
+	MSN_ERROR_WRITE,
+	MSN_ERROR_READ,
+	MSN_ERROR_SIGNOTHER,
+	MSN_ERROR_SERVDOWN
+
+} MsnErrorType;
+
 #include "session.h"
 
 /**
--- a/src/protocols/msn/msn.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/msn.c	Sun Apr 25 22:02:06 2004 +0000
@@ -33,6 +33,9 @@
 #include "multi.h"
 #include "util.h"
 
+#include "notification.h"
+#include "switchboard.h"
+
 #define BUDDY_ALIAS_MAXLEN 387
 
 static GaimPlugin *my_protocol = NULL;
@@ -56,11 +59,15 @@
 static void
 msn_act_id(GaimConnection *gc, const char *entry)
 {
-	MsnSession *session = gc->proto_data;
-	GaimAccount *account = gaim_connection_get_account(gc);
-	char outparams[MSN_BUF_LEN];
+	MsnCmdProc *cmdproc;
+	MsnSession *session;
+	GaimAccount *account;
 	char *alias;
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+	account = gaim_connection_get_account(gc);
+
 	if (entry == NULL || *entry == '\0')
 		alias = g_strdup("");
 	else
@@ -73,36 +80,30 @@
 		return;
 	}
 
-	g_snprintf(outparams, sizeof(outparams), "%s %s",
-			   gaim_account_get_username(account), gaim_url_encode(alias));
+	msn_cmdproc_send(cmdproc, "REA", "%s %s",
+					 gaim_account_get_username(account),
+					 gaim_url_encode(alias));
 
 	g_free(alias);
-
-	if (!msn_servconn_send_command(session->notification_conn,
-								   "REA", outparams)) {
-
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
 }
 
 static void
 msn_set_prp(GaimConnection *gc, const char *type, const char *entry)
 {
-	MsnSession *session = gc->proto_data;
-	char outparams[MSN_BUF_LEN];
+	MsnCmdProc *cmdproc;
+	MsnSession *session;
+
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 
 	if (entry == NULL || *entry == '\0')
-		g_snprintf(outparams, sizeof(outparams), "%s  ", type);
+	{
+		msn_cmdproc_send(cmdproc, "PRP", "%s  ", type);
+	}
 	else
-		g_snprintf(outparams, sizeof(outparams), "%s %s", type,
-				   gaim_url_encode(entry));
-
-	if (!msn_servconn_send_command(session->notification_conn,
-								   "PRP", outparams)) {
-
-		gaim_connection_error(gc, _("Write error"));
-		return;
+	{
+		msn_cmdproc_send(cmdproc, "PRP", "%s %s", type,
+						 gaim_url_encode(entry));
 	}
 }
 
@@ -137,44 +138,39 @@
 }
 
 static void
-send_to_mobile_cb(MsnMobileData *data, const char *entry)
+send_to_mobile(GaimConnection *gc, const char *who, const char *entry)
 {
 	MsnSession *session;
 	MsnServConn *servconn;
+	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
 	MsnPage *page;
-	char *buf;
 	char *payload;
-	size_t len;
 	size_t payload_len;
 
-	session = data->gc->proto_data;
+	session = gc->proto_data;
 	servconn = session->notification_conn;
+	cmdproc = servconn->cmdproc;	
 
 	page = msn_page_new();
 	msn_page_set_body(page, entry);
-	buf = g_strdup_printf("PGD %d %s 1 %d\r\n", ++session->trId,
-						  data->passport, page->size);
 
-	len = strlen(buf);
+	trans = msn_transaction_new("PGD", "%s 1 %d", who, page->size);
 
 	payload = msn_page_gen_payload(page, &payload_len);
 
-	if (payload != NULL)
-	{
-		buf = g_realloc(buf, len + payload_len + 1);
-		memcpy(buf + len, payload, payload_len);
-		len += payload_len;
-		buf[len] = 0;
-	}
+	msn_transaction_set_payload(trans, payload, payload_len);
 
 	msn_page_destroy(page);
 
-	if (!msn_servconn_write(servconn, buf, len))
-	{
-		gaim_connection_error(data->gc, _("Write error"));
-	}
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
 
-	g_free(buf);
+static void
+send_to_mobile_cb(MsnMobileData *data, const char *entry)
+{
+	send_to_mobile(data->gc, data->passport, entry);
+	g_free(data);
 }
 
 static void
@@ -246,9 +242,10 @@
 show_send_to_mobile_cb(GaimConnection *gc, const char *passport)
 {
 	MsnUser *user;
-	MsnSession *session = gc->proto_data;
+	MsnSession *session;
 	MsnMobileData *data;
 
+	session = gc->proto_data;
 	user = msn_users_find_with_passport(session->users, passport);
 
 	data = g_new0(MsnMobileData, 1);
@@ -265,16 +262,18 @@
 static void
 initiate_chat_cb(GaimConnection *gc, const char *passport)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
+	GaimAccount *account;
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnSwitchBoard *swboard;
 	MsnUser *user;
 
-	if ((swboard = msn_session_open_switchboard(session)) == NULL) {
-		gaim_connection_error(gc, _("Write error"));
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 
+	if ((swboard = msn_session_open_switchboard(session)) == NULL)
 		return;
-	}
 
 	user = msn_user_new(session, passport, NULL);
 
@@ -422,20 +421,23 @@
 static GList *
 msn_buddy_menu(GaimConnection *gc, const char *who)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
+	GaimAccount *account;
 	MsnUser *user;
 	struct proto_buddy_menu *pbm;
 	GaimBuddy *b;
 	GList *m = NULL;
 
-	b = gaim_find_buddy(gc->account, who);
+	account = gaim_connection_get_account(gc);
+	b = gaim_find_buddy(account, who);
 
 	g_return_val_if_fail(b != NULL, NULL);
 
 	user = b->proto_data;
 
-	if (user != NULL) {
-		if (user->mobile) {
+	if (user != NULL)
+	{
+		if (user->mobile)
+		{
 			pbm = g_new0(struct proto_buddy_menu, 1);
 			pbm->label    = _("Send to Mobile");
 			pbm->callback = show_send_to_mobile_cb;
@@ -444,7 +446,8 @@
 		}
 	}
 
-	if (g_ascii_strcasecmp(who, gaim_account_get_username(account))) {
+	if (g_ascii_strcasecmp(who, gaim_account_get_username(account)))
+	{
 		pbm = g_new0(struct proto_buddy_menu, 1);
 		pbm->label    = _("Initiate Chat");
 		pbm->callback = initiate_chat_cb;
@@ -510,7 +513,8 @@
 	if (strcmp(username, gaim_account_get_username(account)))
 		gaim_account_set_username(account, username);
 
-	if (!msn_session_connect(session)) {
+	if (!msn_session_connect(session))
+	{
 		gaim_connection_error(gc, _("Unable to connect."));
 
 		return;
@@ -537,13 +541,16 @@
 msn_send_im(GaimConnection *gc, const char *who, const char *message,
 			GaimConvImFlags flags)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
+	GaimAccount *account;
+	MsnSession *session;
 	MsnSwitchBoard *swboard;
 
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
 	swboard = msn_session_find_switch_with_passport(session, who);
 
-	if (g_ascii_strcasecmp(who, gaim_account_get_username(account))) {
+	if (g_ascii_strcasecmp(who, gaim_account_get_username(account)))
+	{
 		MsnMessage *msg;
 		MsnUser *user;
 		char *msgformat;
@@ -560,16 +567,16 @@
 		g_free(msgformat);
 		g_free(msgtext);
 
-		if (swboard != NULL) {
-			if (!msn_switchboard_send_msg(swboard, msg))
-				msn_switchboard_destroy(swboard);
+		if (swboard != NULL)
+		{
+			msn_switchboard_send_msg(swboard, msg);
 		}
-		else {
-			if ((swboard = msn_session_open_switchboard(session)) == NULL) {
+		else
+		{
+			if ((swboard = msn_session_open_switchboard(session)) == NULL)
+			{
 				msn_message_destroy(msg);
 
-				gaim_connection_error(gc, _("Write error"));
-
 				return 1;
 			}
 
@@ -580,7 +587,8 @@
 		msn_user_destroy(user);
 		msn_message_destroy(msg);
 	}
-	else {
+	else
+	{
 		/*
 		 * In MSN, you can't send messages to yourself, so
 		 * we'll fake like we received it ;)
@@ -598,16 +606,20 @@
 static int
 msn_send_typing(GaimConnection *gc, const char *who, int typing)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
+	GaimAccount *account;
+	MsnSession *session;
 	MsnSwitchBoard *swboard;
 	MsnMessage *msg;
 	MsnUser *user;
 
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
+
 	if (!typing)
 		return 0;
 
-	if (!g_ascii_strcasecmp(who, gaim_account_get_username(account))) {
+	if (!g_ascii_strcasecmp(who, gaim_account_get_username(account)))
+	{
 		/* We'll just fake it, since we're sending to ourself. */
 		serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, GAIM_TYPING);
 
@@ -630,8 +642,7 @@
 	msn_message_set_attr(msg, "User-Agent", NULL);
 	msn_message_set_body(msg, "\r\n");
 
-	if (!msn_switchboard_send_msg(swboard, msg))
-		msn_switchboard_destroy(swboard);
+	msn_switchboard_send_msg(swboard, msg);
 
 	msn_user_destroy(user);
 
@@ -641,9 +652,11 @@
 static void
 msn_set_away(GaimConnection *gc, const char *state, const char *msg)
 {
-	MsnSession *session = gc->proto_data;
+	MsnSession *session;
 	const char *away;
 
+	session = gc->proto_data;
+
 	if (gc->away != NULL)
 	{
 		g_free(gc->away);
@@ -689,7 +702,9 @@
 static void
 msn_set_idle(GaimConnection *gc, int idle)
 {
-	MsnSession *session = gc->proto_data;
+	MsnSession *session;
+
+	session = gc->proto_data;
 
 	if (gc->away != NULL)
 		return;
@@ -700,20 +715,24 @@
 static void
 msn_add_buddy(GaimConnection *gc, const char *name, GaimGroup *group)
 {
-	MsnSession *session = gc->proto_data;
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnGroup *msn_group = NULL;
 	const char *who;
-	char outparams[MSN_BUF_LEN];
 	GSList *l;
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 	who = msn_normalize(gc->account, name);
 
-	if (strchr(who, ' ')) {
+	if (strchr(who, ' '))
+	{
 		/* This is a broken blist entry. */
 		return;
 	}
 
-	for (l = session->lists.forward; l != NULL; l = l->next) {
+	for (l = session->lists.forward; l != NULL; l = l->next)
+	{
 		MsnUser *user = l->data;
 
 		if (!gaim_utf8_strcasecmp(who, msn_user_get_passport(user)))
@@ -728,30 +747,25 @@
 
 	if (msn_group != NULL)
 	{
-		g_snprintf(outparams, sizeof(outparams),
-				   "FL %s %s %d", who, who, msn_group_get_id(msn_group));
+		msn_cmdproc_send(cmdproc, "ADD", "FL %s %s %d", who, who,
+						 msn_group_get_id(msn_group));
 	}
 	else
 	{
-		g_snprintf(outparams, sizeof(outparams),
-				   "FL %s %s", who, who);
-	}
-
-	if (!msn_servconn_send_command(session->notification_conn,
-								   "ADD", outparams))
-	{
-		gaim_connection_error(gc, _("Write error"));
-		return;
+		msn_cmdproc_send(cmdproc, "ADD", "FL %s %s", who, who);
 	}
 }
 
 static void
 msn_rem_buddy(GaimConnection *gc, const char *who, const char *group_name)
 {
-	MsnSession *session = gc->proto_data;
-	char outparams[MSN_BUF_LEN];
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnGroup *group;
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
 	if (strchr(who, ' '))
 	{
 		/* This is a broken blist entry. */
@@ -761,30 +775,33 @@
 	group = msn_groups_find_with_name(session->groups, group_name);
 
 	if (group == NULL)
-		g_snprintf(outparams, sizeof(outparams), "FL %s", who);
+	{
+		msn_cmdproc_send(cmdproc, "REM", "FL %s", who);
+	}
 	else
-		g_snprintf(outparams, sizeof(outparams), "FL %s %d", who,
-				   msn_group_get_id(group));
-
-	if (!msn_servconn_send_command(session->notification_conn,
-								   "REM", outparams)) {
-
-		gaim_connection_error(gc, _("Write error"));
-		return;
+	{
+		msn_cmdproc_send(cmdproc, "REM", "FL %s %d", who,
+						 msn_group_get_id(group));
 	}
 }
 
 static void
 msn_add_permit(GaimConnection *gc, const char *who)
 {
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
+
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 
-	if (!strchr(who, '@')) {
+	if (!strchr(who, '@'))
+	{
+		char buf[MSN_BUF_LEN];
+
 		g_snprintf(buf, sizeof(buf),
-			   _("An MSN screen name must be in the form \"user@server.com\". "
-			     "Perhaps you meant %s@hotmail.com. No changes were made "
-				 "to your allow list."), who);
+				   _("An MSN screen name must be in the form \"user@server.com\". "
+					 "Perhaps you meant %s@hotmail.com. No changes were made "
+					 "to your allow list."), who);
 
 		gaim_notify_error(gc, NULL, _("Invalid MSN screen name"), buf);
 		gaim_privacy_permit_remove(gc->account, who, TRUE);
@@ -792,39 +809,36 @@
 		return;
 	}
 
-	if (g_slist_find_custom(gc->account->deny, who, (GCompareFunc)strcmp)) {
-		gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from BL to AL\n", who);
+	if (g_slist_find_custom(gc->account->deny, who, (GCompareFunc)strcmp))
+	{
+		gaim_debug_info("msn", "Moving %s from BL to AL\n", who);
 		gaim_privacy_deny_remove(gc->account, who, TRUE);
 
-		g_snprintf(buf, sizeof(buf), "BL %s", who);
+		msn_cmdproc_send(cmdproc, "REM", "BL %s", who);
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "REM", buf)) {
-
-			gaim_connection_error(gc, _("Write error"));
+		if (cmdproc->error)
 			return;
-		}
 	}
 
-	g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
-
-	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
+	msn_cmdproc_send(cmdproc, "ADD", "AL %s %s", who, who);
 }
 
 static void
 msn_add_deny(GaimConnection *gc, const char *who)
 {
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	MsnCmdProc *cmdproc;
+	MsnSession *session;
+
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 
-	if (!strchr(who, '@')) {
+	if (!strchr(who, '@'))
+	{
+		char buf[MSN_BUF_LEN];
 		g_snprintf(buf, sizeof(buf),
-			   _("An MSN screen name must be in the form \"user@server.com\". "
-			     "Perhaps you meant %s@hotmail.com. No changes were made "
-				 "to your block list."), who);
+				   _("An MSN screen name must be in the form \"user@server.com\". "
+					 "Perhaps you meant %s@hotmail.com. No changes were made "
+					 "to your block list."), who);
 
 		gaim_notify_error(gc, NULL, _("Invalid MSN screen name"), buf);
 
@@ -833,96 +847,82 @@
 		return;
 	}
 
-	if (g_slist_find_custom(gc->account->permit, who, (GCompareFunc)strcmp)) {
+	if (g_slist_find_custom(gc->account->permit, who, (GCompareFunc)strcmp))
+	{
 		gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from AL to BL\n", who);
 		gaim_privacy_permit_remove(gc->account, who, TRUE);
 
-		g_snprintf(buf, sizeof(buf), "AL %s", who);
+		msn_cmdproc_send(cmdproc, "REM", "AL %s", who);
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "REM", buf)) {
-
-			gaim_connection_error(gc, _("Write error"));
+		if (cmdproc->error)
 			return;
-		}
 	}
 
-	g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
-
-	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
+	msn_cmdproc_send(cmdproc, "ADD", "BL %s %s", who, who);
 }
 
 static void
 msn_rem_permit(GaimConnection *gc, const char *who)
 {
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-
-	g_snprintf(buf, sizeof(buf), "AL %s", who);
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 
-	if (!msn_servconn_send_command(session->notification_conn, "REM", buf)) {
-		gaim_connection_error(gc, _("Write error"));
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
+	msn_cmdproc_send(cmdproc, "REM", "AL %s", who);
+
+	if (cmdproc->error)
 		return;
-	}
 
 	gaim_privacy_deny_add(gc->account, who, TRUE);
 
-	g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
-
-	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
+	msn_cmdproc_send(cmdproc, "ADD", "BL %s %s", who, who);
 }
 
 static void
 msn_rem_deny(GaimConnection *gc, const char *who)
 {
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-
-	g_snprintf(buf, sizeof(buf), "BL %s", who);
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 
-	if (!msn_servconn_send_command(session->notification_conn, "REM", buf))
-	{
-		gaim_connection_error(gc, _("Write error"));
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
+	msn_cmdproc_send(cmdproc, "REM", "BL %s", who);
+
+	if (cmdproc->error)
 		return;
-	}
 
 	gaim_privacy_permit_add(gc->account, who, TRUE);
 
-	g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
-
-	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf))
-	{
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
+	msn_cmdproc_send(cmdproc, "ADD", "AL %s %s", who, who);
 }
 
 static void
 msn_set_permit_deny(GaimConnection *gc)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	GaimAccount *account;
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	GSList *s, *t = NULL;
 
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
 	if (account->perm_deny == GAIM_PRIVACY_ALLOW_ALL ||
-		account->perm_deny == GAIM_PRIVACY_DENY_USERS) {
-
-		strcpy(buf, "AL");
+		account->perm_deny == GAIM_PRIVACY_DENY_USERS)
+	{
+		msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
 	}
 	else
-		strcpy(buf, "BL");
+	{
+		msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
+	}
 
-	if (!msn_servconn_send_command(session->notification_conn, "BLP", buf)) {
-		gaim_connection_error(gc, _("Write error"));
+	if (cmdproc->error)
 		return;
-	}
 
 	/*
 	 * This is safe because we'll always come here after we've gotten
@@ -931,15 +931,15 @@
 	 * there's no sense in going through them all.
 	 */
 	if (g_slist_length(gc->account->permit) ==
-		g_slist_length(session->lists.allow)) {
-
+		g_slist_length(session->lists.allow))
+	{
 		g_slist_free(session->lists.allow);
 		session->lists.allow = NULL;
 	}
 
 	if (g_slist_length(gc->account->deny) ==
-		g_slist_length(session->lists.block)) {
-
+		g_slist_length(session->lists.block))
+	{
 		g_slist_free(session->lists.block);
 		session->lists.block = NULL;
 	}
@@ -947,32 +947,31 @@
 	if (session->lists.allow == NULL && session->lists.block == NULL)
 		return;
 
-	if (session->lists.allow != NULL) {
-
+	if (session->lists.allow != NULL)
+	{
 		for (s = g_slist_nth(gc->account->permit,
 							 g_slist_length(session->lists.allow));
 			 s != NULL;
-			 s = s->next) {
-
+			 s = s->next)
+		{
 			char *who = s->data;
 
-			if (!strchr(who, '@')) {
+			if (!strchr(who, '@'))
+			{
 				t = g_slist_append(t, who);
 				continue;
 			}
 
-			if (g_slist_find(session->lists.block, who)) {
+			if (g_slist_find(session->lists.block, who))
+			{
 				t = g_slist_append(t, who);
 				continue;
 			}
 
-			g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
+			msn_cmdproc_send(cmdproc, "ADD", "AL %s %s", who, who);
 
-			if (!msn_servconn_send_command(session->notification_conn,
-										   "ADD", buf)) {
-				gaim_connection_error(gc, _("Write error"));
+			if (cmdproc->error)
 				return;
-			}
 		}
 
 		for (; t != NULL; t = t->next)
@@ -986,31 +985,31 @@
 		session->lists.allow = NULL;
 	}
 
-	if (session->lists.block) {
+	if (session->lists.block)
+	{
 		for (s = g_slist_nth(gc->account->deny,
 							 g_slist_length(session->lists.block));
 			 s != NULL;
-			 s = s->next) {
-
+			 s = s->next)
+		{
 			char *who = s->data;
 
-			if (!strchr(who, '@')) {
+			if (!strchr(who, '@'))
+			{
 				t = g_slist_append(t, who);
 				continue;
 			}
 
-			if (g_slist_find(session->lists.block, who)) {
+			if (g_slist_find(session->lists.block, who))
+			{
 				t = g_slist_append(t, who);
 				continue;
 			}
 
-			g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
+			msn_cmdproc_send(cmdproc, "ADD", "BL %s %s", who, who);
 
-			if (!msn_servconn_send_command(session->notification_conn,
-										   "ADD", buf)) {
-				gaim_connection_error(gc, _("Write error"));
+			if (cmdproc->error)
 				return;
-			}
 		}
 
 		for (; t != NULL; t = t->next)
@@ -1028,32 +1027,37 @@
 msn_chat_invite(GaimConnection *gc, int id, const char *msg,
 				const char *who)
 {
-	MsnSession *session = gc->proto_data;
-	MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id);
+	MsnSession *session;
+	MsnSwitchBoard *swboard;
+
+	session = gc->proto_data;
+	swboard = msn_session_find_switch_with_id(session, id);
 
 	if (swboard == NULL)
 		return;
 
-	if (!msn_switchboard_send_command(swboard, "CAL", who))
-		msn_switchboard_destroy(swboard);
+	msn_cmdproc_send(swboard->cmdproc, "CAL", "%s", who);
 }
 
 static void
 msn_chat_leave(GaimConnection *gc, int id)
 {
-	MsnSession *session = gc->proto_data;
-	MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id);
-	char buf[6];
+	GaimAccount *account;
+	MsnSession *session;
+	MsnSwitchBoard *swboard;
 
-	if (swboard == NULL) {
+	session = gc->proto_data;
+	account = gaim_connection_get_account(gc);
+	swboard = msn_session_find_switch_with_id(session, id);
+
+	if (swboard == NULL)
+	{
 		serv_got_chat_left(gc, id);
 		return;
 	}
 
-	strcpy(buf, "OUT\r\n");
-
-	if (!msn_servconn_write(swboard->servconn, buf, strlen(buf)))
-		msn_switchboard_destroy(swboard);
+	msn_cmdproc_send_quick(swboard->cmdproc, "OUT", NULL, NULL);
+	msn_switchboard_destroy(swboard);
 
 	serv_got_chat_left(gc, id);
 }
@@ -1061,12 +1065,16 @@
 static int
 msn_chat_send(GaimConnection *gc, int id, const char *message)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
-	MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id);
+	GaimAccount *account;
+	MsnSession *session;
+	MsnSwitchBoard *swboard;
 	MsnMessage *msg;
 	char *send, *recv;
 
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
+	swboard = msn_session_find_switch_with_id(session, id);
+
 	if (swboard == NULL)
 		return -EINVAL;
 
@@ -1079,13 +1087,7 @@
 
 	g_free(send);
 
-	if (!msn_switchboard_send_msg(swboard, msg)) {
-		msn_switchboard_destroy(swboard);
-
-		msn_message_destroy(msg);
-
-		return 0;
-	}
+	msn_switchboard_send_msg(swboard, msg);
 
 	msn_message_destroy(msg);
 
@@ -1100,32 +1102,28 @@
 static void
 msn_keepalive(GaimConnection *gc)
 {
-	MsnSession *session = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
+
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 
 	if (!session->http_method)
-	{
-		g_snprintf(buf, sizeof(buf), "PNG\r\n");
-
-		if (msn_servconn_write(session->notification_conn,
-							   buf, strlen(buf)) < 0) {
-
-			gaim_connection_error(gc, _("Write error"));
-			return;
-		}
-	}
+		msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL);
 }
 
 static void
 msn_group_buddy(GaimConnection *gc, const char *who,
 				const char *old_group_name, const char *new_group_name)
 {
-	MsnSession *session = gc->proto_data;
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnGroup *old_group, *new_group;
 	MsnUser *user;
 	const char *friendly;
-	char outparams[MSN_BUF_LEN];
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
 	old_group = msn_groups_find_with_name(session->groups, old_group_name);
 	new_group = msn_groups_find_with_name(session->groups, new_group_name);
 
@@ -1137,15 +1135,13 @@
 	if (old_group != NULL)
 		msn_user_remove_group_id(user, msn_group_get_id(old_group));
 
-	if (new_group == NULL) {
-		g_snprintf(outparams, sizeof(outparams), "%s 0",
-				   gaim_url_encode(new_group_name));
+	if (new_group == NULL)
+	{
+		msn_cmdproc_send(cmdproc, "ADG", "%s 0",
+						 gaim_url_encode(new_group_name));
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "ADG", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
+		if (cmdproc->error)
 			return;
-		}
 
 		/* I hate this. So much. */
 		session->moving_buddy    = TRUE;
@@ -1157,38 +1153,28 @@
 
 		msn_user_ref(session->moving_user);
 	}
-	else {
-		g_snprintf(outparams, sizeof(outparams), "FL %s %s %d",
-				   who, gaim_url_encode(friendly),
-				   msn_group_get_id(new_group));
+	else
+	{
+		msn_cmdproc_send(cmdproc, "ADD", "FL %s %s %d",
+						 who, gaim_url_encode(friendly),
+						 msn_group_get_id(new_group));
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "ADD", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
+		if (cmdproc->error)
 			return;
-		}
 	}
 
-	if (old_group != NULL) {
-		g_snprintf(outparams, sizeof(outparams), "FL %s %d",
-				   who, msn_group_get_id(old_group));
-
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "REM", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
-			return;
-		}
+	if (old_group != NULL)
+	{
+		msn_cmdproc_send(cmdproc, "REM", "AL %s %s", who,
+						 msn_group_get_id(old_group));
 
-		if (msn_users_get_count(msn_group_get_users(old_group)) <= 0) {
-			g_snprintf(outparams, sizeof(outparams), "%d",
-					   msn_group_get_id(old_group));
+		if (cmdproc->error)
+			return;
 
-			if (!msn_servconn_send_command(session->notification_conn,
-										   "RMG", outparams)) {
-
-				gaim_connection_error(gc, _("Write error"));
-				return;
-			}
+		if (msn_users_get_count(msn_group_get_users(old_group)) <= 0)
+		{
+			msn_cmdproc_send(cmdproc, "RMG", "%d",
+							 msn_group_get_id(old_group));
 		}
 	}
 }
@@ -1197,41 +1183,37 @@
 msn_rename_group(GaimConnection *gc, const char *old_group_name,
 				 const char *new_group_name, GList *members)
 {
-	MsnSession *session = gc->proto_data;
-	char outparams[MSN_BUF_LEN];
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnGroup *old_group;
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
 	if ((old_group = msn_groups_find_with_name(session->groups,
-											   old_group_name)) != NULL) {
-
-		g_snprintf(outparams, sizeof(outparams), "%d %s 0",
-				   msn_group_get_id(old_group),
-				   gaim_url_encode(new_group_name));
+											   old_group_name)) != NULL)
+	{
+		msn_cmdproc_send(cmdproc, "REG", "%d %s 0",
+						 msn_group_get_id(old_group),
+						 gaim_url_encode(new_group_name));
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "REG", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
+		if (cmdproc->error)
 			return;
-		}
 
 		msn_group_set_name(old_group, new_group_name);
 	}
-	else {
-		g_snprintf(outparams, sizeof(outparams), "%s 0",
-				   gaim_url_encode(new_group_name));
-
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "ADG", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
-			return;
-		}
+	else
+	{
+		msn_cmdproc_send(cmdproc, "ADG", "%s 0",
+						 gaim_url_encode(new_group_name));
 	}
 }
 
 static void
 msn_buddy_free(GaimBuddy *b)
 {
-	if (b->proto_data != NULL) {
+	if (b->proto_data != NULL)
+	{
 		msn_user_destroy(b->proto_data);
 		b->proto_data = NULL;
 	}
@@ -1240,19 +1222,18 @@
 static void
 msn_convo_closed(GaimConnection *gc, const char *who)
 {
-	GaimAccount *account = gaim_connection_get_account(gc);
-	MsnSession *session = gc->proto_data;
+	GaimAccount *account;
+	MsnSession *session;
 	MsnSwitchBoard *swboard;
 
+	account = gaim_connection_get_account(gc);
+	session = gc->proto_data;
 	swboard = msn_session_find_switch_with_passport(session, who);
 
-	if (swboard != NULL && swboard->chat == NULL) {
-		char sendbuf[256];
-
-		g_snprintf(sendbuf, sizeof(sendbuf), "BYE %s\r\n",
-				   gaim_account_get_username(account));
-
-		msn_servconn_write(swboard->servconn, sendbuf, strlen(sendbuf));
+	if (swboard != NULL && swboard->chat == NULL)
+	{
+		msn_cmdproc_send_quick(swboard->cmdproc, "BYE", "%s",
+							   gaim_account_get_username(account));
 
 		msn_switchboard_destroy(swboard);
 	}
@@ -1290,23 +1271,16 @@
 static void
 msn_remove_group(GaimConnection *gc, const char *name)
 {
-	MsnSession *session = (MsnSession *)gc->proto_data;
+	MsnSession *session;
+	MsnCmdProc *cmdproc;
 	MsnGroup *group;
 
+	session = gc->proto_data;
+	cmdproc = session->notification_conn->cmdproc;
+
 	if ((group = msn_groups_find_with_name(session->groups, name)) != NULL)
 	{
-		char outparams[MSN_BUF_LEN];
-
-		g_snprintf(outparams, sizeof(outparams), "%d",
-				   msn_group_get_id(group));
-
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "RMG", outparams))
-		{
-			gaim_connection_error(gc, _("Write error"));
-
-			return;
-		}
+		msn_cmdproc_send(cmdproc, "RMG", "%d", msn_group_get_id(group));
 	}
 }
 
@@ -1611,6 +1585,22 @@
 	g_free(url);
 }
 
+static gboolean msn_load(GaimPlugin *plugin)
+{
+	msn_notification_init();
+	msn_switchboard_init();
+
+	return TRUE;
+}
+
+static gboolean msn_unload(GaimPlugin *plugin)
+{
+	msn_notification_end();
+	msn_switchboard_end();
+
+	return TRUE;
+}
+
 static GaimPluginPrefFrame *
 get_plugin_pref_frame(GaimPlugin *plugin) {
 	GaimPluginPrefFrame *frame;
@@ -1720,13 +1710,13 @@
 	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
 	GAIM_WEBSITE,                                     /**< homepage       */
 
-	NULL,                                             /**< load           */
-	NULL,                                             /**< unload         */
+	msn_load,                                         /**< load           */
+	msn_unload,                                       /**< unload         */
 	NULL,                                             /**< destroy        */
 
 	NULL,                                             /**< ui_info        */
-	&prpl_info,                                        /**< extra_info     */
-	&prefs_info                                        /**< prefs_info     */
+	&prpl_info,                                       /**< extra_info     */
+	&prefs_info                                       /**< prefs_info     */
 };
 
 static void
--- a/src/protocols/msn/nexus.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/nexus.c	Sun Apr 25 22:02:06 2004 +0000
@@ -209,7 +209,7 @@
 	}
 	else if (strstr(buffer, "HTTP/1.1 200 OK"))
 	{
-		char outparams[MSN_BUF_LEN];
+		MsnCmdProc *cmdproc;
 		char *base, *c;
 		char *login_params;
 
@@ -230,9 +230,10 @@
 		}
 #endif
 
+		cmdproc = session->notification_conn->cmdproc;
 		base  = strstr(buffer, "Authentication-Info: ");
 
-		if(base == NULL)
+		if (base == NULL)
 		{
 			gaim_debug(GAIM_DEBUG_ERROR, "msn",
 					   "Authentication information was not found. This did "
@@ -248,11 +249,7 @@
 
 		login_params = g_strndup(base, c - base);
 
-		g_snprintf(outparams, sizeof(outparams),
-			"TWN S %s", login_params);
-
-		msn_servconn_send_command(session->notification_conn, "USR",
-								  outparams);
+		msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
 
 		g_free(login_params);
 
--- a/src/protocols/msn/notification.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/notification.c	Sun Apr 25 22:02:06 2004 +0000
@@ -32,18 +32,17 @@
 
 } MsnPermitAdd;
 
-static GHashTable *notification_commands  = NULL;
-static GHashTable *notification_msg_types = NULL;
+static MsnTable *cbs_table = NULL;
 
 /**************************************************************************
  * Utility functions
  **************************************************************************/
 static gboolean
-add_buddy(MsnServConn *servconn, MsnUser *user)
+add_buddy(MsnCmdProc *cmdproc, MsnUser *user)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
 	GaimBuddy *b;
 	MsnGroup *group = NULL;
 	GaimGroup *g = NULL;
@@ -51,6 +50,9 @@
 	GSList *sl;
 	GSList *buddies;
 
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
 	buddies = gaim_find_buddies(account, msn_user_get_passport(user));
 
 	for (l = msn_user_get_group_ids(user); l != NULL; l = l->next)
@@ -134,19 +136,20 @@
 static void
 msn_accept_add_cb(MsnPermitAdd *pa)
 {
-	if (g_list_find(gaim_connections_get_all(), pa->gc) != NULL) {
-		MsnSession *session = pa->gc->proto_data;
-		char outparams[MSN_BUF_LEN];
+	if (g_list_find(gaim_connections_get_all(), pa->gc) != NULL)
+	{
+		MsnSession *session;
+		MsnCmdProc *cmdproc;
 
-		g_snprintf(outparams, sizeof(outparams), "AL %s %s",
-				   msn_user_get_passport(pa->user),
-				   gaim_url_encode(msn_user_get_name(pa->user)));
+		session = pa->gc->proto_data;
+		cmdproc = session->notification_conn->cmdproc;
 
-		if (msn_servconn_send_command(session->notification_conn,
-									  "ADD", outparams) <= 0) {
-			gaim_connection_error(pa->gc, _("Write error"));
+		msn_cmdproc_send(cmdproc, "ADD", "AL %s %s",
+						 msn_user_get_passport(pa->user),
+						 gaim_url_encode(msn_user_get_name(pa->user)));
+
+		if (cmdproc->error)
 			return;
-		}
 
 		gaim_privacy_permit_add(pa->gc->account,
 								msn_user_get_passport(pa->user), TRUE);
@@ -162,19 +165,20 @@
 static void
 msn_cancel_add_cb(MsnPermitAdd *pa)
 {
-	if (g_list_find(gaim_connections_get_all(), pa->gc) != NULL) {
-		MsnSession *session = pa->gc->proto_data;
-		char outparams[MSN_BUF_LEN];
+	if (g_list_find(gaim_connections_get_all(), pa->gc) != NULL)
+	{
+		MsnSession *session;
+		MsnCmdProc *cmdproc;
 
-		g_snprintf(outparams, sizeof(outparams), "BL %s %s",
-				   msn_user_get_passport(pa->user),
-				   gaim_url_encode(msn_user_get_name(pa->user)));
+		session = pa->gc->proto_data;
+		cmdproc = session->notification_conn->cmdproc;
 
-		if (msn_servconn_send_command(session->notification_conn,
-									  "ADD", outparams) <= 0) {
-			gaim_connection_error(pa->gc, _("Write error"));
+		msn_cmdproc_send(cmdproc, "ADD", "BL %s %s",
+						 msn_user_get_passport(pa->user),
+						 gaim_url_encode(msn_user_get_name(pa->user)));
+
+		if (cmdproc->error)
 			return;
-		}
 
 		gaim_privacy_deny_add(pa->gc->account,
 							  msn_user_get_passport(pa->user), TRUE);
@@ -185,113 +189,69 @@
 }
 
 /**************************************************************************
- * Catch-all commands
- **************************************************************************/
-static gboolean
-blank_cmd(MsnServConn *servconn, const char *command, const char **params,
-		  size_t param_count)
-{
-	return TRUE;
-}
-
-static gboolean
-unknown_cmd(MsnServConn *servconn, const char *command, const char **params,
-			size_t param_count)
-{
-	int errnum = 0;
-
-	if (isdigit(*command)) {
-		errnum = atoi(command);
-
-		if (errnum == 225) {
-			/*
-			 * Ignore this. It happens as a result of moving a buddy from
-			 * one group that isn't on the server to another that is.
-			 * The user doesn't care if the old group was there or not.
-			 */
-			return TRUE;
-		}
-	}
-
-	msn_error_handle(servconn->session, errnum);
-
-	return TRUE;
-}
-
-
-/**************************************************************************
  * Login
  **************************************************************************/
 
-
-static gboolean
-cvr_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account = servconn->session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
-	char outparams[MSN_BUF_LEN];
+	GaimAccount *account;
+	GaimConnection *gc;
 
-	g_snprintf(outparams, sizeof(outparams),
-			   "TWN I %s", gaim_account_get_username(account));
+	account = cmdproc->session->account;
+	gc = gaim_account_get_connection(account);
 
-	if (!msn_servconn_send_command(servconn, "USR", outparams))
-	{
-		gaim_connection_error(gc, _("Unable to request USR\n"));
-
-		return FALSE;
-	}
-
-	return TRUE;
+	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
+					 gaim_account_get_username(account));
 }
 
-static gboolean
-inf_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+inf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account = servconn->session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
-	char outparams[MSN_BUF_LEN];
+	GaimAccount *account;
+	GaimConnection *gc;
 
-	if (strcmp(params[1], "MD5")) {
+	account = cmdproc->session->account;
+	gc = gaim_account_get_connection(account);
+
+	if (strcmp(cmd->params[1], "MD5"))
+	{
 		gaim_connection_error(gc, _("Unable to login using MD5"));
 
-	return FALSE;
+		return;
 	}
 
-	g_snprintf(outparams, sizeof(outparams), "MD5 I %s",
-			   gaim_account_get_username(account));
+	msn_cmdproc_send(cmdproc, "USR", "MD5 I %s",
+					 gaim_account_get_username(account));
 
-	if (!msn_servconn_send_command(servconn, "USR", outparams)) {
-		gaim_connection_error(gc, _("Unable to send USR"));
-
-		return FALSE;
-	}
+	if (cmdproc->error)
+		return;
 
 	gaim_connection_update_progress(gc, _("Requesting to send password"),
 									5, MSN_CONNECT_STEPS);
-
-	return TRUE;
 }
 
-static gboolean
-usr_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
 	char outparams[MSN_BUF_LEN];
 
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
+
 	/*
 	 * We're either getting the passport connect info (if we're on
 	 * MSNP8 or higher), or a challenge request (MSNP7 and lower).
 	 *
 	 * Let's find out.
 	 */
-	if (!g_ascii_strcasecmp(params[1], "OK"))
+	if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
 	{
-		const char *friendly = gaim_url_decode(params[3]);
+		const char *friendly = gaim_url_decode(cmd->params[3]);
 
 		/* OK */
 
@@ -299,17 +259,15 @@
 
 		session->syncing_lists = TRUE;
 
-		if (!msn_servconn_send_command(servconn, "SYN", "0"))
-		{
-			gaim_connection_error(gc, _("Unable to write"));
+		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
 
-			return FALSE;
-		}
+		if (cmdproc->error)
+			return;
 
 		gaim_connection_update_progress(gc, _("Retrieving buddy list"),
 										7, MSN_CONNECT_STEPS);
 	}
-	else if (!g_ascii_strcasecmp(params[1], "TWN"))
+	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
 	{
 		char **elems, **cur, **tokens;
 
@@ -317,7 +275,7 @@
 		session->nexus = msn_nexus_new(session);
 
 		/* Parse the challenge data. */
-		elems = g_strsplit(params[3], ",", 0);
+		elems = g_strsplit(cmd->params[3], ",", 0);
 
 		for (cur = elems; *cur != NULL; cur++)
 		{
@@ -334,244 +292,229 @@
 		gaim_connection_update_progress(gc, _("Password sent"),
 										6, MSN_CONNECT_STEPS);
 	}
-	else if (!g_ascii_strcasecmp(params[1], "MD5"))
+	else if (!g_ascii_strcasecmp(cmd->params[1], "MD5"))
 	{
 		/* Challenge */
-		const char *challenge = params[3];
-		char buf[MSN_BUF_LEN];
+		const char *challenge;
+		const char *password;
+		char buf[33];
 		md5_state_t st;
 		md5_byte_t di[16];
 		int i;
 
-		g_snprintf(buf, sizeof(buf), "%s%s", challenge,
-				   gaim_account_get_password(account));
+		challenge = cmd->params[3];
+		password = gaim_account_get_password(account);
 
 		md5_init(&st);
-		md5_append(&st, (const md5_byte_t *)buf, strlen(buf));
+		md5_append(&st, (const md5_byte_t *)challenge, strlen(challenge));
+		md5_append(&st, (const md5_byte_t *)password, strlen(password));
 		md5_finish(&st, di);
 
 		g_snprintf(outparams, sizeof(outparams), "MD5 S ");
 
-		for (i = 0; i < 16; i++) {
-			g_snprintf(buf, sizeof(buf), "%02x", di[i]);
-			strcat(outparams, buf);
-		}
+		for (i = 0; i < 16; i++)
+			g_snprintf(buf + (i*2), 3, "%02x", di[i]);
 
-		if (!msn_servconn_send_command(servconn, "USR", outparams)) {
-			gaim_connection_error(gc, _("Unable to send password"));
+		msn_cmdproc_send(cmdproc, "USR", "MD5 S %s", buf);
 
-			return FALSE;
-		}
+		if (cmdproc->error)
+			return;
 
 		gaim_connection_update_progress(gc, _("Password sent"),
 										6, MSN_CONNECT_STEPS);
 	}
-
-	return TRUE;
 }
 
-static gboolean
-ver_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
 	gboolean protocol_supported = FALSE;
-	char outparams[MSN_BUF_LEN];
 	char proto_str[8];
 	size_t i;
 
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
+
 	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
 
-	for (i = 1; i < param_count; i++)
+	for (i = 1; i < cmd->param_count; i++)
 	{
-		if (!strcmp(params[i], proto_str))
+		if (!strcmp(cmd->params[i], proto_str))
 		{
 			protocol_supported = TRUE;
 			break;
 		}
 	}
 
-	if (!protocol_supported) {
+	if (!protocol_supported)
+	{
 		gaim_connection_error(gc, _("Protocol not supported"));
 
-		return FALSE;
+		return;
 	}
 
 	if (session->protocol_ver >= 8)
 	{
-		g_snprintf(outparams, sizeof(outparams),
-				   "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
-				   gaim_account_get_username(account));
-
-		if (!msn_servconn_send_command(servconn, "CVR", outparams))
-		{
-			gaim_connection_error(gc, _("Unable to request CVR\n"));
-
-			return FALSE;
-		}
+		msn_cmdproc_send(cmdproc, "CVR",
+						 "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
+						 gaim_account_get_username(account));
 	}
 	else
 	{
-		if (!msn_servconn_send_command(servconn, "INF", NULL))
-		{
-			gaim_connection_error(gc, _("Unable to request INF\n"));
-
-			return FALSE;
-		}
+		msn_cmdproc_send(cmdproc, "INF", NULL, NULL);
 	}
-
-	return TRUE;
 }
 
 /**************************************************************************
  * Log out
  **************************************************************************/
-static gboolean
-out_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimConnection *gc = servconn->session->account->gc;
+	GaimConnection *gc;
 
-	if (!g_ascii_strcasecmp(params[0], "OTH")) {
+	gc = cmdproc->session->account->gc;
+
+	if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
+	{
 		gc->wants_to_die = TRUE;
 		gaim_connection_error(gc,
-							_("You have been disconnected. You have "
-							  "signed on from another location."));
+							  _("You have been disconnected. You have "
+								"signed on from another location."));
 	}
-	else if (!g_ascii_strcasecmp(params[0], "SSD")) {
+	else if (!g_ascii_strcasecmp(cmd->params[0], "SSD"))
+	{
 		gaim_connection_error(gc,
-							_("You have been disconnected. The MSN servers "
-							  "are going down temporarily."));
+							  _("You have been disconnected. The MSN servers "
+								"are going down temporarily."));
 	}
-
-	return FALSE;
 }
 
 /**************************************************************************
  * Messages
  **************************************************************************/
 static void
-msg_cmd_post(MsnServConn *servconn, char *payload, size_t len)
+msg_cmd_post(MsnCmdProc *cmdproc, char *payload, size_t len)
 {
-	MsnMessage *msg = msn_message_new();
+	MsnMessage *msg;
 
-	msg->passport = servconn->msg_passport;
+	msg = msn_message_new();
 
 	msn_message_parse_payload(msg, payload, len);
 
-	msn_servconn_process_message(servconn, msg);
+	msg->passport = cmdproc->temp;
+	msn_cmdproc_process_msg(cmdproc, msg);
+	g_free(cmdproc->temp);
+	cmdproc->temp = NULL;
 
 	msn_message_destroy(msg);
 }
 
-static gboolean
-msg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n");
 
-	servconn->payload_cb  = msg_cmd_post;
-	servconn->payload_len = atoi(params[2]);
-
-	servconn->msg_passport = g_strdup(params[0]);
-
-	return TRUE;
+	cmdproc->payload_cb  = msg_cmd_post;
+	cmdproc->servconn->payload_len = atoi(cmd->params[2]);
+	cmdproc->temp = g_strdup(cmd->params[0]);
 }
 
 /**************************************************************************
  * Challenges
  **************************************************************************/
-static gboolean
-chl_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
+	MsnTransaction *trans;
 	char buf[MSN_BUF_LEN];
-	char buf2[3];
 	const char *challenge_resp;
+	const char *challenge_str;
 	md5_state_t st;
 	md5_byte_t di[16];
 	int i;
 
+	session = cmdproc->session;
+	gc = session->account->gc;
+
 	md5_init(&st);
-	md5_append(&st, (const md5_byte_t *)params[1], strlen(params[1]));
+	md5_append(&st, (const md5_byte_t *)cmd->params[1], strlen(cmd->params[1]));
 
 	if (session->protocol_ver >= 8)
 	{
 		challenge_resp = "VT6PX?UQTM4WM%YR";
+		challenge_str  = "PROD0038W!61ZTF9";
 	}
 	else
 	{
 		challenge_resp = "Q1P7W2E4J9R8U3S5";
+		challenge_str  = "msmsgs@msnmsgr.com";
 	}
 
 	md5_append(&st, (const md5_byte_t *)challenge_resp,
 			   strlen(challenge_resp));
 	md5_finish(&st, di);
 
-	g_snprintf(buf, sizeof(buf),
-			   "QRY %u %s 32\r\n",
-			   servconn->session->trId++,
-			   (session->protocol_ver >= 8
-				? "PROD0038W!61ZTF9"
-				: "msmsgs@msnmsgr.com"));
+	for (i = 0; i < 16; i++)
+		g_snprintf(buf + (i*2), 3, "%02x", di[i]);
 
-	for (i = 0; i < 16; i++) {
-		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
-		strcat(buf, buf2);
-	}
+	trans = msn_transaction_new("QRY", "%s 32", challenge_str);
 
-	if (msn_servconn_write(servconn, buf, strlen(buf)) <= 0) {
-		gaim_connection_error(gc, _("Unable to write to server"));
-	}
+	msn_transaction_set_payload(trans, buf, 32);
 
-	return TRUE;
+	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 /**************************************************************************
  * Buddy Lists
  **************************************************************************/
-static gboolean
-add_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnUser *user;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
+	GaimAccount *account;
+	GaimConnection *gc;
 	MsnPermitAdd *pa;
 	GSList *sl;
 	const char *list, *passport, *group_id = NULL;
 	const char *friend;
 	char msg[MSN_BUF_LEN];
 
-	list     = params[1];
-	passport = params[3];
-	friend   = gaim_url_decode(params[4]);
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
 
-	if (param_count >= 6)
-		group_id = params[5];
+	list     = cmd->params[1];
+	passport = cmd->params[3];
+	friend   = gaim_url_decode(cmd->params[4]);
 
-	if (!g_ascii_strcasecmp(list, "FL")) {
+	if (cmd->param_count >= 6)
+		group_id = cmd->params[5];
+
+	if (!g_ascii_strcasecmp(list, "FL"))
+	{
 		user = msn_user_new(session, passport, NULL);
 
 		if (group_id != NULL)
 			msn_user_add_group_id(user, atoi(group_id));
 
-		add_buddy(servconn, user);
+		add_buddy(cmdproc, user);
 
-		return TRUE;
+		return;
 	}
 	else if (g_ascii_strcasecmp(list, "RL"))
-		return TRUE;
+		return;
 
-	for (sl = gc->account->permit; sl != NULL; sl = sl->next) {
+	for (sl = gc->account->permit; sl != NULL; sl = sl->next)
 		if (!gaim_utf8_strcasecmp(sl->data, passport))
-			return TRUE;
-	}
+			return;
 
 	user = msn_user_new(session, passport, friend);
 	msn_user_set_name(user, friend);
@@ -587,43 +530,105 @@
 	gaim_request_action(gc, NULL, msg, NULL, 0, pa, 2,
 						_("Authorize"), G_CALLBACK(msn_accept_add_cb),
 						_("Deny"), G_CALLBACK(msn_cancel_add_cb));
-
-	return TRUE;
 }
 
-static gboolean
-adg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+add_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
+	const char *list, *passport;
+	char *reason;
+	char *msg = NULL;
+	char **params;
+
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
+	params = g_strsplit(trans->params, " ", 0);
+
+	list     = params[0];
+	passport = params[1];
+
+	reason = "invalid user";
+
+	if (!strcmp(list, "FL"))
+		msg = g_strdup("Unable to add user on MSN");
+	else if (!strcmp(list, "BL"))
+		msg = g_strdup("Unable to block user on MSN");
+	else if (!strcmp(list, "AL"))
+		msg = g_strdup("Unable to permit user on MSN");
+
+	if (!strcmp(list, "FL"))
+	{
+		reason = g_strdup_printf("%s is not a valid passport account.\n\n"
+								 "This user will be automatically removed "
+								 "from your %s account's buddy list, so this "
+								 "won't appear again.",
+								 passport, gaim_account_get_username(account));
+	}
+	else
+	{
+		reason = g_strdup_printf("%s is not a valid passport account.",
+								 passport);
+	}
+
+	if (msg != NULL)
+	{
+		gaim_notify_error(gc, NULL, msg, reason);
+		g_free(msg);
+	}
+
+	if (!strcmp(list, "FL"))
+	{
+		GaimBuddy *buddy;
+
+		buddy = gaim_find_buddy(account, passport);
+
+		if (buddy != NULL)
+			gaim_blist_remove_buddy(buddy);
+	}
+
+	g_free(reason);
+
+	g_strfreev(params);
+}
+
+static void
+adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnGroup *group;
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	gint group_id;
 	const char *group_name;
 
-	group_id = atoi(params[3]);
+	session = cmdproc->session;
 
-	group_name = gaim_url_decode(params[2]);
+	group_id = atoi(cmd->params[3]);
+
+	group_name = gaim_url_decode(cmd->params[2]);
 
 	group = msn_group_new(session, group_id, group_name);
 
 	msn_groups_add(session->groups, group);
-
-	return TRUE;
 }
 
-static gboolean
-blp_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimConnection *gc = servconn->session->account->gc;
+	GaimConnection *gc;
 	const char *list_name;
 
-	if (servconn->session->protocol_ver >= 8)
-		list_name = params[0];
+	gc = cmdproc->session->account->gc;
+
+	if (cmdproc->session->protocol_ver >= 8)
+		list_name = cmd->params[0];
 	else
-		list_name = params[2];
+		list_name = cmd->params[2];
 
-	if (!g_ascii_strcasecmp(list_name, "AL")) {
+	if (!g_ascii_strcasecmp(list_name, "AL"))
+	{
 		/*
 		 * If the current setting is AL, messages from users who
 		 * are not in BL will be delivered.
@@ -632,7 +637,8 @@
 		 */
 		gc->account->perm_deny = GAIM_PRIVACY_DENY_USERS;
 	}
-	else {
+	else
+	{
 		/* If the current setting is BL, only messages from people
 		 * who are in the AL will be delivered.
 		 *
@@ -640,44 +646,49 @@
 		 */
 		gc->account->perm_deny = GAIM_PRIVACY_ALLOW_USERS;
 	}
-
-	return TRUE;
 }
 
-static gboolean
-bpr_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	const char *passport, *type, *value;
 	GaimBuddy *b;
 	MsnUser *user;
 
-	if (param_count == 4)
+	session = cmdproc->session;
+	gc = session->account->gc;
+
+	if (cmd->param_count == 4)
 	{
-		passport = params[1];
-		type     = params[2];
-		value    = params[3];
+		passport = cmd->params[1];
+		type     = cmd->params[2];
+		value    = cmd->params[3];
 	}
-	else if (param_count == 2)
+	else if (cmd->param_count == 2)
 	{
 		passport = msn_user_get_passport(session->last_user_added);
-		type     = params[0];
-		value    = params[1];
+		type     = cmd->params[0];
+		value    = cmd->params[1];
 	}
 	else
-		return TRUE;
+		return;
 
 	user = msn_users_find_with_passport(session->users, passport);
 
-	if (value != NULL) {
-		if (!strcmp(type, "MOB")) {
-			if (!strcmp(value, "Y") || !strcmp(value, "N")) {
+	if (value != NULL)
+	{
+		if (!strcmp(type, "MOB"))
+		{
+			if (!strcmp(value, "Y") || !strcmp(value, "N"))
+			{
 				user->mobile = (!strcmp(value, "Y") ? TRUE : FALSE);
 
-				if ((b = gaim_find_buddy(gc->account, passport)) != NULL) {
-					if (GAIM_BUDDY_IS_ONLINE(b)) {
+				if ((b = gaim_find_buddy(gc->account, passport)) != NULL)
+				{
+					if (GAIM_BUDDY_IS_ONLINE(b))
+					{
 						serv_got_update(gc, (char *)passport,
 										1, 0, 0, 0, b->uc);
 					}
@@ -691,36 +702,35 @@
 		else if (!strcmp(type, "PHM"))
 			msn_user_set_mobile_phone(user, gaim_url_decode(value));
 	}
-
-	return TRUE;
 }
 
-static gboolean
-fln_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimConnection *gc = servconn->session->account->gc;
+	GaimConnection *gc;
 
-	serv_got_update(gc, (char *)params[0], 0, 0, 0, 0, 0);
+	gc = cmdproc->session->account->gc;
 
-	return TRUE;
+	serv_got_update(gc, (char *)cmd->params[0], 0, 0, 0, 0, 0);
 }
 
-static gboolean
-iln_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
 	int status = 0;
 	const char *state, *passport, *friend;
 	GaimBuddy *b;
 
-	state    = params[1];
-	passport = params[2];
-	friend   = gaim_url_decode(params[3]);
+	session = cmdproc->session;
+	gc = session->account->gc;
+
+	state    = cmd->params[1];
+	passport = cmd->params[2];
+	friend   = gaim_url_decode(cmd->params[3]);
 
 	user = msn_users_find_with_passport(session->users, passport);
 
@@ -728,9 +738,9 @@
 
 	msn_user_set_name(user, friend);
 
-	if (session->protocol_ver >= 9 && param_count == 6)
+	if (session->protocol_ver >= 9 && cmd->param_count == 6)
 	{
-		msnobj = msn_object_new_from_string(gaim_url_decode(params[5]));
+		msnobj = msn_object_new_from_string(gaim_url_decode(cmd->params[5]));
 		msn_user_set_object(user, msnobj);
 	}
 
@@ -751,43 +761,39 @@
 		status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
 
 	serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status);
-
-	return TRUE;
 }
 
-static gboolean
-ipg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+ipg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	servconn->payload_cb  = NULL;
-	servconn->payload_len = atoi(params[2]);
-
-	return TRUE;
+	cmdproc->payload_cb = NULL;
+	cmdproc->servconn->payload_len = atoi(cmd->params[2]);
 }
 
-static gboolean
-lsg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnGroup *group;
 	GaimGroup *g;
 	const char *name;
 	int num_groups, group_id;
 
+	session = cmdproc->session;
+
 	if (session->protocol_ver >= 8)
 	{
-		group_id = atoi(params[0]);
-		name = gaim_url_decode(params[1]);
+		group_id = atoi(cmd->params[0]);
+		name = gaim_url_decode(cmd->params[1]);
 	}
 	else
 	{
-		num_groups = atoi(params[3]);
-		group_id   = atoi(params[4]);
-		name       = gaim_url_decode(params[5]);
+		num_groups = atoi(cmd->params[3]);
+		group_id   = atoi(cmd->params[4]);
+		name       = gaim_url_decode(cmd->params[5]);
 
 		if (num_groups == 0)
-			return TRUE;
+			return;
 
 		if (!strcmp(name, "~"))
 			name = _("Buddies");
@@ -797,33 +803,35 @@
 
 	msn_groups_add(session->groups, group);
 
-	if ((g = gaim_find_group(name)) == NULL) {
+	if ((g = gaim_find_group(name)) == NULL)
+	{
 		g = gaim_group_new(name);
 		gaim_blist_add_group(g, NULL);
 	}
-
-	return TRUE;
 }
 
-static gboolean
-lst_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
 	const char *passport = NULL;
 	const char *friend = NULL;
 
+	session = cmdproc->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
+
 	if (session->protocol_ver >= 8)
 	{
 		const char *group_nums;
 		int list_op;
 
-		passport   = params[0];
-		friend     = gaim_url_decode(params[1]);
-		list_op    = atoi(params[2]);
-		group_nums = params[3];
+		passport   = cmd->params[0];
+		friend     = gaim_url_decode(cmd->params[1]);
+		list_op    = atoi(cmd->params[2]);
+		group_nums = cmd->params[3];
 
 		if (list_op & MSN_LIST_FL_OP)
 		{
@@ -921,7 +929,7 @@
 				gaim_account_get_buddy_icon(session->account));
 
 			if (!msn_session_change_status(session, "NLN"))
-				return FALSE;
+				return;
 
 			gaim_connection_set_state(gc, GAIM_CONNECTED);
 			serv_finish_login(gc);
@@ -947,7 +955,7 @@
 				session->lists.forward =
 					g_slist_remove(session->lists.forward, user);
 
-				add_buddy(servconn, user);
+				add_buddy(cmdproc, user);
 
 				buddies = gaim_find_buddies(account,
 											msn_user_get_passport(user));
@@ -981,24 +989,24 @@
 		int user_num;
 		int num_users;
 
-		list_name = params[1];
-		user_num  = atoi(params[3]);
-		num_users = atoi(params[4]);
+		list_name = cmd->params[1];
+		user_num  = atoi(cmd->params[3]);
+		num_users = atoi(cmd->params[4]);
 
 		if (g_ascii_strcasecmp(list_name, "RL") &&
 			user_num == 0 && num_users == 0)
 		{
-			return TRUE; /* There are no users on this list. */
+			return; /* There are no users on this list. */
 		}
 
 		if (num_users > 0)
 		{
-			passport  = params[5];
-			friend    = gaim_url_decode(params[6]);
+			passport  = cmd->params[5];
+			friend    = gaim_url_decode(cmd->params[6]);
 		}
 
 		if (session->syncing_lists && session->lists_synced)
-			return TRUE;
+			return;
 
 		if (!g_ascii_strcasecmp(list_name, "FL") && user_num != 0)
 		{
@@ -1007,8 +1015,8 @@
 
 			user = msn_user_new(session, passport, friend);
 
-			if (param_count == 8)
-				msn_user_add_group_id(user, atoi(params[7]));
+			if (cmd->param_count == 8)
+				msn_user_add_group_id(user, atoi(cmd->params[7]));
 
 			session->lists.forward =
 				g_slist_append(session->lists.forward, user);
@@ -1081,13 +1089,13 @@
 			}
 
 			if (user_num != num_users)
-				return TRUE; /* This isn't the last one in the RL. */
+				return; /* This isn't the last one in the RL. */
 
 			/* Now we're at the last one, so we can do final work. */
 			if (!session->lists_synced)
 			{
 				if (!msn_session_change_status(session, "NLN"))
-					return FALSE;
+					return;
 
 				gaim_connection_set_state(gc, GAIM_CONNECTED);
 				serv_finish_login(gc);
@@ -1112,23 +1120,20 @@
 				session->lists.forward =
 					g_slist_remove(session->lists.forward, user);
 
-				add_buddy(servconn, user);
+				add_buddy(cmdproc, user);
 			}
 
 			session->syncing_lists = FALSE;
 			session->lists_synced  = TRUE;
 		}
 	}
-
-	return TRUE;
 }
 
-static gboolean
-nln_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
 	const char *state;
@@ -1136,9 +1141,12 @@
 	const char *friend;
 	int status = 0;
 
-	state    = params[0];
-	passport = params[1];
-	friend   = gaim_url_decode(params[2]);
+	session = cmdproc->session;
+	gc = session->account->gc;
+
+	state    = cmd->params[0];
+	passport = cmd->params[1];
+	friend   = gaim_url_decode(cmd->params[2]);
 
 	user = msn_users_find_with_passport(session->users, passport);
 
@@ -1146,9 +1154,9 @@
 
 	msn_user_set_name(user, friend);
 
-	if (session->protocol_ver >= 9 && param_count == 5)
+	if (session->protocol_ver >= 9 && cmd->param_count == 5)
 	{
-		msnobj = msn_object_new_from_string(gaim_url_decode(params[4]));
+		msnobj = msn_object_new_from_string(gaim_url_decode(cmd->params[4]));
 		msn_user_set_object(user, msnobj);
 	}
 
@@ -1166,31 +1174,28 @@
 		status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
 
 	serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status);
+}
 
-	return TRUE;
+static void
+not_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	cmdproc->payload_cb  = NULL;
+	cmdproc->servconn->payload_len = atoi(cmd->params[1]);
 }
 
-static gboolean
-not_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	servconn->payload_cb  = NULL;
-	servconn->payload_len = atoi(params[1]);
-
-	return TRUE;
-}
-
-static gboolean
-prp_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
-{
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	const char *type, *value;
 
-	type  = params[2];
-	value = params[3];
+	session = cmdproc->session;
 
-	if (param_count == 4) {
+	type  = cmd->params[2];
+	value = cmd->params[3];
+
+	if (cmd->param_count == 4)
+	{
 		if (!strcmp(type, "PHH"))
 			msn_user_set_home_phone(session->user, gaim_url_decode(value));
 		else if (!strcmp(type, "PHW"))
@@ -1198,37 +1203,34 @@
 		else if (!strcmp(type, "PHM"))
 			msn_user_set_mobile_phone(session->user, gaim_url_decode(value));
 	}
-
-	return TRUE;
 }
 
-static gboolean
-rea_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	const char *friend;
 
-	friend = gaim_url_decode(params[3]);
+	session = cmdproc->session;
+	gc = session->account->gc;
+	friend = gaim_url_decode(cmd->params[3]);
 
 	gaim_connection_set_display_name(gc, friend);
-
-	return TRUE;
 }
 
-static gboolean
-reg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnGroup *group;
 	int group_id;
 	const char *group_name;
 
-	group_id = atoi(params[2]);
+	session = cmdproc->session;
+	group_id = atoi(cmd->params[2]);
 
-	group_name = gaim_url_decode(params[3]);
+	group_name = gaim_url_decode(cmd->params[3]);
 
 	group = msn_groups_find_with_id(session->groups, group_id);
 
@@ -1237,21 +1239,21 @@
 
 	if (group != NULL)
 		msn_group_set_name(group, group_name);
-
-	return TRUE;
 }
 
-static gboolean
-rem_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	const char *passport = params[3];
+	MsnSession *session;
+	const char *passport;
 
-	if (param_count == 5)
+	session = cmdproc->session;
+	passport = cmd->params[3];
+
+	if (cmd->param_count == 5)
 	{
 		MsnUser *user;
-		int group_id = atoi(params[4]);
+		int group_id = atoi(cmd->params[4]);
 
 		user = msn_users_find_with_passport(session->users, passport);
 
@@ -1259,11 +1261,11 @@
 	}
 
 	/* I hate this. */
-	if (session->moving_buddy) {
+	/* shx: it won't be here for long. */
+	if (session->moving_buddy)
+	{
 		MsnGroup *group, *old_group;
-		GaimConnection *gc = session->account->gc;
 		const char *friendly;
-		char outparams[MSN_BUF_LEN];
 
 		group = msn_groups_find_with_name(session->groups,
 										  session->dest_group_name);
@@ -1273,15 +1275,14 @@
 		session->moving_buddy = FALSE;
 		session->old_group    = NULL;
 
-		if (group == NULL) {
+		if (group == NULL)
+		{
 			gaim_debug(GAIM_DEBUG_ERROR, "msn",
 					   "Still don't have a group ID for %s while moving %s!\n",
 					   session->dest_group_name, passport);
 
 			g_free(session->dest_group_name);
 			session->dest_group_name = NULL;
-
-			return TRUE;
 		}
 
 		g_free(session->dest_group_name);
@@ -1290,15 +1291,12 @@
 		if ((friendly = msn_user_get_name(session->moving_user)) == NULL)
 			friendly = passport;
 
-		g_snprintf(outparams, sizeof(outparams), "FL %s %s %d",
-				   passport, gaim_url_encode(friendly),
-				   msn_group_get_id(group));
+		msn_cmdproc_send(cmdproc, "ADD", "FL %s %s %d",
+						 passport, gaim_url_encode(friendly),
+						 msn_group_get_id(group));
 
-		if (!msn_servconn_send_command(session->notification_conn,
-									   "ADD", outparams)) {
-			gaim_connection_error(gc, _("Write error"));
-			return FALSE;
-		}
+		if (cmdproc->error)
+			return;
 
 		if (old_group != NULL)
 			msn_group_remove_user(old_group, session->moving_user);
@@ -1308,67 +1306,61 @@
 		session->moving_user = NULL;
 
 		if (old_group != NULL &&
-			msn_users_get_count(msn_group_get_users(old_group)) <= 0) {
-
-			g_snprintf(outparams, sizeof(outparams), "%d",
-					   msn_group_get_id(old_group));
-
-			if (!msn_servconn_send_command(session->notification_conn,
-										   "RMG", outparams)) {
-
-				gaim_connection_error(gc, _("Write error"));
-				return FALSE;
-			}
+			msn_users_get_count(msn_group_get_users(old_group)) <= 0)
+		{
+			msn_cmdproc_send(cmdproc, "REM", "%s", "%d",
+							 msn_group_get_id(old_group));
 		}
 	}
-
-	return TRUE;
 }
 
-static gboolean
-rmg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnGroup *group;
 
-	group = msn_groups_find_with_id(session->groups, atoi(params[2]));
+	session = cmdproc->session;
+	group = msn_groups_find_with_id(session->groups, atoi(cmd->params[2]));
 
 	if (group != NULL)
 		msn_groups_remove(session->groups, group);
-
-	return TRUE;
 }
 
-static gboolean
-syn_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = gaim_account_get_connection(session->account);
+	MsnSession *session;
+	GaimConnection *gc;
+
+	session = cmdproc->session;
+	gc = gaim_account_get_connection(session->account);
 
 	if (session->protocol_ver >= 8)
 	{
-		if (param_count == 2)
+		if (cmd->param_count == 2)
 		{
-			char buf[256];
+			char *buf;
 
 			/*
 			 * This can happen if we sent a SYN with an up-to-date
 			 * buddy list revision, but we send 0 to get a full list.
 			 * So, error out.
 			 */
-			snprintf(buf, sizeof(buf),
+			buf = g_strdup_printf(
 				_("Your MSN buddy list for %s is temporarily unavailable. "
-				  "Please wait and try again."),
+				"Please wait and try again."),
 				gaim_account_get_username(session->account));
+
 			gaim_connection_error(gc, buf);
 
-			return FALSE;
+			g_free(buf);
+
+			return;
 		}
 
-		session->total_users  = atoi(params[2]);
-		session->total_groups = atoi(params[3]);
+		session->total_users  = atoi(cmd->params[2]);
+		session->total_groups = atoi(cmd->params[3]);
 
 		if (session->total_users == 0)
 		{
@@ -1379,19 +1371,16 @@
 			session->lists_synced  = TRUE;
 		}
 	}
-
-	return TRUE;
 }
 
 /**************************************************************************
  * Misc commands
  **************************************************************************/
-static gboolean
-url_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
+	MsnSession *session;
+	GaimAccount *account;
 	const char *rru;
 	const char *url;
 	md5_state_t st;
@@ -1402,8 +1391,11 @@
 	char sendbuf[64];
 	int i;
 
-	rru = params[1];
-	url = params[2];
+	session = cmdproc->session;
+	account = session->account;
+
+	rru = cmd->params[1];
+	url = cmd->params[2];
 
 	g_snprintf(buf, sizeof(buf), "%s%lu%s",
 			   session->passport_info.mspauth,
@@ -1416,22 +1408,26 @@
 
 	memset(sendbuf, 0, sizeof(sendbuf));
 
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < 16; i++)
+	{
 		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
 		strcat(sendbuf, buf2);
 	}
 
-	if (session->passport_info.file != NULL) {
+	if (session->passport_info.file != NULL)
+	{
 		unlink(session->passport_info.file);
 		g_free(session->passport_info.file);
 	}
 
-	if ((fd = gaim_mkstemp(&session->passport_info.file)) == NULL) {
+	if ((fd = gaim_mkstemp(&session->passport_info.file)) == NULL)
+	{
 		gaim_debug(GAIM_DEBUG_ERROR, "msn",
 				   "Error opening temp passport file: %s\n",
 				   strerror(errno));
 	}
-	else {
+	else
+	{
 		fputs("<html>\n"
 			  "<head>\n"
 			  "<noscript>\n"
@@ -1467,7 +1463,8 @@
 		fprintf(fd, "</form></body>\n");
 		fprintf(fd, "</html>\n");
 
-		if (fclose(fd)) {
+		if (fclose(fd))
+		{
 			gaim_debug(GAIM_DEBUG_ERROR, "msn",
 					   "Error closing temp passport file: %s\n",
 					   strerror(errno));
@@ -1475,7 +1472,8 @@
 			unlink(session->passport_info.file);
 			g_free(session->passport_info.file);
 		}
-		else {
+		else
+		{
 			/*
 			 * Renaming file with .html extension, so that the
 			 * win32 open_url will work.
@@ -1483,9 +1481,10 @@
 			char *tmp;
 
 			if ((tmp = g_strdup_printf("%s.html",
-					session->passport_info.file)) != NULL) {
-
-				if (rename(session->passport_info.file, tmp) == 0) {
+					session->passport_info.file)) != NULL)
+			{
+				if (rename(session->passport_info.file, tmp) == 0)
+				{
 					g_free(session->passport_info.file);
 					session->passport_info.file = tmp;
 				}
@@ -1494,28 +1493,27 @@
 			}
 		}
 	}
-
-	return TRUE;
 }
 /**************************************************************************
  * Switchboards
  **************************************************************************/
-static gboolean
-rng_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnSwitchBoard *swboard;
 	MsnUser *user;
 	const char *session_id;
 	char *host, *c;
 	int port;
 
-	session_id = params[0];
+	session = cmdproc->session;
+	session_id = cmd->params[0];
 
-	host = g_strdup(params[1]);
+	host = g_strdup(cmd->params[1]);
 
-	if ((c = strchr(host, ':')) != NULL) {
+	if ((c = strchr(host, ':')) != NULL)
+	{
 		*c = '\0';
 		port = atoi(c + 1);
 	}
@@ -1527,80 +1525,87 @@
 
 	swboard = msn_switchboard_new(session);
 
-	user = msn_user_new(session, params[4], NULL);
+	user = msn_user_new(session, cmd->params[4], NULL);
 
 	msn_switchboard_set_invited(swboard, TRUE);
-	msn_switchboard_set_session_id(swboard, params[0]);
-	msn_switchboard_set_auth_key(swboard, params[3]);
+	msn_switchboard_set_session_id(swboard, cmd->params[0]);
+	msn_switchboard_set_auth_key(swboard, cmd->params[3]);
 	msn_switchboard_set_user(swboard, user);
 
-	if (!msn_switchboard_connect(swboard, host, port)) {
+	if (!msn_switchboard_connect(swboard, host, port))
+	{
 		gaim_debug(GAIM_DEBUG_ERROR, "msn",
 				   "Unable to connect to switchboard on %s, port %d\n",
 				   host, port);
 
 		g_free(host);
 
-		return FALSE;
+		return;
 	}
 
 	g_free(host);
-
-	return TRUE;
 }
 
-static gboolean
-xfr_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+xfr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	MsnSwitchBoard *swboard;
-	GaimConnection *gc = session->account->gc;
+	GaimConnection *gc;
 	char *host;
 	char *c;
 	int port;
 
-	if (strcmp(params[1], "SB") && strcmp(params[1], "NS")) {
+	session = cmdproc->session;
+	gc = session->account->gc;
+
+	if (strcmp(cmd->params[1], "SB") && strcmp(cmd->params[1], "NS"))
+	{
 		gaim_connection_error(gc, _("Got invalid XFR"));
 
-		return FALSE;
+		return;
 	}
 
-	host = g_strdup(params[2]);
+	host = g_strdup(cmd->params[2]);
 
-	if ((c = strchr(host, ':')) != NULL) {
+	if ((c = strchr(host, ':')) != NULL)
+	{
 		*c = '\0';
 		port = atoi(c + 1);
 	}
 	else
 		port = 1863;
 
-	if (!strcmp(params[1], "SB")) {
+	if (!strcmp(cmd->params[1], "SB"))
+	{
 		swboard = msn_session_find_unused_switch(session);
 
-		if (swboard == NULL) {
-			gaim_debug(GAIM_DEBUG_ERROR, "msn",
-					   "Received an XFR SB request when there's no unused "
-					   "switchboards!\n");
-			return FALSE;
+		if (swboard == NULL)
+		{
+			gaim_debug_error("msn",
+							 "Received an XFR SB request when there's no unused "
+							 "switchboards!\n");
+			return;
 		}
 
-		msn_switchboard_set_auth_key(swboard, params[4]);
+		msn_switchboard_set_auth_key(swboard, cmd->params[4]);
 
 		if (session->http_method)
 			port = 80;
 
-		if (!msn_switchboard_connect(swboard, host, port)) {
-			gaim_debug(GAIM_DEBUG_ERROR, "msn",
-					   "Unable to connect to switchboard on %s, port %d\n",
-					   host, port);
+		if (!msn_switchboard_connect(swboard, host, port))
+		{
+			gaim_debug_error("msn",
+							 "Unable to connect to switchboard on %s, port %d\n",
+							 host, port);
 
 			g_free(host);
 
-			return FALSE;
+			return;
 		}
 	}
-	else if (!strcmp(params[1], "NS")) {
+	else if (!strcmp(cmd->params[1], "NS"))
+	{
 		if (!msn_notification_connect(session->notification_conn, host,
 									  port))
 		{
@@ -1609,27 +1614,30 @@
 
 			g_free(host);
 
-			return FALSE;
+			return;
 		}
 	}
 
 	g_free(host);
-
-	return TRUE;
 }
 
 /**************************************************************************
  * Message Types
  **************************************************************************/
-static gboolean
-profile_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	MsnSession *session = servconn->session;
+	MsnSession *session;
 	const char *value;
+	const char *passport;
 
-	if (strcmp(msg->passport, "Hotmail")) {
+	session = cmdproc->session;
+	passport = msg->passport;
+
+	if (strcmp(passport, "Hotmail"))
+	{
 		/* This isn't an official message. */
-		return TRUE;
+		return;
 	}
 
 	if ((value = msn_message_get_attr(msg, "kv")) != NULL)
@@ -1646,39 +1654,45 @@
 
 	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
 		session->passport_info.client_port = ntohs(atoi(value));
-
-	return TRUE;
 }
 
-static gboolean
-initial_email_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	GHashTable *table;
 	const char *unread;
+	const char *passport;
 
-	if (strcmp(msg->passport, "Hotmail")) {
+	session = cmdproc->session;
+	gc = session->account->gc;
+	passport = msg->passport;
+
+	if (strcmp(passport, "Hotmail"))
+	{
 		/* This isn't an official message. */
-		return TRUE;
+		return;
 	}
 
 	if (!gaim_account_get_check_mail(session->account))
-		return TRUE;
+		return;
 
-	if (session->passport_info.file == NULL) {
-		msn_servconn_send_command(servconn, "URL", "INBOX");
+	if (session->passport_info.file == NULL)
+	{
+		msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX");
 
-		msn_servconn_queue_message(servconn, "URL", msg);
+		msn_cmdproc_queue_message(cmdproc, "URL", msg);
 
-		return TRUE;
+		return;
 	}
 
 	table = msn_message_get_hashtable_from_body(msg);
 
 	unread = g_hash_table_lookup(table, "Inbox-Unread");
 
-	if (unread != NULL) {
+	if (unread != NULL)
+	{
 		int count = atoi(unread);
 
 		if (count != 0)
@@ -1696,34 +1710,39 @@
 	}
 
 	g_hash_table_destroy(table);
-
-	return TRUE;
 }
 
-static gboolean
-email_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	MsnSession *session;
+	GaimConnection *gc;
 	GHashTable *table;
 	char *from, *subject, *tmp;
+	const char *passport;
+
+	session = cmdproc->session;
+	gc = session->account->gc;
+	passport = msg->passport;
 
 	from = subject = NULL;
 
-	if (strcmp(msg->passport, "Hotmail")) {
+	if (strcmp(passport, "Hotmail"))
+	{
 		/* This isn't an official message. */
-		return TRUE;
+		return;
 	}
 
 	if (!gaim_account_get_check_mail(session->account))
-		return TRUE;
+		return;
 
-	if (session->passport_info.file == NULL) {
-		msn_servconn_send_command(servconn, "URL", "INBOX");
+	if (session->passport_info.file == NULL)
+	{
+		msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX");
 
-		msn_servconn_queue_message(servconn, "URL", msg);
+		msn_cmdproc_queue_message(cmdproc, "URL", msg);
 
-		return TRUE;
+		return;
 	}
 
 	table = msn_message_get_hashtable_from_body(msg);
@@ -1747,29 +1766,33 @@
 		g_free(subject);
 
 	g_hash_table_destroy(table);
-
-	return TRUE;
 }
 
-static gboolean
-system_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 	GHashTable *table;
 	const char *type_s;
+	const char *passport;
 
-	if (strcmp(msg->passport, "Hotmail")) {
+	passport = msg->passport;
+
+	if (strcmp(passport, "Hotmail"))
+	{
 		/* This isn't an official message. */
-		return TRUE;
+		return;
 	}
 
 	table = msn_message_get_hashtable_from_body(msg);
 
-	if ((type_s = g_hash_table_lookup(table, "Type")) != NULL) {
+	if ((type_s = g_hash_table_lookup(table, "Type")) != NULL)
+	{
 		int type = atoi(type_s);
 		char buf[MSN_BUF_LEN];
 		int minutes;
 
-		switch (type) {
+		switch (type)
+		{
 			case 1:
 				minutes = atoi(g_hash_table_lookup(table, "Arg1"));
 				g_snprintf(buf, sizeof(buf), ngettext(
@@ -1791,41 +1814,46 @@
 		}
 
 		if (*buf != '\0')
-			gaim_notify_info(servconn->session->account->gc, NULL, buf, NULL);
+			gaim_notify_info(cmdproc->session->account->gc, NULL, buf, NULL);
 	}
 
 	g_hash_table_destroy(table);
-
-	return TRUE;
 }
 
 static gboolean
 connect_cb(MsnServConn *servconn)
 {
-	MsnSession *session = servconn->session;
-	GaimAccount *account = session->account;
-	GaimConnection *gc = gaim_account_get_connection(account);
-	char proto_vers[256];
+	MsnCmdProc *cmdproc;
+	MsnSession *session;
+	GaimAccount *account;
+	GaimConnection *gc;
+	char **a, **c, *vers;
 	size_t i;
 
-	proto_vers[0] = '\0';
+	g_return_val_if_fail(servconn != NULL, FALSE);
 
-	for (i = 7; i <= session->protocol_ver; i++)
-	{
-		char old_buf[256];
+	cmdproc = servconn->cmdproc;
+	session = servconn->session;
+	account = session->account;
+	gc = gaim_account_get_connection(account);
 
-		strcpy(old_buf, proto_vers);
+	/* Allocate an array for CVR0, NULL, and all the versions */
+	a = c = g_new0(char *, session->protocol_ver - 8 + 3);
 
-		g_snprintf(proto_vers, sizeof(proto_vers), "MSNP%d %s", (int)i, old_buf);
-	}
+	for (i = session->protocol_ver; i >= 8; i--)
+		*c++ = g_strdup_printf("MSNP%d", i);
+	
+	*c++ = g_strdup("CVR0");
 
-	strncat(proto_vers, "CVR0", sizeof(proto_vers));
+	vers = g_strjoinv(" ", a);
 
-	if (!msn_servconn_send_command(servconn, "VER", proto_vers))
-	{
-		gaim_connection_error(gc, _("Unable to write to server"));
+	msn_cmdproc_send(cmdproc, "VER", "%s", vers);
+
+	g_strfreev(a);
+	g_free(vers);
+	
+	if (cmdproc->error)
 		return FALSE;
-	}
 
 	session->user = msn_user_new(session,
 								 gaim_account_get_username(account), NULL);
@@ -1836,77 +1864,97 @@
 	return TRUE;
 }
 
+void
+msn_notification_init(void)
+{
+	cbs_table = msn_table_new();
+
+	/* Register the command callbacks. */
+
+	/* Syncing */
+	msn_table_add_cmd(cbs_table, NULL, "GTC", NULL);
+	msn_table_add_cmd(cbs_table, NULL, "BLP", blp_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "PRP", prp_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "LSG", lsg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "LST", lst_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd);
+
+	/* Syncronous */
+	/* msn_table_add_cmd(cbs_table, "CHG", "CHG", chg_cmd); */
+	msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
+	msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
+	msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
+	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
+	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
+	msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
+	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
+	msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
+	msn_table_add_cmd(cbs_table, "INF", "INF", inf_cmd);
+	msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
+	msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
+	/* msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd); */
+	/* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
+	msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
+	msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
+	msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
+	msn_table_add_cmd(cbs_table, "XFR", "XFR", xfr_cmd);
+
+	/* Asyncronous */
+	msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
+
+	msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
+
+	msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
+	msn_table_add_cmd(cbs_table, NULL, "QNG", NULL);
+	msn_table_add_cmd(cbs_table, NULL, "FLN", fln_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "NLN", nln_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
+
+	msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
+
+	msn_table_add_error(cbs_table, "ADD", add_error);
+
+	/* Register the message type callbacks. */
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsprofile",
+							profile_msg);
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsinitialemailnotification",
+							initial_email_msg);
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsemailnotification",
+							email_msg);
+	msn_table_add_msg_type(cbs_table,
+							"application/x-msmsgssystemmessage",
+							system_msg);
+}
+
+void
+msn_notification_end(void)
+{
+	msn_table_destroy(cbs_table);
+}
+
 MsnServConn *
 msn_notification_new(MsnSession *session)
 {
 	MsnServConn *notification;
+	MsnCmdProc *cmdproc;
 
-	notification = msn_servconn_new(session);
+	notification = msn_servconn_new(session, MSN_SERVER_NS);
+	cmdproc = notification->cmdproc;
 
 	msn_servconn_set_connect_cb(notification, connect_cb);
 
 	if (session->http_method)
 		notification->http_data->server_type = "NS";
 
-	if (notification_commands == NULL) {
-		/* Register the command callbacks. */
-		msn_servconn_register_command(notification, "ADD",       add_cmd);
-		msn_servconn_register_command(notification, "ADG",       adg_cmd);
-		msn_servconn_register_command(notification, "BLP",       blp_cmd);
-		msn_servconn_register_command(notification, "BPR",       bpr_cmd);
-		msn_servconn_register_command(notification, "CHG",       blank_cmd);
-		msn_servconn_register_command(notification, "CHL",       chl_cmd);
-		msn_servconn_register_command(notification, "CVR",       cvr_cmd);
-		msn_servconn_register_command(notification, "FLN",       fln_cmd);
-		msn_servconn_register_command(notification, "GTC",       blank_cmd);
-		msn_servconn_register_command(notification, "ILN",       iln_cmd);
-		msn_servconn_register_command(notification, "INF",       inf_cmd);
-		msn_servconn_register_command(notification, "IPG",       ipg_cmd);
-		msn_servconn_register_command(notification, "LSG",       lsg_cmd);
-		msn_servconn_register_command(notification, "LST",       lst_cmd);
-		msn_servconn_register_command(notification, "MSG",       msg_cmd);
-		msn_servconn_register_command(notification, "NLN",       nln_cmd);
-		msn_servconn_register_command(notification, "NOT",       not_cmd);
-		msn_servconn_register_command(notification, "OUT",       out_cmd);
-		msn_servconn_register_command(notification, "PRP",       prp_cmd);
-		msn_servconn_register_command(notification, "QNG",       blank_cmd);
-		msn_servconn_register_command(notification, "QRY",       blank_cmd);
-		msn_servconn_register_command(notification, "REA",       rea_cmd);
-		msn_servconn_register_command(notification, "REG",       reg_cmd);
-		msn_servconn_register_command(notification, "REM",       rem_cmd);
-		msn_servconn_register_command(notification, "RMG",       rmg_cmd);
-		msn_servconn_register_command(notification, "RNG",       rng_cmd);
-		msn_servconn_register_command(notification, "SYN",       syn_cmd);
-		msn_servconn_register_command(notification, "URL",       url_cmd);
-		msn_servconn_register_command(notification, "USR",       usr_cmd);
-		msn_servconn_register_command(notification, "VER",       ver_cmd);
-		msn_servconn_register_command(notification, "XFR",       xfr_cmd);
-		msn_servconn_register_command(notification, "_UNKNOWN_", unknown_cmd);
-
-		/* Register the message type callbacks. */
-		msn_servconn_register_msg_type(notification, "text/x-msmsgsprofile",
-									   profile_msg);
-		msn_servconn_register_msg_type(notification,
-									   "text/x-msmsgsinitialemailnotification",
-									   initial_email_msg);
-		msn_servconn_register_msg_type(notification,
-									   "text/x-msmsgsemailnotification",
-									   email_msg);
-		msn_servconn_register_msg_type(notification,
-									   "application/x-msmsgssystemmessage",
-									   system_msg);
-
-		/* Save these for future use. */
-		notification_commands  = notification->commands;
-		notification_msg_types = notification->msg_types;
-	}
-	else {
-		g_hash_table_destroy(notification->commands);
-		g_hash_table_destroy(notification->msg_types);
-
-		notification->commands  = notification_commands;
-		notification->msg_types = notification_msg_types;
-	}
+	cmdproc->cbs_table  = cbs_table;
 
 	return notification;
 }
--- a/src/protocols/msn/notification.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/notification.h	Sun Apr 25 22:02:06 2004 +0000
@@ -25,6 +25,16 @@
 #include "session.h"
 #include "servconn.h"
 
+/**
+ * Initialize the variables for notifiaction server creation.
+ */
+void msn_notification_init(void);
+
+/**
+ * Destroy the variables for notification server creation.
+ */
+void msn_notification_end(void);
+
 MsnServConn *msn_notification_new(MsnSession *session);
 gboolean msn_notification_connect(MsnServConn *notification,
 							  const char *host, int port);
--- a/src/protocols/msn/page.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/page.h	Sun Apr 25 22:02:06 2004 +0000
@@ -53,12 +53,12 @@
 void msn_page_destroy(MsnPage *page);
 
 /**
- * Converts a page to a payload string.
+ * Generates the payload data of a page.
  *
  * @param page     The page.
  * @param ret_size The returned size of the payload.
  *
- * @return The payload string of a page.
+ * @return The payload data of a page.
  */
 char *msn_page_gen_payload(const MsnPage *page, size_t *ret_size);
 
--- a/src/protocols/msn/servconn.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/servconn.c	Sun Apr 25 22:02:06 2004 +0000
@@ -25,36 +25,6 @@
 
 static void read_cb(gpointer data, gint source, GaimInputCondition cond);
 
-typedef struct
-{
-	char *command;
-	MsnMessage *msg;
-
-} MsnQueueEntry;
-
-gboolean
-msn_servconn_process_message(MsnServConn *servconn, MsnMessage *msg)
-{
-	MsnServConnMsgCb cb;
-
-	cb = g_hash_table_lookup(servconn->msg_types,
-							 msn_message_get_content_type(msg));
-
-	if (cb == NULL)
-	{
-		gaim_debug(GAIM_DEBUG_WARNING, "msn",
-				   "Unhandled content-type '%s': %s\n",
-				   msn_message_get_content_type(msg),
-				   msn_message_get_body(msg));
-
-		return FALSE;
-	}
-
-	cb(servconn, msg);
-
-	return TRUE;
-}
-
 static void
 connect_cb(gpointer data, gint source, GaimInputCondition cond)
 {
@@ -79,7 +49,7 @@
 }
 
 MsnServConn *
-msn_servconn_new(MsnSession *session)
+msn_servconn_new(MsnSession *session, MsnServerType type)
 {
 	MsnServConn *servconn;
 
@@ -87,7 +57,11 @@
 
 	servconn = g_new0(MsnServConn, 1);
 
+	servconn->type = type;
+
 	servconn->session = session;
+	servconn->cmdproc = msn_cmdproc_new(session);
+	servconn->cmdproc->servconn = servconn;
 
 	if (session->http_method)
 	{
@@ -95,12 +69,7 @@
 		servconn->http_data->virgin = TRUE;
 	}
 
-	servconn->commands = g_hash_table_new_full(g_str_hash, g_str_equal,
-											   g_free, NULL);
-
-	servconn->msg_types = g_hash_table_new_full(g_str_hash, g_str_equal,
-												g_free, NULL);
-
+	servconn->num = session->servconns_count++;
 	session->servconns = g_list_append(session->servconns, servconn);
 
 	return servconn;
@@ -175,19 +144,6 @@
 	servconn->rx_len = 0;
 	servconn->payload_len = 0;
 
-	while (servconn->txqueue != NULL) {
-		g_free(servconn->txqueue->data);
-
-		servconn->txqueue = g_slist_remove(servconn->txqueue,
-										   servconn->txqueue->data);
-	}
-
-	while (servconn->msg_queue != NULL) {
-		MsnQueueEntry *entry = servconn->msg_queue->data;
-
-		msn_servconn_unqueue_message(servconn, entry->msg);
-	}
-
 	if (servconn->disconnect_cb != NULL)
 		servconn->disconnect_cb(servconn);
 
@@ -201,6 +157,12 @@
 
 	g_return_if_fail(servconn != NULL);
 
+	if (servconn->processing)
+	{
+		servconn->wasted = TRUE;
+		return;
+	}
+
 	session = servconn->session;
 
 	session->servconns = g_list_remove(session->servconns, servconn);
@@ -208,49 +170,10 @@
 	if (servconn->connected)
 		msn_servconn_disconnect(servconn);
 
-#if 0
-	/* shx: not used */
-	if (servconn->host != NULL)
-		g_free(servconn->host);
-#endif
-
+	msn_cmdproc_destroy(servconn->cmdproc);
 	g_free(servconn);
 }
 
-#if 0
-/* shx: this isn't used */
-
-void
-msn_servconn_set_server(MsnServConn *servconn, const char *server, int port)
-{
-	g_return_if_fail(servconn != NULL);
-	g_return_if_fail(server != NULL);
-	g_return_if_fail(port > 0);
-
-	if (servconn->host != NULL)
-		g_free(servconn->host);
-
-	servconn->host = g_strdup(server);
-	servconn->port = port;
-}
-
-const char *
-msn_servconn_get_server(const MsnServConn *servconn)
-{
-	g_return_val_if_fail(servconn != NULL, NULL);
-
-	return servconn->host;
-}
-
-int
-msn_servconn_get_port(const MsnServConn *servconn)
-{
-	g_return_val_if_fail(servconn != NULL, 0);
-
-	return servconn->port;
-}
-#endif
-
 void
 msn_servconn_set_connect_cb(MsnServConn *servconn,
 							gboolean (*connect_cb)(MsnServConn *servconn))
@@ -270,228 +193,95 @@
 	servconn->disconnect_cb = disconnect_cb;
 }
 
-size_t
-msn_servconn_write(MsnServConn *servconn, const char *buf, size_t size)
+static void
+show_error(MsnServConn *servconn)
 {
-	g_return_val_if_fail(servconn != NULL, 0);
-
-	gaim_debug(GAIM_DEBUG_MISC, "msn", "C: %s%s", buf,
-			   (*(buf + size - 1) == '\n' ? "" : "\n"));
-
-	if (servconn->session->http_method)
-		return msn_http_servconn_write(servconn, buf, size,
-									   servconn->http_data->server_type);
-	else
-		return write(servconn->fd, buf, size);
-}
+	GaimConnection *gc;
+	char *tmp;
+	char *cmd;
 
-gboolean
-msn_servconn_send_command(MsnServConn *servconn, const char *command,
-						  const char *params)
-{
-	char buf[MSN_BUF_LEN];
-
-	g_return_val_if_fail(servconn != NULL, FALSE);
-	g_return_val_if_fail(command != NULL, FALSE);
-
-	if (params == NULL)
-		g_snprintf(buf, sizeof(buf), "%s %u\r\n", command,
-				   servconn->session->trId++);
-	else
-		g_snprintf(buf, sizeof(buf), "%s %u %s\r\n",
-				   command, servconn->session->trId++, params);
-
-	return (msn_servconn_write(servconn, buf, strlen(buf)) > 0);
-}
+	const char *names[] = { "Notification", "Switchboard" };
+	const char *name;
+	
+	gc = gaim_account_get_connection(servconn->session->account);
+	name = names[servconn->type];
 
-void
-msn_servconn_queue_message(MsnServConn *servconn, const char *command,
-						   MsnMessage *msg)
-{
-	MsnQueueEntry *entry;
-
-	g_return_if_fail(servconn != NULL);
-	g_return_if_fail(msg != NULL);
-
-	entry          = g_new0(MsnQueueEntry, 1);
-	entry->msg     = msg;
-	entry->command = (command == NULL ? NULL : g_strdup(command));
-
-	servconn->msg_queue = g_slist_append(servconn->msg_queue, entry);
-
-	msn_message_ref(msg);
-}
-
-void
-msn_servconn_unqueue_message(MsnServConn *servconn, MsnMessage *msg)
-{
-	MsnQueueEntry *entry = NULL;
-	GSList *l;
-
-	g_return_if_fail(servconn != NULL);
-	g_return_if_fail(msg != NULL);
-
-	for (l = servconn->msg_queue; l != NULL; l = l->next)
+	switch (servconn->cmdproc->error)
 	{
-		entry = l->data;
-
-		if (entry->msg == msg)
+		case MSN_ERROR_CONNECT:
+			tmp = g_strdup_printf(_("Unable to connect to %s server"),
+								  name);
+			break;
+		case MSN_ERROR_WRITE:
+			tmp = g_strdup_printf(_("Error writing to %s server"), name);
 			break;
-
-		entry = NULL;
+		case MSN_ERROR_READ:
+			cmd = servconn->cmdproc->last_trans;
+			tmp = g_strdup_printf(_("Error reading from %s server. Last"
+									"command was:\n %s"), name, cmd);
+			break;
+		default:
+			tmp = g_strdup_printf(_("Unknown error from %s server"), name);
+			break;
 	}
 
-	g_return_if_fail(entry != NULL);
-
-	msn_message_unref(msg);
-
-	servconn->msg_queue = g_slist_remove(servconn->msg_queue, entry);
-
-	if (entry->command != NULL)
-		g_free(entry->command);
-
-	g_free(entry);
-}
-
-void
-msn_servconn_register_command(MsnServConn *servconn, const char *command,
-							  MsnServConnCommandCb cb)
-{
-	char *command_up;
-
-	g_return_if_fail(servconn != NULL);
-	g_return_if_fail(command != NULL);
-	g_return_if_fail(cb != NULL);
-
-	command_up = g_ascii_strup(command, -1);
-
-	g_hash_table_insert(servconn->commands, command_up, cb);
-}
-
-void
-msn_servconn_register_msg_type(MsnServConn *servconn,
-							   const char *content_type,
-							   MsnServConnMsgCb cb)
-{
-	g_return_if_fail(servconn != NULL);
-	g_return_if_fail(content_type != NULL);
-	g_return_if_fail(cb != NULL);
-
-	g_hash_table_insert(servconn->msg_types, g_strdup(content_type), cb);
+	gaim_connection_error(gc, tmp);
+	
+	g_free(tmp);
 }
 
 static void
 failed_io(MsnServConn *servconn)
 {
-	GaimConnection *gc =
-		gaim_account_get_connection(servconn->session->account);
-
-	gaim_connection_error(gc, _("IO Error."));
+	show_error(servconn);
 
 	msn_servconn_disconnect(servconn);
 }
 
-static void
-process_payload(MsnServConn *servconn, char *payload, int payload_len)
+size_t
+msn_servconn_write(MsnServConn *servconn, const char *buf, size_t size)
 {
-	g_return_if_fail(servconn             != NULL);
-	g_return_if_fail(servconn->payload_cb != NULL);
-
-	servconn->payload_cb(servconn, payload, payload_len);
-}
+	size_t ret = FALSE;
 
-static int
-process_cmd_text(MsnServConn *servconn, const char *command)
-{
-	MsnSession *session = servconn->session;
-	MsnServConnCommandCb cb;
-	GSList *l, *l_next = NULL;
-	gboolean result;
-	size_t param_count = 0;
-	char *param_start;
-	char **params = NULL;
+	g_return_val_if_fail(servconn != NULL, 0);
 
-#if 0
-	if (servconn->last_cmd != NULL)
-		gfree(servconn->last_cmd);
-
-	servconn->last_cmd = gstrdup(command);
-#endif
-
-	/**
-	 * See how many spaces we have in this.
-	 */
-	param_start = strchr(command, ' ');
-
-	if (param_start != NULL) {
-		params = g_strsplit(param_start + 1, " ", 0);
-
-		for (param_count = 0; params[param_count] != NULL; param_count++)
-			;
-
-		*param_start = '\0';
+	if (servconn->session->http_method)
+	{
+		ret = msn_http_servconn_write(servconn, buf, size,
+									   servconn->http_data->server_type);
+	}
+	else
+	{
+		ret = write(servconn->fd, buf, size);
 	}
 
-	cb = g_hash_table_lookup(servconn->commands, command);
-
-	if (cb == NULL) {
-		cb = g_hash_table_lookup(servconn->commands, "_UNKNOWN_");
-
-		if (cb == NULL) {
-			gaim_debug(GAIM_DEBUG_WARNING, "msn",
-					   "Unhandled command '%s'\n", command);
-
-			if (params != NULL)
-				g_strfreev(params);
-
-			return TRUE;
-		}
+	if (ret < 0)
+	{
+		servconn->cmdproc->error = MSN_ERROR_WRITE;
+		failed_io(servconn);
 	}
 
-	result = cb(servconn, command, (const char **)params, param_count);
-
-	if (params != NULL)
-		g_strfreev(params);
-
-	if (g_list_find(session->servconns, servconn) == NULL)
-		return result;
-
-	/* Process all queued messages that are waiting on this command. */
-	for (l = servconn->msg_queue; l != NULL; l = l_next) {
-		MsnQueueEntry *entry = l->data;
-		MsnMessage *msg;
-
-		l_next = l->next;
-
-		if (entry->command == NULL ||
-			!g_ascii_strcasecmp(entry->command, command)) {
-
-			msg = entry->msg;
-
-			msn_servconn_process_message(servconn, msg);
-
-			msn_servconn_unqueue_message(servconn, msg);
-
-			entry->msg = NULL;
-		}
-	}
-
-	return result;
+	return ret;
 }
 
 static void
 read_cb(gpointer data, gint source, GaimInputCondition cond)
 {
-	MsnServConn *servconn = (MsnServConn *)data;
-	MsnSession *session = servconn->session;
+	MsnServConn *servconn;
+	MsnSession *session;
 	char buf[MSN_BUF_LEN];
 	char *cur, *end, *old_rx_buf;
 	int len, cur_len;
 
+	servconn = data;
+	session = servconn->session;
+
 	len = read(servconn->fd, buf, sizeof(buf) - 1);
 
 	if (len <= 0)
 	{
+		servconn->cmdproc->error = MSN_ERROR_READ;
+
 		failed_io(servconn);
 
 		return;
@@ -568,6 +358,8 @@
 
 	end = old_rx_buf = servconn->rx_buf;
 
+	servconn->processing = TRUE;
+
 	do
 	{
 		cur = end;
@@ -598,13 +390,12 @@
 
 		if (servconn->payload_len)
 		{
-			process_payload(servconn, cur, cur_len);
+			msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len);
 			servconn->payload_len = 0;
 		}
 		else
 		{
-			gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s\n", cur);
-			process_cmd_text(servconn, cur);
+			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
 		}
 	} while (servconn->connected && servconn->rx_len);
 
@@ -616,6 +407,8 @@
 			servconn->rx_buf = NULL;
 	}
 
+	servconn->processing = FALSE;
+
 	if (servconn->wasted)
 		msn_servconn_destroy(servconn);
 
--- a/src/protocols/msn/servconn.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/servconn.h	Sun Apr 25 22:02:06 2004 +0000
@@ -22,64 +22,55 @@
 #ifndef _MSN_SERVCONN_H_
 #define _MSN_SERVCONN_H_
 
-#include "proxy.h"
+typedef struct _MsnServConn MsnServConn;
 
-typedef struct _MsnServConn MsnServConn;
+#include "cmdproc.h"
+#include "proxy.h"
 
 #include "msg.h"
 #include "httpmethod.h"
 
-typedef gboolean (*MsnServConnCommandCb)(MsnServConn *servconn,
-										 const char *cmd, const char **params,
-										 size_t param_count);
+#include "session.h"
 
-typedef gboolean (*MsnServConnMsgCb)(MsnServConn *servconn, MsnMessage *msg);
+typedef enum
+{
+	MSN_SERVER_NS,
+	MSN_SERVER_SB,
+	MSN_SERVER_NX,
+	MSN_SERVER_DC,
+	MSN_SERVER_HT
 
-typedef void (*MsnPayloadCb)(MsnServConn *servconn, char *payload,
-							 size_t len);
-
-#include "session.h"
+} MsnServerType;
 
 struct _MsnServConn
 {
+	MsnServerType type;
 	MsnSession *session;
+	MsnCmdProc *cmdproc;
 
 	gboolean connected;
+	gboolean processing;
 	gboolean wasted;
 
+	int num;
+
 	MsnHttpMethodData *http_data;
 
-#if 0
-	/* shx: not used */
-	char *host;
-	int port;
-#endif
-
 	int fd;
 	int inpa;
 
 	char *rx_buf;
 	int rx_len;
 
-	MsnPayloadCb payload_cb;
 	int payload_len;
 
-	GSList *msg_queue;
-
-	GSList *txqueue;
-
-	char *msg_passport;
-
-	GHashTable *commands;
-	GHashTable *msg_types;
-
 	gboolean (*connect_cb)(MsnServConn *servconn);
 	void (*disconnect_cb)(MsnServConn *servconn);
 
 	void *data;
 };
 
-MsnServConn *msn_servconn_new(MsnSession *session);
+MsnServConn *msn_servconn_new(MsnSession *session, MsnServerType type);
 
 void msn_servconn_destroy(MsnServConn *servconn);
 
@@ -87,15 +78,6 @@
 							  int port);
 void msn_servconn_disconnect(MsnServConn *servconn);
 
-#if 0
-/* shx: not used */
-void msn_servconn_set_server(MsnServConn *servconn, const char *server,
-							 int port);
-
-const char *msn_servconn_get_server(const MsnServConn *servconn);
-int msn_servconn_get_port(const MsnServConn *servconn);
-#endif
-
 void msn_servconn_set_connect_cb(MsnServConn *servconn,
 		gboolean (*connect_cb)(MsnServConn *servconn));
 
@@ -105,23 +87,4 @@
 size_t msn_servconn_write(MsnServConn *servconn, const char *buf,
 						  size_t size);
 
-gboolean msn_servconn_send_command(MsnServConn *servconn, const char *command,
-								   const char *params);
-
-void msn_servconn_queue_message(MsnServConn *servconn, const char *command,
-								MsnMessage *msg);
-
-void msn_servconn_unqueue_message(MsnServConn *servconn, MsnMessage *msg);
-
-void msn_servconn_register_command(MsnServConn *servconn,
-								   const char *command,
-								   MsnServConnCommandCb cb);
-
-void msn_servconn_register_msg_type(MsnServConn *servconn,
-									const char *content_type,
-									MsnServConnMsgCb cb);
-
-gboolean msn_servconn_process_message(MsnServConn *servconn,
-									  MsnMessage *msg);
-
 #endif /* _MSN_SERVCONN_H_ */
--- a/src/protocols/msn/session.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/session.c	Sun Apr 25 22:02:06 2004 +0000
@@ -158,13 +158,16 @@
 msn_session_open_switchboard(MsnSession *session)
 {
 	MsnSwitchBoard *swboard;
+	MsnCmdProc *cmdproc;
 
 	g_return_val_if_fail(session != NULL, NULL);
 
-	if (msn_servconn_send_command(session->notification_conn, "XFR", "SB") < 0)
-	{
+	cmdproc = session->notification_conn->cmdproc;
+
+	msn_cmdproc_send(cmdproc, "XFR", "%s", "SB");
+
+	if (cmdproc->error)
 		return NULL;
-	}
 
 	swboard = msn_switchboard_new(session);
 
@@ -174,13 +177,14 @@
 gboolean
 msn_session_change_status(MsnSession *session, const char *state)
 {
-	MsnUser *user = session->user;
+	MsnCmdProc *cmdproc;
+	MsnUser *user;
 	MsnObject *msnobj;
-	char buf[MSN_BUF_LEN];
 
 	g_return_val_if_fail(session != NULL, FALSE);
 	g_return_val_if_fail(state   != NULL, FALSE);
 
+	user = session->user;
 	msnobj = msn_user_get_object(user);
 
 	if (state != session->away_state)
@@ -191,26 +195,22 @@
 		session->away_state = g_strdup(state);
 	}
 
+	cmdproc = session->notification_conn->cmdproc;
+
 	if (msnobj == NULL)
-		g_snprintf(buf, sizeof(buf), "%s %d", state, MSN_CLIENT_ID);
+	{
+		msn_cmdproc_send(cmdproc, "CHG", "%s %d", state, MSN_CLIENT_ID);
+	}
 	else
 	{
 		char *msnobj_str = msn_object_to_string(msnobj);
 
-		g_snprintf(buf, sizeof(buf), "%s %d %s", state, MSN_CLIENT_ID,
-				   gaim_url_encode(msnobj_str));
+		msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state, MSN_CLIENT_ID,
+						 gaim_url_encode(msnobj_str));
 
 		g_free(msnobj_str);
 	}
 
-	if (!msn_servconn_send_command(session->notification_conn, "CHG", buf))
-	{
-		gaim_connection_error(gaim_account_get_connection(session->account),
-							  _("Write error"));
-
-		return FALSE;
-	}
-
 	return TRUE;
 }
 
--- a/src/protocols/msn/session.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/session.h	Sun Apr 25 22:02:06 2004 +0000
@@ -49,14 +49,13 @@
 
 	MsnNexus *nexus;
 
-	unsigned int trId;
-
 	gboolean http_method;
 	gint http_poll_timer;
 
 	MsnUsers *users;
 	MsnGroups *groups;
 
+	int servconns_count;
 	GList *servconns;
 	GList *switches;
 
--- a/src/protocols/msn/switchboard.c	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/switchboard.c	Sun Apr 25 22:02:06 2004 +0000
@@ -25,13 +25,12 @@
 #include "switchboard.h"
 #include "utils.h"
 
-static GHashTable *switchboard_commands  = NULL;
-static GHashTable *switchboard_msg_types = NULL;
+static MsnTable *cbs_table = NULL;
 
 /**************************************************************************
  * Utility functions
  **************************************************************************/
-static gboolean
+static void
 send_clientcaps(MsnSwitchBoard *swboard)
 {
 	MsnMessage *msg;
@@ -42,54 +41,24 @@
 	msn_message_set_attr(msg, "User-Agent", NULL);
 	msn_message_set_body(msg, MSN_CLIENTINFO);
 
-	if (!msn_switchboard_send_msg(swboard, msg))
-	{
-		gaim_debug_warning("msn",
-						   "Unable to send clientcaps. "
-						   "Disconnecting from switchboard.\n");
-		msn_switchboard_destroy(swboard);
-
-		msn_message_destroy(msg);
-
-		return FALSE;
-	}
+	msn_switchboard_send_msg(swboard, msg);
 
 	msn_message_destroy(msg);
-
-	return TRUE;
-}
-
-/**************************************************************************
- * Catch-all commands
- **************************************************************************/
-static gboolean
-blank_cmd(MsnServConn *servconn, const char *command, const char **params,
-		   size_t param_count)
-{
-	return TRUE;
-}
-
-static gboolean
-unknown_cmd(MsnServConn *servconn, const char *command, const char **params,
-			 size_t param_count)
-{
-	gaim_debug(GAIM_DEBUG_ERROR, "msg",
-			   "Handled switchboard message: %s\n", command);
-
-	return FALSE;
 }
 
 /**************************************************************************
  * Switchboard Commands
  **************************************************************************/
-static gboolean
-ans_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSwitchBoard *swboard = servconn->data;
-	MsnSession *session = servconn->session;
+	MsnSwitchBoard *swboard;
+	MsnSession *session;
 
-	/*send_clientcaps(swboard);*/
+	swboard = cmdproc->servconn->data;
+	session = cmdproc->session;
+
+	/* send_clientcaps(swboard); */
 
 	if (0 && session->protocol_ver >= 9)
 	{
@@ -112,19 +81,21 @@
 		}
 	}
 
-	return TRUE;
+	swboard->joined = TRUE;
 }
 
-static gboolean
-bye_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account = servconn->session->account;
-	MsnSwitchBoard *swboard = servconn->data;
-	const char *user = params[0];
+	GaimAccount *account;
+	MsnSwitchBoard *swboard;
+	const char *user = cmd->params[0];
+
+	account = cmdproc->session->account;
+	swboard = cmdproc->servconn->data;
 
 	if (swboard->hidden)
-		return TRUE;
+		return;
 
 	if (swboard->chat != NULL)
 		gaim_conv_chat_remove_user(GAIM_CONV_CHAT(swboard->chat), user, NULL);
@@ -142,7 +113,7 @@
 
 		*buf = '\0';
 
-		if (param_count == 2 && atoi(params[1]) == 1)
+		if (cmd->param_count == 2 && atoi(cmd->params[1]) == 1)
 		{
 			if (gaim_prefs_get_bool("/plugins/prpl/msn/conv_timeout_notice"))
 			{
@@ -169,32 +140,33 @@
 		}
 
 		msn_switchboard_destroy(swboard);
-
-		return FALSE;
 	}
-
-	return TRUE;
 }
 
-static gboolean
-iro_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account = servconn->session->account;
-	GaimConnection *gc = account->gc;
-	MsnSwitchBoard *swboard = servconn->data;
+	GaimAccount *account;
+	GaimConnection *gc;
+	MsnSwitchBoard *swboard;
 
-	swboard->total_users = atoi(params[2]) + 1;
+	account = cmdproc->session->account;
+	gc = account->gc;
+	swboard = cmdproc->servconn->data;
 
-	if (swboard->total_users > 2) {
-		if (swboard->chat == NULL) {
+	swboard->total_users = atoi(cmd->params[2]) + 1;
+
+	if (swboard->total_users > 2)
+	{
+		if (swboard->chat == NULL)
+		{
 			GaimConversation *conv;
 
 			conv = gaim_find_conversation_with_account(
 				msn_user_get_passport(swboard->user), account);
 
-			servconn->session->last_chat_id++;
-			swboard->chat_id = servconn->session->last_chat_id;
+			cmdproc->session->last_chat_id++;
+			swboard->chat_id = cmdproc->session->last_chat_id;
 			swboard->chat = serv_got_joined_chat(gc, swboard->chat_id,
 												 "MSN Chat");
 
@@ -204,36 +176,37 @@
 			gaim_conversation_destroy(conv);
 		}
 
-		gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->chat), params[3], NULL);
+		gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->chat), cmd->params[3], NULL);
 	}
-
-	return TRUE;
 }
 
-static gboolean
-joi_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account = servconn->session->account;
-	GaimConnection *gc = account->gc;
-	MsnSwitchBoard *swboard = servconn->data;
+	GaimAccount *account;
+	GaimConnection *gc;
+	MsnSwitchBoard *swboard;
 	const char *passport;
 
-	passport = params[0];
+	account = cmdproc->session->account;
+	gc = account->gc;
+	swboard = cmdproc->servconn->data;
+	passport = cmd->params[0];
 
-	if (swboard->total_users == 2 && swboard->chat == NULL) {
+	if (swboard->total_users == 2 && swboard->chat == NULL)
+	{
 		GaimConversation *conv;
 
 		conv = gaim_find_conversation_with_account(
 			msn_user_get_passport(swboard->user), account);
 
-		servconn->session->last_chat_id++;
-		swboard->chat_id = servconn->session->last_chat_id;
+		cmdproc->session->last_chat_id++;
+		swboard->chat_id = cmdproc->session->last_chat_id;
 		swboard->chat = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat");
 		gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->chat),
-						   msn_user_get_passport(swboard->user), NULL);
+								msn_user_get_passport(swboard->user), NULL);
 		gaim_conv_chat_add_user(GAIM_CONV_CHAT(swboard->chat),
-						   gaim_account_get_username(account), NULL);
+								gaim_account_get_username(account), NULL);
 
 		msn_user_unref(swboard->user);
 
@@ -245,69 +218,60 @@
 
 	swboard->total_users++;
 
-	while (servconn->txqueue) {
-		char *buf = servconn->txqueue->data;
-
-		servconn->txqueue = g_slist_remove(servconn->txqueue, buf);
+	swboard->joined = TRUE;
 
-		if (msn_servconn_write(swboard->servconn, buf, strlen(buf)) < 0) {
-			msn_switchboard_destroy(swboard);
+	msn_cmdproc_process_queue(cmdproc);
 
-			return FALSE;
-		}
-	}
-
-	return send_clientcaps(swboard);
+	send_clientcaps(swboard);
 }
 
 static void
-msg_cmd_post(MsnServConn *servconn, char *payload, size_t len)
+msg_cmd_post(MsnCmdProc *cmdproc, char *payload, size_t len)
 {
 	MsnMessage *msg = msn_message_new();
 
-	msg->passport = servconn->msg_passport;
-
 	msn_message_parse_payload(msg, payload, len);
 
-	msn_servconn_process_message(servconn, msg);
+	msg->passport = cmdproc->temp;
+	msn_cmdproc_process_msg(cmdproc, msg);
+	g_free(cmdproc->temp);
+	cmdproc->temp = NULL;
 
 	msn_message_destroy(msg);
 }
 
-static gboolean
-msg_cmd(MsnServConn *servconn, const char *command, const char **params,
-		size_t param_count)
+static void
+msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n");
 
-	servconn->payload_cb  = msg_cmd_post;
-	servconn->payload_len = atoi(params[2]);
-
-	servconn->msg_passport = g_strdup(params[0]);
-
-	return TRUE;
+	cmdproc->payload_cb  = msg_cmd_post;
+	cmdproc->servconn->payload_len = atoi(cmd->params[2]);
+	cmdproc->temp = g_strdup(cmd->params[0]);
 }
 
-static gboolean
-nak_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	/*
 	 * TODO: Investigate this, as it seems to occur frequently with
 	 *       the old prpl.
+	 *
+	 * shx: This will only happend in the new protocol when we ask for it
+	 *      in the message flags.
 	 */
-	gaim_notify_error(servconn->session->account->gc, NULL,
+	gaim_notify_error(cmdproc->servconn->session->account->gc, NULL,
 					  _("An MSN message may not have been received."), NULL);
-
-	return TRUE;
 }
 
-static gboolean
-out_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimConnection *gc = servconn->session->account->gc;
-	MsnSwitchBoard *swboard = servconn->data;
+	GaimConnection *gc;
+	MsnSwitchBoard *swboard;
+
+	gc = cmdproc->servconn->session->account->gc;
+	swboard = cmdproc->servconn->data;
 
 	if (swboard->chat != NULL)
 	{
@@ -316,46 +280,42 @@
 	}
 
 	msn_switchboard_destroy(swboard);
-
-	return FALSE;
 }
 
-static gboolean
-usr_cmd(MsnServConn *servconn, const char *command, const char **params,
-		 size_t param_count)
+static void
+usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	MsnSwitchBoard *swboard = servconn->data;
+	MsnSwitchBoard *swboard;
 
-	if (!msn_switchboard_send_command(swboard, "CAL",
-									  msn_user_get_passport(swboard->user)))
-	{
-		msn_switchboard_destroy(swboard);
+	swboard = cmdproc->servconn->data;
 
-		return FALSE;
-	}
-
-	return TRUE;
+	msn_cmdproc_send(swboard->cmdproc, "CAL", "%s",
+					 msn_user_get_passport(swboard->user));
 }
 
 /**************************************************************************
  * Message Types
  **************************************************************************/
-static gboolean
-plain_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	GaimConnection *gc = servconn->session->account->gc;
-	MsnSwitchBoard *swboard = servconn->data;
+	GaimConnection *gc;
+	MsnSwitchBoard *swboard;
 	char *body;
+	char *passport;
 	const char *value;
 
+	gc = cmdproc->session->account->gc;
+	swboard = cmdproc->servconn->data;
 	body = gaim_escape_html(msn_message_get_body(msg));
+	passport = msg->passport;
 
-	if (!strcmp(msg->passport, "messenger@microsoft.com") &&
+	if (!strcmp(passport, "messenger@microsoft.com") &&
 		strstr(body, "immediate security update"))
 	{
 		g_free(body);
 
-		return TRUE;
+		return;
 	}
 
 #if 0
@@ -366,7 +326,8 @@
 	}
 #endif
 
-	if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) {
+	if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
+	{
 		char *pre_format, *post_format;
 
 		msn_parse_format(value, &pre_format, &post_format);
@@ -381,49 +342,51 @@
 	{
 		serv_got_chat_in(gc,
 						 gaim_conv_chat_get_id(GAIM_CONV_CHAT(swboard->chat)),
-						 msg->passport, 0, body, time(NULL));
+						 passport, 0, body, time(NULL));
 	}
 	else
-		serv_got_im(gc, msg->passport, body, 0, time(NULL));
+		serv_got_im(gc, passport, body, 0, time(NULL));
 
 	g_free(body);
-
-	return TRUE;
 }
 
-static gboolean
-control_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	GaimConnection *gc = servconn->session->account->gc;
-	MsnSwitchBoard *swboard = servconn->data;
+	GaimConnection *gc;
+	MsnSwitchBoard *swboard;
+	char *passport;
 	const char *value;
 
+	gc = cmdproc->session->account->gc;
+	swboard = cmdproc->servconn->data;
+	passport = msg->passport;
+
 	if (swboard->chat == NULL &&
-		(value = msn_message_get_attr(msg, "TypingUser")) != NULL) {
-
-		serv_got_typing(gc, msg->passport, MSN_TYPING_RECV_TIMEOUT,
+		(value = msn_message_get_attr(msg, "TypingUser")) != NULL)
+	{
+		serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
 						GAIM_TYPING);
 	}
-
-	return TRUE;
 }
 
-static gboolean
-clientcaps_msg(MsnServConn *servconn, MsnMessage *msg)
+static void
+clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 #if 0
-	MsnSession *session = servconn->session;
-	MsnSwitchBoard *swboard = servconn->data;
+	MsnSession *session;
+	MsnSwitchBoard *swboard;
 	MsnUser *user;
 	GHashTable *clientcaps;
 	const char *value;
 
+	session = cmdproc->session;
+	swboard = cmdproc->servconn->data;
+
 	user = msn_user_new(session, msg->passport, NULL);
 
 	clientcaps = msn_message_get_hashtable_from_body(msg);
 #endif
-
-	return TRUE;
 }
 
 /**************************************************************************
@@ -432,35 +395,31 @@
 static gboolean
 connect_cb(MsnServConn *servconn)
 {
-	GaimAccount *account = servconn->session->account;
-	MsnSwitchBoard *swboard = servconn->data;
-	char outparams[MSN_BUF_LEN];
+	GaimAccount *account;
+	MsnSwitchBoard *swboard;
+
+	account = servconn->session->account;
+	swboard = servconn->data;
 
 	swboard->in_use = TRUE;
 
-	gaim_debug(GAIM_DEBUG_INFO, "msn", "Connecting to switchboard...\n");
-
-	if (msn_switchboard_is_invited(swboard)) {
-		g_snprintf(outparams, sizeof(outparams), "%s %s %s",
-				   gaim_account_get_username(account),
-				   swboard->auth_key, swboard->session_id);
-
-		if (!msn_switchboard_send_command(swboard, "ANS", outparams)) {
-			msn_switchboard_destroy(swboard);
+	gaim_debug_info("msn", "Connecting to switchboard...\n");
 
-			return FALSE;
-		}
+	if (msn_switchboard_is_invited(swboard))
+	{
+		msn_cmdproc_send(swboard->cmdproc, "ANS", "%s %s %s",
+						 gaim_account_get_username(account),
+						 swboard->auth_key, swboard->session_id);
 	}
-	else {
-		g_snprintf(outparams, sizeof(outparams), "%s %s",
-				   gaim_account_get_username(account), swboard->auth_key);
+	else
+	{
+		msn_cmdproc_send(swboard->cmdproc, "USR", "%s %s",
+						 gaim_account_get_username(account),
+						 swboard->auth_key);
+	}
 
-		if (!msn_switchboard_send_command(swboard, "USR", outparams)) {
-			msn_switchboard_destroy(swboard);
-
-			return FALSE;
-		}
-	}
+	if (swboard->cmdproc->error)
+		return FALSE;
 
 	return TRUE;
 }
@@ -471,21 +430,66 @@
 	MsnSwitchBoard *swboard;
 
 	swboard = servconn->data;
+
 	if (!swboard->destroying)
 		msn_switchboard_destroy(swboard);
 }
 
+void
+msn_switchboard_init(void)
+{
+	cbs_table = msn_table_new();
+
+	/* Register the command callbacks. */
+	msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
+
+	msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd);
+	msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd);
+
+	msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd);
+
+	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
+
+	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
+
+	/* Register the message type callbacks. */
+	msn_table_add_msg_type(cbs_table, "text/plain",
+							plain_msg);
+	msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
+							control_msg);
+	msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
+							clientcaps_msg);
+	msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
+							clientcaps_msg);
+#if 0
+	msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p",
+									msn_p2p_msg);
+#endif
+}
+
+void
+msn_switchboard_end(void)
+{
+	msn_table_destroy(cbs_table);
+}
+
 MsnSwitchBoard *
 msn_switchboard_new(MsnSession *session)
 {
 	MsnSwitchBoard *swboard;
 	MsnServConn *servconn;
+	MsnCmdProc *cmdproc;
 
 	g_return_val_if_fail(session != NULL, NULL);
 
 	swboard = g_new0(MsnSwitchBoard, 1);
 
-	swboard->servconn = servconn = msn_servconn_new(session);
+	swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVER_SB);
+	cmdproc = swboard->cmdproc = servconn->cmdproc;
+
 	msn_servconn_set_connect_cb(servconn, connect_cb);
 	msn_servconn_set_disconnect_cb(servconn, disconnect_cb);
 
@@ -496,45 +500,7 @@
 
 	session->switches = g_list_append(session->switches, swboard);
 
-	if (switchboard_commands == NULL) {
-		/* Register the command callbacks. */
-		msn_servconn_register_command(servconn, "ACK",      blank_cmd);
-		msn_servconn_register_command(servconn, "ANS",      ans_cmd);
-		msn_servconn_register_command(servconn, "BYE",      bye_cmd);
-		msn_servconn_register_command(servconn, "CAL",      blank_cmd);
-		msn_servconn_register_command(servconn, "IRO",      iro_cmd);
-		msn_servconn_register_command(servconn, "JOI",      joi_cmd);
-		msn_servconn_register_command(servconn, "MSG",      msg_cmd);
-		msn_servconn_register_command(servconn, "NAK",      nak_cmd);
-		msn_servconn_register_command(servconn, "NLN",      blank_cmd);
-		msn_servconn_register_command(servconn, "OUT",      out_cmd);
-		msn_servconn_register_command(servconn, "USR",      usr_cmd);
-		msn_servconn_register_command(servconn, "_UNKNOWN_",unknown_cmd);
-
-		/* Register the message type callbacks. */
-		msn_servconn_register_msg_type(servconn, "text/plain",plain_msg);
-		msn_servconn_register_msg_type(servconn, "text/x-msmsgscontrol",
-									   control_msg);
-		msn_servconn_register_msg_type(servconn, "text/x-clientcaps",
-									   clientcaps_msg);
-		msn_servconn_register_msg_type(servconn, "text/x-clientinfo",
-									   clientcaps_msg);
-#if 0
-		msn_servconn_register_msg_type(servconn, "application/x-msnmsgrp2p",
-									   msn_p2p_msg);
-#endif
-
-		/* Save these for future use. */
-		switchboard_commands  = servconn->commands;
-		switchboard_msg_types = servconn->msg_types;
-	}
-	else {
-		g_hash_table_destroy(servconn->commands);
-		g_hash_table_destroy(servconn->msg_types);
-
-		servconn->commands  = switchboard_commands;
-		servconn->msg_types = switchboard_msg_types;
-	}
+	cmdproc->cbs_table  = cbs_table;
 
 	return swboard;
 }
@@ -662,75 +628,28 @@
 	swboard->in_use = FALSE;
 }
 
-gboolean
+void
 msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
 {
-	char *buf;
+	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
 	char *payload;
-	size_t len;
 	size_t payload_len;
-	int ret;
-#if 0
-	FILE *fp;
-#endif
 
-	g_return_val_if_fail(swboard != NULL, FALSE);
-	g_return_val_if_fail(msg     != NULL, FALSE);
+	g_return_if_fail(swboard != NULL);
+	g_return_if_fail(msg     != NULL);
 
-	buf = g_strdup_printf("MSG %d %c %d\r\n", ++swboard->trId,
-						  msn_message_get_flag(msg), (int)msg->size);
-
-	len = strlen(buf);
-
+	cmdproc = swboard->servconn->cmdproc;
 	payload = msn_message_gen_payload(msg, &payload_len);
 
-	if (payload != NULL)
-	{
-		buf = g_realloc(buf, len + payload_len + 1);
-		memcpy(buf + len, payload, payload_len);
-		len += payload_len;
-		buf[len] = 0;
-	}
+	trans = msn_transaction_new("MSG", "%c %d", msn_message_get_flag(msg),
+								payload_len);
 
-	if (swboard->servconn->txqueue != NULL || !swboard->in_use)
-	{
-		gaim_debug(GAIM_DEBUG_INFO, "msn", "Appending message to queue.\n");
-
-		swboard->servconn->txqueue =
-			g_slist_append(swboard->servconn->txqueue, buf);
-
-		return TRUE;
-	}
-
-	ret = msn_servconn_write(swboard->servconn, buf, len);
+	trans->payload = payload;
+	trans->payload_len = payload_len;
 
-#if 0
-	/* Windows doesn't like Unix paths */
-	fp = fopen("/tmp/msn-msg", "ab");
-	fwrite(buf, 1, len, fp);
-	fclose(fp);
-#endif
-
-	g_free(buf);
-
-	return (ret > 0);
+	if (!g_queue_is_empty(cmdproc->txqueue) || !swboard->joined)
+		msn_cmdproc_queue_trans(cmdproc, trans);
+	else
+		msn_cmdproc_send_trans(cmdproc, trans);
 }
-
-gboolean
-msn_switchboard_send_command(MsnSwitchBoard *swboard, const char *command,
-							 const char *params)
-{
-	char buf[MSN_BUF_LEN];
-
-	g_return_val_if_fail(swboard != NULL, FALSE);
-	g_return_val_if_fail(command != NULL, FALSE);
-
-	if (params == NULL)
-		g_snprintf(buf, sizeof(buf), "%s %u\r\n", command,
-				   ++swboard->trId);
-	else
-		g_snprintf(buf, sizeof(buf), "%s %u %s\r\n",
-				   command, ++swboard->trId, params);
-
-	return (msn_servconn_write(swboard->servconn, buf, strlen(buf)) > 0);
-}
--- a/src/protocols/msn/switchboard.h	Sun Apr 25 17:01:38 2004 +0000
+++ b/src/protocols/msn/switchboard.h	Sun Apr 25 22:02:06 2004 +0000
@@ -29,11 +29,13 @@
 #include "msg.h"
 #include "msnslp.h"
 #include "servconn.h"
+#include "cmdproc.h"
 #include "user.h"
 
 struct _MsnSwitchBoard
 {
 	MsnServConn *servconn;
+	MsnCmdProc *cmdproc;
 	MsnUser *user;
 
 	char *auth_key;
@@ -45,6 +47,7 @@
 	GaimConversation *chat;
 
 	gboolean in_use;
+	gboolean joined;
 
 	int total_users;
 
@@ -52,7 +55,6 @@
 	int msglen;
 
 	int chat_id;
-	int trId;
 
 	gboolean hidden;
 
@@ -60,6 +62,16 @@
 };
 
 /**
+ * Initialize the variables for switchboard creation.
+ */
+void msn_switchboard_init(void);
+
+/**
+ * Destroy the variables for switchboard creation.
+ */
+void msn_switchboard_end(void);
+
+/**
  * Creates a new switchboard.
  *
  * @param session The MSN session.
@@ -167,23 +179,8 @@
  *
  * @param swboard The switchboard.
  * @param msg     The message to send.
- *
- * @return @c TRUE if successful, or @c FALSE otherwise.
  */
-gboolean msn_switchboard_send_msg(MsnSwitchBoard *swboard,
-								  MsnMessage *msg);
-
-/**
- * Sends a command to the switchboard.
- *
- * @param swboard The switchboard.
- * @param command The command.
- * @param params  The parameters.
- *
- * @return @c TRUE if successful, or @c FALSE otherwise.
- */
-gboolean msn_switchboard_send_command(MsnSwitchBoard *swboard,
-									  const char *command,
-									  const char *params);
+void msn_switchboard_send_msg(MsnSwitchBoard *swboard,
+							  MsnMessage *msg);
 
 #endif /* _MSN_SWITCHBOARD_H_ */