changeset 10481:bcfea6c3d5c9

[gaim-migrate @ 11769] Patch 1093958 from Felipe Contreras. It fixes stuff. I also made some tweaks to make valgrind a bit happier. committer: Tailor Script <tailor@pidgin.im>
author Stu Tomlinson <stu@nosnilmot.com>
date Fri, 07 Jan 2005 02:48:33 +0000
parents 8e0a91d11362
children 1ac8f10ce68d
files src/protocols/msn/cmdproc.c src/protocols/msn/cmdproc.h src/protocols/msn/error.h src/protocols/msn/httpconn.c src/protocols/msn/httpconn.h src/protocols/msn/msg.c src/protocols/msn/msn.c src/protocols/msn/msn.h src/protocols/msn/nexus.c src/protocols/msn/notification.c src/protocols/msn/notification.h src/protocols/msn/servconn.c src/protocols/msn/servconn.h src/protocols/msn/session.c src/protocols/msn/session.h src/protocols/msn/slp.h src/protocols/msn/slpcall.c src/protocols/msn/slplink.c src/protocols/msn/state.c src/protocols/msn/switchboard.c src/protocols/msn/switchboard.h src/protocols/msn/transaction.c
diffstat 22 files changed, 726 insertions(+), 739 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/msn/cmdproc.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/cmdproc.c	Fri Jan 07 02:48:33 2005 +0000
@@ -43,15 +43,13 @@
 {
 	MsnTransaction *trans;
 
-	if (cmdproc->last_trans != NULL)
-		g_free(cmdproc->last_trans);
-
 	while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL)
 		msn_transaction_destroy(trans);
 
 	g_queue_free(cmdproc->txqueue);
 
 	msn_history_destroy(cmdproc->history);
+	g_free(cmdproc);
 }
 
 void
@@ -59,11 +57,8 @@
 {
 	MsnTransaction *trans;
 
-	while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL &&
-		   cmdproc->error == 0)
-	{
+	while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL)
 		msn_cmdproc_send_trans(cmdproc, trans);
-	}
 }
 
 void
@@ -109,19 +104,17 @@
 	size_t len;
 
 	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(cmdproc->ready);
 	g_return_if_fail(trans != NULL);
 
 	servconn = cmdproc->servconn;
+
+	if (!servconn->connected)
+		return;
+
 	msn_history_add(cmdproc->history, trans);
 
 	data = msn_transaction_to_string(trans);
 
-	if (cmdproc->last_trans != NULL)
-		g_free(cmdproc->last_trans);
-
-	cmdproc->last_trans = g_strdup(data);
-
 	len = strlen(data);
 
 	show_debug_cmd(cmdproc, FALSE, data);
@@ -153,11 +146,13 @@
 	size_t len;
 
 	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(cmdproc->ready);
 	g_return_if_fail(command != NULL);
 
 	servconn = cmdproc->servconn;
 
+	if (!servconn->connected)
+		return;
+
 	if (format != NULL)
 	{
 		va_start(arg, format);
@@ -189,9 +184,11 @@
 	va_list arg;
 
 	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(cmdproc->ready);
 	g_return_if_fail(command != NULL);
 
+	if (!cmdproc->servconn->connected)
+		return;
+
 	trans = g_new0(MsnTransaction, 1);
 
 	trans->command = g_strdup(command);
@@ -317,19 +314,8 @@
 						   cmd->command);
 	}
 
-#if 1
-	/* TODO this is really ugly */
-	/* Since commands have not stored payload and we need it for pendent
-	 * commands at the time we process again the same command we will try
-	 * to read again the payload of payload_len size but we will actually
-	 * read sometime else, and reading from server synchronization goes to
-	 * hell. */
-	/* Now we store the payload in the command when we queue them :D */
-
 	if (trans != NULL && trans->pendent_cmd != NULL)
-		if (cmdproc->ready)
-			msn_transaction_unqueue_cmd(trans, cmdproc);
-#endif
+		msn_transaction_unqueue_cmd(trans, cmdproc);
 }
 
 void
@@ -344,32 +330,3 @@
 
 	msn_cmdproc_process_cmd(cmdproc, cmdproc->last_cmd);
 }
-
-void
-msn_cmdproc_show_error(MsnCmdProc *cmdproc, int error)
-{
-	GaimConnection *gc =
-		gaim_account_get_connection(cmdproc->session->account);
-
-	char *tmp;
-
-	tmp = NULL;
-
-	switch (error)
-	{
-		case MSN_ERROR_MISC:
-			tmp = _("Miscellaneous error"); break;
-		case MSN_ERROR_SIGNOTHER:
-			gc->wants_to_die = TRUE;
-			tmp = _("You have signed on from another location."); break;
-		case MSN_ERROR_SERVDOWN:
-			tmp = _("The MSN servers are going down temporarily."); break;
-		default:
-			break;
-	}
-
-	if (tmp != NULL)
-	{
-		gaim_connection_error(gc, tmp);
-	}
-}
--- a/src/protocols/msn/cmdproc.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/cmdproc.h	Fri Jan 07 02:48:33 2005 +0000
@@ -33,11 +33,6 @@
 #include "table.h"
 #include "history.h"
 
-#if 0
-typedef void (*MsnPayloadCb)(MsnCmdProc *cmdproc, char *payload,
-							 size_t len);
-#endif
-
 struct _MsnCmdProc
 {
 	MsnSession *session;
@@ -45,14 +40,9 @@
 
 	GQueue *txqueue;
 
-	gboolean ready;
-	MsnErrorType error;
-
 	MsnCommand *last_cmd;
-	char *last_trans;
 
 	MsnTable *cbs_table;
-	/* MsnPayloadCb payload_cb; */
 
 	MsnHistory *history;
 
@@ -81,6 +71,4 @@
 
 void msn_cmdproc_disconnect(MsnCmdProc *cmdproc);
 
-void msn_cmdproc_show_error(MsnCmdProc *cmdproc, int error);
-
 #endif /* _MSN_CMDPROC_H_ */
--- a/src/protocols/msn/error.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/error.h	Fri Jan 07 02:48:33 2005 +0000
@@ -24,18 +24,6 @@
 #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/httpconn.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/httpconn.c	Fri Jan 07 02:48:33 2005 +0000
@@ -28,7 +28,7 @@
 typedef struct
 {
 	MsnHttpConn *httpconn;
-	char *buffer;
+	char *data;
 	size_t size;
 
 } MsnHttpQueueData;
@@ -48,6 +48,8 @@
 
 	httpconn = g_new0(MsnHttpConn, 1);
 
+	gaim_debug_info("msn", "new httpconn (%p)\n", httpconn);
+
 	/* TODO: Remove this */
 	httpconn->session = servconn->session;
 
@@ -61,87 +63,55 @@
 {
 	g_return_if_fail(httpconn != NULL);
 
+	gaim_debug_info("msn", "destroy httpconn (%p)\n", httpconn);
+
 	if (httpconn->connected)
 		msn_httpconn_disconnect(httpconn);
 
-	if (httpconn->host != NULL)
-		g_free(httpconn->host);
-
 	g_free(httpconn);
 }
 
-static void
-show_error(MsnHttpConn *httpconn)
+static ssize_t
+write_raw(MsnHttpConn *httpconn, const char *header,
+		  const char *body, size_t body_len)
 {
-	GaimConnection *gc;
-	char *tmp;
-	char *cmd;
-
-	const char *names[] = { "Notification", "Switchboard" };
-	const char *name;
-
-	gc = gaim_account_get_connection(httpconn->servconn->session->account);
-	name = names[httpconn->servconn->type];
+	char *buf;
+	size_t buf_len;
 
-	switch (httpconn->servconn->cmdproc->error)
-	{
-		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;
-		case MSN_ERROR_READ:
-			cmd = httpconn->servconn->cmdproc->last_trans;
-			tmp = g_strdup_printf(_("Error reading from %s server"), name);
-			gaim_debug_info("msn", "Last command was: %s\n", cmd);
-			break;
-		default:
-			tmp = g_strdup_printf(_("Unknown error from %s server"), name);
-			break;
-	}
-
-	if (httpconn->servconn->type == MSN_SERVER_NS)
-	{
-		gaim_connection_error(gc, tmp);
-	}
-	else
-	{
-		MsnSwitchBoard *swboard;
-		swboard = httpconn->servconn->cmdproc->data;
-		swboard->error = MSN_SB_ERROR_CONNECTION;
-	}
-
-	g_free(tmp);
-}
-
-static ssize_t
-write_raw(MsnHttpConn *httpconn, const void *buffer, size_t len)
-{
 	ssize_t s;
 	ssize_t res; /* result of the write operation */
 
 #ifdef MSN_DEBUG_HTTP
-	gaim_debug_misc("msn", "Writing HTTP: {%s}\n", buffer);
+	gaim_debug_misc("msn", "Writing HTTP (header): {%s}\n", header);
 #endif
 
+	buf = g_strdup_printf("%s\r\n", header);
+	buf_len = strlen(buf);
+
+	if (body != NULL)
+	{
+		buf = g_realloc(buf, buf_len + body_len);
+		memcpy(buf + buf_len, body, body_len);
+		buf_len += body_len;
+	}
+
 	s = 0;
 
 	do
 	{
-		res = write(httpconn->fd, buffer, len);
+		res = write(httpconn->fd, buf, buf_len);
 		if (res >= 0)
 		{
 			s += res;
 		}
 		else if (errno != EAGAIN)
 		{
-			httpconn->servconn->cmdproc->error = MSN_ERROR_WRITE;
-			show_error(httpconn);
+			msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE);
 			return -1;
 		}
-	} while (s < len);
+	} while (s < buf_len);
+
+	g_free(buf);
 
 	return s;
 }
@@ -149,8 +119,8 @@
 void
 msn_httpconn_poll(MsnHttpConn *httpconn)
 {
+	char *header;
 	int r;
-	char *temp;
 
 	g_return_if_fail(httpconn != NULL);
 
@@ -160,7 +130,7 @@
 		return;
 	}
 
-	temp = g_strdup_printf(
+	header = g_strdup_printf(
 		"POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n"
 		"Accept: */*\r\n"
 		"Accept-Language: en-us\r\n"
@@ -170,15 +140,14 @@
 		"Connection: Keep-Alive\r\n"
 		"Pragma: no-cache\r\n"
 		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: 0\r\n"
-		"\r\n",
+		"Content-Length: 0\r\n",
 		httpconn->host,
 		httpconn->full_session_id,
 		httpconn->host);
 
-	r = write_raw(httpconn, temp, strlen(temp));
+	r = write_raw(httpconn, header, NULL, -1);
 
-	g_free(temp);
+	g_free(header);
 
 	if (r > 0)
 	{
@@ -224,7 +193,7 @@
 	else
 	{
 		gaim_debug_error("msn", "HTTP: Connection error\n");
-		show_error(httpconn);
+		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT);
 	}
 }
 
@@ -257,7 +226,9 @@
 msn_httpconn_disconnect(MsnHttpConn *httpconn)
 {
 	g_return_if_fail(httpconn != NULL);
-	g_return_if_fail(httpconn->connected);
+
+	if (!httpconn->connected)
+		return;
 
 	if (httpconn->timer)
 		gaim_timeout_remove(httpconn->timer);
@@ -302,7 +273,7 @@
 	if (len <= 0)
 	{
 		gaim_debug_error("msn", "HTTP: Read error\n");
-		show_error(httpconn);
+		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
 		msn_httpconn_disconnect(httpconn);
 
 		return;
@@ -329,7 +300,7 @@
 	if (error)
 	{
 		gaim_debug_error("msn", "HTTP: Special error\n");
-		show_error(httpconn);
+		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
 		msn_httpconn_disconnect(httpconn);
 
 		return;
@@ -341,6 +312,7 @@
 #if 0
 		gaim_debug_info("msn", "HTTP: nothing to do here\n");
 #endif
+		g_free(result_msg);
 		g_free(httpconn->rx_buf);
 		httpconn->rx_buf = NULL;
 		httpconn->rx_len = 0;
@@ -351,6 +323,7 @@
 	httpconn->rx_buf = NULL;
 	httpconn->rx_len = 0;
 
+	g_free(servconn->rx_buf);
 	servconn->rx_buf = result_msg;
 	servconn->rx_len = result_len;
 
@@ -425,10 +398,10 @@
 		httpconn->queue = g_list_remove(httpconn->queue, queue_data);
 
 		msn_httpconn_write(queue_data->httpconn,
-						   queue_data->buffer,
+						   queue_data->data,
 						   queue_data->size);
 
-		g_free(queue_data->buffer);
+		g_free(queue_data->data);
 		g_free(queue_data);
 	}
 	else
@@ -438,22 +411,21 @@
 }
 
 size_t
-msn_httpconn_write(MsnHttpConn *httpconn, const char *buf, size_t size)
+msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t size)
 {
 	char *params;
-	char *temp;
+	char *header;
 	gboolean first;
 	const char *server_types[] = { "NS", "SB" };
 	const char *server_type;
 	size_t r; /* result of the write operation */
-	size_t len;
 	char *host;
 	MsnServConn *servconn;
 
 	/* TODO: remove http data from servconn */
 
 	g_return_val_if_fail(httpconn != NULL, 0);
-	g_return_val_if_fail(buf      != NULL, 0);
+	g_return_val_if_fail(data     != NULL, 0);
 	g_return_val_if_fail(size      > 0,    0);
 
 	servconn = httpconn->servconn;
@@ -463,7 +435,7 @@
 		MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1);
 
 		queue_data->httpconn = httpconn;
-		queue_data->buffer   = g_memdup(buf, size);
+		queue_data->data     = g_memdup(data, size);
 		queue_data->size     = size;
 
 		httpconn->queue = g_list_append(httpconn->queue, queue_data);
@@ -495,7 +467,7 @@
 								 httpconn->full_session_id);
 	}
 
-	temp = g_strdup_printf(
+	header = g_strdup_printf(
 		"POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n"
 		"Accept: */*\r\n"
 		"Accept-Language: en-us\r\n"
@@ -505,8 +477,7 @@
 		"Connection: Keep-Alive\r\n"
 		"Pragma: no-cache\r\n"
 		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: %d\r\n"
-		"\r\n",
+		"Content-Length: %d\r\n",
 		host,
 		params,
 		host,
@@ -514,15 +485,9 @@
 
 	g_free(params);
 
-	len = strlen(temp);
-	temp = g_realloc(temp, len + size + 1);
-	memcpy(temp + len, buf, size);
-	len += size;
-	temp[len] = '\0';
+	r = write_raw(httpconn, header, data, size);
 
-	r = write_raw(httpconn, temp, len);
-
-	g_free(temp);
+	g_free(header);
 
 	if (r > 0)
 	{
@@ -541,7 +506,7 @@
 {
 	GaimConnection *gc;
 	const char *s, *c;
-	char *headers, *body;
+	char *header, *body;
 	const char *body_start;
 	char *tmp;
 	size_t body_len = 0;
@@ -604,12 +569,12 @@
 	if ((s = strstr(buf, "\r\n\r\n")) == NULL)
 		return FALSE;
 
-	headers = g_strndup(buf, s - buf);
+	header = g_strndup(buf, s - buf);
 	s += 4; /* Skip \r\n */
 	body_start = s;
 	body_len = size - (body_start - buf);
 
-	if ((s = strstr(headers, "Content-Length: ")) != NULL)
+	if ((s = strstr(header, "Content-Length: ")) != NULL)
 	{
 		int tmp_len;
 
@@ -617,7 +582,7 @@
 
 		if ((c = strchr(s, '\r')) == NULL)
 		{
-			g_free(headers);
+			g_free(header);
 
 			return FALSE;
 		}
@@ -628,7 +593,7 @@
 
 		if (body_len != tmp_len)
 		{
-			g_free(headers);
+			g_free(header);
 
 #if 0
 			gaim_debug_warning("msn",
@@ -640,14 +605,16 @@
 		}
 	}
 
-	body = g_memdup(body_start, body_len);
+	body = g_malloc0(body_len + 1);
+	memcpy(body, body_start, body_len);
 
 #ifdef MSN_DEBUG_HTTP
-	gaim_debug_misc("msn", "Incoming HTTP buffer: {%s\r\n\r\n%s}\n", headers, body);
+	gaim_debug_misc("msn", "Incoming HTTP buffer (header): {%s\r\n}\n",
+					header);
 #endif
 
 	/* Now we should be able to process the data. */
-	if ((s = strstr(headers, "X-MSN-Messenger: ")) != NULL)
+	if ((s = strstr(header, "X-MSN-Messenger: ")) != NULL)
 	{
 		char *full_session_id, *gw_ip, *session_action;
 		char *t, *session_id;
@@ -659,10 +626,12 @@
 
 		if ((c = strchr(s, '\r')) == NULL)
 		{
-			gaim_connection_error(gc, "Malformed X-MSN-Messenger field.");
+			msn_session_set_error(httpconn->session,
+								  MSN_ERROR_HTTP_MALFORMED, NULL);
 			gaim_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}",
 							 buf);
 
+			g_free(body);
 			return FALSE;
 		}
 
@@ -731,7 +700,7 @@
 		}
 	}
 
-	g_free(headers);
+	g_free(header);
 
 	*ret_buf  = body;
 	*ret_size = body_len;
--- a/src/protocols/msn/httpconn.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/httpconn.h	Fri Jan 07 02:48:33 2005 +0000
@@ -28,101 +28,79 @@
 
 #include "servconn.h"
 
+/**
+ * An HTTP Connection.
+ */
 struct _MsnHttpConn
 {
-	MsnSession *session;
-	MsnServConn *servconn;
-
-	char *full_session_id;
-	char *session_id;
+	MsnSession *session; /**< The MSN Session. */
+	MsnServConn *servconn; /**< The connection object. */
 
-	int timer;
+	char *full_session_id; /**< The full session id. */
+	char *session_id; /**< The trimmed session id. */
 
-	gboolean waiting_response;
-	gboolean dirty; /**< The flag that states if we should poll. */
-	gboolean connected;
+	int timer; /**< The timer for polling. */
 
-	char *host;
-	GList *queue;
-
-	int fd;
-	int inpa;
+	gboolean waiting_response; /**< The flag that states if we are waiting
+								 a response from the server. */
+	gboolean dirty;            /**< The flag that states if we should poll. */
+	gboolean connected;        /**< The flag that states if the connection is on. */
+	gboolean virgin;           /**< The flag that states if this connection
+								 should specify the host (not gateway) to
+								 connect to. */
 
-	char *rx_buf;
-	int rx_len;
+	char *host; /**< The HTTP gateway host. */
+	GList *queue; /**< The queue of data chunks to write. */
 
-#if 0
-	GQueue *servconn_queue;
-#endif
+	int fd; /**< The connection's file descriptor. */
+	int inpa; /**< The connection's input handler. */
 
-	gboolean virgin;
+	char *rx_buf; /**< The receive buffer. */
+	int rx_len; /**< The receive buffer lenght. */
 };
 
+/**
+ * Creates a new HTTP connection object.
+ *
+ * @param servconn The connection object.
+ *
+ * @return The new object.
+ */
 MsnHttpConn *msn_httpconn_new(MsnServConn *servconn);
-void msn_httpconn_destroy(MsnHttpConn *httpconn);
-size_t msn_httpconn_write(MsnHttpConn *httpconn, const char *buf, size_t size);
-
-gboolean msn_httpconn_connect(MsnHttpConn *httpconn,
-							  const char *host, int port);
-void msn_httpconn_disconnect(MsnHttpConn *httpconn);
-
-#if 0
-void msn_httpconn_queue_servconn(MsnHttpConn *httpconn, MsnServConn *servconn);
-#endif
-
-#if 0
-/**
- * Initializes the HTTP data for a session.
- *
- * @param session The session.
- */
-void msn_http_session_init(MsnSession *session);
 
 /**
- * Uninitializes the HTTP data for a session.
+ * Destroys an HTTP connection object.
  *
- * @param session The session.
+ * @param httpconn The HTTP connection object.
  */
-void msn_http_session_uninit(MsnSession *session);
+void msn_httpconn_destroy(MsnHttpConn *httpconn);
 
 /**
- * Writes data to the server using the HTTP connection method.
+ * Writes a chunk of data to the HTTP connection.
  *
  * @param servconn    The server connection.
- * @param buf         The data to write.
+ * @param data        The data to write.
  * @param size        The size of the data to write.
- * @param server_type The optional server type.
  *
  * @return The number of bytes written.
  */
-size_t msn_http_servconn_write(MsnServConn *servconn, const char *buf,
-							   size_t size, const char *server_type);
-
-/**
- * Polls the server for data.
- *
- * @param servconn The server connection.
- */
-void msn_http_servconn_poll(MsnServConn *servconn);
+size_t msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t size);
 
 /**
- * Processes an incoming message and returns a string the rest of MSN
- * can deal with.
+ * Connects the HTTP connection object to a host.
  *
- * @param servconn The server connection.
- * @param buf      The incoming buffer.
- * @param size     The incoming size.
- * @param ret_buf  The returned buffer.
- * @param ret_len  The returned length.
- * @param error    TRUE if there was an HTTP error.
+ * @param httpconn The HTTP connection object.
+ * @param host The host to connect to.
+ * @param port The port to connect to.
+ */
+gboolean msn_httpconn_connect(MsnHttpConn *httpconn,
+							  const char *host, int port);
+
+/**
+ * Disconnects the HTTP connection object.
  *
- * @return TRUE if the returned buffer is ready to be processed.
- *         FALSE otherwise.
+ * @param httpconn The HTTP connection object.
  */
-gboolean msn_http_servconn_parse_data(MsnServConn *servconn,
-									  const char *buf, size_t size,
-									  char **ret_buf, size_t *ret_size,
-									  gboolean *error);
-#endif
+void msn_httpconn_disconnect(MsnHttpConn *httpconn);
 
 #endif /* _MSN_HTTPCONN_H_ */
--- a/src/protocols/msn/msg.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/msg.c	Fri Jan 07 02:48:33 2005 +0000
@@ -197,7 +197,8 @@
 
 	g_return_if_fail(payload != NULL);
 
-	tmp_base = tmp = g_memdup(payload, payload_len);
+	tmp_base = tmp = g_malloc0(payload_len + 1);
+	memcpy(tmp_base, payload, payload_len);
 
 	/* Parse the attributes. */
 	end = strstr(tmp, "\r\n\r\n");
--- a/src/protocols/msn/msn.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/msn.c	Fri Jan 07 02:48:33 2005 +0000
@@ -673,22 +673,23 @@
 		http_method = TRUE;
 
 	host = gaim_account_get_string(account, "server", MSN_SERVER);
-	port = gaim_account_get_int(account,    "port",   MSN_PORT);
+	port = gaim_account_get_int(account, "port", MSN_PORT);
 
-	session = msn_session_new(account, host, port, http_method);
+	session = msn_session_new(account);
 
 	gc->proto_data = session;
 	gc->flags |= GAIM_CONNECTION_HTML | GAIM_CONNECTION_FORMATTING_WBFO | GAIM_CONNECTION_NO_BGCOLOR | GAIM_CONNECTION_NO_FONTSIZE | GAIM_CONNECTION_NO_URLDESC;
 
-	gaim_connection_update_progress(gc, _("Connecting"), 0, MSN_CONNECT_STEPS);
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
 
 	/* Hmm, I don't like this. */
+	/* XXX shx: Me neither */
 	username = msn_normalize(account, gaim_account_get_username(account));
 
 	if (strcmp(username, gaim_account_get_username(account)))
 		gaim_account_set_username(account, username);
 
-	msn_session_connect(session);
+	msn_session_connect(session, host, port, http_method);
 }
 
 static void
@@ -740,14 +741,7 @@
 		session = gc->proto_data;
 		swboard = msn_session_get_swboard(session, who);
 
-		if (!g_queue_is_empty(swboard->im_queue) || swboard->empty)
-		{
-			msn_switchboard_queue_msg(swboard, msg);
-		}
-		else
-		{
-			msn_switchboard_send_msg(swboard, msg);
-		}
+		msn_switchboard_send_msg(swboard, msg, TRUE);
 	}
 	else
 	{
@@ -802,13 +796,7 @@
 
 	swboard = msn_session_find_swboard(session, who);
 
-	if (swboard == NULL)
-		return 0;
-
-	if (swboard->empty)
-		return 0;
-
-	if (!g_queue_is_empty(swboard->im_queue))
+	if (swboard == NULL || !msn_switchboard_can_send(swboard))
 		return 0;
 
 	msg = msn_message_new(MSN_MSG_TYPING);
@@ -818,7 +806,7 @@
 						 gaim_account_get_username(account));
 	msn_message_set_bin_data(msg, "\r\n", 2);
 
-	msn_switchboard_send_msg(swboard, msg);
+	msn_switchboard_send_msg(swboard, msg, FALSE);
 
 	msn_message_destroy(msg);
 
@@ -1150,7 +1138,7 @@
 
 	msg = msn_message_new_plain(msgtext);
 	msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
-	msn_switchboard_send_msg(swboard, msg);
+	msn_switchboard_send_msg(swboard, msg, FALSE);
 	msn_message_destroy(msg);
 
 	g_free(msgformat);
@@ -1169,6 +1157,7 @@
 	MsnCmdProc *cmdproc;
 
 	session = gc->proto_data;
+
 	cmdproc = session->notification->cmdproc;
 
 	if (!session->http_method)
--- a/src/protocols/msn/msn.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/msn.h	Fri Jan 07 02:48:33 2005 +0000
@@ -45,8 +45,13 @@
 
 #include "ft.h"
 
-/* XXX */
-#include "gaim.h"
+/* #define MSN_DEBUG_MSG 1 */
+/* #define MSN_DEBUG_SLPMSG 1 */
+/* #define MSN_DEBUG_HTTP 1 */
+
+/* #define MSN_DEBUG_SLP 1 */
+/* #define MSN_DEBUG_SLP_VERBOSE 1 */
+/* #define MSN_DEBUG_SLP_FILES 1 */
 
 #define MSN_BUF_LEN 8192
 
@@ -67,8 +72,6 @@
 
 #define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}"
 
-#define MSN_CONNECT_STEPS 8
-
 #define MSN_CLIENTINFO \
 	"Client-Name: Gaim/" VERSION "\r\n" \
 	"Chat-Logging: Y\r\n"
--- a/src/protocols/msn/nexus.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/nexus.c	Fri Jan 07 02:48:33 2005 +0000
@@ -26,6 +26,38 @@
 #include "notification.h"
 
 /**************************************************************************
+ * Main
+ **************************************************************************/
+
+MsnNexus *
+msn_nexus_new(MsnSession *session)
+{
+	MsnNexus *nexus;
+
+	nexus = g_new0(MsnNexus, 1);
+	nexus->session = session;
+	nexus->challenge_data = g_hash_table_new_full(g_str_hash, g_str_equal,
+												  g_free, g_free);
+
+	return nexus;
+}
+
+void
+msn_nexus_destroy(MsnNexus *nexus)
+{
+	if (nexus->login_host != NULL)
+		g_free(nexus->login_host);
+
+	if (nexus->login_path != NULL)
+		g_free(nexus->login_path);
+
+	if (nexus->challenge_data != NULL)
+		g_hash_table_destroy(nexus->challenge_data);
+
+	g_free(nexus);
+}
+
+/**************************************************************************
  * Util
  **************************************************************************/
 
@@ -76,7 +108,7 @@
 	gc = gaim_account_get_connection(account);
 	g_return_if_fail(gc != NULL);
 
-	gaim_connection_error(gc, _("Unable to connect to server"));
+	msn_session_set_error(session, MSN_ERROR_AUTH, _("Unable to connect"));
 
 	msn_nexus_destroy(nexus);
 	session->nexus = NULL;
@@ -100,6 +132,8 @@
 	session = nexus->session;
 	g_return_if_fail(session != NULL);
 
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
+
 	username =
 		g_strdup(gaim_url_encode(gaim_account_get_username(session->account)));
 
@@ -191,14 +225,15 @@
 	}
 	else if (strstr(buffer, "HTTP/1.1 401 Unauthorized") != NULL)
 	{
-		GaimConnection *gc;
-		const char *error, *c;
-		char *temp;
+		const char *error;
 
 		if ((error = strstr(buffer, "WWW-Authenticate")) != NULL)
 		{
 			if ((error = strstr(error, "cbtxt=")) != NULL)
 			{
+				const char *c;
+				char *temp;
+
 				error += strlen("cbtxt=");
 
 				if ((c = strchr(error, '\n')) == NULL)
@@ -210,16 +245,7 @@
 			}
 		}
 
-		gc = gaim_account_get_connection(session->account);
-
-		if (error == NULL)
-		{
-			gaim_connection_error(gc,
-				_("Unknown error when attempting to authorize with "
-				  "MSN login server."));
-		}
-		else
-			gaim_connection_error(gc, error);
+		msn_session_set_error(session, MSN_ERROR_AUTH, error);
 	}
 	else if (strstr(buffer, "HTTP/1.1 200 OK"))
 	{
@@ -264,6 +290,10 @@
 	g_free(buffer);
 }
 
+/**************************************************************************
+ * Connect
+ **************************************************************************/
+
 static void
 nexus_connect_cb(gpointer data, GaimSslConnection *gsc,
 				 GaimInputCondition cond)
@@ -282,6 +312,8 @@
 	session = nexus->session;
 	g_return_if_fail(session != NULL);
 
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH);
+
 	request_str = g_strdup_printf("GET /rdr/pprdr.asp\r\n\r\n");
 
 	if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0)
@@ -332,38 +364,6 @@
 					 login_error_cb, nexus);
 }
 
-/**************************************************************************
- * Nexus
- **************************************************************************/
-
-MsnNexus *
-msn_nexus_new(MsnSession *session)
-{
-	MsnNexus *nexus;
-
-	nexus = g_new0(MsnNexus, 1);
-	nexus->session = session;
-	nexus->challenge_data = g_hash_table_new_full(g_str_hash, g_str_equal,
-												  g_free, g_free);
-
-	return nexus;
-}
-
-void
-msn_nexus_destroy(MsnNexus *nexus)
-{
-	if (nexus->login_host != NULL)
-		g_free(nexus->login_host);
-
-	if (nexus->login_path != NULL)
-		g_free(nexus->login_path);
-
-	if (nexus->challenge_data != NULL)
-		g_hash_table_destroy(nexus->challenge_data);
-
-	g_free(nexus);
-}
-
 void
 msn_nexus_connect(MsnNexus *nexus)
 {
--- a/src/protocols/msn/notification.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/notification.c	Fri Jan 07 02:48:33 2005 +0000
@@ -32,13 +32,12 @@
 #include "sync.h"
 #include "slplink.h"
 
-#define BUDDY_ALIAS_MAXLEN 388
-
 static MsnTable *cbs_table;
 
 /**************************************************************************
  * Main
  **************************************************************************/
+
 static void
 destroy_cb(MsnServConn *servconn)
 {
@@ -61,7 +60,7 @@
 	notification = g_new0(MsnNotification, 1);
 
 	notification->session = session;
-	notification->servconn = servconn = msn_servconn_new(session, MSN_SERVER_NS);
+	notification->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_NS);
 	msn_servconn_set_destroy_cb(servconn, destroy_cb);
 
 	notification->cmdproc = servconn->cmdproc;
@@ -74,20 +73,19 @@
 void
 msn_notification_destroy(MsnNotification *notification)
 {
-	if (notification->destroying)
-		return;
+	notification->cmdproc->data = NULL;
 
-	notification->destroying = TRUE;
+	msn_servconn_set_destroy_cb(notification->servconn, NULL);
 
 	msn_servconn_destroy(notification->servconn);
 
-	notification->session->notification = NULL;
 	g_free(notification);
 }
 
 /**************************************************************************
  * Connect
  **************************************************************************/
+
 static void
 connect_cb(MsnServConn *servconn)
 {
@@ -113,17 +111,11 @@
 
 	vers = g_strjoinv(" ", a);
 
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
 	msn_cmdproc_send(cmdproc, "VER", "%s", vers);
 
 	g_strfreev(a);
 	g_free(vers);
-
-	if (cmdproc->error)
-		return;
-
-	if (session->user == NULL)
-		session->user = msn_user_new(session->userlist,
-									 gaim_account_get_username(account), NULL);
 }
 
 gboolean
@@ -155,6 +147,7 @@
 /**************************************************************************
  * Util
  **************************************************************************/
+
 static void
 group_error_helper(MsnSession *session, const char *msg, int group_id, int error)
 {
@@ -198,6 +191,7 @@
 /**************************************************************************
  * Login
  **************************************************************************/
+
 void
 msn_got_login_params(MsnSession *session, const char *login_params)
 {
@@ -205,6 +199,8 @@
 
 	cmdproc = session->notification->cmdproc;
 
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
+
 	msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
 }
 
@@ -220,31 +216,6 @@
 }
 
 static void
-inf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	GaimAccount *account;
-	GaimConnection *gc;
-
-	account = cmdproc->session->account;
-	gc = gaim_account_get_connection(account);
-
-	if (strcmp(cmd->params[1], "MD5"))
-	{
-		msn_cmdproc_show_error(cmdproc, MSN_ERROR_MISC);
-		return;
-	}
-
-	msn_cmdproc_send(cmdproc, "USR", "MD5 I %s",
-					 gaim_account_get_username(account));
-
-	if (cmdproc->error)
-		return;
-
-	gaim_connection_update_progress(gc, _("Requesting to send password"),
-									5, MSN_CONNECT_STEPS);
-}
-
-static void
 usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
@@ -255,33 +226,22 @@
 	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(cmd->params[1], "OK"))
 	{
+		/* OK */
 		const char *friendly = gaim_url_decode(cmd->params[3]);
 
-		/* OK */
-
 		gaim_connection_set_display_name(gc, friendly);
 
-		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
+		msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
 
-		if (cmdproc->error)
-			return;
-
-		gaim_connection_update_progress(gc, _("Retrieving buddy list"),
-										7, MSN_CONNECT_STEPS);
+		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
 	}
 	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
 	{
+		/* Passport authentication */
 		char **elems, **cur, **tokens;
 
-		/* Passport authentication */
 		session->nexus = msn_nexus_new(session);
 
 		/* Parse the challenge data. */
@@ -298,39 +258,9 @@
 
 		g_strfreev(elems);
 
-		msn_nexus_connect(session->nexus);
-
-		gaim_connection_update_progress(gc, _("Password sent"),
-										6, MSN_CONNECT_STEPS);
-	}
-	else if (!g_ascii_strcasecmp(cmd->params[1], "MD5"))
-	{
-		/* Challenge */
-		const char *challenge;
-		const char *password;
-		char buf[33];
-		md5_state_t st;
-		md5_byte_t di[16];
-		int i;
+		msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);
 
-		challenge = cmd->params[3];
-		password = gaim_account_get_password(account);
-
-		md5_init(&st);
-		md5_append(&st, (const md5_byte_t *)challenge, strlen(challenge));
-		md5_append(&st, (const md5_byte_t *)password, strlen(password));
-		md5_finish(&st, di);
-
-		for (i = 0; i < 16; i++)
-			g_snprintf(buf + (i*2), 3, "%02x", di[i]);
-
-		msn_cmdproc_send(cmdproc, "USR", "MD5 S %s", buf);
-
-		if (cmdproc->error)
-			return;
-
-		gaim_connection_update_progress(gc, _("Password sent"),
-										6, MSN_CONNECT_STEPS);
+		msn_nexus_connect(session->nexus);
 	}
 }
 
@@ -359,7 +289,8 @@
 
 	if (!protocol_supported)
 	{
-		msn_cmdproc_show_error(cmdproc, MSN_ERROR_MISC);
+		msn_session_set_error(session, MSN_ERROR_UNSUPORTED_PROTOCOL,
+							  NULL);
 		return;
 	}
 
@@ -371,13 +302,15 @@
 /**************************************************************************
  * Log out
  **************************************************************************/
+
 static void
 out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
-		msn_cmdproc_show_error(cmdproc, MSN_ERROR_SIGNOTHER);
+		msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER,
+							  NULL);
 	else if (!g_ascii_strcasecmp(cmd->params[0], "SSD"))
-		msn_cmdproc_show_error(cmdproc, MSN_ERROR_SERVDOWN);
+		msn_session_set_error(cmdproc->session, MSN_ERROR_SERV_DOWN, NULL);
 }
 
 void
@@ -393,6 +326,7 @@
 /**************************************************************************
  * Messages
  **************************************************************************/
+
 static void
 msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
@@ -431,6 +365,7 @@
 /**************************************************************************
  * Challenges
  **************************************************************************/
+
 static void
 chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -464,6 +399,7 @@
 /**************************************************************************
  * Buddy Lists
  **************************************************************************/
+
 static void
 add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -874,22 +810,13 @@
 
 	if (cmd->param_count == 2)
 	{
-		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.
 		 */
-		buf = g_strdup_printf(
-				_("Your MSN buddy list for %s is temporarily unavailable. "
-				  "Please wait and try again."),
-				gaim_account_get_username(session->account));
 
-		gaim_connection_error(gaim_account_get_connection(session->account),
-							  buf);
-
-		g_free(buf);
-
+		msn_session_set_error(cmdproc->session, MSN_ERROR_BAD_BLIST, NULL);
 		return;
 	}
 
@@ -916,6 +843,7 @@
 /**************************************************************************
  * Misc commands
  **************************************************************************/
+
 static void
 url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -1018,6 +946,7 @@
 /**************************************************************************
  * Switchboards
  **************************************************************************/
+
 static void
 rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -1056,7 +985,8 @@
 
 	if (strcmp(cmd->params[1], "SB") && strcmp(cmd->params[1], "NS"))
 	{
-		msn_cmdproc_show_error(cmdproc, MSN_ERROR_MISC);
+		/* Maybe we can have a generic bad command error. */
+		gaim_debug_error("msn", "Bad XFR command (%s)\n", cmd->params[1]);
 		return;
 	}
 
@@ -1065,19 +995,6 @@
 	if (!strcmp(cmd->params[1], "SB"))
 	{
 		gaim_debug_error("msn", "This shouldn't be handled here.\n");
-#if 0
-		swboard = cmd->trans->data;
-
-		if (swboard != NULL)
-		{
-			msn_switchboard_set_auth_key(swboard, cmd->params[4]);
-
-			if (session->http_method)
-				port = 80;
-
-			msn_switchboard_connect(swboard, host, port);
-		}
-#endif
 	}
 	else if (!strcmp(cmd->params[1], "NS"))
 	{
@@ -1085,6 +1002,9 @@
 
 		session = cmdproc->session;
 
+		if (!session->logged_in)
+			msn_session_set_login_step(session, MSN_LOGIN_STEP_TRANSFER);
+
 		msn_notification_connect(session->notification, host, port);
 	}
 
@@ -1094,6 +1014,7 @@
 /**************************************************************************
  * Message Types
  **************************************************************************/
+
 static void
 profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
@@ -1343,6 +1264,7 @@
 /**************************************************************************
  * Init
  **************************************************************************/
+
 void
 msn_notification_init(void)
 {
@@ -1360,7 +1282,6 @@
 	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); */
--- a/src/protocols/msn/notification.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/notification.h	Fri Jan 07 02:48:33 2005 +0000
@@ -36,9 +36,6 @@
 	MsnCmdProc *cmdproc;
 	MsnServConn *servconn;
 
-	gboolean destroying;	/**< A flag that states if the notification is on
-							  the process of being destroyed. */
-
 	gboolean in_use;
 };
 
--- a/src/protocols/msn/servconn.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/servconn.c	Fri Jan 07 02:48:33 2005 +0000
@@ -27,6 +27,10 @@
 
 static void read_cb(gpointer data, gint source, GaimInputCondition cond);
 
+/**************************************************************************
+ * Main
+ **************************************************************************/
+
 MsnServConn *
 msn_servconn_new(MsnSession *session, MsnServConnType type)
 {
@@ -42,8 +46,7 @@
 	servconn->cmdproc = msn_cmdproc_new(session);
 	servconn->cmdproc->servconn = servconn;
 
-	if (session->http_method)
-		servconn->httpconn = msn_httpconn_new(servconn);
+	servconn->httpconn = msn_httpconn_new(servconn);
 
 	servconn->num = session->servconns_count++;
 
@@ -61,11 +64,6 @@
 		return;
 	}
 
-	if (servconn->destroying)
-		return;
-
-	servconn->destroying = TRUE;
-
 	if (servconn->connected)
 		msn_servconn_disconnect(servconn);
 
@@ -82,52 +80,83 @@
 	g_free(servconn);
 }
 
-static void
-show_error(MsnServConn *servconn)
+void
+msn_servconn_set_connect_cb(MsnServConn *servconn,
+							void (*connect_cb)(MsnServConn *))
+{
+	g_return_if_fail(servconn != NULL);
+	servconn->connect_cb = connect_cb;
+}
+
+void
+msn_servconn_set_disconnect_cb(MsnServConn *servconn,
+							   void (*disconnect_cb)(MsnServConn *))
 {
-	GaimConnection *gc;
+	g_return_if_fail(servconn != NULL);
+
+	servconn->disconnect_cb = disconnect_cb;
+}
+
+void
+msn_servconn_set_destroy_cb(MsnServConn *servconn,
+							void (*destroy_cb)(MsnServConn *))
+{
+	g_return_if_fail(servconn != NULL);
+
+	servconn->destroy_cb = destroy_cb;
+}
+
+/**************************************************************************
+ * Utility
+ **************************************************************************/
+
+void
+msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error)
+{
 	char *tmp;
-	char *cmd;
+	const char *reason;
 
 	const char *names[] = { "Notification", "Switchboard" };
 	const char *name;
 
-	gc = gaim_account_get_connection(servconn->session->account);
 	name = names[servconn->type];
 
-	switch (servconn->cmdproc->error)
+	switch (error)
 	{
-		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;
-		case MSN_ERROR_READ:
-			cmd = servconn->cmdproc->last_trans;
-			tmp = g_strdup_printf(_("Error reading from %s server"), name);
-			gaim_debug_info("msn", "Last command was: %s\n", cmd);
-			break;
+		case MSN_SERVCONN_ERROR_CONNECT:
+			reason = _("Unable to connect"); break;
+		case MSN_SERVCONN_ERROR_WRITE:
+			reason = _("Writing error"); break;
+		case MSN_SERVCONN_ERROR_READ:
+			reason = _("Reading error"); break;
 		default:
-			tmp = g_strdup_printf(_("Unknown error from %s server"), name);
-			break;
+			reason = _("Unknown error"); break;
 	}
 
-	if (servconn->type != MSN_SERVER_SB)
+	tmp = g_strdup_printf(_("Connection error from %s server (%s):\n%s"),
+						  name, servconn->host, reason);
+
+	if (servconn->type == MSN_SERVCONN_NS)
 	{
-		gaim_connection_error(gc, tmp);
+		msn_session_set_error(servconn->session, MSN_ERROR_SERVCONN, tmp);
 	}
-	else
+	else if (servconn->type == MSN_SERVCONN_SB)
 	{
 		MsnSwitchBoard *swboard;
 		swboard = servconn->cmdproc->data;
-		swboard->error = MSN_SB_ERROR_CONNECTION;
+		if (swboard != NULL)
+			swboard->error = MSN_SB_ERROR_CONNECTION;
 	}
 
+	msn_servconn_disconnect(servconn);
+
 	g_free(tmp);
 }
 
+/**************************************************************************
+ * Connect
+ **************************************************************************/
+
 static void
 connect_cb(gpointer data, gint source, GaimInputCondition cond)
 {
@@ -146,7 +175,6 @@
 	if (source > 0)
 	{
 		servconn->connected = TRUE;
-		servconn->cmdproc->ready = TRUE;
 
 		/* Someone wants to know we connected. */
 		servconn->connect_cb(servconn);
@@ -155,8 +183,7 @@
 	}
 	else
 	{
-		servconn->cmdproc->error = MSN_ERROR_CONNECT;
-		show_error(servconn);
+		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_CONNECT);
 	}
 }
 
@@ -188,7 +215,6 @@
 			msn_httpconn_connect(servconn->httpconn, host, port);
 
 		servconn->connected = TRUE;
-		servconn->cmdproc->ready = TRUE;
 		servconn->httpconn->virgin = TRUE;
 
 		/* Someone wants to know we connected. */
@@ -216,6 +242,7 @@
 
 	if (!servconn->connected)
 	{
+		/* We could not connect. */
 		if (servconn->disconnect_cb != NULL)
 			servconn->disconnect_cb(servconn);
 
@@ -224,7 +251,7 @@
 
 	if (servconn->session->http_method)
 	{
-		/* Fake disconnection */
+		/* Fake disconnection. */
 		if (servconn->disconnect_cb != NULL)
 			servconn->disconnect_cb(servconn);
 
@@ -244,48 +271,11 @@
 	servconn->payload_len = 0;
 
 	servconn->connected = FALSE;
-	servconn->cmdproc->ready = FALSE;
 
 	if (servconn->disconnect_cb != NULL)
 		servconn->disconnect_cb(servconn);
 }
 
-void
-msn_servconn_set_connect_cb(MsnServConn *servconn,
-							void (*connect_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-	servconn->connect_cb = connect_cb;
-}
-
-void
-msn_servconn_set_disconnect_cb(MsnServConn *servconn,
-							   void (*disconnect_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-
-	servconn->disconnect_cb = disconnect_cb;
-}
-
-void
-msn_servconn_set_destroy_cb(MsnServConn *servconn,
-							   void (*destroy_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-
-	servconn->destroy_cb = destroy_cb;
-}
-
-static void
-failed_io(MsnServConn *servconn)
-{
-	g_return_if_fail(servconn != NULL);
-
-	show_error(servconn);
-
-	msn_servconn_disconnect(servconn);
-}
-
 size_t
 msn_servconn_write(MsnServConn *servconn, const char *buf, size_t len)
 {
@@ -297,14 +287,16 @@
 	{
 		switch (servconn->type)
 		{
-			case MSN_SERVER_NS:
-			case MSN_SERVER_SB:
+			case MSN_SERVCONN_NS:
+			case MSN_SERVCONN_SB:
 				ret = write(servconn->fd, buf, len);
 				break;
-			case MSN_SERVER_DC:
+#if 0
+			case MSN_SERVCONN_DC:
 				ret = write(servconn->fd, &buf, sizeof(len));
 				ret = write(servconn->fd, buf, len);
 				break;
+#endif
 			default:
 				ret = write(servconn->fd, buf, len);
 				break;
@@ -317,8 +309,7 @@
 
 	if (ret == -1)
 	{
-		servconn->cmdproc->error = MSN_ERROR_WRITE;
-		failed_io(servconn);
+		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE);
 	}
 
 	return ret;
@@ -340,9 +331,7 @@
 
 	if (len <= 0)
 	{
-		servconn->cmdproc->error = MSN_ERROR_READ;
-
-		failed_io(servconn);
+		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ);
 
 		return;
 	}
--- a/src/protocols/msn/servconn.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/servconn.h	Fri Jan 07 02:48:33 2005 +0000
@@ -32,21 +32,30 @@
 #include "proxy.h"
 #include "httpconn.h"
 
-/*
- * Connection types
+/**
+ * Connection error types.
  */
 typedef enum
 {
-	MSN_SERVER_NS,
-	MSN_SERVER_SB,
-	MSN_SERVER_NX,
-	MSN_SERVER_DC,
-	MSN_SERVER_HT
+	MSN_SERVCONN_ERROR_NONE,
+	MSN_SERVCONN_ERROR_CONNECT,
+	MSN_SERVCONN_ERROR_WRITE,
+	MSN_SERVCONN_ERROR_READ,
+
+} MsnServConnError;
+
+/**
+ * Connection types.
+ */
+typedef enum
+{
+	MSN_SERVCONN_NS,
+	MSN_SERVCONN_SB
 
 } MsnServConnType;
 
-/*
- * A Connection
+/**
+ * A Connection.
  */
 struct _MsnServConn
 {
@@ -58,8 +67,6 @@
 	gboolean processing;  /**< A flag that states if something is working
 							with this connection. */
 	gboolean wasted;      /**< A flag that states if it should be destroyed. */
-	gboolean destroying;  /**< A flag that states if the connection is on
-							the process of being destroyed. */
 
 	char *host; /**< The host this connection is connected or should be
 				  connected to. */
@@ -148,4 +155,12 @@
 size_t msn_servconn_write(MsnServConn *servconn, const char *buf,
 						  size_t size);
 
+/**
+ * Function to call whenever an error related to a switchboard occurs.
+ *
+ * @param servconn The servconn.
+ * @param error The error that happened.
+ */
+void msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error);
+
 #endif /* _MSN_SERVCONN_H_ */
--- a/src/protocols/msn/session.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/session.c	Fri Jan 07 02:48:33 2005 +0000
@@ -28,8 +28,7 @@
 #include "dialog.h"
 
 MsnSession *
-msn_session_new(GaimAccount *account, const char *host, int port,
-				gboolean http_method)
+msn_session_new(GaimAccount *account)
 {
 	MsnSession *session;
 
@@ -37,15 +36,14 @@
 
 	session = g_new0(MsnSession, 1);
 
-	session->account       = account;
-	session->dispatch_host = g_strdup(host);
-	session->dispatch_port = port;
-	session->http_method   = http_method;
-
+	session->account = account;
 	session->notification = msn_notification_new(session);
 	session->userlist = msn_userlist_new(session);
 	session->sync_userlist = msn_userlist_new(session);
 
+	session->user = msn_user_new(session->userlist,
+								 gaim_account_get_username(account), NULL);
+
 	session->protocol_ver = 9;
 
 	return session;
@@ -61,9 +59,6 @@
 	if (session->connected)
 		msn_session_disconnect(session);
 
-	if (session->dispatch_host != NULL)
-		g_free(session->dispatch_host);
-
 	if (session->notification != NULL)
 		msn_notification_destroy(session->notification);
 
@@ -102,16 +97,21 @@
 	if (session->nexus != NULL)
 		msn_nexus_destroy(session->nexus);
 
+	if (session->user != NULL)
+		msn_user_destroy(session->user);
+
 	g_free(session);
 }
 
 gboolean
-msn_session_connect(MsnSession *session)
+msn_session_connect(MsnSession *session, const char *host, int port,
+					gboolean http_method)
 {
 	g_return_val_if_fail(session != NULL, FALSE);
 	g_return_val_if_fail(!session->connected, TRUE);
 
 	session->connected = TRUE;
+	session->http_method = http_method;
 
 	if (session->notification == NULL)
 	{
@@ -119,9 +119,7 @@
 		g_return_val_if_reached(FALSE);
 	}
 
-	if (msn_notification_connect(session->notification,
-								 session->dispatch_host,
-								 session->dispatch_port))
+	if (msn_notification_connect(session->notification, host, port))
 	{
 		return TRUE;
 	}
@@ -135,13 +133,13 @@
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->connected);
 
+	session->connected = FALSE;
+
 	while (session->switches != NULL)
 		msn_switchboard_close(session->switches->data);
 
 	if (session->notification != NULL)
 		msn_notification_close(session->notification);
-
-	session->connected = FALSE;
 }
 
 /* TODO: This must go away when conversation is redesigned */
@@ -278,6 +276,86 @@
 }
 
 void
+msn_session_set_error(MsnSession *session, MsnErrorType error,
+					  const char *info)
+{
+	GaimConnection *gc;
+	char *msg;
+
+	gc = session->account->gc;
+
+	switch (error)
+	{
+		case MSN_ERROR_SERVCONN:
+			msg = g_strdup(info);
+			break;
+		case MSN_ERROR_UNSUPORTED_PROTOCOL:
+			msg = g_strdup(_("Our protocol is not supported by the "
+							 "server."));
+			break;
+		case MSN_ERROR_HTTP_MALFORMED:
+			msg = g_strdup(_("Error parsing HTTP."));
+			break;
+		case MSN_ERROR_SIGN_OTHER:
+			gc->wants_to_die = TRUE;
+			msg = g_strdup(_("You have signed on from another location."));
+			break;
+		case MSN_ERROR_SERV_DOWN:
+			msg = g_strdup(_("The MSN servers are going down "
+							 "temporarily."));
+			break;
+		case MSN_ERROR_AUTH:
+			msg = g_strdup_printf(_("Unable to authenticate: %s"),
+								  (info == NULL ) ?
+								  _("Unknown error") : info);
+			break;
+		case MSN_ERROR_BAD_BLIST:
+			msg = g_strdup(_("Your MSN buddy list is temporarily "
+							 "unavailable. Please wait and try "
+							 "again."));
+			break;
+		default:
+			msg = g_strdup(_("Unknown error."));
+			break;
+	}
+
+	msn_session_disconnect(session);
+	gaim_connection_error(gc, msg);
+
+	g_free(msg);
+}
+
+static const char *
+get_login_step_text(MsnSession *session)
+{
+	const char *steps_text[] = {
+		_("Connecting"),
+		_("Handshaking"),
+		_("Transfering"),
+		_("Starting authentication"),
+		_("Getting cookie"),
+		_("Authenticating"),
+		_("Sending cookie"),
+		_("Retrieving buddy list")
+	};
+
+	return steps_text[session->login_step];
+}
+
+void
+msn_session_set_login_step(MsnSession *session, MsnLoginStep step)
+{
+	GaimConnection *gc;
+
+	gc = session->account->gc;
+
+	session->login_step = step;
+
+	gaim_connection_update_progress(gc, get_login_step_text(session), step,
+									MSN_LOGIN_STEPS);
+}
+
+void
 msn_session_finish_login(MsnSession *session)
 {
 	GaimAccount *account;
--- a/src/protocols/msn/session.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/session.h	Fri Jan 07 02:48:33 2005 +0000
@@ -40,6 +40,40 @@
 #include "userlist.h"
 #include "sync.h"
 
+/**
+ * Types of errors.
+ */
+typedef enum
+{
+	MSN_ERROR_SERVCONN,
+	MSN_ERROR_UNSUPORTED_PROTOCOL,
+	MSN_ERROR_HTTP_MALFORMED,
+	MSN_ERROR_AUTH,
+	MSN_ERROR_BAD_BLIST,
+	MSN_ERROR_SIGN_OTHER,
+	MSN_ERROR_SERV_DOWN
+
+} MsnErrorType;
+
+/**
+ * Login steps.
+ */
+typedef enum
+{
+	MSN_LOGIN_STEP_START,
+	MSN_LOGIN_STEP_HANDSHAKE,
+	MSN_LOGIN_STEP_TRANSFER,
+	MSN_LOGIN_STEP_AUTH_START,
+	MSN_LOGIN_STEP_AUTH,
+	MSN_LOGIN_STEP_GET_COOKIE,
+	MSN_LOGIN_STEP_AUTH_END,
+	MSN_LOGIN_STEP_SYN,
+	MSN_LOGIN_STEP_END
+
+} MsnLoginStep;
+
+#define MSN_LOGIN_STEPS MSN_LOGIN_STEP_END
+
 struct _MsnSession
 {
 	GaimAccount *account;
@@ -48,18 +82,16 @@
 
 	guint protocol_ver;
 
-	char *dispatch_host;
-	int dispatch_port;
+	MsnLoginStep login_step; /**< The current step in the login process. */
 
 	gboolean connected;
 	gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */
 	gboolean destroying; /**< A flag that states if the session is being destroyed. */
+	gboolean http_method;
 
 	MsnNotification *notification;
 	MsnNexus *nexus;
-
-	gboolean http_method;
-	gint http_poll_timer;
+	MsnSync *sync;
 
 	MsnUserList *userlist;
 	MsnUserList *sync_userlist;
@@ -67,8 +99,9 @@
 	int servconns_count; /**< The count of server connections. */
 	GList *switches; /**< The list of all the switchboards. */
 	GList *directconns; /**< The list of all the directconnections. */
+	GList *slplinks; /**< The list of all the slplinks. */
 
-	int conv_seq;
+	int conv_seq; /**< The current conversation sequence number. */
 
 	struct
 	{
@@ -81,27 +114,16 @@
 		int client_port;
 
 	} passport_info;
-
-	/* You have no idea how much I hate all that is below. */
-	/* shx: What? ;) */
-
-	MsnSync *sync;
-
-	GList *slplinks;
 };
 
 /**
  * Creates an MSN session.
  *
  * @param account The account.
- * @param host    The dispatch server host.
- * @param port    The dispatch server port.
  *
  * @return The new MSN session.
  */
-MsnSession *msn_session_new(GaimAccount *account,
-							const char *host, int port,
-							gboolean http_method);
+MsnSession *msn_session_new(GaimAccount *account);
 
 /**
  * Destroys an MSN session.
@@ -113,11 +135,16 @@
 /**
  * Connects to and initiates an MSN session.
  *
- * @param session The MSN session.
+ * @param session     The MSN session.
+ * @param host        The dispatch server host.
+ * @param port        The dispatch server port.
+ * @param http_method Whether to use or not http_method.
  *
  * @return @c TRUE on success, @c FALSE on failure.
  */
-gboolean msn_session_connect(MsnSession *session);
+gboolean msn_session_connect(MsnSession *session,
+							 const char *host, int port,
+							 gboolean http_method);
 
 /**
  * Disconnects from an MSN session.
@@ -142,6 +169,29 @@
 MsnSwitchBoard *msn_session_get_swboard(MsnSession *session,
 										const char *username);
 
+/**
+ * Sets an error for the MSN session.
+ *
+ * @param session The MSN session.
+ * @param error The error.
+ * @param info Extra information.
+ */
+void msn_session_set_error(MsnSession *session, MsnErrorType error,
+						   const char *info);
+
+/**
+ * Sets the current step in the login proccess.
+ *
+ * @param session The MSN session.
+ * @param step The current step.
+ */
+void msn_session_set_login_step(MsnSession *session, MsnLoginStep step);
+
+/**
+ * Finish the login proccess.
+ *
+ * @param session The MSN session.
+ */
 void msn_session_finish_login(MsnSession *session);
 
 #endif /* _MSN_SESSION_H_ */
--- a/src/protocols/msn/slp.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/slp.h	Fri Jan 07 02:48:33 2005 +0000
@@ -24,13 +24,6 @@
 #ifndef _MSN_SLP_H_
 #define _MSN_SLP_H_
 
-/* #define MSN_DEBUG_MSG 1 */
-/* #define MSN_DEBUG_SLPMSG 1 */
-
-/* #define MSN_DEBUG_SLP 1 */
-/* #define MSN_DEBUG_SLP_VERBOSE 1 */
-/* #define MSN_DEBUG_SLP_FILES 1 */
-
 #include "slpcall.h"
 
 #include "ft.h"
--- a/src/protocols/msn/slpcall.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/slpcall.c	Fri Jan 07 02:48:33 2005 +0000
@@ -96,11 +96,15 @@
 
 		g_return_if_fail(slpmsg != NULL);
 
+#if 0
 		gaim_debug_info("msn", "slpcall destroy: trying slp_msg (%p)\n",
 						slpmsg);
+#endif
 
 		if (slpmsg->slpcall == slpcall)
+		{
 			msn_slpmsg_destroy(slpmsg);
+		}
 	}
 
 	if (slpcall->end_cb != NULL)
@@ -188,7 +192,7 @@
 {
 	MsnSlpCall *slpcall;
 
-	gaim_debug_info("msn", "slpcall timeout\n");
+	gaim_debug_info("msn", "slpcall timeout (%p)\n", slpcall);
 
 	slpcall = data;
 
--- a/src/protocols/msn/slplink.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/slplink.c	Fri Jan 07 02:48:33 2005 +0000
@@ -213,15 +213,7 @@
 			slplink->swboard->slplink = slplink;
 		}
 
-		if (!g_queue_is_empty(slplink->swboard->im_queue) ||
-			slplink->swboard->empty)
-		{
-			msn_switchboard_queue_msg(slplink->swboard, msg);
-		}
-		else
-		{
-			msn_switchboard_send_msg(slplink->swboard, msg);
-		}
+		msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
 	}
 }
 
--- a/src/protocols/msn/state.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/state.c	Fri Jan 07 02:48:33 2005 +0000
@@ -46,12 +46,13 @@
 	MsnObject *msnobj;
 	const char *state_text;
 
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->notification != NULL);
+
 	cmdproc = session->notification->cmdproc;
 	user = session->user;
 	state_text = msn_state_get_text(state);
 
-	g_return_if_fail(session != NULL);
-
 	msnobj = msn_user_get_object(user);
 
 	if (msnobj == NULL)
--- a/src/protocols/msn/switchboard.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/switchboard.c	Fri Jan 07 02:48:33 2005 +0000
@@ -35,8 +35,9 @@
 							 MsnMsgErrorType error);
 
 /**************************************************************************
- * Main stuff
+ * Main
  **************************************************************************/
+
 MsnSwitchBoard *
 msn_switchboard_new(MsnSession *session)
 {
@@ -48,10 +49,10 @@
 	swboard = g_new0(MsnSwitchBoard, 1);
 
 	swboard->session = session;
-	swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVER_SB);
+	swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_SB);
 	swboard->cmdproc = servconn->cmdproc;
 
-	swboard->im_queue = g_queue_new();
+	swboard->msg_queue = g_queue_new();
 	swboard->empty = TRUE;
 
 	swboard->cmdproc->data = swboard;
@@ -71,17 +72,12 @@
 
 	g_return_if_fail(swboard != NULL);
 
-	if (swboard->destroying)
-		return;
-
-	swboard->destroying = TRUE;
-
 	/* If it linked us is because its looking for trouble */
 	if (swboard->slplink != NULL)
 		msn_slplink_destroy(swboard->slplink);
 
 	/* Destroy the message queue */
-	while ((msg = g_queue_pop_head(swboard->im_queue)) != NULL)
+	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
 	{
 		if (swboard->error != MSN_SB_ERROR_NONE)
 		{
@@ -92,7 +88,7 @@
 		msn_message_unref(msg);
 	}
 
-	g_queue_free(swboard->im_queue);
+	g_queue_free(swboard->msg_queue);
 
 	for (l = swboard->ack_list; l != NULL; l = l->next)
 		msn_message_unref(l->data);
@@ -112,8 +108,17 @@
 	session = swboard->session;
 	session->switches = g_list_remove(session->switches, swboard);
 
+#if 0
+	/* This should never happen or we are in trouble. */
 	if (swboard->servconn != NULL)
 		msn_servconn_destroy(swboard->servconn);
+#endif
+
+	swboard->cmdproc->data = NULL;
+
+	msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
+
+	msn_servconn_destroy(swboard->servconn);
 
 	g_free(swboard);
 }
@@ -172,8 +177,9 @@
 }
 
 /**************************************************************************
- * Utility functions
+ * Utility
  **************************************************************************/
+
 static void
 send_clientcaps(MsnSwitchBoard *swboard)
 {
@@ -184,7 +190,7 @@
 	msn_message_set_flag(msg, 'U');
 	msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));
 
-	msn_switchboard_send_msg(swboard, msg);
+	msn_switchboard_send_msg(swboard, msg, TRUE);
 
 	msn_message_destroy(msg);
 }
@@ -425,8 +431,165 @@
 }
 
 /**************************************************************************
+ * Message Stuff
+ **************************************************************************/
+
+/** Called when a message times out. */
+static void
+msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
+{
+	MsnMessage *msg;
+
+	msg = trans->data;
+
+	msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
+}
+
+/** Called when we receive an error of a message. */
+static void
+msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN);
+}
+
+#if 0
+/** Called when we receive an ack of a special message. */
+static void
+msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	MsnMessage *msg;
+
+	msg = cmd->trans->data;
+
+	if (msg->ack_cb != NULL)
+		msg->ack_cb(msg->ack_data);
+
+	msn_message_unref(msg);
+}
+
+/** Called when we receive a nak of a special message. */
+static void
+msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	MsnMessage *msg;
+
+	msg = cmd->trans->data;
+
+	msn_message_unref(msg);
+}
+#endif
+
+static void
+release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
+{
+	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
+	char *payload;
+	gsize payload_len;
+
+	g_return_if_fail(swboard != NULL);
+	g_return_if_fail(msg     != NULL);
+
+	cmdproc = swboard->cmdproc;
+
+	payload = msn_message_gen_payload(msg, &payload_len);
+
+	/* msn_message_show_readable(msg, "SB SEND", FALSE); */
+
+	trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
+								msn_message_get_flag(msg), payload_len);
+
+	/* Data for callbacks */
+	msn_transaction_set_data(trans, msg);
+
+	if (msg->type == MSN_MSG_TEXT)
+	{
+		msg->ack_ref = TRUE;
+		msn_message_ref(msg);
+		swboard->ack_list = g_list_append(swboard->ack_list, msg);
+		msn_transaction_set_timeout_cb(trans, msg_timeout);
+	}
+	else if (msg->type == MSN_MSG_SLP)
+	{
+		msg->ack_ref = TRUE;
+		msn_message_ref(msg);
+		swboard->ack_list = g_list_append(swboard->ack_list, msg);
+		msn_transaction_set_timeout_cb(trans, msg_timeout);
+#if 0
+		if (msg->ack_cb != NULL)
+		{
+			msn_transaction_add_cb(trans, "ACK", msg_ack);
+			msn_transaction_add_cb(trans, "NAK", msg_nak);
+		}
+#endif
+	}
+
+	trans->payload = payload;
+	trans->payload_len = payload_len;
+
+	msg->trans = trans;
+
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static void
+queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
+{
+	g_return_if_fail(swboard != NULL);
+	g_return_if_fail(msg     != NULL);
+
+	gaim_debug_info("msn", "Appending message to queue.\n");
+
+	g_queue_push_tail(swboard->msg_queue, msg);
+
+	msn_message_ref(msg);
+}
+
+static void
+process_queue(MsnSwitchBoard *swboard)
+{
+	MsnMessage *msg;
+
+	g_return_if_fail(swboard != NULL);
+
+	gaim_debug_info("msn", "Processing queue\n");
+
+	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
+	{
+		gaim_debug_info("msn", "Sending message\n");
+		release_msg(swboard, msg);
+		msn_message_unref(msg);
+	}
+}
+
+gboolean
+msn_switchboard_can_send(MsnSwitchBoard *swboard)
+{
+	g_return_val_if_fail(swboard != NULL, FALSE);
+
+	if (swboard->empty || !g_queue_is_empty(swboard->msg_queue))
+		return FALSE;
+
+	return TRUE;
+}
+
+void
+msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
+						 gboolean queue)
+{
+	g_return_if_fail(swboard != NULL);
+	g_return_if_fail(msg     != NULL);
+
+	if (msn_switchboard_can_send(swboard))
+		release_msg(swboard, msg);
+	else if (queue)
+		queue_msg(swboard, msg);
+}
+
+/**************************************************************************
  * Switchboard Commands
  **************************************************************************/
+
 static void
 ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -532,7 +695,7 @@
 
 	msn_switchboard_add_user(swboard, passport);
 
-	msn_switchboard_process_queue(swboard);
+	process_queue(swboard);
 
 	if (!session->http_method)
 		send_clientcaps(swboard);
@@ -739,137 +902,6 @@
 }
 
 /**************************************************************************
- * Message stuff
- **************************************************************************/
-/** Called when a message times out. */
-static void
-msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
-	MsnMessage *msg;
-
-	msg = trans->data;
-
-	msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
-}
-
-/** Called when we receive an error of a message. */
-static void
-msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN);
-}
-
-#if 0
-/** Called when we receive an ack of a special message. */
-static void
-msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-
-	if (msg->ack_cb != NULL)
-		msg->ack_cb(msg->ack_data);
-
-	msn_message_unref(msg);
-}
-
-/** Called when we receive a nak of a special message. */
-static void
-msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-
-	msn_message_unref(msg);
-}
-#endif
-
-void
-msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
-	char *payload;
-	gsize payload_len;
-
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	cmdproc = swboard->cmdproc;
-
-	payload = msn_message_gen_payload(msg, &payload_len);
-
-	/* msn_message_show_readable(msg, "SB SEND", FALSE); */
-
-	trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
-								msn_message_get_flag(msg), payload_len);
-
-	/* Data for callbacks */
-	msn_transaction_set_data(trans, msg);
-
-	if (msg->type == MSN_MSG_TEXT)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
-	}
-	else if (msg->type == MSN_MSG_SLP)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
-#if 0
-		if (msg->ack_cb != NULL)
-		{
-			msn_transaction_add_cb(trans, "ACK", msg_ack);
-			msn_transaction_add_cb(trans, "NAK", msg_nak);
-		}
-#endif
-	}
-
-	trans->payload = payload;
-	trans->payload_len = payload_len;
-
-	msg->trans = trans;
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-void
-msn_switchboard_queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	gaim_debug_info("msn", "Appending message to queue.\n");
-
-	g_queue_push_tail(swboard->im_queue, msg);
-
-	msn_message_ref(msg);
-}
-
-void
-msn_switchboard_process_queue(MsnSwitchBoard *swboard)
-{
-	MsnMessage *msg;
-
-	g_return_if_fail(swboard != NULL);
-
-	gaim_debug_info("msn", "Processing queue\n");
-
-	while ((msg = g_queue_pop_head(swboard->im_queue)) != NULL)
-	{
-		gaim_debug_info("msn", "Sending message\n");
-		msn_switchboard_send_msg(swboard, msg);
-		msn_message_unref(msg);
-	}
-}
-
-/**************************************************************************
  * Connect stuff
  **************************************************************************/
 static void
@@ -882,8 +914,6 @@
 	cmdproc = servconn->cmdproc;
 	g_return_if_fail(cmdproc != NULL);
 
-	cmdproc->ready = TRUE;
-
 	account = cmdproc->session->account;
 	swboard = cmdproc->data;
 	g_return_if_fail(swboard != NULL);
@@ -912,6 +942,8 @@
 	swboard = servconn->cmdproc->data;
 	g_return_if_fail(swboard != NULL);
 
+	msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
+
 	msn_switchboard_destroy(swboard);
 }
 
@@ -1003,6 +1035,7 @@
 /**************************************************************************
  * Create & Transfer stuff
  **************************************************************************/
+
 static void
 got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -1062,23 +1095,29 @@
 {
 	g_return_if_fail(swboard != NULL);
 
-	if (g_queue_is_empty(swboard->im_queue))
+	if (swboard->error != MSN_SB_ERROR_NONE)
+	{
+		msn_switchboard_destroy(swboard);
+	}
+	else if (g_queue_is_empty(swboard->msg_queue) ||
+			 !swboard->session->connected)
 	{
 		MsnCmdProc *cmdproc;
-
 		cmdproc = swboard->cmdproc;
-
 		msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);
 
 		msn_switchboard_destroy(swboard);
 	}
 	else
+	{
 		swboard->closed = TRUE;
+	}
 }
 
 /**************************************************************************
  * Init stuff
  **************************************************************************/
+
 void
 msn_switchboard_init(void)
 {
--- a/src/protocols/msn/switchboard.h	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/switchboard.h	Fri Jan 07 02:48:33 2005 +0000
@@ -35,8 +35,8 @@
 
 #include "slplink.h"
 
-/*
- * A switchboard error
+/**
+ * A switchboard error.
  */
 typedef enum
 {
@@ -49,9 +49,10 @@
 
 } MsnSBErrorType;
 
-/*
- * A switchboard  A place where a bunch of users send messages to the rest
- * of the users.
+/**
+ * A switchboard.
+ *
+ * A place where a bunch of users send messages to the rest of the users.
  */
 struct _MsnSwitchBoard
 {
@@ -71,8 +72,6 @@
 							  users in it. */
 	gboolean invited;		/**< A flag that states if we were invited to the
 							  switchboard. */
-	gboolean destroying;	/**< A flag that states if the switchboard is on
-							  the process of being destroyed. */
 	gboolean ready;			/**< A flag that states if this switchboard is
 							  ready to be used. */
 	gboolean closed;		/**< A flag that states if the switchboard has
@@ -84,7 +83,7 @@
 
 	int chat_id;
 
-	GQueue *im_queue; /**< Queue of messages to send. */
+	GQueue *msg_queue; /**< Queue of messages to send. */
 	GList *ack_list; /**< List of messages waiting for an ack. */
 
 	MsnSBErrorType error; /**< The error that occurred in this switchboard
@@ -197,9 +196,28 @@
  */
 void msn_switchboard_close(MsnSwitchBoard *swboard);
 
-void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg);
-void msn_switchboard_queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg);
-void msn_switchboard_process_queue(MsnSwitchBoard *swboard);
+/**
+ * Returns whether or not we currently can send a message through this
+ * switchboard.
+ *
+ * @param swboard The switchboard.
+ *
+ * @return @c TRUE if a message can be sent, @c FALSE otherwise.
+ */
+gboolean msn_switchboard_can_send(MsnSwitchBoard *swboard);
+
+/**
+ * Sends a message through this switchboard.
+ *
+ * @param swboard The switchboard.
+ * @param msg The message.
+ * @param queue A flag that states if we want this message to be queued (in
+ * the case it cannot currently be sent).
+ *
+ * @return @c TRUE if a message can be sent, @c FALSE otherwise.
+ */
+void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
+							  gboolean queue);
 
 gboolean msn_switchboard_chat_leave(MsnSwitchBoard *swboard);
 gboolean msn_switchboard_chat_invite(MsnSwitchBoard *swboard, const char *who);
@@ -208,13 +226,27 @@
 void msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user);
 
 /**
- * Processes application/x-msnmsgrp2p messages.
+ * Processes peer to peer messages.
  *
  * @param cmdproc The command processor.
  * @param msg     The message.
  */
 void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
+/**
+ * Processes emoticon messages.
+ *
+ * @param cmdproc The command processor.
+ * @param msg     The message.
+ */
 void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
+/**
+ * Processes INVITE messages.
+ *
+ * @param cmdproc The command processor.
+ * @param msg     The message.
+ */
 void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
 
 #endif /* _MSN_SWITCHBOARD_H_ */
--- a/src/protocols/msn/transaction.c	Thu Jan 06 23:11:12 2005 +0000
+++ b/src/protocols/msn/transaction.c	Fri Jan 07 02:48:33 2005 +0000
@@ -112,6 +112,9 @@
 {
 	MsnCommand *cmd;
 
+	if (!cmdproc->servconn->connected)
+		return;
+
 	gaim_debug_info("msn", "unqueueing command.\n");
 	cmd = trans->pendent_cmd;