diff src/protocols/msn/msn.c @ 5309:e2e53316a21d

[gaim-migrate @ 5681] Announcing the new MSN prpl! It probably has some bugs, and for the time being, there is no file transfer. That's good though, because the current MSN file transfer is a little broken. I've had many corrupted files. I'll commit new file transfer code when it's written. I want this heavily tested before 0.63! If you use MSN, please talk to people on it. Let me know of any oddities, crashes, bugs, whatever. I'll fix things as I find them. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Tue, 06 May 2003 02:06:56 +0000
parents abe4d103e300
children bd98232872a3
line wrap: on
line diff
--- a/src/protocols/msn/msn.c	Tue May 06 00:34:54 2003 +0000
+++ b/src/protocols/msn/msn.c	Tue May 06 02:06:56 2003 +0000
@@ -3,7 +3,7 @@
  *
  * gaim
  *
- * Parts Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org>
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,13 +18,12 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
 #include "msn.h"
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
+#include "away.h"
+#include "msg.h"
+#include "session.h"
+#include "utils.h"
 
 #define BUDDY_ALIAS_MAXLEN 388
 
@@ -33,1320 +32,92 @@
 /* for win32 compatability */
 G_MODULE_IMPORT GSList *connections;
 
-static void msn_login_callback(gpointer, gint, GaimInputCondition);
-static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition);
-
-static char *msn_normalize(const char *s)
-{
-	static char buf[BUF_LEN];
-
-	g_return_val_if_fail(s != NULL, NULL);
-
-	g_snprintf(buf, sizeof(buf), "%s%s", s, strchr(s, '@') ? "" : "@hotmail.com");
-
-	return buf;
-}
-
-char *
-handle_errcode(char *buf, gboolean show)
-{
-	int errcode;
-	static char msg[MSN_BUF_LEN];
-
-	buf[4] = 0;
-	errcode = atoi(buf);
-
-	switch (errcode) {
-		case 200:
-			g_snprintf(msg, sizeof(msg), _("Syntax Error (probably a Gaim bug)"));
-			break;
-		case 201:
-			g_snprintf(msg, sizeof(msg), _("Invalid Parameter (probably a Gaim bug)"));
-			break;
-		case 205:
-			g_snprintf(msg, sizeof(msg), _("Invalid User"));
-			break;
-		case 206:
-			g_snprintf(msg, sizeof(msg), _("Fully Qualified Domain Name missing"));
-			break;
-		case 207:
-			g_snprintf(msg, sizeof(msg), _("Already Login"));
-			break;
-		case 208:
-			g_snprintf(msg, sizeof(msg), _("Invalid Username"));
-			break;
-		case 209:
-			g_snprintf(msg, sizeof(msg), _("Invalid Friendly Name"));
-			break;
-		case 210:
-			g_snprintf(msg, sizeof(msg), _("List Full"));
-			break;
-		case 215:
-			g_snprintf(msg, sizeof(msg), _("Already there"));
-			break;
-		case 216:
-			g_snprintf(msg, sizeof(msg), _("Not on list"));
-			break;
-		case 217:
-			g_snprintf(msg, sizeof(msg), _("User is offline"));
-			break;
-		case 218:
-			g_snprintf(msg, sizeof(msg), _("Already in the mode"));
-			break;
-		case 219:
-			g_snprintf(msg, sizeof(msg), _("Already in opposite list"));
-			break;
-		case 280:
-			g_snprintf(msg, sizeof(msg), _("Switchboard failed"));
-			break;
-		case 281:
-			g_snprintf(msg, sizeof(msg), _("Notify Transfer failed"));
-			break;
-
-		case 300:
-			g_snprintf(msg, sizeof(msg), _("Required fields missing"));
-			break;
-		case 302:
-			g_snprintf(msg, sizeof(msg), _("Not logged in"));
-			break;
-
-		case 500:
-			g_snprintf(msg, sizeof(msg), _("Internal server error"));
-			break;
-		case 501:
-			g_snprintf(msg, sizeof(msg), _("Database server error"));
-			break;
-		case 510:
-			g_snprintf(msg, sizeof(msg), _("File operation error"));
-			break;
-		case 520:
-			g_snprintf(msg, sizeof(msg), _("Memory allocation error"));
-			break;
-
-		case 600:
-			g_snprintf(msg, sizeof(msg), _("Server busy"));
-			break;
-		case 601:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
-			break;
-		case 602:
-			g_snprintf(msg, sizeof(msg), _("Peer Notification server down"));
-			break;
-		case 603:
-			g_snprintf(msg, sizeof(msg), _("Database connect error"));
-			break;
-		case 604:
-			g_snprintf(msg, sizeof(msg), _("Server is going down (abandon ship)"));
-			break;
-
-		case 707:
-			g_snprintf(msg, sizeof(msg), _("Error creating connection"));
-			break;
-		case 711:
-			g_snprintf(msg, sizeof(msg), _("Unable to write"));
-			break;
-		case 712:
-			g_snprintf(msg, sizeof(msg), _("Session overload"));
-			break;
-		case 713:
-			g_snprintf(msg, sizeof(msg), _("User is too active"));
-			break;
-		case 714:
-			g_snprintf(msg, sizeof(msg), _("Too many sessions"));
-			break;
-		case 715:
-			g_snprintf(msg, sizeof(msg), _("Not expected"));
-			break;
-		case 717:
-			g_snprintf(msg, sizeof(msg), _("Bad friend file"));
-			break;
-
-		case 911:
-			g_snprintf(msg, sizeof(msg), _("Authentication failed"));
-			break;
-		case 913:
-			g_snprintf(msg, sizeof(msg), _("Not allowed when offline"));
-			break;
-	        case 920:
-			g_snprintf(msg, sizeof(msg), _("Not accepting new users"));
-			break;
-	        case 924:
-			g_snprintf(msg, sizeof(msg), _("User unverified"));
-			break;
-		default:
-			g_snprintf(msg, sizeof(msg), _("Unknown Error Code"));
-			break;
-	}
-
-	if (show)
-		do_error_dialog(msg, NULL, GAIM_ERROR);
-
-	return msg;
-}
-
-
-char *url_decode(const char *msg)
-{
-	static char buf[MSN_BUF_LEN];
-	int i, j = 0;
-	char *bum;
-
-	bzero(buf, sizeof(buf));
-	for (i = 0; i < strlen(msg); i++) {
-		char hex[3];
-		if (msg[i] != '%') {
-			buf[j++] = msg[i];
-			continue;
-		}
-		strncpy(hex, msg + ++i, 2); hex[2] = 0;
-		/* i is pointing to the start of the number */
-		i++; /* now it's at the end and at the start of the for loop
-			will be at the next character */
-		buf[j++] = strtol(hex, NULL, 16);
-	}
-	buf[j] = 0;
-
-	if(!g_utf8_validate(buf, -1, (const char **)&bum))
-			*bum = '\0';
-
-	return buf;
-}
-
-static char *url_encode(const char *msg)
-{
-	static char buf[MSN_BUF_LEN];
-	int i, j = 0;
-
-	bzero(buf, sizeof(buf));
-	for (i = 0; i < strlen(msg); i++) {
-		if (isalnum(msg[i]))
-			buf[j++] = msg[i];
-		else {
-			sprintf(buf + j, "%%%02x", (unsigned char)msg[i]);
-			j += 3;
-		}
-	}
-	buf[j] = 0;
-
-	return buf;
-}
-
-static void handle_hotmail(struct gaim_connection *gc, char *data)
-{
-	char login_url[2048];
-	char buf[MSN_BUF_LEN];
-	struct msn_data *md = gc->proto_data;
-
-	if (strchr(gc->username, '@') != strstr(gc->username, "@hotmail.com"))
-		/* We can only get Hotmail notification from hotmail users */
-		return;
-
-	if (!md->passport) {
-		g_snprintf(buf, sizeof(buf), "URL %u INBOX\r\n", ++md->trId);
-
-		if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-			return;
-		}
-	} else {
-		g_snprintf(login_url, sizeof(login_url), "%s", md->passport);
-
-		if (strstr(data, "Content-Type: text/x-msmsgsinitialemailnotification;")) {
-			char *x = strstr(data, "Inbox-Unread:");
-			if (!x) return;
-			x += strlen("Inbox-Unread: ");
-			connection_has_mail(gc, atoi(x), NULL, NULL, login_url);
-		} else if (strstr(data, "Content-Type: text/x-msmsgsemailnotification;")) {
-			char *from = strstr(data, "From:");
-			char *subject = strstr(data, "Subject:");
-			char *x;
-			if (!from || !subject) {
-				connection_has_mail(gc, 1, NULL, NULL, login_url);
-				return;
-			}
-			from += strlen("From: ");
-			x = strstr(from, "\r\n"); *x = 0;
-			subject += strlen("Subject: ");
-			x = strstr(subject, "\r\n"); *x = 0;
-			connection_has_mail(gc, -1, from, subject, login_url);
-		}
-	}
-}
-
-struct msn_add_permit {
-	struct gaim_connection *gc;
-	char *user;
-	char *friend;
-};
-
-static void msn_accept_add(struct msn_add_permit *map)
-{
-	if(g_slist_find(connections, map->gc)) {
-		struct msn_data *md = map->gc->proto_data;
-		char buf[MSN_BUF_LEN];
-
-		g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, map->user, url_encode(map->friend));
-
-		if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-			hide_login_progress(map->gc, _("Write error"));
-			signoff(map->gc);
-			return;
-		}
-		gaim_privacy_permit_add(map->gc->account, map->user);
-		build_allow_list(); /* er. right. we'll need to have a thing for this in CUI too */
-		show_got_added(map->gc, NULL, map->user, map->friend, NULL);
-	}
-
-	g_free(map->user);
-	g_free(map->friend);
-	g_free(map);
-}
-
-static void msn_cancel_add(struct msn_add_permit *map)
-{
-	if(g_slist_find(connections, map->gc)) {
-		struct msn_data *md = map->gc->proto_data;
-		char buf[MSN_BUF_LEN];
-
-		g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, map->user, url_encode(map->friend));
-		if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-			hide_login_progress(map->gc, _("Write error"));
-			signoff(map->gc);
-			return;
-		}
-		gaim_privacy_deny_add(map->gc->account, map->user);
-		build_block_list();
-	}
-
-	g_free(map->user);
-	g_free(map->friend);
-	g_free(map);
-}
-
-static int msn_process_main(struct gaim_connection *gc, char *buf)
-{
-	struct msn_data *md = gc->proto_data;
-	char sendbuf[MSN_BUF_LEN];
-
-	if (!g_ascii_strncasecmp(buf, "ADD", 3)) {
-		char *list, *user, *friend, *tmp = buf;
-		struct msn_add_permit *ap;
-		GSList *perm = gc->account->permit;
-		char msg[MSN_BUF_LEN];
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		list = tmp;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		user = tmp;
-
-		GET_NEXT(tmp);
-		friend = url_decode(tmp);
-
-		if (g_ascii_strcasecmp(list, "RL"))
-			return 1;
-
-		while (perm) {
-			if (!gaim_utf8_strcasecmp(perm->data, user))
-				return 1;
-			perm = perm->next;
-		}
-
-		ap = g_new0(struct msn_add_permit, 1);
-		ap->user = g_strdup(user);
-		ap->friend = g_strdup(friend);
-		ap->gc = gc;
-
-		g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add %s to his or her buddy list."),
-				ap->user, ap->friend, ap->gc->username);
-
-		//	do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE);
-	} else if (!g_ascii_strncasecmp(buf, "BLP", 3)) {
-		char *type, *tmp = buf;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		type = tmp;
-
-		if (!g_ascii_strcasecmp(type, "AL")) {
-			/* If the current setting is AL, messages
-			 * from users who are not in BL will be delivered
-			 *
-			 * In other words, deny some */
-			gc->account->permdeny = DENY_SOME;
-		} else {
-			/* If the current
-			 * setting is BL, only messages from people who are in the AL will be
-			 * delivered.
-			 *
-			 * In other words, permit some */
-			gc->account->permdeny = PERMIT_SOME;
-		}
-	} else if (!g_ascii_strncasecmp(buf, "BPR", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "CHG", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "CHL", 3)) {
-		char *hash = buf;
-		char buf2[MSN_BUF_LEN];
-		md5_state_t st;
-		md5_byte_t di[16];
-		int i;
-
-		GET_NEXT(hash);
-		GET_NEXT(hash);
-
-		md5_init(&st);
-		md5_append(&st, (const md5_byte_t *)hash, strlen(hash));
-		md5_append(&st, (const md5_byte_t *)"Q1P7W2E4J9R8U3S5", strlen("Q1P7W2E4J9R8U3S5"));
-		md5_finish(&st, di);
-
-		g_snprintf(sendbuf, sizeof(sendbuf), "QRY %u msmsgs@msnmsgr.com 32\r\n", ++md->trId);
-		for (i = 0; i < 16; i++) {
-			g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
-			strcat(sendbuf, buf2);
-		}
-
-		if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-			hide_login_progress(gc, _("Unable to write to server"));
-			signoff(gc);
-		}
-
-	} else if (!g_ascii_strncasecmp(buf, "FLN", 3)) {
-		char *usr = buf;
-
-		GET_NEXT(usr);
-		serv_got_update(gc, usr, 0, 0, 0, 0, 0);
-	} else if (!g_ascii_strncasecmp(buf, "GTC", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "INF", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "ILN", 3)) {
-		char *state, *user, *friend, *tmp = buf;
-		int status = 0;
-
-		GET_NEXT(tmp);
-
-		GET_NEXT(tmp);
-		state = tmp;
-
-		GET_NEXT(tmp);
-		user = tmp;
-
-		GET_NEXT(tmp);
-		friend = url_decode(tmp);
-
-		serv_got_alias(gc, user, friend);
+static char *msn_normalize(const char *str);
 
-		if (!g_ascii_strcasecmp(state, "BSY")) {
-			status |= UC_UNAVAILABLE | (MSN_BUSY << 1);
-		} else if (!g_ascii_strcasecmp(state, "IDL")) {
-			status |= UC_UNAVAILABLE | (MSN_IDLE << 1);
-		} else if (!g_ascii_strcasecmp(state, "BRB")) {
-			status |= UC_UNAVAILABLE | (MSN_BRB << 1);
-		} else if (!g_ascii_strcasecmp(state, "AWY")) {
-			status |= UC_UNAVAILABLE | (MSN_AWAY << 1);
-		} else if (!g_ascii_strcasecmp(state, "PHN")) {
-			status |= UC_UNAVAILABLE | (MSN_PHONE << 1);
-		} else if (!g_ascii_strcasecmp(state, "LUN")) {
-			status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
-		}
-
-		serv_got_update(gc, user, 1, 0, 0, 0, status);
-	} else if (!g_ascii_strncasecmp(buf, "LST", 3)) {
-		char *which, *who, *friend, *tmp = buf;
-		struct msn_add_permit *ap; /* for any as yet undealt with buddies who've added you to their buddy list when you were off-line.  How dare they! */
-		GSList *perm = gc->account->permit; /* current permit list */
-		GSList *denyl = gc->account->deny;
-		char msg[MSN_BUF_LEN];
-		int new = 1;
-		int pos, tot;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		which = tmp;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		pos = strtol(tmp, NULL, 10);
-		GET_NEXT(tmp);
-		tot = strtol(tmp, NULL, 10);
-		GET_NEXT(tmp);
-		who = tmp;
-
-		GET_NEXT(tmp);
-		friend = url_decode(tmp);
-
-		if (!g_ascii_strcasecmp(which, "FL") && pos) {
-			struct msn_buddy *b = g_new0(struct msn_buddy, 1);
-			b->user = g_strdup(who);
-			b->friend = g_strdup(friend);
-			md->fl = g_slist_append(md->fl, b);
-		} else if (!g_ascii_strcasecmp(which, "AL") && pos) {
-			if (g_slist_find_custom(gc->account->deny, who,
-							(GCompareFunc)strcmp)) {
-				gaim_debug(GAIM_DEBUG_INFO, "msn",
-						   "moving from deny to permit: %s", who);
-				gaim_privacy_deny_remove(gc->account, who);
-			}
-			gaim_privacy_permit_add(gc->account, who);
-		} else if (!g_ascii_strcasecmp(which, "BL") && pos) {
-			gaim_privacy_deny_add(gc->account, who);
-		} else if (!g_ascii_strcasecmp(which, "RL")) {
-		    if (pos) {
-			while(perm) {
-				if(!g_ascii_strcasecmp(perm->data, who))
-					new = 0;
-				perm = perm->next;
-			}
-			while(denyl) {
-			  if(!g_ascii_strcasecmp(denyl->data, who))
-			    new = 0;
-			  denyl = denyl->next;
-			}
-			if(new) {
-				gaim_debug(GAIM_DEBUG_INFO, "msn",
-						   "Unresolved MSN RL entry\n");
-				ap = g_new0(struct msn_add_permit, 1);
-				ap->user = g_strdup(who);
-				ap->friend = g_strdup(friend);
-				ap->gc = gc;
-                         
-				g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add you to their buddy list"),ap->user, ap->friend);
-				do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE);
-			}
-		    }
-			
-			if (pos != tot) 
-				return 1; /* this isn't the last one in the RL, so return. */
-
-			g_snprintf(sendbuf, sizeof(sendbuf), "CHG %u NLN\r\n", ++md->trId);
-			if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-				hide_login_progress(gc, _("Unable to write"));
-				signoff(gc);
-				return 0;
-			}
-
-			account_online(gc);
-			serv_finish_login(gc);
-
-			md->permit = g_slist_copy(gc->account->permit);
-			md->deny = g_slist_copy(gc->account->deny);
-
-			while (md->fl) {
-				struct msn_buddy *mb = md->fl->data;
-				struct buddy *b = gaim_find_buddy(gc->account, mb->user);
-				md->fl = g_slist_remove(md->fl, mb);
-				if(!b) {
-					struct group *g;
-					printf("I'm adding %s now..\n", mb->user);
-					if (!(g = gaim_find_group(_("Buddies")))) {
-						printf("How could I not exitst!?!\n");
-						g = gaim_group_new(_("Buddies"));
-						gaim_blist_add_group(g, NULL);
-					}
-					b = gaim_buddy_new(gc->account, mb->user, NULL);
-					gaim_blist_add_buddy(b,g,NULL);
-				}
-				serv_got_alias(gc, mb->user, mb->friend);
-				g_free(mb->user);
-				g_free(mb->friend);
-				g_free(mb);
-			}
-		}
-	} else if (!g_ascii_strncasecmp(buf, "MSG", 3)) {
-		char *user, *tmp = buf;
-		int length;
-
-		GET_NEXT(tmp);
-		user = tmp;
-
-		GET_NEXT(tmp);
-
-		GET_NEXT(tmp);
-		length = atoi(tmp);
-
-		md->msg = TRUE;
-		md->msguser = g_strdup(user);
-		md->msglen = length;
-	} else if (!g_ascii_strncasecmp(buf, "NLN", 3)) {
-		char *state, *user, *friend, *tmp = buf;
-		int status = 0;
-
-		GET_NEXT(tmp);
-		state = tmp;
-
-		GET_NEXT(tmp);
-		user = tmp;
-
-		GET_NEXT(tmp);
-		friend = url_decode(tmp);
-
-		serv_got_alias(gc, user, friend);
-
-		if (!g_ascii_strcasecmp(state, "BSY")) {
-			status |= UC_UNAVAILABLE | (MSN_BUSY << 1);
-		} else if (!g_ascii_strcasecmp(state, "IDL")) {
-			status |= UC_UNAVAILABLE | (MSN_IDLE << 1);
-		} else if (!g_ascii_strcasecmp(state, "BRB")) {
-			status |= UC_UNAVAILABLE | (MSN_BRB << 1);
-		} else if (!g_ascii_strcasecmp(state, "AWY")) {
-			status |= UC_UNAVAILABLE | (MSN_AWAY << 1);
-		} else if (!g_ascii_strcasecmp(state, "PHN")) {
-			status |= UC_UNAVAILABLE | (MSN_PHONE << 1);
-		} else if (!g_ascii_strcasecmp(state, "LUN")) {
-			status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
-		}
-
-		serv_got_update(gc, user, 1, 0, 0, 0, status);
-	} else if (!g_ascii_strncasecmp(buf, "OUT", 3)) {
-		char *tmp = buf;
-
-		GET_NEXT(tmp);
-		if (!g_ascii_strncasecmp(tmp, "OTH", 3)) {
-			hide_login_progress(gc, _("You have been disconnected. You have "
-						  "signed on from another location."));
-			signoff(gc);
-			return 0;
-		}
-	} else if (!g_ascii_strncasecmp(buf, "PRP", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "QNG", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "QRY", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "REA", 3)) {
-		char *friend, *tmp = buf;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-
-		friend = url_decode(tmp);
-
-		g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend);
-	} else if (!g_ascii_strncasecmp(buf, "REM", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "RNG", 3)) {
-		struct msn_switchboard *ms;
-		char *sessid, *ssaddr, *auth, *user;
-		int port, i = 0;
-		char *tmp = buf;
-
-		GET_NEXT(tmp);
-		sessid = tmp;
-
-		GET_NEXT(tmp);
-		ssaddr = tmp;
-
-		GET_NEXT(tmp);
-
-		GET_NEXT(tmp);
-		auth = tmp;
-
-		GET_NEXT(tmp);
-		user = tmp;
-		GET_NEXT(tmp);
+static void
+msn_act_id(gpointer data, char *entry)
+{
+	struct gaim_connection *gc = data;
+	MsnSession *session = gc->proto_data;
+	char outparams[MSN_BUF_LEN];
+	char *alias;
 
-		while (ssaddr[i] && ssaddr[i] != ':') i++;
-		if (ssaddr[i] == ':') {
-			char *x = &ssaddr[i + 1];
-			ssaddr[i] = 0;
-			port = atoi(x);
-		} else
-			port = 1863;
-
-		ms = g_new0(struct msn_switchboard, 1);
-		if (proxy_connect(gc->account, ssaddr, port, msn_rng_connect, ms) != 0) {
-			g_free(ms);
-			return 1;
-		}
-		ms->user = g_strdup(user);
-		ms->sessid = g_strdup(sessid);
-		ms->auth = g_strdup(auth);
-		ms->gc = gc;
-	} else if (!g_ascii_strncasecmp(buf, "URL", 3)) {
-		char *tmp = buf;
-		FILE *fd;
-		md5_state_t st;
-		md5_byte_t di[16];
-		int i;
-		char buf2[64];
-		char sendbuf[64];
-		char hippy[2048];
-		char *rru;
-		char *passport;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		rru = tmp;
-		GET_NEXT(tmp);
-		passport = tmp;
-		
-		g_snprintf(hippy, sizeof(hippy), "%s%lu%s", md->mspauth, time(NULL) - md->sl, gc->password);
-
-		md5_init(&st);
-		md5_append(&st, (const md5_byte_t *)hippy, strlen(hippy));
-		md5_finish(&st, di);
-
-		bzero(sendbuf, sizeof(sendbuf));
-		for (i = 0; i < 16; i++) {
-			g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
-			strcat(sendbuf, buf2);
-		}
-
-		if (md->passport) {
-			unlink(md->passport);
-			g_free(md->passport);
-		}
+	if (entry == NULL || *entry == '\0') 
+		alias = g_strdup("");
+	else
+		alias = g_strdup(entry);
 
-		if( (fd = gaim_mkstemp(&(md->passport))) == NULL ) {
-		  gaim_debug(GAIM_DEBUG_ERROR, "msn",
-					 "Error opening temp file: %s\n", strerror(errno)); 
-		}
-		else {
-			fputs("<html>\n"
-				"<head>\n"
-				"<noscript>\n"
-				"<meta http-equiv=Refresh content=\"0; url=http://www.hotmail.com\">\n"
-				"</noscript>\n"
-				"</head>\n\n", fd);
-		
-		        fprintf(fd, "<body onload=\"document.pform.submit(); \">\n");
-		        fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n", passport);
-		        fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n");
-		        fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n", gc->username);
-		        fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n", gc->username);
-			fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n", md->sid);
-			fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n", md->kv);
-			fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n");
-			fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n", time(NULL) - md->sl);
-			fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n", rru);
-			fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n", md->mspauth);
-			fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n", sendbuf); // Digest me
-			fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n");
-			fprintf(fd, "<input type=\"hidden\" name=\"js\" value=\"yes\">\n");
-			fprintf(fd, "</form></body>\n");
-			fprintf(fd, "</html>\n");
-			if (fclose(fd)) {
-				gaim_debug(GAIM_DEBUG_ERROR, "msn",
-						   "Error closing temp file: %s\n", strerror(errno)); 
-				unlink(md->passport);
-				g_free(md->passport);
-			}
-			else {
-				/*
-				 * Renaming file with .html extension, so that
-				 * win32 open_url will work.
-				 */
-				char *tmp;
-				if ((tmp = g_strdup_printf("%s.html", md->passport)) != NULL) {
-					if (rename(md->passport, tmp) == 0) {
-						g_free(md->passport);
-						md->passport = tmp;
-					} else
-						g_free(tmp);
-				}
-			}
-		}
-	} else if (!g_ascii_strncasecmp(buf, "SYN", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "USR", 3)) {
-	} else if (!g_ascii_strncasecmp(buf, "XFR", 3)) {
-		char *host = strstr(buf, "SB");
-		int port;
-		int i = 0;
-		gboolean switchboard = TRUE;
-		char *tmp;
-
-		if (!host) {
-			host = strstr(buf, "NS");
-			if (!host) {
-				hide_login_progress(gc, _("Got invalid XFR\n"));
-				signoff(gc);
-				return 0;
-			}
-			switchboard = FALSE;
-		}
-
-		GET_NEXT(host);
-		while (host[i] && host[i] != ':') i++;
-		if (host[i] == ':') {
-			tmp = &host[i + 1];
-			host[i] = 0;
-			while (isdigit(*tmp)) tmp++;
-			*tmp++ = 0;
-			port = atoi(&host[i + 1]);
-		} else {
-			port = 1863;
-			tmp = host;
-			GET_NEXT(tmp);
-		}
-
-		if (switchboard) {
-			struct msn_switchboard *ms;
-
-			GET_NEXT(tmp);
-
-			ms = msn_switchboard_connect(gc, host, port);
-
-			if (ms == NULL)
-				return 1;
-
-			ms->auth = g_strdup(tmp);
-		} else {
-			close(md->fd);
-			gaim_input_remove(md->inpa);
-			md->inpa = 0;
-			if (proxy_connect(gc->account, host, port, msn_login_xfr_connect, gc) != 0) {
-				hide_login_progress(gc, _("Error transferring"));
-				signoff(gc);
-				return 0;
-			}
-		}
-	} else if (isdigit(*buf)) {
-		handle_errcode(buf, TRUE);
-	} else {
-		gaim_debug(GAIM_DEBUG_WARNING, "msn", "Unhandled message!\n");
-	}
-
-	return 1;
-}
-
-static void msn_process_main_msg(struct gaim_connection *gc, char *msg)
-{
-	struct msn_data *md = gc->proto_data;
-	char *skiphead;
-	char *content;
-
-	content = strstr(msg, "Content-Type: ");
-
-	if ((content) && (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgsprofile",
-				strlen("Content-Type: text/x-msmsgsprofile")))) {
-
-		char *kv,*sid,*mspauth;
-
-		kv = strstr(msg, "kv: ");
-		sid = strstr(msg, "sid: ");
-		mspauth = strstr(msg, "MSPAuth: ");
-
-		if (kv) {
-			char *tmp;
-
-			kv += strlen("kv: ");
-			tmp = strstr(kv, "\r\n"); *tmp = 0;
-			md->kv = g_strdup(kv);
-		}
-
-		if (sid) {
-			char *tmp;
-
-			sid += strlen("sid: ");
-			tmp = strstr(sid, "\r\n"); *tmp = 0;
-			md->sid = g_strdup(sid);
-		}
-
-		if (mspauth) {
-			char *tmp;
-
-			mspauth += strlen("MSPAuth: ");
-			tmp = strstr(mspauth, "\r\n"); *tmp = 0;
-			md->mspauth = g_strdup(mspauth);
-		}
-
-	}
-
-
-
-	if (!g_ascii_strcasecmp(md->msguser, "hotmail")) {
-		handle_hotmail(gc, msg);
+	if (strlen(alias) >= BUDDY_ALIAS_MAXLEN) {
+		do_error_dialog(_("Your new MSN friendly name is too long."),
+						NULL, GAIM_ERROR);
 		return;
 	}
 
-
-	skiphead = strstr(msg, "\r\n\r\n");
-	if (!skiphead || !skiphead[4])
-		return;
-	skiphead += 4;
-	strip_linefeed(skiphead);
-
-	serv_got_im(gc, md->msguser, skiphead, 0, time(NULL), -1);
-}
-
-static void msn_callback(gpointer data, gint source, GaimInputCondition cond)
-{
-	struct gaim_connection *gc = data;
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-	int cont = 1;
-	int len;
-
-	len = read(md->fd, buf, sizeof(buf));
-	if (len <= 0) {
-		hide_login_progress_error(gc, _("Error reading from server"));
-		signoff(gc);
-		return;
-	}
-
-	md->rxqueue = g_realloc(md->rxqueue, len + md->rxlen);
-	memcpy(md->rxqueue + md->rxlen, buf, len);
-	md->rxlen += len;
-
-	while (cont) {
-		if (!md->rxlen)
-			return;
-
-		if (md->msg) {
-			char *msg;
-			if (md->msglen > md->rxlen)
-				return;
-			msg = md->rxqueue;
-			md->rxlen -= md->msglen;
-			if (md->rxlen) {
-				md->rxqueue = g_memdup(msg + md->msglen, md->rxlen);
-			} else {
-				md->rxqueue = NULL;
-				msg = g_realloc(msg, md->msglen + 1);
-			}
-			msg[md->msglen] = 0;
-			md->msglen = 0;
-			md->msg = FALSE;
-
-			msn_process_main_msg(gc, msg);
-
-			g_free(md->msguser);
-			g_free(msg);
-		} else {
-			char *end = md->rxqueue;
-			int cmdlen;
-			char *cmd;
-			int i = 0;
-
-			while (i + 1 < md->rxlen) {
-				if (*end == '\r' && end[1] == '\n')
-					break;
-				end++; i++;
-			}
-			if (i + 1 == md->rxlen)
-				return;
-
-			cmdlen = end - md->rxqueue + 2;
-			cmd = md->rxqueue;
-			md->rxlen -= cmdlen;
-			if (md->rxlen) {
-				md->rxqueue = g_memdup(cmd + cmdlen, md->rxlen);
-			} else {
-				md->rxqueue = NULL;
-				cmd = g_realloc(cmd, cmdlen + 1);
-			}
-			cmd[cmdlen] = 0;
-
-			gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd);
-			g_strchomp(cmd);
-			cont = msn_process_main(gc, cmd);
-
-			g_free(cmd);
-		}
-	}
-}
+	g_snprintf(outparams, sizeof(outparams), "%s %s",
+			   gc->username, msn_url_encode(alias));
 
-static void msn_login_xfr_connect(gpointer data, gint source, GaimInputCondition cond)
-{
-	struct gaim_connection *gc = data;
-	struct msn_data *md;
-	char buf[MSN_BUF_LEN];
-
-	if (!g_slist_find(connections, gc)) {
-		close(source);
-		return;
-	}
-
-	md = gc->proto_data;
-
-	if (md->fd != source)
-		md->fd = source;
-
-	if (md->fd == -1) {
-		hide_login_progress(gc, _("Unable to connect to Notification Server"));
-		signoff(gc);
-		return;
-	}
-
-	g_snprintf(buf, sizeof(buf), "VER %u MSNP5\r\n", ++md->trId);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Unable to talk to Notification Server"));
-		signoff(gc);
-		return;
-	}
-
-	md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_login_callback, gc);
-}
-
-static int msn_process_login(struct gaim_connection *gc, char *buf)
-{
-	struct msn_data *md = gc->proto_data;
-	char sendbuf[MSN_BUF_LEN];
-
-	if (!g_ascii_strncasecmp(buf, "VER", 3)) {
-		/* we got VER, check to see that MSNP5 is in the list, then send INF */
-		if (!strstr(buf, "MSNP5")) {
-			hide_login_progress(gc, _("Protocol not supported"));
-			signoff(gc);
-			return 0;
-		}
-
-		g_snprintf(sendbuf, sizeof(sendbuf), "INF %u\r\n", ++md->trId);
-		if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-			hide_login_progress(gc, _("Unable to request INF\n"));
-			signoff(gc);
-			return 0;
-		}
-	} else if (!g_ascii_strncasecmp(buf, "INF", 3)) {
-		/* check to make sure we can use md5 */
-		if (!strstr(buf, "MD5")) {
-			hide_login_progress(gc, _("Unable to login using MD5"));
-			signoff(gc);
-			return 0;
-		}
-
-		g_snprintf(sendbuf, sizeof(sendbuf), "USR %u MD5 I %s\r\n", ++md->trId, gc->username);
-		if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-			hide_login_progress(gc, _("Unable to send USR\n"));
-			signoff(gc);
-			return 0;
-		}
-
-		set_login_progress(gc, 3, _("Requesting to send password"));
-	} else if (!g_ascii_strncasecmp(buf, "USR", 3)) {
-		char *resp, *friend, *tmp = buf;
-
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		resp = tmp;
-		GET_NEXT(tmp);
-		GET_NEXT(tmp);
-		friend = url_decode(tmp);
-		GET_NEXT(tmp);
-
-		/* so here, we're either getting the challenge or the OK */
-		if (!g_ascii_strcasecmp(resp, "OK")) {
-			g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend);
-
-			g_snprintf(sendbuf, sizeof(sendbuf), "SYN %u 0\r\n", ++md->trId);
-			if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-				hide_login_progress(gc, _("Unable to write"));
-				signoff(gc);
-				return 0;
-			}
+	g_free(alias);
 
-			gaim_input_remove(md->inpa);
-			md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_callback, gc);
-			return 0;
-		} else if (!g_ascii_strcasecmp(resp, "MD5")) {
-			char buf2[MSN_BUF_LEN];
-			md5_state_t st;
-			md5_byte_t di[16];
-			int i;
-
-			g_snprintf(buf2, sizeof(buf2), "%s%s", friend, gc->password);
-
-			md5_init(&st);
-			md5_append(&st, (const md5_byte_t *)buf2, strlen(buf2));
-			md5_finish(&st, di);
-
-			g_snprintf(sendbuf, sizeof(sendbuf), "USR %u MD5 S ", ++md->trId);
-			for (i = 0; i < 16; i++) {
-				g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
-				strcat(sendbuf, buf2);
-			}
-			strcat(sendbuf, "\r\n");
-
-			if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) {
-				hide_login_progress(gc, _("Unable to send password"));
-				signoff(gc);
-				return 0;
-			}
-
-			set_login_progress(gc, 4, _("Password sent"));
-		}
-	} else if (!g_ascii_strncasecmp(buf, "XFR", 3)) {
-		char *host = strstr(buf, "NS");
-		int port;
-		int i = 0;
-
-		if (!host) {
-			hide_login_progress(gc, _("Got invalid XFR\n"));
-			signoff(gc);
-			return 0;
-		}
-
-		GET_NEXT(host);
-		while (host[i] && host[i] != ':') i++;
-		if (host[i] == ':') {
-			char *x = &host[i + 1];
-			host[i] = 0;
-			port = atoi(x);
-		} else
-			port = 1863;
-
-		close(md->fd);
-		gaim_input_remove(md->inpa);
-		md->inpa = 0;
-		md->fd = 0;
-		md->sl = time(NULL);
-		if (proxy_connect(gc->account, host, port, msn_login_xfr_connect, gc) != 0) {
-			hide_login_progress(gc, _("Unable to transfer"));
-			signoff(gc);
-		}
-		return 0;
-	} else {
-		if (isdigit(*buf))
-			hide_login_progress(gc, handle_errcode(buf, FALSE));
-		else
-			hide_login_progress(gc, _("Unable to parse message"));
-		signoff(gc);
-		return 0;
-	}
-
-	return 1;
-}
-
-static void msn_login_callback(gpointer data, gint source, GaimInputCondition cond)
-{
-	struct gaim_connection *gc = data;
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-	int cont = 1;
-	int len;
-
-	len = read(md->fd, buf, sizeof(buf));
-	if (len <= 0) {
-		hide_login_progress(gc, _("Error reading from server"));
-		signoff(gc);
-		return;
-	}
+	if (!msn_servconn_send_command(session->notification_conn,
+								   "REA", outparams)) {
 
-	md->rxqueue = g_realloc(md->rxqueue, len + md->rxlen);
-	memcpy(md->rxqueue + md->rxlen, buf, len);
-	md->rxlen += len;
-
-	while (cont) {
-		char *end = md->rxqueue;
-		int cmdlen;
-		char *cmd;
-		int i = 0;
-
-		if (!md->rxlen)
-			return;
-
-		while (i + 1 < md->rxlen) {
-			if (*end == '\r' && end[1] == '\n')
-				break;
-			end++; i++;
-		}
-		if (i + 1 == md->rxlen)
-			return;
-
-		cmdlen = end - md->rxqueue + 2;
-		cmd = md->rxqueue;
-		md->rxlen -= cmdlen;
-		if (md->rxlen) {
-			md->rxqueue = g_memdup(cmd + cmdlen, md->rxlen);
-		} else {
-			md->rxqueue = NULL;
-			cmd = g_realloc(cmd, cmdlen + 1);
-		}
-		cmd[cmdlen] = 0;
-
-		gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd);
-		g_strchomp(cmd);
-		cont = msn_process_login(gc, cmd);
-
-		g_free(cmd);
-	}
-}
-
-static void msn_login_connect(gpointer data, gint source, GaimInputCondition cond)
-{
-	struct gaim_connection *gc = data;
-	struct msn_data *md;
-	char buf[1024];
-
-	if (!g_slist_find(connections, gc)) {
-		close(source);
-		return;
-	}
-
-	md = gc->proto_data;
-
-	if (md->fd != source)
-		md->fd = source;
-
-	if (md->fd == -1) {
-		hide_login_progress(gc, _("Unable to connect"));
-		signoff(gc);
-		return;
-	}
-
-	g_snprintf(buf, sizeof(buf), "VER %u MSNP5\r\n", ++md->trId);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Unable to write to server"));
-		signoff(gc);
-		return;
-	}
-
-	md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_login_callback, gc);
-	set_login_progress(gc, 2,_("Synching with server"));
-}
-
-static void msn_login(struct gaim_account *account)
-{
-	struct gaim_connection *gc = new_gaim_conn(account);
-	gc->proto_data = g_new0(struct msn_data, 1);
-
-	set_login_progress(gc, 1, _("Connecting"));
-
-	g_snprintf(gc->username, sizeof(gc->username), "%s", msn_normalize(gc->username));
-
-	if (proxy_connect(account, account->proto_opt[USEROPT_MSNSERVER][0] ?
-				account->proto_opt[USEROPT_MSNSERVER] : MSN_SERVER,
-				account->proto_opt[USEROPT_MSNPORT][0] ?
-				atoi(account->proto_opt[USEROPT_MSNPORT]) : MSN_PORT,
-				msn_login_connect, gc) != 0) {
-		hide_login_progress(gc, _("Unable to connect"));
+		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 	}
 }
 
-static void msn_close(struct gaim_connection *gc)
+static void
+msn_show_set_friendly_name(struct gaim_connection *gc)
 {
-	struct msn_data *md = gc->proto_data;
-	close(md->fd);
-	if (md->inpa)
-		gaim_input_remove(md->inpa);
-	g_free(md->rxqueue);
-	if (md->msg)
-		g_free(md->msguser);
-	if (md->passport) {
-		unlink(md->passport);
-      		g_free(md->passport);
-	}
-	while (md->switches)
-		msn_kill_switch(md->switches->data);
-	while (md->fl) {
-		struct msn_buddy *tmp = md->fl->data;
-		md->fl = g_slist_remove(md->fl, tmp);
-		g_free(tmp->user);
-		g_free(tmp->friend);
-		g_free(tmp);
-	}
-	g_slist_free(md->permit);
-	g_slist_free(md->deny);
-	g_free(md);
+	do_prompt_dialog(_("Set Friendly Name:"), gc->displayname,
+					 gc, msn_act_id, NULL);
 }
 
-static int msn_send_typing(struct gaim_connection *gc, char *who, int typing) {
-	struct msn_switchboard *ms = msn_find_switch(gc, who);
-	char header[MSN_BUF_LEN] =   "MIME-Version: 1.0\r\n"
-				     "Content-Type: text/x-msmsgscontrol\r\n" 
-				     "TypingUser: ";
-	char buf [MSN_BUF_LEN];
-	if (!ms || !typing)
-		return 0;
-	g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s\r\n\r\n\r\n",
-		   ++ms->trId,
-		   strlen(header) + strlen("\r\n\r\n\r\n") + strlen(gc->username),
-		   header, gc->username);
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-	           msn_kill_switch(ms);
-	return MSN_TYPING_SEND_TIMEOUT;
+
+/**************************************************************************
+ * Protocol Plugin ops
+ **************************************************************************/
+
+static const char *
+msn_list_icon(struct gaim_account *a, struct buddy *b)
+{
+	return "msn";
 }
 
-static int msn_send_im(struct gaim_connection *gc, const char *who, const char *message, int len, int flags)
+static void
+msn_list_emblems(struct buddy *b, char **se, char **sw,
+				 char **nw, char **ne)
 {
-	struct msn_data *md = gc->proto_data;
-	struct msn_switchboard *ms = msn_find_switch(gc, who);
-	char buf[MSN_BUF_LEN];
-
-	if (ms) {
-		char *send;
-
-		if (ms->txqueue) {
-			gaim_debug(GAIM_DEBUG_INFO, "msn", "appending to queue\n");
-			ms->txqueue = g_slist_append(ms->txqueue, g_strdup(message));
-			return 1;
-		}
-
-		send = add_cr(message);
-		g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s", ++ms->trId,
-				strlen(MIME_HEADER) + strlen(send),
-				MIME_HEADER, send);
-		g_free(send);
-		if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-			msn_kill_switch(ms);
-	} else if (strcmp(who, gc->username)) {
-		g_snprintf(buf, MSN_BUF_LEN, "XFR %u SB\r\n", ++md->trId);
-		if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-			hide_login_progress(gc, _("Write error"));
-			signoff(gc);
-			return 1;
-		}
-
-		ms = g_new0(struct msn_switchboard, 1);
-		md->switches = g_slist_append(md->switches, ms);
-		ms->user = g_strdup(who);
-		ms->txqueue = g_slist_append(ms->txqueue, g_strdup(message));
-		ms->gc = gc;
-		ms->fd = -1;
-	} else
-		/* in msn you can't send messages to yourself, so we'll fake like we received it ;) */
-		serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER, time(NULL), -1);
-	return 1;
+	if (b->present == GAIM_BUDDY_OFFLINE)
+		*se = "offline";
+	else if ((b->uc >> 1) == 2 || (b->uc >> 1) == 6)
+		*se = "occupied";
+	else if (b->uc)
+		*se = "away";
 }
 
-static int msn_chat_send(struct gaim_connection *gc, int id, char *message)
+static char *
+msn_status_text(struct buddy *b)
 {
-	struct msn_switchboard *ms = msn_find_switch_by_id(gc, id);
-	char buf[MSN_BUF_LEN];
-	char *send;
-
-	if (!ms)
-		return -EINVAL;
+	if (b->uc & UC_UNAVAILABLE)
+		return g_strdup(msn_away_get_text(b->uc >> 1));
 
-	send = add_cr(message);
-	g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s", ++ms->trId,
-			strlen(MIME_HEADER) + strlen(send),
-			MIME_HEADER, send);
-	g_free(send);
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0) {
-		msn_kill_switch(ms);
-		return 0;
-	}
-	serv_got_chat_in(gc, id, gc->username, 0, message, time(NULL));
-	return 0;
+	return NULL;
 }
 
-static void msn_chat_invite(struct gaim_connection *gc, int id, const char *msg, const char *who)
+static char *
+msn_tooltip_text(struct buddy *b)
 {
-	struct msn_switchboard *ms = msn_find_switch_by_id(gc, id);
-	char buf[MSN_BUF_LEN];
+	if (GAIM_BUDDY_IS_ONLINE(b)) {
+		return g_strdup_printf(_("<b>Status:</b> %s"),
+							   msn_away_get_text(b->uc >> 1));
+	}
 
-	if (!ms)
-		return;
-
-	g_snprintf(buf, sizeof(buf), "CAL %u %s\r\n", ++ms->trId, who);
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-		msn_kill_switch(ms);
+	return NULL;
 }
 
-static void msn_chat_leave(struct gaim_connection *gc, int id)
-{
-	struct msn_switchboard *ms = msn_find_switch_by_id(gc, id);
-	char buf[MSN_BUF_LEN];
-
-	if (!ms)
-		return;
-
-	g_snprintf(buf, sizeof(buf), "OUT\r\n");
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-		msn_kill_switch(ms);
-}
-
-static GList *msn_away_states(struct gaim_connection *gc)
+static GList *
+msn_away_states(struct gaim_connection *gc)
 {
 	GList *m = NULL;
 
@@ -1361,21 +132,185 @@
 	return m;
 }
 
-static void msn_set_away(struct gaim_connection *gc, char *state, char *msg)
+static GList *
+msn_actions(struct gaim_connection *gc)
+{
+	GList *m = NULL;
+	struct proto_actions_menu *pam;
+
+	pam = g_new0(struct proto_actions_menu, 1);
+	pam->label = _("Set Friendly Name");
+	pam->callback = msn_show_set_friendly_name;
+	pam->gc = gc;
+	m = g_list_append(m, pam);
+
+	return m;
+}
+
+static GList *
+msn_buddy_menu(struct gaim_connection *gc, const char *who)
+{
+	GList *m = NULL;
+
+	return m;
+}
+
+static void
+msn_login(struct gaim_account *account)
+{
+	struct gaim_connection *gc;
+	MsnSession *session;
+	const char *server;
+	int port;
+
+	server = (*account->proto_opt[USEROPT_MSNSERVER]
+			  ? account->proto_opt[USEROPT_MSNSERVER]
+			  : MSN_SERVER);
+	port = (*account->proto_opt[USEROPT_MSNPORT]
+			? atoi(account->proto_opt[USEROPT_MSNPORT])
+			: MSN_PORT);
+
+
+	gc = new_gaim_conn(account);
+
+	session = msn_session_new(account, server, port);
+	session->prpl = my_protocol;
+
+	gc->proto_data = session;
+
+	set_login_progress(gc, 1, _("Connecting"));
+
+	g_snprintf(gc->username, sizeof(gc->username), "%s",
+			   msn_normalize(gc->username));
+
+	if (!msn_session_connect(session)) {
+		hide_login_progress(gc, _("Unable to connect"));
+		signoff(gc);
+	}
+}
+
+static void
+msn_close(struct gaim_connection *gc)
+{
+	MsnSession *session = gc->proto_data;
+
+	msn_session_destroy(session);
+
+	gc->proto_data = NULL;
+}
+
+static int
+msn_send_im(struct gaim_connection *gc, const char *who, const char *message,
+			int len, int flags)
 {
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *swboard;
+
+	swboard = msn_session_find_switch_with_passport(session, who);
+
+	if (g_ascii_strcasecmp(who, gc->username)) {
+		MsnMessage *msg;
+		MsnUser *user;
+
+		user = msn_user_new(session, who, NULL);
+
+		msg = msn_message_new();
+		msn_message_set_receiver(msg, user);
+		msn_message_set_attr(msg, "X-MMS-IM-Format",
+							 "FN=Arial; EF=; CO=0; PF=0");
+		msn_message_set_body(msg, message);
+
+		if (swboard != NULL) {
+			if (!msn_switchboard_send_msg(swboard, msg))
+				msn_switchboard_destroy(swboard);
+		}
+		else {
+			if ((swboard = msn_session_open_switchboard(session)) == NULL) {
+				msn_message_destroy(msg);
+
+				hide_login_progress(gc, _("Write error"));
+				signoff(gc);
+
+				return 1;
+			}
+
+			msn_switchboard_set_user(swboard, user);
+			msn_switchboard_send_msg(swboard, msg);
+		}
+
+		msn_user_destroy(user);
+		msn_message_destroy(msg);
+	}
+	else {
+		/*
+		 * In MSN, you can't send messages to yourself, so
+		 * we'll fake like we received it ;)
+		 */
+		serv_got_typing_stopped(gc, (char *)who);
+		serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER,
+					time(NULL), -1);
+	}
+
+	return 1;
+}
+
+static int
+msn_send_typing(struct gaim_connection *gc, char *who, int typing)
+{
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *swboard;
+	MsnMessage *msg;
+	MsnUser *user;
+
+	if (!typing)
+		return 0;
+
+	if (!g_ascii_strcasecmp(who, gc->username)) {
+		/* We'll just fake it, since we're sending to ourself. */
+		serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, TYPING);
+
+		return MSN_TYPING_SEND_TIMEOUT;
+	}
+
+	swboard = msn_session_find_switch_with_passport(session, who);
+
+	if (swboard == NULL)
+		return 0;
+
+	user = msn_user_new(session, who, NULL);
+
+	msg = msn_message_new();
+	msn_message_set_content_type(msg, "text/x-msmsgscontrol");
+	msn_message_set_receiver(msg, user);
+	msn_message_set_charset(msg, NULL);
+	msn_message_set_attr(msg, "TypingUser", gc->username);
+	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_user_destroy(user);
+
+	return MSN_TYPING_SEND_TIMEOUT;
+}
+
+static void
+msn_set_away(struct gaim_connection *gc, char *state, char *msg)
+{
+	MsnSession *session = gc->proto_data;
 	const char *away;
 
-	if (gc->away) {
+	if (gc->away != NULL) {
 		g_free(gc->away);
 		gc->away = NULL;
 	}
 
-	if (msg) {
+	if (msg != NULL) {
 		gc->away = g_strdup("");
 		away = "AWY";
-	} else if (state) {
+	}
+	else if (state) {
 		gc->away = g_strdup("");
 
 		if (!strcmp(state, _("Away From Computer")))
@@ -1395,415 +330,460 @@
 			gc->away = NULL;
 			away = "NLN";
 		}
-	} else if (gc->is_idle)
+	}
+	else if (gc->is_idle)
 		away = "IDL";
 	else
 		away = "NLN";
 
-	g_snprintf(buf, sizeof(buf), "CHG %u %s\r\n", ++md->trId, away);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (!msn_servconn_send_command(session->notification_conn, "CHG", away)) {
+		hide_login_progress(gc, _("Write error"));
+		signoff(gc);
+	}
+}
+
+static void
+msn_set_idle(struct gaim_connection *gc, int idle)
+{
+	MsnSession *session = gc->proto_data;
+
+	if (gc->away != NULL)
+		return;
+
+	if (!msn_servconn_send_command(session->notification_conn, "CHG",
+								   (idle ? "IDL" : "NLN"))) {
+
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
+	}
+}
+
+static void
+msn_add_buddy(struct gaim_connection *gc, const char *name)
+{
+	MsnSession *session = gc->proto_data;
+	char *who;
+	char outparams[MSN_BUF_LEN];
+	GSList *l;
+
+	who = msn_normalize(name);
+
+	if (strchr(who, ' ')) {
+		/* This is a broken blist entry. */
 		return;
 	}
+
+	for (l = session->lists.forward; l != NULL; l = l->next) {
+		MsnUser *user = l->data;
+
+		if (!gaim_utf8_strcasecmp(who, msn_user_get_passport(user)))
+			break;
+	}
+
+	if (l != NULL)
+		return;
+
+	g_snprintf(outparams, sizeof(outparams),
+			   "FL %s %s", who, who);
+
+	if (!msn_servconn_send_command(session->notification_conn,
+								   "ADD", outparams)) {
+		hide_login_progress(gc, _("Write error"));
+		signoff(gc);
+	}
+}
+
+static void
+msn_rem_buddy(struct gaim_connection *gc, char *who, char *group)
+{
+	MsnSession *session = gc->proto_data;
+	char outparams[MSN_BUF_LEN];
+
+	g_snprintf(outparams, sizeof(outparams), "FL %s", who);
+
+	if (!msn_servconn_send_command(session->notification_conn,
+								   "REM", outparams)) {
+
+		hide_login_progress(gc, _("Write error"));
+		signoff(gc);
+	}
 }
 
-static void msn_set_idle(struct gaim_connection *gc, int idle)
+static void
+msn_add_permit(struct gaim_connection *gc, const char *who)
 {
-	struct msn_data *md = gc->proto_data;
-	char buf[64];
+	MsnSession *session = gc->proto_data;
+	char buf[MSN_BUF_LEN];
+
+	if (!strchr(who, '@')) {
+		g_snprintf(buf, sizeof(buf), 
+			   _("An MSN screenname must be in the form \"user@server.com\". "
+			     "Perhaps you meant %s@hotmail.com. No changes were made "
+				 "to your allow list."), who);
+
+		do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR);
+		gaim_privacy_permit_remove(gc->account, who);
+
+		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);
+		gaim_privacy_deny_remove(gc->account, who);
+
+		g_snprintf(buf, sizeof(buf), "BL %s", who);
+
+		if (!msn_servconn_send_command(session->notification_conn,
+									   "REM", buf)) {
+
+			hide_login_progress(gc, _("Write error"));
+			signoff(gc);
+			return;
+		}
+	}
+
+	g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
 
-	if (gc->away)
+	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
+		hide_login_progress(gc, _("Write error"));
+		signoff(gc);
+	}
+}
+
+static void
+msn_add_deny(struct gaim_connection *gc, const char *who)
+{
+	MsnSession *session = gc->proto_data;
+	char buf[MSN_BUF_LEN];
+
+	if (!strchr(who, '@')) {
+		g_snprintf(buf, sizeof(buf), 
+			   _("An MSN screenname must be in the form \"user@server.com\". "
+			     "Perhaps you meant %s@hotmail.com. No changes were made "
+				 "to your block list."), who);
+
+		do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR);
+
+		gaim_privacy_deny_remove(gc->account, who);
+
 		return;
-	g_snprintf(buf, sizeof(buf),
-		idle ? "CHG %d IDL\r\n" : "CHG %u NLN\r\n", ++md->trId);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	}
+
+	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);
+
+		g_snprintf(buf, sizeof(buf), "AL %s", who);
+
+		if (!msn_servconn_send_command(session->notification_conn,
+									   "REM", buf)) {
+
+			hide_login_progress(gc, _("Write error"));
+			signoff(gc);
+			return;
+		}
+	}
+
+	g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
+
+	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
 }
 
-static const char *msn_list_icon(struct gaim_account *a, struct buddy *b)
-{
-	return "msn";
-}
-
-static void msn_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
-{
-	if (b->present == GAIM_BUDDY_OFFLINE)
-		*se = "offline";
-	else if (b->uc >> 1 == 2 || b->uc >> 1 == 6)
-		*se = "occupied";
-	else if (b->uc)
-		*se = "away";
-}
-
-static char *msn_get_away_text(int s)
+static void
+msn_rem_permit(struct gaim_connection *gc, const char *who)
 {
-	switch (s) {
-		case MSN_BUSY :
-			return _("Busy");
-		case MSN_BRB :
-			return _("Be Right Back");
-		case MSN_AWAY :
-			return _("Away From Computer");
-		case MSN_PHONE :
-			return _("On The Phone");
-		case MSN_LUNCH :
-			return _("Out To Lunch");
-		case MSN_IDLE :
-			return _("Idle");
-		default:
-			return _("Available");
-	}
-}
+	MsnSession *session = gc->proto_data;
+	char buf[MSN_BUF_LEN];
 
-static char *msn_status_text(struct buddy *b) {
-	if (b->uc & UC_UNAVAILABLE)
-		return g_strdup(msn_get_away_text(b->uc >> 1));
-	return NULL;
-}
+	g_snprintf(buf, sizeof(buf), "AL %s", who);
 
-static char *msn_tooltip_text(struct buddy *b) {
-	if (GAIM_BUDDY_IS_ONLINE(b))
-		return g_strdup_printf(_("<b>Status:</b> %s"), msn_get_away_text(b->uc >> 1));
-
-	return NULL;
-}
-
-static GList *msn_buddy_menu(struct gaim_connection *gc, const char *who)
-{
-	GList *m = NULL;
-
-	return m;
-}
+	if (!msn_servconn_send_command(session->notification_conn, "REM", buf)) {
+		hide_login_progress(gc, _("Write error"));
+		signoff(gc);
+		return;
+	}
 
-static void msn_add_buddy(struct gaim_connection *gc, const char *name)
-{
-	struct msn_data *md = gc->proto_data;
-	char *who = msn_normalize(name);
-	char buf[MSN_BUF_LEN];
-	GSList *l = md->fl;
-
-	if (who[0] == '@')
-		/* how did this happen? */
-		return;
+	gaim_privacy_deny_add(gc->account, who);
 
-	if (strchr(who, ' '))
-		/* This is a broken blist entry. */
-		return;
+	g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
 
-	while (l) {
-		struct msn_buddy *b = l->data;
-		if (!gaim_utf8_strcasecmp(who, b->user))
-			break;
-		l = l->next;
-	}
-	if (l)
-		return;
-
-	g_snprintf(buf, sizeof(buf), "ADD %u FL %s %s\r\n", ++md->trId, who, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
 }
 
-static void msn_rem_buddy(struct gaim_connection *gc, char *who, char *group)
+static void
+msn_rem_deny(struct gaim_connection *gc, const char *who)
 {
-	struct msn_data *md = gc->proto_data;
+	MsnSession *session = gc->proto_data;
 	char buf[MSN_BUF_LEN];
 
-	g_snprintf(buf, sizeof(buf), "REM %u FL %s\r\n", ++md->trId, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
-	}
-}
+	g_snprintf(buf, sizeof(buf), "BL %s", who);
 
-static void msn_act_id(gpointer data, char *entry)
-{
-	struct gaim_connection *gc = data;
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-	char *alias;
-
-	if (!entry || *entry == '\0') 
-		alias = g_strdup("");
-	else
-		alias = g_strdup(entry);
-	
-	if (strlen(alias) >= BUDDY_ALIAS_MAXLEN) {
-		do_error_dialog(_("New MSN friendly name too long."), NULL, GAIM_ERROR);
-		return;
-	}
-	
-	g_snprintf(buf, sizeof(buf), "REA %u %s %s\r\n", ++md->trId, gc->username, url_encode(alias));
-	g_free(alias);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (!msn_servconn_send_command(session->notification_conn, "REM", buf)) {
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
-}
 
-static void msn_show_set_friendly_name(struct gaim_connection *gc)
-{
-	do_prompt_dialog(_("Set Friendly Name:"), gc->displayname, gc, msn_act_id, NULL);
-}
-
-static GList *msn_actions(struct gaim_connection *gc)
-{
-	GList *m = NULL;
-	struct proto_actions_menu *pam;
-
-	pam = g_new0(struct proto_actions_menu, 1);
-	pam->label = _("Set Friendly Name");
-	pam->callback = msn_show_set_friendly_name;
-	pam->gc = gc;
-	m = g_list_append(m, pam);
-
-	return m;
-}
+	gaim_privacy_permit_add(gc->account, who);
 
-static void msn_convo_closed(struct gaim_connection *gc, char *who)
-{
-	struct msn_switchboard *ms = msn_find_switch(gc, who);
-
-	if (ms) {
-		char sendbuf[256];
-
-		g_snprintf(sendbuf, sizeof(sendbuf), "BYE %s\r\n", gc->username);
-
-		msn_write(ms->fd, sendbuf, strlen(sendbuf));
+	g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
 
-		msn_kill_switch(ms);
-	}
-}
-
-static void msn_keepalive(struct gaim_connection *gc)
-{
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-
-	g_snprintf(buf, sizeof(buf), "PNG\r\n");
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) {
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
 }
 
-static void msn_set_permit_deny(struct gaim_connection *gc)
+static void
+msn_set_permit_deny(struct gaim_connection *gc)
 {
-	struct msn_data *md = gc->proto_data;
+	MsnSession *session = gc->proto_data;
 	char buf[MSN_BUF_LEN];
 	GSList *s, *t = NULL;
 
-	if (gc->account->permdeny == PERMIT_ALL || gc->account->permdeny == DENY_SOME)
-		g_snprintf(buf, sizeof(buf), "BLP %u AL\r\n", ++md->trId);
+	if (gc->account->permdeny == PERMIT_ALL ||
+		gc->account->permdeny == DENY_SOME) {
+
+		strcpy(buf, "AL");
+	}
 	else
-		g_snprintf(buf, sizeof(buf), "BLP %u BL\r\n", ++md->trId);
+		strcpy(buf, "BL");
 
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (!msn_servconn_send_command(session->notification_conn, "BLP", buf)) {
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
 
-	/* this is safe because we'll always come here after we've gotten the list off the server,
-	 * and data is never removed. So if the lengths are equal we don't know about anyone locally
-	 * and so there's no sense in going through them all. */
-	if (g_slist_length(gc->account->permit) == g_slist_length(md->permit)) {
-		g_slist_free(md->permit);
-		md->permit = NULL;
+	/*
+	 * This is safe because we'll always come here after we've gotten
+	 * the list off the server, and data is never removed. So if the
+	 * lengths are equal we don't know about anyone locally and so
+	 * there's no sense in going through them all.
+	 */
+	if (g_slist_length(gc->account->permit) ==
+		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(md->deny)) {
-		g_slist_free(md->deny);
-		md->deny = NULL;
+
+	if (g_slist_length(gc->account->deny) ==
+		g_slist_length(session->lists.block)) {
+
+		g_slist_free(session->lists.block);
+		session->lists.block = NULL;
 	}
-	if (!md->permit && !md->deny)
+
+	if (session->lists.allow == NULL && session->lists.block == NULL)
 		return;
 
-	if (md->permit) {
-		s = g_slist_nth(gc->account->permit, g_slist_length(md->permit));
-		while (s) {
+	if (session->lists.allow != NULL) {
+
+		for (s = g_slist_nth(gc->account->permit,
+							 g_slist_length(session->lists.allow));
+			 s != NULL;
+			 s = s->next) {
+
 			char *who = s->data;
-			s = s->next;
+
 			if (!strchr(who, '@')) {
 				t = g_slist_append(t, who);
 				continue;
 			}
-			if (g_slist_find(md->deny, who)) {
+
+			if (g_slist_find(session->lists.block, who)) {
 				t = g_slist_append(t, who);
 				continue;
 			}
-			g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who);
-			if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+
+			g_snprintf(buf, sizeof(buf), "AL %s %s", who, who);
+
+			if (!msn_servconn_send_command(session->notification_conn,
+										   "ADD", buf)) {
 				hide_login_progress(gc, _("Write error"));
 				signoff(gc);
 				return;
 			}
 		}
-		while (t) {
+
+		for (; t != NULL; t = t->next)
 			gaim_privacy_permit_remove(gc->account, t->data);
-			t = t->next;
-		}
-		if (t)
+
+		if (t != NULL)
 			g_slist_free(t);
-	t = NULL;
-	g_slist_free(md->permit);
-	md->permit = NULL;
+
+		t = NULL;
+		g_slist_free(session->lists.allow);
+		session->lists.allow = NULL;
 	}
-	
-	if (md->deny) {
-		s = g_slist_nth(gc->account->deny, g_slist_length(md->deny));
-		while (s) {
+
+	if (session->lists.block) {
+		for (s = g_slist_nth(gc->account->deny,
+							 g_slist_length(session->lists.block));
+			 s != NULL;
+			 s = s->next) {
+
 			char *who = s->data;
-			s = s->next;
+
 			if (!strchr(who, '@')) {
 				t = g_slist_append(t, who);
 				continue;
 			}
-			if (g_slist_find(md->deny, who)) {
+
+			if (g_slist_find(session->lists.block, who)) {
 				t = g_slist_append(t, who);
 				continue;
 			}
-			g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who);
-			if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+
+			g_snprintf(buf, sizeof(buf), "BL %s %s", who, who);
+
+			if (!msn_servconn_send_command(session->notification_conn,
+										   "ADD", buf)) {
 				hide_login_progress(gc, _("Write error"));
 				signoff(gc);
 				return;
 			}
 		}
-		while (t) {
+
+		for (; t != NULL; t = t->next)
 			gaim_privacy_deny_remove(gc->account, t->data);
-			t = t->next;
-		}
-		if (t)
+
+		if (t != NULL)
 			g_slist_free(t);
-	g_slist_free(md->deny);
-	md->deny = NULL;
+
+		g_slist_free(session->lists.block);
+		session->lists.block = NULL;
 	}
 }
 
-static void msn_add_permit(struct gaim_connection *gc, const char *who)
+static void
+msn_chat_invite(struct gaim_connection *gc, int id, const char *msg,
+				const char *who)
+{
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *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);
+}
+
+static void
+msn_chat_leave(struct gaim_connection *gc, int id)
+{
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id);
+	char buf[6];
+
+	if (swboard == NULL)
+		return;
+
+	strcpy(buf, "OUT\r\n");
+
+	if (!msn_servconn_write(swboard->servconn, buf, strlen(buf)))
+		msn_switchboard_destroy(swboard);
+}
+
+static int
+msn_chat_send(struct gaim_connection *gc, int id, char *message)
 {
-	struct msn_data *md = gc->proto_data;
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id);
+	MsnMessage *msg;
+	char *send;
+
+	if (swboard == NULL)
+		return -EINVAL;
+
+	send = add_cr(message);
+
+	msg = msn_message_new();
+	msn_message_set_attr(msg, "X-MMS-IM-Format", "FN=Arial; EF=; CO=0; PF=0");
+	msn_message_set_body(msg, send);
+
+	g_free(send);
+
+	if (!msn_switchboard_send_msg(swboard, msg)) {
+		msn_switchboard_destroy(swboard);
+
+		msn_message_destroy(msg);
+
+		return 0;
+	}
+
+	msn_message_destroy(msg);
+
+	serv_got_chat_in(gc, id, gc->username, 0, message, time(NULL));
+
+	return 0;
+}
+
+static void
+msn_keepalive(struct gaim_connection *gc)
+{
+	MsnSession *session = gc->proto_data;
 	char buf[MSN_BUF_LEN];
 
-	if (!strchr(who, '@')) {
-		g_snprintf(buf, sizeof(buf), 
-			   _("An MSN screenname must be in the form \"user@server.com\".  "
-			     "Perhaps you meant %s@hotmail.com.  No changes were made to your "
-			     "allow list."), who);
-		do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR);
-		gaim_privacy_permit_remove(gc->account, who);
-		return;
-	}
+	g_snprintf(buf, sizeof(buf), "PNG\r\n");
 
-	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);
-		gaim_privacy_deny_remove(gc->account, who);
-		g_snprintf(buf, sizeof(buf), "REM %u BL %s\r\n", ++md->trId, who);
-			if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-				hide_login_progress(gc, _("Write error"));
-				signoff(gc);
-				return;
-			}
-	}
-	g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
+	if (msn_servconn_write(session->notification_conn,
+						   buf, strlen(buf)) < 0) {
+
 		hide_login_progress(gc, _("Write error"));
 		signoff(gc);
 		return;
 	}
 }
 
-static void msn_rem_permit(struct gaim_connection *gc, const char *who)
+static void
+msn_buddy_free(struct buddy *b)
 {
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	if (b->proto_data != NULL)
+		g_free(b->proto_data);
+}
 
-	g_snprintf(buf, sizeof(buf), "REM %u AL %s\r\n", ++md->trId, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
-	}
+static void
+msn_convo_closed(struct gaim_connection *gc, char *who)
+{
+	MsnSession *session = gc->proto_data;
+	MsnSwitchBoard *swboard;
+	
+	swboard = msn_session_find_switch_with_passport(session, who);
 
-	gaim_privacy_deny_add(gc->account, who);
-	g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
+	if (swboard != NULL) {
+		char sendbuf[256];
+
+		g_snprintf(sendbuf, sizeof(sendbuf), "BYE %s\r\n", gc->username);
+
+		msn_servconn_write(swboard->servconn, sendbuf, strlen(sendbuf));
+
+		msn_switchboard_destroy(swboard);
 	}
 }
 
-static void msn_add_deny(struct gaim_connection *gc, const char *who)
+static char *
+msn_normalize(const char *str)
 {
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
-
-	if (!strchr(who, '@')) {
-		g_snprintf(buf, sizeof(buf), 
-			   _("An MSN screenname must be in the form \"user@server.com\".  "
-			     "Perhaps you meant %s@hotmail.com.  No changes were made to your "
-			     "block list."), who);
-		do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR);
-		gaim_privacy_deny_remove(gc->account, who);
-		return;
-	}
-
-	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);
-		g_snprintf(buf, sizeof(buf), "REM %u AL %s\r\n", ++md->trId, who);
-		if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-			hide_login_progress(gc, _("Write error"));
-			signoff(gc);
-			return;
-		}
-	}
-
+	static char buf[BUF_LEN];
 
-	g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
-	}
-}
-
-static void msn_rem_deny(struct gaim_connection *gc, const char *who)
-{
-	struct msn_data *md = gc->proto_data;
-	char buf[MSN_BUF_LEN];
+	g_return_val_if_fail(str != NULL, NULL);
 
-	g_snprintf(buf, sizeof(buf), "REM %u BL %s\r\n", ++md->trId, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
-	}
+	g_snprintf(buf, sizeof(buf), "%s%s", str,
+			   (strchr(str, '@') ? "" : "@hotmail.com"));
 
-	gaim_privacy_permit_add(gc->account, who);
-	g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who);
-	if (msn_write(md->fd, buf, strlen(buf)) < 0) {
-		hide_login_progress(gc, _("Write error"));
-		signoff(gc);
-		return;
-	}
-}
-
-static void msn_buddy_free(struct buddy *b)
-{
-	if (b->proto_data)
-		g_free(b->proto_data);
+	return buf;
 }
 
 static GaimPluginProtocolInfo prpl_info =