changeset 6701:b7e113a59b51

[gaim-migrate @ 7227] Updated to MSN Protocol 9. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Tue, 02 Sep 2003 04:32:16 +0000
parents 57161e3abbb5
children 302ee2792e91
files ChangeLog Makefile.am configure.ac src/Makefile.am src/core.c src/protocols/msn/Makefile.am src/protocols/msn/buddyicon.c src/protocols/msn/buddyicon.h src/protocols/msn/dispatch.c src/protocols/msn/dispatch.h src/protocols/msn/error.c src/protocols/msn/error.h src/protocols/msn/ft.c src/protocols/msn/group.c src/protocols/msn/group.h src/protocols/msn/md5.h src/protocols/msn/msg.c src/protocols/msn/msg.h src/protocols/msn/msn.c src/protocols/msn/msn.h src/protocols/msn/msnobject.c src/protocols/msn/msnobject.h src/protocols/msn/msnslp.c src/protocols/msn/msnslp.h src/protocols/msn/notification.c src/protocols/msn/notification.h src/protocols/msn/page.c src/protocols/msn/page.h src/protocols/msn/servconn.c src/protocols/msn/servconn.h src/protocols/msn/session.c src/protocols/msn/session.h src/protocols/msn/state.c src/protocols/msn/state.h src/protocols/msn/switchboard.h src/protocols/msn/user.c src/protocols/msn/user.h src/protocols/msn/utils.c src/protocols/msn/utils.h
diffstat 39 files changed, 1969 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Sep 02 04:28:34 2003 +0000
+++ b/ChangeLog	Tue Sep 02 04:32:16 2003 +0000
@@ -2,6 +2,7 @@
 
 version 0.69
 	* Contact (aka Person, aka Meta-Contact, aka Buddy Merging, etc) Support
+	* Updated MSN support to the MSN Protocol version 9.
 	* Yahoo now shows people using the java chat client (Tim Ringenbach)
 
 version 0.68 (09/01/2003):
--- a/Makefile.am	Tue Sep 02 04:28:34 2003 +0000
+++ b/Makefile.am	Tue Sep 02 04:32:16 2003 +0000
@@ -24,6 +24,9 @@
 dist-hook: gaim.spec
 	cp gaim.spec $(distdir)
 
+distcheck-hook: plugins/perl/common/Gaim.pm
+#	cp plugins/perl/common/Gaim.pm $(distdir)/plugins/perl/common
+
 appsdir = $(datadir)/applications
 apps_DATA = gaim.desktop
 
--- a/configure.ac	Tue Sep 02 04:28:34 2003 +0000
+++ b/configure.ac	Tue Sep 02 04:32:16 2003 +0000
@@ -334,6 +334,244 @@
 	AM_CONDITIONAL(USE_PERL, false)
 fi
 
+dnl #######################################################################
+dnl # SSL support
+dnl #
+dnl # Thanks go to Evolution for the checks.
+dnl #######################################################################
+AC_ARG_ENABLE(ssl,
+	[  --enable-ssl=[yes,no,static]    Turn on Secure Sockets Layer support [default=yes]],
+	[enable_nss="$enableval"],
+	[enable_nss="yes"])
+
+AC_ARG_WITH(nspr-includes,
+	[  --with-nspr-includes=PREFIX     Specify location of Mozilla nspr4 includes.],
+	[with_nspr_includes="$withval"])
+
+AC_ARG_WITH(nspr-libs,
+	[  --with-nspr-libs=PREFIX         Specify location of Mozilla nsp4 libs.],
+	[with_nspr_libs="$withval"])
+
+AC_ARG_WITH(nss-includes,
+	[  --with-nss-includes=PREFIX      Specify location of Mozilla nss3 includes.],
+	[with_nss_includes="$withval"])
+
+AC_ARG_WITH(nss-libs,
+	[  --with-nss-libs=PREFIX          Specify location of Mozilla nss3 libs.],
+	[with_nss_libs="$withval"])
+
+msg_ssl="no"
+
+if test "x$enable_nss" = "xyes" || test "x$enable_nss" = "xstatic"; then
+	if test -n "$with_nspr_includes" || test -n "$with_nspr_libs" || \
+	   test -n "$with_nss_includes"  || test -n "$with_nss_libs"  ||
+	   test "x$enable_nss" = "xstatic"; then
+
+		nss_manual_check="yes"
+	else
+		nss_manual_check="no"
+	fi
+
+	if test "x$nss_manual_check" = "xno"; then
+		PKG_CHECK_MODULES(NSS, mozilla-nss, have_nss="yes")
+
+		if test "x$have_nss" = "xyes"; then
+			mozilla_nspr="mozilla-nspr"
+			mozilla_nss="mozilla-nss"
+
+			AC_DEFINE(HAVE_NSS, 1, [Define if you have Mozilla NSS])
+
+			msg_ssl="yes"
+		else
+			nss_manual_check="yes"
+		fi
+	fi
+
+	if test "x$nss_manual_check" = "xyes"; then
+		mozilla_nss=""
+		have_nspr_includes="no"
+
+		if test "x$with_nspr_includes" != "xno"; then
+			CPPFLAGS_save=$CPPFLAGS
+
+			AC_MSG_CHECKING(for Mozilla nspr4 includes in $with_nspr_includes)
+			AC_MSG_RESULT("")
+
+			CPPFLAGS="$CPPFLAGS -I$with_nspr_includes"
+			AC_CHECK_HEADERS(nspr.h prio.h, [ moz_nspr_includes="yes" ])
+			CPPFLAGS=$CPPFLAGS_save
+
+			if test "x$moz_nspr_includes" != "xno" -a \
+			        "x$moz_nspr_includes" != "x"; then
+
+				have_nspr_includes="yes"
+				NSPR_CFLAGS="-I$with_nspr_includes"
+			fi
+		else
+			AC_MSG_CHECKING(for Mozilla nspr4 includes)
+			AC_MSG_RESULT(no)
+		fi
+
+		have_nspr_libs="no"
+
+		if test "x$with_nspr_libs"     != "xno" -a \
+		        "x$have_nspr_includes" != "xno"; then
+
+			CFLAGS_save=$CFLAGS
+			LDFLAGS_save=$LDFLAGS
+
+			if test "$enable_nss" = "static"; then
+				if test -z "$with_nspr_libs"; then
+					AC_MSG_ERROR(
+						[Static linkage requested, but path to nspr libraries not set.]
+						[Please specify the path to libnspr4.a]
+						[Example: --with-nspr-libs=/usr/lib])
+				else
+					nsprlibs="-ldl $with_nspr_libs/libplc4.a $with_nspr_libs/libplds4.a $with_nspr_libs/libnspr4.a $PTHREAD_LIB"
+				fi
+			else
+				nsprlibs="-ldl -lplc4 -lplds4 -lnspr4 $PTHREAD_LIB"
+			fi
+
+			AC_CACHE_CHECK([for Mozilla nspr libraries], moz_nspr_libs,
+			[
+				LIBS_save=$LIBS
+				CFLAGS="$CFLAGS $NSPR_CFLAGS"
+
+				LIBS="$nsprlibs"
+
+				if test "x$with_nspr_libs" != "x"; then
+					LDFLAGS="$LDFLAGS -L$with_nspr_libs"
+				else
+					LDFLAGS="$LDFLAGS"
+				fi
+
+				AC_TRY_LINK_FUNC(PR_Init,
+					[moz_nspr_libs="yes"],
+					[moz_nspr_libs="no"])
+
+				CFLAGS=$CFLAGS_save
+				LDFLAGS=$LDFLAGS_save
+				LIBS=$LIBS_save
+			])
+
+			if test "x$moz_nspr_libs" != "xno"; then
+				have_nspr_libs="yes"
+				NSPR_LIBS="-L$with_nspr_libs $nsprlibs"
+			else
+				NSPR_CFLAGS=""
+			fi
+		else
+			AC_MSG_CHECKING(for Mozilla nspr4 libraries)
+			AC_MSG_RESULT(no)
+		fi
+
+		if test "x$with_nss_includes" != "xno" -a \
+				"x$have_nspr_libs"    != "xno"; then
+
+			CPPFLAGS_save=$CPPFLAGS
+
+			AC_MSG_CHECKING(for Mozilla nss3 includes in $with_nss_includes)
+			AC_MSG_RESULT("")
+
+			if test "x$with_nspr_includes" != "x"; then
+				CPPFLAGS="$CPPFLAGS -I$with_nspr_includs -I$with_nss_includes"
+			else
+				CPPFLAGS="$CPPFLAGS -I$with_nss_includes"
+			fi
+
+			AC_CHECK_HEADERS(nss.h ssl.h smime.h,
+				[have_nss_includes="yes"],
+				[have_nss_includes="no"])
+
+			CPPFLAGS=$CPPFLAGS_save
+
+			if test "x$have_nss_includes" = "xyes"; then
+				have_nss_includes="yes"
+				NSS_CFLAGS="-I$with_nss_includes"
+			else
+				NSPR_CFLAGS=""
+				NSPR_LIBS=""
+			fi
+		else
+			AC_MSG_CHECKING(for Mozilla nss3 includes)
+			AC_MSG_RESULT(no)
+		fi
+
+		if test "x$with_nss_libs"     != "xno" -a \
+				"x$have_nss_includes" != "xno"; then
+
+			LDFLAGS_save=$LDFLAGS
+
+			if test "$enable_nss" = "static"; then
+				if test -z "$with_nss_libs"; then
+					AC_MSG_ERROR(
+						[Static linkage requested, but path to nss libraries not set.]
+						[Please specify the path to libnss3.a]
+						[Example: --with-nspr-libs=/usr/lib/mozilla])
+				else
+					nsslibs="-ldb1 $with_nss_libs/libnssckfw.a $with_nss_libs/libasn1.a $with_nss_libs/libcrmf.a $with_nss_libs/libswfci.a $with_nss_libs/libjar.a $with_nss_libs/libpkcs12.a $with_nss_libs/libpkcs7.a $with_nss_libs/libpki1.a $with_nss_libs/libsmime.a $with_nss_libs/libssl.a $with_nss_libs/libnss.a $with_nss_libs/libpk11wrap.a $with_nss_libs/libsoftokn.a $with_nss_libs/libfreebl.a $with_nss_libs/libnsspki.a $with_nss_libs/libnssdev.a $with_nss_libs/libcryptohi.a $with_nss_libs/libcerthi.a $with_nss_libs/libcertdb.a $with_nss_libs/libsecutil.a $with_nss_libs/libnssb.a"
+
+					case "$host" in
+						*solaris*)
+							nsslibs="$nsslibs $with_nss_libs/libfreeb1.a"
+							;;
+					esac
+				fi
+			else
+				nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3"
+			fi
+
+			AC_CACHE_CHECK([for Mozilla nss libraries], moz_nss_libs,
+			[
+				LIBS_save=$LIBS
+				LDFLAGS="$LDFLAGS -L$with_nspr_libs $nsprlibs -L$with_nss_libs $nsslibs"
+				LIBS="$nsslibs $nsprlibs"
+
+				AC_TRY_LINK_FUNC(NSS_Init,
+					[moz_nss_libs="yes"],
+					[moz_nss_libs="no"])
+
+				if test "x$moz_nss_libs" = "xno"; then
+					nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3"
+					LDFLAGS="$LDFLAGS -L$with_nspr_libs $nsprlibs -L$with_nss_libs $nsslibs"
+					AC_TRY_LINK_FUNC(NSS_Init,
+						[moz_nss_libs="yes"],
+						[moz_nss_libs="no"])
+				fi
+
+				LDFLAGS=$LDFLAGS_save
+				LIBS=$LIBS_save
+			])
+
+			if test "x$moz_nss_libs" != "xno"; then
+				AC_DEFINE(HAVE_NSS)
+
+				NSS_LIBS="-L$with_nss_libs $nsslibs"
+
+				if test "$enable_nss" = "static"; then
+					msg_ssl="yes (static)"
+				else
+					msg_ssl="yes"
+				fi
+			else
+				NSS_CFLAGS=""
+				NSPR_CFLAGS=""
+				NSPR_LIBS=""
+			fi
+		else
+			AC_MSG_CHECKING(for Mozilla nss libraries)
+			AC_MSG_ERROR(no)
+		fi
+
+		NSS_CFLAGS="$NSPR_CFLAGS $NSS_CFLAGS"
+		NSS_LIBS="$NSPR_LIBS $NSS_LIBS"
+	fi
+
+	AC_SUBST(NSS_CFLAGS)
+	AC_SUBST(NSS_LIBS)
+fi
+
 dnl Check for Tcl
 if test "$enable_tcl" = yes; then
 	AC_MSG_CHECKING([for tclConfig.sh])
@@ -439,7 +677,7 @@
 	LDADD="$LDADD -static"
 	DEBUG_CFLAGS="$DEBUG_CFLAGS -Wall -g"
 	AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.])
-fi                           
+fi
 
 AC_SUBST(DEBUG_CFLAGS)
 AC_SUBST(LDADD)
@@ -571,6 +809,7 @@
 echo Build with Audio support...... : $enable_audio
 echo Build with NAS support........ : $enable_nas
 echo Build with GtkSpell support... : $enable_gtkspell
+echo Build with Mozilla NSS support : $msg_ssl
 echo
 echo Use XScreenSaver Extension.... : $enable_xss
 echo Use X Session Management...... : $enable_sm
--- a/src/Makefile.am	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/Makefile.am	Tue Sep 02 04:32:16 2003 +0000
@@ -89,6 +89,8 @@
 	status.h \
 	sound.c \
 	sound.h \
+	sslconn.c \
+	sslconn.h \
 	util.c \
 	util.h \
 	value.c \
@@ -162,7 +164,8 @@
 	$(XSS_LIBS) \
 	$(SM_LIBS) \
 	$(INTLLIBS) \
-	$(GTKSPELL_LIBS)
+	$(GTKSPELL_LIBS) \
+	$(NSS_LIBS)
 
 gaim_remote_SOURCES = \
 	gaim-remote.c
@@ -181,4 +184,5 @@
 	$(AO_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
-	$(GTKSPELL_CFLAGS)
+	$(GTKSPELL_CFLAGS) \
+	$(NSS_CFLAGS)
--- a/src/core.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/core.c	Tue Sep 02 04:32:16 2003 +0000
@@ -32,6 +32,7 @@
 #include "privacy.h"
 #include "proxy.h"
 #include "signals.h"
+#include "sslconn.h"
 #include "sound.h"
 
 struct GaimCore
@@ -87,6 +88,7 @@
 	gaim_pounces_init();
 	gaim_proxy_init();
 	gaim_sound_init();
+	gaim_ssl_init();
 	gaim_xfers_init();
 
 	if (ops != NULL && ops->ui_init != NULL)
@@ -121,6 +123,7 @@
 	gaim_debug(GAIM_DEBUG_INFO, "main", "Unloading all plugins\n");
 	gaim_plugins_destroy_all();
 
+	gaim_ssl_uninit();
 	gaim_blist_uninit();
 	gaim_conversations_uninit();
 	gaim_connections_uninit();
--- a/src/protocols/msn/Makefile.am	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/Makefile.am	Tue Sep 02 04:32:16 2003 +0000
@@ -19,6 +19,10 @@
 	msg.h \
 	msn.c \
 	msn.h \
+	msnobject.c \
+	msnobject.h \
+	msnslp.c \
+	msnslp.h \
 	notification.c \
 	notification.h \
 	page.c \
--- a/src/protocols/msn/buddyicon.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/buddyicon.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/buddyicon.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/buddyicon.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,7 +24,9 @@
 
 typedef struct _MsnBuddyIconXfer MsnBuddyIconXfer;
 
+#include "msg.h"
 #include "servconn.h"
+#include "user.h"
 
 /**
  * State of a buddy icon transfer.
--- a/src/protocols/msn/dispatch.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/dispatch.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -27,28 +27,19 @@
 static GHashTable *dispatch_commands = NULL;
 
 static gboolean
-ver_cmd(MsnServConn *servconn, const char *command, const char **params,
+cvr_cmd(MsnServConn *servconn, const char *command, const char **params,
 		size_t param_count)
 {
-	GaimConnection *gc = servconn->session->account->gc;
-	size_t i;
-	gboolean msnp5_found = FALSE;
+	GaimAccount *account = servconn->session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
+	char outparams[MSN_BUF_LEN];
 
-	for (i = 1; i < param_count; i++) {
-		if (!strcmp(params[i], "MSNP5")) {
-			msnp5_found = TRUE;
-			break;
-		}
-	}
+	g_snprintf(outparams, sizeof(outparams),
+			   "TWN I %s", gaim_account_get_username(account));
 
-	if (!msnp5_found) {
-		gaim_connection_error(gc, _("Protocol not supported"));
-
-		return FALSE;
-	}
-
-	if (!msn_servconn_send_command(servconn, "INF", NULL)) {
-		gaim_connection_error(gc, _("Unable to request INF"));
+	if (!msn_servconn_send_command(servconn, "USR", outparams))
+	{
+		gaim_connection_error(gc, _("Unable to request USR\n"));
 
 		return FALSE;
 	}
@@ -86,6 +77,62 @@
 }
 
 static gboolean
+ver_cmd(MsnServConn *servconn, const char *command, const char **params,
+		size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	GaimAccount *account = session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
+	gboolean protocol_supported = FALSE;
+	char outparams[MSN_BUF_LEN];
+	char proto_str[8];
+	size_t i;
+
+	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
+
+	for (i = 1; i < param_count; i++)
+	{
+		if (!strcmp(params[i], proto_str))
+		{
+			protocol_supported = TRUE;
+			break;
+		}
+	}
+
+	if (!protocol_supported)
+	{
+		gaim_connection_error(gc, _("Protocol version not supported"));
+
+		return FALSE;
+	}
+
+	if (session->protocol_ver >= 8)
+	{
+		g_snprintf(outparams, sizeof(outparams),
+				   "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
+				   gaim_account_get_username(account));
+
+		if (!msn_servconn_send_command(servconn, "CVR", outparams))
+		{
+			gaim_connection_error(gc, _("Unable to request CVR\n"));
+
+			return FALSE;
+		}
+	}
+	else
+	{
+		if (!msn_servconn_send_command(servconn, "INF", NULL))
+		{
+			gaim_connection_error(gc, _("Unable to request INF\n"));
+
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
 xfr_cmd(MsnServConn *servconn, const char *command, const char **params,
 		size_t param_count)
 {
@@ -95,7 +142,8 @@
 	int port;
 	char *c;
 
-	if (param_count < 2 || strcmp(params[1], "NS")) {
+	if (param_count < 2 || strcmp(params[1], "NS"))
+	{
 		gaim_connection_error(gc, _("Got invalid XFR"));
 
 		return FALSE;
@@ -103,7 +151,8 @@
 
 	host = g_strdup(params[2]);
 
-	if ((c = strchr(host, ':')) != NULL) {
+	if ((c = strchr(host, ':')) != NULL)
+	{
 		*c = '\0';
 
 		port = atoi(c + 1);
@@ -117,14 +166,16 @@
 	msn_servconn_destroy(servconn);
 	session->dispatch_conn = NULL;
 
+	/* Reset our transaction ID. */
+	session->trId = 0;
+
 	/* Now connect to the switchboard. */
 	session->notification_conn = msn_notification_new(session, host, port);
 
 	g_free(host);
 
-	if (!msn_servconn_connect(session->notification_conn)) {
+	if (!msn_servconn_connect(session->notification_conn))
 		gaim_connection_error(gc, _("Unable to transfer"));
-	}
 
 	return FALSE;
 }
@@ -135,7 +186,8 @@
 {
 	GaimConnection *gc = servconn->session->account->gc;
 
-	if (isdigit(*command)) {
+	if (isdigit(*command))
+	{
 		char buf[4];
 
 		strncpy(buf, command, 4);
@@ -155,8 +207,11 @@
 	MsnServConn *dispatch = data;
 	MsnSession *session = dispatch->session;
 	GaimConnection *gc = session->account->gc;
+	char proto_vers[256];
+	size_t i;
 
-	if (source == -1) {
+	if (source == -1)
+	{
 		gaim_connection_error(session->account->gc, _("Unable to connect"));
 		return FALSE;
 	}
@@ -166,8 +221,21 @@
 	if (dispatch->fd != source)
 		dispatch->fd = source;
 
-	if (!msn_servconn_send_command(dispatch, "VER",
-								   "MSNP7 MSNP6 MSNP5 MSNP4 CVR0")) {
+	proto_vers[0] = '\0';
+
+	for (i = session->protocol_ver; i >= 7; i--)
+	{
+		char old_buf[256];
+
+		strcpy(old_buf, proto_vers);
+
+		g_snprintf(proto_vers, sizeof(proto_vers), "MSNP%d %s", i, old_buf);
+	}
+
+	strncat(proto_vers, "CVR0", sizeof(proto_vers));
+
+	if (!msn_servconn_send_command(dispatch, "VER", proto_vers))
+	{
 		gaim_connection_error(gc, _("Unable to write to server"));
 		return FALSE;
 	}
@@ -195,15 +263,17 @@
 	MsnServConn *dispatch;
 
 	dispatch = msn_servconn_new(session);
-	
+
 	msn_servconn_set_server(dispatch, server, port);
 	msn_servconn_set_connect_cb(dispatch, connect_cb);
 	msn_servconn_set_failed_read_cb(dispatch, failed_read_cb);
 
 	if (dispatch_commands == NULL) {
 		/* Register the command callbacks. */
+
+		msn_servconn_register_command(dispatch, "CVR",       cvr_cmd);
+		msn_servconn_register_command(dispatch, "INF",       inf_cmd);
 		msn_servconn_register_command(dispatch, "VER",       ver_cmd);
-		msn_servconn_register_command(dispatch, "INF",       inf_cmd);
 		msn_servconn_register_command(dispatch, "XFR",       xfr_cmd);
 		msn_servconn_register_command(dispatch, "_unknown_", unknown_cmd);
 
--- a/src/protocols/msn/dispatch.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/dispatch.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/error.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/error.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/error.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/error.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/ft.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/ft.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/group.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/group.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/group.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/group.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,6 +25,8 @@
 typedef struct _MsnGroup  MsnGroup;
 typedef struct _MsnGroups MsnGroups;
 
+#include <stdio.h>
+
 #include "session.h"
 #include "user.h"
 
@@ -196,7 +198,7 @@
  * Returns the number of groups in a groups list.
  *
  * @param groups The groups list.
- * 
+ *
  * @return The number of groups.
  */
 size_t msn_groups_get_count(const MsnGroups *groups);
--- a/src/protocols/msn/md5.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/md5.h	Tue Sep 02 04:32:16 2003 +0000
@@ -61,7 +61,7 @@
 } md5_state_t;
 
 #ifdef __cplusplus
-extern "C" 
+extern "C"
 {
 #endif
 
--- a/src/protocols/msn/msg.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/msg.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -72,7 +72,7 @@
 msn_message_new_from_str(MsnSession *session, const char *str)
 {
 	MsnMessage *msg;
-	char *tmp_base, *tmp, *field1, *field2, *c;
+	char *tmp_base, *msg_base, *tmp, *field1, *field2, *c;
 
 	g_return_val_if_fail(str != NULL, NULL);
 	g_return_val_if_fail(!g_ascii_strncasecmp(str, "MSG", 3), NULL);
@@ -127,6 +127,8 @@
 		msg->flag = *field2;
 	}
 
+	msg_base = tmp;
+
 	/* Back to the parsination. */
 	while (*tmp != '\r') {
 		char *key, *value;
@@ -164,7 +166,49 @@
 	tmp += 2;
 
 	/* Now we *should* be at the body. */
-	msn_message_set_body(msg, tmp);
+	if (!strcmp(msn_message_get_content_type(msg), "application/x-msnmsgrp2p"))
+	{
+		msn_message_set_body(msg, tmp);
+	}
+	else
+	{
+		char header[48];
+		char footer[4];
+
+		msg->msnslp_message = TRUE;
+
+		memcpy(header, tmp, 48);
+
+		tmp += 48;
+
+		msg->body = g_memdup(tmp, msg->size - (tmp - msg_base) + 1);
+
+		tmp++;
+
+		memcpy(footer, tmp, 4);
+
+		/* Import the header. */
+		memcpy(&msg->msnslp_header.session_id,      tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.id,              tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.offset,          tmp, 4); tmp += 8;
+		memcpy(&msg->msnslp_header.total_size,      tmp, 4); tmp += 8;
+		memcpy(&msg->msnslp_header.length,          tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.flags,           tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.prev_id,         tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.prev_f9,         tmp, 4); tmp += 4;
+		memcpy(&msg->msnslp_header.prev_total_size, tmp, 4); tmp += 8;
+
+		/* Convert to the right endianness */
+		msg->msnslp_header.session_id = ntohs(msg->msnslp_header.session_id);
+		msg->msnslp_header.id         = ntohs(msg->msnslp_header.id);
+		msg->msnslp_header.length     = ntohs(msg->msnslp_header.length);
+		msg->msnslp_header.flags      = ntohs(msg->msnslp_header.flags);
+		msg->msnslp_header.prev_id    = ntohs(msg->msnslp_header.prev_id);
+		msg->msnslp_header.prev_f9    = ntohs(msg->msnslp_header.prev_f9);
+
+		/* Import the footer. */
+		msg->msnslp_footer.app_id = (long)footer;
+	}
 
 	g_free(tmp_base);
 
@@ -303,9 +347,53 @@
 		g_strlcat(str, buf, len);
 	}
 
-	g_snprintf(buf, sizeof(buf), "\r\n%s", msn_message_get_body(msg));
+	if (msg->msnslp_message)
+	{
+		char *c;
+		char blank[4];
+		int session_id, id, offset, total_size, length, flags;
+		int prev_id, prev_f9, prev_total_size;
+
+		memcpy(blank, 0, 4);
+
+		c = str + strlen(str);
+
+		session_id      = htons(msg->msnslp_header.session_id);
+		id              = htons(msg->msnslp_header.id);
+		offset          = htons(msg->msnslp_header.offset);
+		total_size      = htons(msg->msnslp_header.total_size);
+		length          = htons(msg->msnslp_header.length);
+		flags           = htons(msg->msnslp_header.flags);
+		prev_id         = htons(msg->msnslp_header.prev_id);
+		prev_f9         = htons(msg->msnslp_header.prev_f9);
+		prev_total_size = htons(msg->msnslp_header.prev_total_size);
 
-	g_strlcat(str, buf, len);
+		memcpy(c, &session_id,      4); c += 4;
+		memcpy(c, &id,              4); c += 4;
+		memcpy(c, &offset,          4); c += 4;
+		memcpy(c, blank,            4); c += 4;
+		memcpy(c, &total_size,      4); c += 4;
+		memcpy(c, blank,            4); c += 4;
+		memcpy(c, &length,          4); c += 4;
+		memcpy(c, &flags,           4); c += 4;
+		memcpy(c, &prev_id,         4); c += 4;
+		memcpy(c, &prev_f9,         4); c += 4;
+		memcpy(c, &prev_total_size, 4); c += 4;
+		memcpy(c, blank,            4); c += 4;
+
+		strncpy(c, msn_message_get_body(msg), len);
+
+		c += strlen(msn_message_get_body(msg));
+
+		memcpy(c, blank,                      1); c++;
+		memcpy(c, &msg->msnslp_footer.app_id, 4); c += 4;
+	}
+	else
+	{
+		g_snprintf(buf, sizeof(buf), "\r\n%s", msn_message_get_body(msg));
+
+		g_strlcat(str, buf, len);
+	}
 
 	if (msg->size != strlen(msg_start)) {
 		gaim_debug(GAIM_DEBUG_ERROR, "msn",
--- a/src/protocols/msn/msg.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/msg.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +24,7 @@
 
 typedef struct _MsnMessage MsnMessage;
 
+#include "msnslp.h"
 #include "session.h"
 #include "user.h"
 
@@ -34,6 +35,8 @@
 {
 	size_t ref_count;           /**< The reference count.       */
 
+	gboolean msnslp_message;
+
 	MsnUser *sender;
 	MsnUser *receiver;
 
@@ -48,10 +51,15 @@
 	char *charset;
 	char *body;
 
+	MsnSlpHeader msnslp_header;
+	MsnSlpFooter msnslp_footer;
+
 	GHashTable *attr_table;
 	GList *attr_list;
 };
 
+#define MSN_MESSAGE(msg) ((MsnMessage *)(msg))
+
 /**
  * Creates a new, empty message.
  *
@@ -71,6 +79,8 @@
 
 /**
  * Destroys a message.
+ *
+ * @param msg The message to destroy.
  */
 void msn_message_destroy(MsnMessage *msg);
 
--- a/src/protocols/msn/msn.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/msn.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -51,7 +51,7 @@
 	char outparams[MSN_BUF_LEN];
 	char *alias;
 
-	if (entry == NULL || *entry == '\0') 
+	if (entry == NULL || *entry == '\0')
 		alias = g_strdup("");
 	else
 		alias = g_strdup(entry);
@@ -694,7 +694,7 @@
 	char buf[MSN_BUF_LEN];
 
 	if (!strchr(who, '@')) {
-		g_snprintf(buf, sizeof(buf), 
+		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);
@@ -734,7 +734,7 @@
 	char buf[MSN_BUF_LEN];
 
 	if (!strchr(who, '@')) {
-		g_snprintf(buf, sizeof(buf), 
+		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);
@@ -1295,7 +1295,6 @@
 static void
 msn_get_info(GaimConnection *gc, const char *name)
 {
-	/* MsnSession *session = (MsnSession *)gc->proto_data; */
 	char url[256];
 	g_snprintf(url, sizeof url, "%s%s", PROFILE_URL, name);
 	grab_url(url, FALSE, msn_got_info, NULL,"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",1);
--- a/src/protocols/msn/msn.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/msn.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -37,14 +37,15 @@
 #include "prpl.h"
 #include "request.h"
 #include "server.h"
+#include "sslconn.h"
 #include "util.h"
 
 /* XXX */
 #include "gaim.h"
 
 #ifdef _WIN32
-#include "win32dep.h"
-#include "stdint.h"
+# include "win32dep.h"
+# include "stdint.h"
 #endif
 
 #define MSN_BUF_LEN 8192
@@ -73,4 +74,14 @@
 	"Chat-Logging: Y\r\n" \
 	"Buddy-Icons: 1\r\n"
 
+
+typedef enum
+{
+	MSN_LIST_FL_OP = 0x01,
+	MSN_LIST_AL_OP = 0x02,
+	MSN_LIST_BL_OP = 0x04,
+	MSN_LIST_RL_OP = 0x08
+
+} MsnListOp;
+
 #endif /* _MSN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/msn/msnobject.c	Tue Sep 02 04:32:16 2003 +0000
@@ -0,0 +1,225 @@
+/**
+ * @file msnobject.c MSNObject API
+ *
+ * gaim
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "msnobject.h"
+
+#define GET_STRING_TAG(field, id) \
+	if ((tag = strstr(str, id "=\"")) != NULL) \
+	{ \
+		tag += strlen(id "=\""); \
+		c = strchr(tag, '"'); \
+		obj->field = g_strndup(tag, tag - c); \
+	}
+
+#define GET_INT_TAG(field, id) \
+	if ((tag = strstr(str, id "=\"")) != NULL) \
+	{ \
+		char buf[16]; \
+		tag += strlen(id "=\""); \
+		c = strchr(tag, '"'); \
+		strncpy(buf, tag, tag - c); \
+		obj->field = atoi(buf); \
+	}
+
+MsnObject *
+msn_object_new(void)
+{
+	MsnObject *obj;
+
+	obj = g_new0(MsnObject, 1);
+
+	msn_object_set_type(obj, MSN_OBJECT_UNKNOWN);
+	msn_object_set_friendly(obj, "AAA=");
+
+	return obj;
+}
+
+MsnObject *
+msn_object_new_from_string(const char *str)
+{
+	MsnObject *obj;
+	char *tag, *c;
+
+	g_return_val_if_fail(str != NULL, NULL);
+	g_return_val_if_fail(!strncmp(str, "<msnobj ", 8), NULL);
+
+	obj = msn_object_new();
+
+	g_return_val_if_fail(str != NULL, NULL);
+
+	GET_STRING_TAG(creator,  "Creator");
+	GET_INT_TAG(size,        "Size");
+	GET_INT_TAG(type,        "Type");
+	GET_STRING_TAG(location, "Location");
+	GET_STRING_TAG(friendly, "Friendly");
+	GET_STRING_TAG(sha1d,    "SHA1D");
+	GET_STRING_TAG(sha1c,    "SHA1C");
+
+	return obj;
+}
+
+char *
+msn_object_to_string(const MsnObject *obj)
+{
+	char *str;
+
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	str = g_strdup_printf("<msnobj Creator=\"%s\" Size=\"%d\" Type=\"%d\" "
+						  "Location=\"%s\" Friendly=\"%s\" SHA1D=\"%s\" "
+						  "SHA1C=\"%s\"/>",
+						  msn_object_get_creator(obj),
+						  msn_object_get_size(obj),
+						  msn_object_get_type(obj),
+						  msn_object_get_location(obj),
+						  msn_object_get_friendly(obj),
+						  msn_object_get_sha1d(obj),
+						  msn_object_get_sha1c(obj));
+
+	return str;
+}
+
+void
+msn_object_set_creator(MsnObject *obj, const char *creator)
+{
+	g_return_if_fail(obj != NULL);
+
+	if (obj->creator != NULL)
+		g_free(obj->creator);
+
+	obj->creator = (creator == NULL ? NULL : g_strdup(creator));
+}
+
+void
+msn_object_set_size(MsnObject *obj, int size)
+{
+	g_return_if_fail(obj != NULL);
+
+	obj->size = size;
+}
+
+void
+msn_object_set_type(MsnObject *obj, MsnObjectType type)
+{
+	g_return_if_fail(obj != NULL);
+
+	obj->type = type;
+}
+
+void
+msn_object_set_location(MsnObject *obj, const char *location)
+{
+	g_return_if_fail(obj != NULL);
+
+	if (obj->location != NULL)
+		g_free(obj->location);
+
+	obj->location = (location == NULL ? NULL : g_strdup(location));
+}
+
+void
+msn_object_set_friendly(MsnObject *obj, const char *friendly)
+{
+	g_return_if_fail(obj != NULL);
+
+	if (obj->friendly != NULL)
+		g_free(obj->friendly);
+
+	obj->friendly = (friendly == NULL ? NULL : g_strdup(friendly));
+}
+
+void
+msn_object_set_sha1d(MsnObject *obj, const char *sha1d)
+{
+	g_return_if_fail(obj != NULL);
+
+	if (obj->sha1d != NULL)
+		g_free(obj->sha1d);
+
+	obj->sha1d = (sha1d == NULL ? NULL : g_strdup(sha1d));
+}
+
+void
+msn_object_set_sha1c(MsnObject *obj, const char *sha1c)
+{
+	g_return_if_fail(obj != NULL);
+
+	if (obj->sha1c != NULL)
+		g_free(obj->sha1c);
+
+	obj->sha1c = (sha1c == NULL ? NULL : g_strdup(sha1c));
+}
+
+const char *
+msn_object_get_creator(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	return obj->creator;
+}
+
+int
+msn_object_get_size(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, 0);
+
+	return obj->size;
+}
+
+MsnObjectType
+msn_object_get_type(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, MSN_OBJECT_UNKNOWN);
+
+	return obj->type;
+}
+
+const char *
+msn_object_get_location(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	return obj->location;
+}
+
+const char *
+msn_object_get_friendly(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	return obj->friendly;
+}
+
+const char *
+msn_object_get_sha1d(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	return obj->sha1d;
+}
+
+const char *
+msn_object_get_sha1c(const MsnObject *obj)
+{
+	g_return_val_if_fail(obj != NULL, NULL);
+
+	return obj->sha1c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/msn/msnobject.h	Tue Sep 02 04:32:16 2003 +0000
@@ -0,0 +1,194 @@
+/**
+ * @file msnobject.h MSNObject API
+ *
+ * gaim
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _MSN_OBJECT_H_
+#define _MSN_OBJECT_H_
+
+#include "internal.h"
+
+typedef enum
+{
+	MSN_OBJECT_UNKNOWN    = -1, /**< Unknown object        */
+	MSN_OBJECT_RESERVED1  =  1, /**< Reserved              */
+	MSN_OBJECT_EMOTICON   =  2, /**< Custom Emoticon       */
+	MSN_OBJECT_USERTILE   =  3, /**< UserTile (buddy icon) */
+	MSN_OBJECT_RESERVED2  =  4, /**< Reserved              */
+	MSN_OBJECT_BACKGROUND =  5  /**< Background            */
+
+} MsnObjectType;
+
+typedef struct
+{
+	char *creator;
+	int size;
+	MsnObjectType type;
+	char *location;
+	char *friendly;
+	char *sha1d;
+	char *sha1c;
+
+} MsnObject;
+
+/**
+ * Creates a MsnObject structure.
+ *
+ * @return A new MsnObject structure.
+ */
+MsnObject *msn_object_new(void);
+
+/**
+ * Creates a MsnObject structure from a string.
+ *
+ * @param str The string.
+ *
+ * @return The new MsnObject structure.
+ */
+MsnObject *msn_object_new_from_string(const char *str);
+
+/**
+ * Destroys an MsnObject structure.
+ *
+ * @param obj The object structure.
+ */
+void msn_object_destroy(MsnObject *obj);
+
+/**
+ * Outputs a string representation of an MsnObject.
+ *
+ * @param obj The object.
+ *
+ * @return The string representation. This must be freed.
+ */
+char *msn_object_to_string(const MsnObject *obj);
+
+/**
+ * Sets the creator field in a MsnObject.
+ *
+ * @param creator The creator value.
+ */
+void msn_object_set_creator(MsnObject *obj, const char *creator);
+
+/**
+ * Sets the size field in a MsnObject.
+ *
+ * @param size The size value.
+ */
+void msn_object_set_size(MsnObject *obj, int size);
+
+/**
+ * Sets the type field in a MsnObject.
+ *
+ * @param type The type value.
+ */
+void msn_object_set_type(MsnObject *obj, MsnObjectType type);
+
+/**
+ * Sets the location field in a MsnObject.
+ *
+ * @param location The location value.
+ */
+void msn_object_set_location(MsnObject *obj, const char *location);
+
+/**
+ * Sets the friendly name field in a MsnObject.
+ *
+ * @param friendly The friendly name value.
+ */
+void msn_object_set_friendly(MsnObject *obj, const char *friendly);
+
+/**
+ * Sets the SHA1D field in a MsnObject.
+ *
+ * @param sha1d The sha1d value.
+ */
+void msn_object_set_sha1d(MsnObject *obj, const char *sha1d);
+
+/**
+ * Sets the SHA1C field in a MsnObject.
+ *
+ * @param sha1c The sha1c value.
+ */
+void msn_object_set_sha1c(MsnObject *obj, const char *sha1c);
+
+/**
+ * Returns a MsnObject's creator value.
+ *
+ * @param obj The object.
+ *
+ * @return The creator value.
+ */
+const char *msn_object_get_creator(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's size value.
+ *
+ * @param obj The object.
+ *
+ * @return The size value.
+ */
+int msn_object_get_size(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's type.
+ *
+ * @param obj The object.
+ *
+ * @return The object type.
+ */
+MsnObjectType msn_object_get_type(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's location value.
+ *
+ * @param obj The object.
+ *
+ * @return The location value.
+ */
+const char *msn_object_get_location(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's friendly name value.
+ *
+ * @param obj The object.
+ *
+ * @return The friendly name value.
+ */
+const char *msn_object_get_friendly(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's SHA1D value.
+ *
+ * @param obj The object.
+ *
+ * @return The SHA1D value.
+ */
+const char *msn_object_get_sha1d(const MsnObject *obj);
+
+/**
+ * Returns a MsnObject's SHA1C value.
+ *
+ * @param obj The object.
+ *
+ * @return The SHA1C value.
+ */
+const char *msn_object_get_sha1c(const MsnObject *obj);
+
+#endif /* _MSN_OBJECT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/msn/msnslp.c	Tue Sep 02 04:32:16 2003 +0000
@@ -0,0 +1,53 @@
+/**
+ * @file msnslp.c MSNSLP support
+ *
+ * gaim
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "msnslp.h"
+
+MsnSlpSession *
+msn_slp_session_new(MsnSwitchBoard *swboard, gboolean local_initiated)
+{
+	MsnSlpSession *slpsession;
+
+	g_return_val_if_fail(swboard != NULL, NULL);
+
+	slpsession = g_new0(MsnSlpSession, 1);
+
+	slpsession->swboard = swboard;
+	slpsession->local_initiated = local_initiated;
+
+	return slpsession;
+}
+
+void
+msn_slp_session_destroy(MsnSlpSession *session)
+{
+	g_return_if_fail(session != NULL);
+
+	g_free(session);
+}
+
+void
+msn_slp_session_send_msg(MsnSlpSession *session, MsnMessage *msg)
+{
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(msg != NULL);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/msn/msnslp.h	Tue Sep 02 04:32:16 2003 +0000
@@ -0,0 +1,86 @@
+/**
+ * @file msnslp.h MSNSLP support
+ *
+ * gaim
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _MSN_SLP_H_
+#define _MSN_SLP_H_
+
+typedef struct
+{
+	long session_id;
+	long id;
+	long offset;
+	long total_size;
+	long length;
+	long flags;
+	long prev_id;
+	long prev_f9;
+	long prev_total_size;
+
+} MsnSlpHeader;
+
+typedef struct
+{
+	long app_id;
+
+} MsnSlpFooter;
+
+#include "switchboard.h"
+
+typedef struct
+{
+	gboolean local_initiated;
+
+	MsnSwitchBoard *swboard;
+
+	int session_id;
+	int prev_msg_id;
+
+} MsnSlpSession;
+
+/**
+ * Creates a MSNSLP session.
+ *
+ * @param swboard         The switchboard.
+ * @param local_initiated TRUE if the session was initiated locally.
+ *
+ * @return The new MSNSLP session handle.
+ */
+MsnSlpSession *msn_slp_session_new(MsnSwitchBoard *swboard,
+								   gboolean local_initiated);
+
+/**
+ * Destroys a MSNSLP session handle.
+ *
+ * This does not close the connection.
+ *
+ * @param slpsession The MSNSLP session to destroy.
+ */
+void msn_slp_session_destroy(MsnSlpSession *slpsession);
+
+/**
+ * Sends a message over a MSNSLP session.
+ *
+ * @param slpsession The MSNSLP session to send the message over.
+ * @param msg        The message to send.
+ */
+void msn_slp_session_send_msg(MsnSlpSession *session, MsnMessage *msg);
+
+#endif /* _MSN_SLP_H_ */
--- a/src/protocols/msn/notification.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/notification.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -42,64 +42,81 @@
 add_buddy(MsnServConn *servconn, MsnUser *user)
 {
 	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
+	GaimAccount *account = session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
 	GaimBuddy *b;
 	MsnGroup *group = NULL;
 	GaimGroup *g = NULL;
-	int group_id;
+	GList *l, *l2;
+	GSList *buddies;
+
+	buddies = gaim_find_buddies(account, msn_user_get_passport(user));
+
+	for (l = msn_user_get_group_ids(user); l != NULL; l = l->next)
+	{
+		int group_id = GPOINTER_TO_INT(l->data);
+		GSList *l3;
+
+		if (group_id > -1)
+			group = msn_groups_find_with_id(session->groups, group_id);
 
-	group_id = msn_user_get_group_id(user);
+		if (group == NULL)
+		{
+			gaim_debug(GAIM_DEBUG_WARNING, "msn",
+					   "Group ID %d for user %s was not defined.\n",
+					   group_id, msn_user_get_passport(user));
 
-	if (group_id > -1)
-		group = msn_groups_find_with_id(session->groups, group_id);
+			/* Find a group that we can stick this guy into. Lamer. */
+			l2 = msn_groups_get_list(session->groups);
+
+			if (l2 != NULL)
+			{
+				group = l2->data;
+
+				msn_user_add_group_id(user, msn_group_get_id(group));
+			}
+		}
 
-	if (group == NULL) {
-		GList *l;
-		gaim_debug(GAIM_DEBUG_WARNING, "msn",
-				   "Group ID %d for user %s was not defined.\n",
-				   group_id, msn_user_get_passport(user));
+		if (group == NULL ||
+			(g = gaim_find_group(msn_group_get_name(group))) == NULL)
+		{
+			gaim_debug(GAIM_DEBUG_ERROR, "msn",
+					   "Group '%s' appears in server-side "
+					   "buddy list, but not here!",
+					   msn_group_get_name(group));
+		}
+
+		if (group != NULL)
+			msn_group_add_user(group, user);
+
+		b = NULL;
+
+		for (l3 = buddies; l3 != NULL; l3 = l3->next)
+		{
+			b = (GaimBuddy *)l3->data;
 
-		/* Find a group that we can stick this guy into. Lamer. */
-		l = msn_groups_get_list(session->groups);
+			if (gaim_find_buddys_group(b) == g)
+				break;
+
+			b = NULL;
+		}
 
-		if (l != NULL) {
-			group = l->data;
+		if (b == NULL)
+		{
+			b = gaim_buddy_new(account,
+							   msn_user_get_passport(user), NULL);
 
-			msn_user_set_group_id(user, msn_group_get_id(group));
+			gaim_blist_add_buddy(b, NULL, g, NULL);
 		}
+
+		gaim_debug(GAIM_DEBUG_INFO, "msn",
+				   "Adding MsnUser to %s's proto_data (group %d, %s)\n",
+				   b->name, group_id, (g == NULL ? "(null)" : g->name));
+
+		b->proto_data = user;
 	}
 
-	if (group == NULL ||
-		(g = gaim_find_group(msn_group_get_name(group))) == NULL) {
-
-		gaim_debug(GAIM_DEBUG_ERROR, "msn",
-				   "Group '%s' appears in server-side "
-				   "buddy list, but not here!",
-				   msn_group_get_name(group));
-	}
-
-	if (group != NULL)
-		msn_group_add_user(group, user);
-
-	if (g == NULL) {
-		/* Should never happen. */
-
-		if ((g = gaim_find_group(_("Buddies"))) == NULL) {
-			g = gaim_group_new(_("Buddies"));
-			gaim_blist_add_group(g, NULL);
-		}
-	}
-
-	b = gaim_find_buddy(gc->account, msn_user_get_passport(user));
-
-	if (b == NULL) {
-		b = gaim_buddy_new(gc->account,
-						   msn_user_get_passport(user), NULL);
-
-		gaim_blist_add_buddy(b, NULL, g, NULL);
-	}
-
-	b->proto_data = user;
+	g_slist_free(buddies);
 
 	serv_got_alias(gc, (char *)msn_user_get_passport(user),
 				   (char *)msn_user_get_name(user));
@@ -107,6 +124,34 @@
 	return TRUE;
 }
 
+static size_t
+msn_ssl_read(GaimSslConnection *gsc, char **dest_buffer)
+{
+	size_t size = 0, s;
+	char *buffer = NULL;
+	char temp_buf[4096];
+
+	while ((s = gaim_ssl_read(gsc, temp_buf, sizeof(temp_buf))) > 0)
+	{
+		char *new_buffer = g_new(char, size + s + 1);
+
+		if (buffer != NULL)
+			strncpy(new_buffer, buffer, size);
+
+		g_free(buffer);
+		buffer = new_buffer;
+
+		strncpy(buffer + size, temp_buf, s);
+
+		buffer[size + s] = '\0';
+
+		size += s;
+	}
+
+	*dest_buffer = buffer;
+
+	return size;
+}
 
 /**************************************************************************
  * Callbacks
@@ -207,29 +252,22 @@
 /**************************************************************************
  * Login
  **************************************************************************/
+
+
 static gboolean
-ver_cmd(MsnServConn *servconn, const char *command, const char **params,
+cvr_cmd(MsnServConn *servconn, const char *command, const char **params,
 		size_t param_count)
 {
-	GaimConnection *gc = servconn->session->account->gc;
-	size_t i;
-	gboolean msnp5_found = FALSE;
+	GaimAccount *account = servconn->session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
+	char outparams[MSN_BUF_LEN];
 
-	for (i = 1; i < param_count; i++) {
-		if (!strcmp(params[i], "MSNP5")) {
-			msnp5_found = TRUE;
-			break;
-		}
-	}
+	g_snprintf(outparams, sizeof(outparams),
+			   "TWN I %s", gaim_account_get_username(account));
 
-	if (!msnp5_found) {
-		gaim_connection_error(gc, _("Protocol not supported"));
-
-		return FALSE;
-	}
-
-	if (!msn_servconn_send_command(servconn, "INF", NULL)) {
-		gaim_connection_error(gc, _("Unable to request INF"));
+	if (!msn_servconn_send_command(servconn, "USR", outparams))
+	{
+		gaim_connection_error(gc, _("Unable to request USR\n"));
 
 		return FALSE;
 	}
@@ -248,7 +286,7 @@
 	if (strcmp(params[1], "MD5")) {
 		gaim_connection_error(gc, _("Unable to login using MD5"));
 
-		return FALSE;
+	return FALSE;
 	}
 
 	g_snprintf(outparams, sizeof(outparams), "MD5 I %s",
@@ -266,6 +304,268 @@
 	return TRUE;
 }
 
+static void
+login_connect_cb(gpointer data, GaimSslConnection *gsc,
+				 GaimInputCondition cond)
+{
+	MsnServConn *servconn = (MsnServConn *)data;
+	MsnSession *session = servconn->session;
+	GaimConnection *gc = gaim_account_get_connection(session->account);
+	char *username, *password;
+	char *request_str;
+	char *buffer = NULL;
+	size_t s;
+
+	username =
+		g_strdup(msn_url_encode(gaim_account_get_username(session->account)));
+	password =
+		g_strdup(msn_url_encode(gaim_account_get_password(session->account)));
+
+	request_str = g_strdup_printf(
+		"GET %s HTTP/1.1\r\n"
+		"Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s,pwd=%s,"
+		"lc=%s,id=%s,tw=%s,fs=%s,ct=%s,kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
+		"User-Agent: MSMSGS\r\n"
+		"Host: %s\r\n"
+		"Connection: Keep-Alive\r\n"
+		"Cache-Control: no-cache\r\n"
+		"\r\n",
+		session->ssl_login_path,
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "ru"),
+		username, password,
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "lc"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "id"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "tw"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "fs"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "ct"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "kpp"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "kv"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "ver"),
+		(char *)g_hash_table_lookup(session->ssl_challenge_data, "tpf"),
+		session->ssl_login_host);
+
+	gaim_debug(GAIM_DEBUG_MISC, "msn", "Sending: {%s}\n", request_str);
+
+	g_free(username);
+	g_free(password);
+
+	if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0)
+	{
+		gaim_connection_error(gc, _("Unable to write to MSN Nexus server."));
+
+		return;
+	}
+
+	if ((s = msn_ssl_read(gsc, &buffer)) <= 0)
+	{
+		gaim_connection_error(gc, _("Unable to read from MSN Nexus server."));
+
+		if (buffer != NULL)
+			g_free(buffer);
+
+		return;
+	}
+
+	gaim_ssl_close(gsc);
+
+	gaim_debug(GAIM_DEBUG_MISC, "msn", "ssl buffer: {%s}", buffer);
+
+	if (strstr(buffer, "HTTP/1.1 302") != NULL)
+	{
+		/* Redirect. */
+		char *location, *c;
+
+		if ((location = strstr(buffer, "Location: ")) == NULL)
+		{
+			gaim_connection_error(gc,
+				_("MSN Nexus server returned invalid redirect information."));
+
+			g_free(buffer);
+
+			return;
+		}
+
+		location = strchr(location, ' ') + 1;
+
+		if ((c = strchr(location, '\r')) != NULL)
+			*c = '\0';
+
+		/* Skip the http:// */
+		if ((c = strchr(location, '/')) != NULL)
+			location = c + 2;
+
+		if ((c = strchr(location, '/')) != NULL)
+		{
+			session->ssl_login_path = g_strdup(c);
+
+			*c = '\0';
+		}
+
+		session->ssl_login_host = g_strdup(location);
+
+		session->ssl_conn = gaim_ssl_connect(session->account,
+											 session->ssl_login_host,
+											 GAIM_SSL_DEFAULT_PORT,
+											 login_connect_cb, servconn);
+	}
+	else if (strstr(buffer, "HTTP/1.1 401 Unauthorized") != NULL)
+	{
+		char *error;
+
+		if ((error = strstr(buffer, "WWW-Authenticate")) != NULL)
+		{
+			if ((error = strstr(buffer, "cbtxt=")) != NULL)
+				error += strlen("cbtxt=");
+
+			error = msn_url_decode(error);
+		}
+
+
+		if (error == NULL)
+		{
+			gaim_connection_error(gc,
+				_("Unknown error when attempting to authorize with "
+				  "MSN login server."));
+		}
+		else
+			gaim_connection_error(gc, error);
+	}
+	else
+	{
+		char *base, *c;
+		char outparams[MSN_BUF_LEN];
+
+		g_free(session->ssl_login_host);
+		g_free(session->ssl_login_path);
+		g_hash_table_destroy(session->ssl_challenge_data);
+
+		session->ssl_login_host = NULL;
+		session->ssl_login_path = NULL;
+		session->ssl_challenge_data = NULL;
+
+#if 0
+		/* All your base are belong to us. */
+		base = buffer;
+
+		/* For great cookie! */
+		while ((base = strstr(base, "Set-Cookie: ")) != NULL)
+		{
+			base += strlen("Set-Cookie: ");
+
+			c = strchr(base, ';');
+
+			session->login_cookies =
+				g_list_append(session->login_cookies,
+							  g_strndup(base, c - base));
+		}
+#endif
+
+		if ((base = strstr(buffer, "Authentication-Info: ")) == NULL)
+		{
+			gaim_debug(GAIM_DEBUG_ERROR, "msn",
+					   "Authentication information was not found. This did "
+					   "not just happen, but if it did, you're screwed. "
+					   "Report this.\n");
+
+			return;
+		}
+
+		base  = strstr(base, "from-PP='");
+		base += strlen("from-PP='");
+		c     = strchr(base, '\'');
+
+		session->ssl_login_params = g_strndup(base, c - base);
+
+		g_snprintf(outparams, sizeof(outparams),
+				   "TWN S %s", session->ssl_login_params);
+
+		g_free(session->ssl_login_params);
+		session->ssl_login_params = NULL;
+
+		if (!msn_servconn_send_command(session->notification_conn, "USR",
+									   outparams))
+		{
+			gaim_connection_error(gc, _("Unable to request USR\n"));
+		}
+	}
+
+	g_free(buffer);
+}
+
+static void
+nexus_connect_cb(gpointer data, GaimSslConnection *gsc,
+				 GaimInputCondition cond)
+{
+	MsnServConn *servconn = (MsnServConn *)data;
+	MsnSession *session = servconn->session;
+	GaimConnection *gc = gaim_account_get_connection(session->account);
+	char *request_str;
+	char *da_login;
+	char *base, *c;
+	char *buffer = NULL;
+	size_t s;
+
+	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)
+	{
+		gaim_connection_error(gc, _("Unable to write to MSN Nexus server."));
+		return;
+	}
+
+	g_free(session->ssl_url);
+	session->ssl_url = NULL;
+
+	/* Get the PassportURLs line. */
+	if ((s = msn_ssl_read(gsc, &buffer)) <= 0)
+	{
+		gaim_connection_error(gc, _("Unable to read from MSN Nexus server."));
+
+		if (buffer != NULL)
+			g_free(buffer);
+
+		return;
+	}
+
+	if ((base = strstr(buffer, "PassportURLs")) == NULL)
+	{
+		gaim_connection_error(gc,
+				_("MSN Nexus server returned invalid information."));
+
+		g_free(buffer);
+
+		return;
+	}
+
+	if ((da_login = strstr(base, "DALogin=")) != NULL)
+	{
+		if ((da_login = strchr(da_login, '=')) != NULL)
+			da_login++;
+
+		if ((c = strchr(da_login, ',')) != NULL)
+			*c = '\0';
+
+		if ((c = strchr(da_login, '/')) != NULL)
+		{
+			session->ssl_login_path = g_strdup(c);
+
+			*c = '\0';
+		}
+
+		session->ssl_login_host = g_strdup(da_login);
+	}
+
+	g_free(buffer);
+
+	gaim_ssl_close(gsc);
+
+	/* Now begin the connection to the login server. */
+	session->ssl_conn = gaim_ssl_connect(session->account,
+										 session->ssl_login_host,
+										 GAIM_SSL_DEFAULT_PORT,
+										 login_connect_cb, servconn);
+}
+
 static gboolean
 usr_cmd(MsnServConn *servconn, const char *command, const char **params,
 		size_t param_count)
@@ -275,8 +575,14 @@
 	GaimConnection *gc = gaim_account_get_connection(account);
 	char outparams[MSN_BUF_LEN];
 
-	/* We're either getting the challenge or the OK. Let's find out. */
-	if (!g_ascii_strcasecmp(params[1], "OK")) {
+	/*
+	 * We're either getting the passport connect info (if we're on
+	 * MSNP8 or higher), or a challenge request (MSNP7 and lower).
+	 *
+	 * Let's find out.
+	 */
+	if (!g_ascii_strcasecmp(params[1], "OK"))
+	{
 		const char *friendly = msn_url_decode(params[3]);
 
 		/* OK */
@@ -285,7 +591,8 @@
 
 		session->syncing_lists = TRUE;
 
-		if (!msn_servconn_send_command(servconn, "SYN", "0")) {
+		if (!msn_servconn_send_command(servconn, "SYN", "0"))
+		{
 			gaim_connection_error(gc, _("Unable to write"));
 
 			return FALSE;
@@ -294,7 +601,73 @@
 		gaim_connection_update_progress(gc, _("Retrieving buddy list"),
 										7, MSN_CONNECT_STEPS);
 	}
-	else {
+	else if (!g_ascii_strcasecmp(params[1], "TWN"))
+	{
+		/* Passport authentication */
+		char *challenge_data;
+		char *key, *value = NULL;
+		char *c;
+
+		if (session->ssl_challenge_data != NULL)
+			g_hash_table_destroy(session->ssl_challenge_data);
+
+		session->ssl_challenge_data =
+			g_hash_table_new_full(g_str_hash, g_str_equal,
+								  g_free, g_free);
+
+		/* Parse the challenge data. */
+
+		challenge_data = g_strdup(params[3]);
+
+		for (c = challenge_data, key = challenge_data; *c != '\0'; c++)
+		{
+			if (*c == '=')
+			{
+				*c = '\0';
+
+				value = c + 1;
+			}
+			else if (*c == ',')
+			{
+				*c = '\0';
+
+				g_hash_table_insert(session->ssl_challenge_data,
+									g_strdup(key), g_strdup(value));
+
+				key = c + 1;
+			}
+		}
+
+#if 0
+		passport_str = g_strdup(msn_url_decode(params[3]));
+
+		for (c = passport_str; *c != '\0'; c++)
+		{
+			if (*c == ',')
+				*c = '&';
+		}
+
+		session->ssl_url = passport_str;
+#endif
+
+		session->ssl_conn = gaim_ssl_connect(session->account,
+											 "nexus.passport.com",
+											 GAIM_SSL_DEFAULT_PORT,
+											 nexus_connect_cb, servconn);
+
+		if (session->ssl_conn == NULL)
+		{
+			gaim_connection_error(gc,
+				_("Unable to connect to passport server"));
+
+			return FALSE;
+		}
+
+		gaim_connection_update_progress(gc, _("Password sent"),
+										6, MSN_CONNECT_STEPS);
+	}
+	else if (!g_ascii_strcasecmp(params[1], "MD5"))
+	{
 		/* Challenge */
 		const char *challenge = params[3];
 		char buf[MSN_BUF_LEN];
@@ -329,6 +702,61 @@
 	return TRUE;
 }
 
+static gboolean
+ver_cmd(MsnServConn *servconn, const char *command, const char **params,
+		size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	GaimAccount *account = session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
+	gboolean protocol_supported = FALSE;
+	char outparams[MSN_BUF_LEN];
+	char proto_str[8];
+	size_t i;
+
+	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
+
+	for (i = 1; i < param_count; i++)
+	{
+		if (!strcmp(params[i], proto_str))
+		{
+			protocol_supported = TRUE;
+			break;
+		}
+	}
+
+	if (!protocol_supported) {
+		gaim_connection_error(gc, _("Protocol not supported"));
+
+		return FALSE;
+	}
+
+	if (session->protocol_ver >= 8)
+	{
+		g_snprintf(outparams, sizeof(outparams),
+				   "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
+				   gaim_account_get_username(account));
+
+		if (!msn_servconn_send_command(servconn, "CVR", outparams))
+		{
+			gaim_connection_error(gc, _("Unable to request CVR\n"));
+
+			return FALSE;
+		}
+	}
+	else
+	{
+		if (!msn_servconn_send_command(servconn, "INF", NULL))
+		{
+			gaim_connection_error(gc, _("Unable to request INF\n"));
+
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
 /**************************************************************************
  * Log out
  **************************************************************************/
@@ -378,22 +806,37 @@
 chl_cmd(MsnServConn *servconn, const char *command, const char **params,
 		size_t param_count)
 {
-	GaimConnection *gc = servconn->session->account->gc;
+	MsnSession *session = servconn->session;
+	GaimConnection *gc = session->account->gc;
 	char buf[MSN_BUF_LEN];
 	char buf2[3];
+	const char *challenge_resp;
 	md5_state_t st;
 	md5_byte_t di[16];
 	int i;
 
 	md5_init(&st);
 	md5_append(&st, (const md5_byte_t *)params[1], strlen(params[1]));
-	md5_append(&st, (const md5_byte_t *)"Q1P7W2E4J9R8U3S5",
-			   strlen("Q1P7W2E4J9R8U3S5"));
+
+	if (session->protocol_ver >= 8)
+	{
+		challenge_resp = "VT6PX?UQTM4WM%YR";
+	}
+	else
+	{
+		challenge_resp = "Q1P7W2E4J9R8U3S5";
+	}
+
+	md5_append(&st, (const md5_byte_t *)challenge_resp,
+			   strlen(challenge_resp));
 	md5_finish(&st, di);
 
 	g_snprintf(buf, sizeof(buf),
-			   "QRY %u msmsgs@msnmsgr.com 32\r\n",
-			   servconn->session->trId++);
+			   "QRY %u %s 32\r\n",
+			   servconn->session->trId++,
+			   (session->protocol_ver >= 8
+				? "PROD0038W!61ZTF9"
+				: "msmsgs@msnmsgr.com"));
 
 	for (i = 0; i < 16; i++) {
 		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
@@ -435,7 +878,7 @@
 		user = msn_user_new(session, passport, NULL);
 
 		if (group_id != NULL)
-			msn_user_set_group_id(user, atoi(group_id));
+			msn_user_add_group_id(user, atoi(group_id));
 
 		add_buddy(servconn, user);
 
@@ -491,8 +934,14 @@
 		size_t param_count)
 {
 	GaimConnection *gc = servconn->session->account->gc;
+	const char *list_name;
 
-	if (!g_ascii_strcasecmp(params[2], "AL")) {
+	if (servconn->session->protocol_ver >= 8)
+		list_name = params[0];
+	else
+		list_name = params[2];
+
+	if (!g_ascii_strcasecmp(list_name, "AL")) {
 		/*
 		 * If the current setting is AL, messages from users who
 		 * are not in BL will be delivered.
@@ -523,13 +972,21 @@
 	GaimBuddy *b;
 	MsnUser *user;
 
-	if (param_count < 4)
+	if (param_count == 4)
+	{
+		passport = params[1];
+		type     = params[2];
+		value    = params[3];
+	}
+	else if (param_count == 2)
+	{
+		passport = msn_user_get_passport(session->last_user_added);
+		type     = params[0];
+		value    = params[1];
+	}
+	else
 		return TRUE;
 
-	passport = params[1];
-	type     = params[2];
-	value    = params[3];
-
 	user = msn_users_find_with_passport(session->users, passport);
 
 	if (value != NULL) {
@@ -627,18 +1084,25 @@
 	MsnGroup *group;
 	GaimGroup *g;
 	const char *name;
-	int group_num, num_groups, group_id;
+	int num_groups, group_id;
 
-	group_num  = atoi(params[2]);
-	num_groups = atoi(params[3]);
-	group_id   = atoi(params[4]);
-	name       = msn_url_decode(params[5]);
+	if (session->protocol_ver >= 8)
+	{
+		group_id = atoi(params[0]);
+		name = msn_url_decode(params[1]);
+	}
+	else
+	{
+		num_groups = atoi(params[3]);
+		group_id   = atoi(params[4]);
+		name       = msn_url_decode(params[5]);
 
-	if (num_groups == 0)
-		return TRUE;
+		if (num_groups == 0)
+			return TRUE;
 
-	if (!strcmp(name, "~"))
-		name = _("Buddies");
+		if (!strcmp(name, "~"))
+			name = _("Buddies");
+	}
 
 	group = msn_group_new(session, group_id, name);
 
@@ -657,79 +1121,86 @@
 		size_t param_count)
 {
 	MsnSession *session = servconn->session;
-	GaimConnection *gc = session->account->gc;
-	int user_num;
-	int num_users;
-	const char *type;
+	GaimAccount *account = session->account;
+	GaimConnection *gc = gaim_account_get_connection(account);
 	const char *passport = NULL;
 	const char *friend = NULL;
 
-	type      = params[1];
-	user_num  = atoi(params[3]);
-	num_users = atoi(params[4]);
-
-	if (g_ascii_strcasecmp(type, "RL") && user_num == 0 && num_users == 0)
-		return TRUE; /* There are no users on this list. */
+	if (session->protocol_ver >= 8)
+	{
+		const char *group_nums;
+		int list_op;
 
-	if (num_users > 0) {
-		passport  = params[5];
-		friend    = msn_url_decode(params[6]);
-	}
+		passport   = params[0];
+		friend     = msn_url_decode(params[1]);
+		list_op    = atoi(params[2]);
+		group_nums = params[3];
 
-	if (session->syncing_lists && session->lists_synced)
-		return TRUE;
+		if (list_op & MSN_LIST_FL_OP)
+		{
+			MsnUser *user;
+			char **c;
+			char **tokens;
+
+			user = msn_user_new(session, passport, friend);
 
-	if (!g_ascii_strcasecmp(type, "FL") && user_num != 0) {
-		/* These are users on our contact list. */
-		MsnUser *user;
-
-		user = msn_user_new(session, passport, friend);
-
-		if (param_count == 8)
-			msn_user_set_group_id(user, atoi(params[7]));
+			tokens = g_strsplit(group_nums, ",", -1);
 
-		session->lists.forward = g_slist_append(session->lists.forward, user);
-	}
-	else if (!g_ascii_strcasecmp(type, "AL") && user_num != 0) {
-		/* These are users who are allowed to see our status. */
-		if (g_slist_find_custom(gc->account->deny, passport,
-								(GCompareFunc)strcmp)) {
+			gaim_debug(GAIM_DEBUG_MISC, "msn",
+					   "Fetching group IDs from '%s'\n", group_nums);
+			for (c = tokens; *c != NULL; c++)
+			{
+				gaim_debug(GAIM_DEBUG_MISC, "msn",
+						   "Appending group ID %d\n", atoi(*c));
+				msn_user_add_group_id(user, atoi(*c));
+			}
 
-			gaim_debug(GAIM_DEBUG_INFO, "msn",
-					   "Moving user from deny list to permit: %s (%s)\n",
-					   passport, friend);
+			g_strfreev(tokens);
 
-			gaim_privacy_deny_remove(gc->account, passport, TRUE);
+			session->lists.forward =
+				g_slist_append(session->lists.forward, user);
+
+			session->last_user_added = user;
 		}
 
-		gaim_privacy_permit_add(gc->account, passport, TRUE);
-	}
-	else if (!g_ascii_strcasecmp(type, "BL") && user_num != 0) {
-		/* These are users who are not allowed to see our status. */
-		gaim_privacy_deny_add(gc->account, passport, TRUE);
-	}
-	else if (!g_ascii_strcasecmp(type, "RL")) {
-		/* These are users who have us on their contact list. */
-		if (user_num > 0) {
+		if (list_op & MSN_LIST_AL_OP)
+		{
+			/* These are users who are allowed to see our status. */
+
+			if (g_slist_find_custom(account->deny, passport,
+									(GCompareFunc)strcmp))
+			{
+				gaim_privacy_deny_remove(gc->account, passport, TRUE);
+			}
+
+			gaim_privacy_permit_add(account, passport, TRUE);
+		}
+
+		if (list_op & MSN_LIST_BL_OP)
+		{
+			/* These are users who are not allowed to see our status. */
+			gaim_privacy_deny_add(account, passport, TRUE);
+		}
+
+		if (list_op & MSN_LIST_RL_OP)
+		{
+			/* These are users who have us on their contact list. */
+
 			gboolean new_entry = TRUE;
 
-			if (g_slist_find_custom(gc->account->permit, passport,
-									(GCompareFunc)g_ascii_strcasecmp)) {
-				new_entry = FALSE;
-			}
-			
-			if (g_slist_find_custom(gc->account->deny, passport,
-									(GCompareFunc)g_ascii_strcasecmp)) {
+			if (g_slist_find_custom(account->permit, passport,
+									(GCompareFunc)g_ascii_strcasecmp) ||
+				g_slist_find_custom(account->deny, passport,
+									(GCompareFunc)g_ascii_strcasecmp))
+			{
 				new_entry = FALSE;
 			}
 
-			if (new_entry) {
+			if (new_entry)
+			{
 				MsnPermitAdd *pa;
 				char msg[MSN_BUF_LEN];
 
-				gaim_debug(GAIM_DEBUG_WARNING, "msn",
-						   "Unresolved MSN RL entry: %s\n", passport);
-
 				pa       = g_new0(MsnPermitAdd, 1);
 				pa->user = msn_user_new(session, passport, friend);
 				pa->gc   = gc;
@@ -748,12 +1219,12 @@
 			}
 		}
 
-		if (user_num != num_users)
-			return TRUE; /* This isn't the last one in the RL. */
+		session->num_users++;
 
-		/* Now we're at the last one, so we can do final work. */
-		if (!session->lists_synced) {
-			if (!msn_servconn_send_command(servconn, "CHG", "NLN")) {
+		if (session->num_users == session->total_users)
+		{
+			if (!msn_servconn_send_command(servconn, "CHG", "NLN"))
+			{
 				gaim_connection_error(gc, _("Unable to write"));
 
 				return FALSE;
@@ -761,31 +1232,178 @@
 
 			gaim_connection_set_state(gc, GAIM_CONNECTED);
 			serv_finish_login(gc);
+
+			if (session->lists.allow == NULL)
+				session->lists.allow = g_slist_copy(account->permit);
+			else
+				session->lists.allow = g_slist_concat(session->lists.allow,
+													  account->permit);
+
+			if (session->lists.block == NULL)
+				session->lists.block = g_slist_copy(account->permit);
+			else
+				session->lists.block = g_slist_concat(session->lists.block,
+													  account->deny);
+
+			while (session->lists.forward != NULL)
+			{
+				MsnUser *user = session->lists.forward->data;
+
+				session->lists.forward =
+					g_slist_remove(session->lists.forward, user);
+
+				add_buddy(servconn, user);
+			}
+
+			session->syncing_lists = FALSE;
+			session->lists_synced  = TRUE;
+		}
+	}
+	else
+	{
+		const char *list_name;
+		int user_num;
+		int num_users;
+
+		list_name = params[1];
+		user_num  = atoi(params[3]);
+		num_users = atoi(params[4]);
+
+		if (g_ascii_strcasecmp(list_name, "RL") &&
+			user_num == 0 && num_users == 0)
+		{
+			return TRUE; /* There are no users on this list. */
+		}
+
+		if (num_users > 0)
+		{
+			passport  = params[5];
+			friend    = msn_url_decode(params[6]);
 		}
 
-		if (session->lists.allow == NULL)
-			session->lists.allow = g_slist_copy(gc->account->permit);
-		else
-			session->lists.allow = g_slist_concat(session->lists.allow,
-												  gc->account->permit);
+		if (session->syncing_lists && session->lists_synced)
+			return TRUE;
+
+		if (!g_ascii_strcasecmp(list_name, "FL") && user_num != 0)
+		{
+			/* These are users on our contact list. */
+			MsnUser *user;
+
+			user = msn_user_new(session, passport, friend);
+
+			if (param_count == 8)
+				msn_user_add_group_id(user, atoi(params[7]));
+
+			session->lists.forward =
+				g_slist_append(session->lists.forward, user);
+		}
+		else if (!g_ascii_strcasecmp(list_name, "AL") && user_num != 0)
+		{
+			/* These are users who are allowed to see our status. */
+			if (g_slist_find_custom(gc->account->deny, passport,
+									(GCompareFunc)strcmp))
+			{
+				gaim_debug(GAIM_DEBUG_INFO, "msn",
+						   "Moving user from deny list to permit: %s (%s)\n",
+						   passport, friend);
+
+				gaim_privacy_deny_remove(gc->account, passport, TRUE);
+			}
 
-		if (session->lists.block == NULL)
-			session->lists.block = g_slist_copy(gc->account->deny);
-		else
-			session->lists.block = g_slist_concat(session->lists.block,
-												  gc->account->deny);
+			gaim_privacy_permit_add(gc->account, passport, TRUE);
+		}
+		else if (!g_ascii_strcasecmp(list_name, "BL") && user_num != 0)
+		{
+			/* These are users who are not allowed to see our status. */
+			gaim_privacy_deny_add(gc->account, passport, TRUE);
+		}
+		else if (!g_ascii_strcasecmp(list_name, "RL"))
+		{
+			/* These are users who have us on their contact list. */
+			if (user_num > 0)
+			{
+				gboolean new_entry = TRUE;
+
+				if (g_slist_find_custom(gc->account->permit, passport,
+										(GCompareFunc)g_ascii_strcasecmp))
+				{
+					new_entry = FALSE;
+				}
+
+				if (g_slist_find_custom(gc->account->deny, passport,
+										(GCompareFunc)g_ascii_strcasecmp))
+				{
+					new_entry = FALSE;
+				}
+
+				if (new_entry)
+				{
+					MsnPermitAdd *pa;
+					char msg[MSN_BUF_LEN];
 
-		while (session->lists.forward != NULL) {
-			MsnUser *user = session->lists.forward->data;
+					gaim_debug(GAIM_DEBUG_WARNING, "msn",
+							   "Unresolved MSN RL entry: %s\n", passport);
+
+					pa       = g_new0(MsnPermitAdd, 1);
+					pa->user = msn_user_new(session, passport, friend);
+					pa->gc   = gc;
+
+					g_snprintf(msg, sizeof(msg),
+							   _("The user %s (%s) wants to add you to their "
+								 "buddy list."),
+							   msn_user_get_passport(pa->user),
+							   msn_user_get_name(pa->user));
 
-			session->lists.forward = g_slist_remove(session->lists.forward,
-													user);
+					gaim_request_action(gc, NULL, msg, NULL, 0, pa, 2,
+										_("Authorize"),
+										G_CALLBACK(msn_accept_add_cb),
+										_("Deny"),
+										G_CALLBACK(msn_cancel_add_cb));
+				}
+			}
+
+			if (user_num != num_users)
+				return TRUE; /* This isn't the last one in the RL. */
+
+			/* Now we're at the last one, so we can do final work. */
+			if (!session->lists_synced)
+			{
+				if (!msn_servconn_send_command(servconn, "CHG", "NLN"))
+				{
+					gaim_connection_error(gc, _("Unable to write"));
 
-			add_buddy(servconn, user);
-		}
+					return FALSE;
+				}
+
+				gaim_connection_set_state(gc, GAIM_CONNECTED);
+				serv_finish_login(gc);
+			}
+
+			if (session->lists.allow == NULL)
+				session->lists.allow = g_slist_copy(gc->account->permit);
+			else
+				session->lists.allow = g_slist_concat(session->lists.allow,
+													  gc->account->permit);
 
-		session->syncing_lists = FALSE;
-		session->lists_synced  = TRUE;
+			if (session->lists.block == NULL)
+				session->lists.block = g_slist_copy(gc->account->deny);
+			else
+				session->lists.block = g_slist_concat(session->lists.block,
+													  gc->account->deny);
+
+			while (session->lists.forward != NULL)
+			{
+				MsnUser *user = session->lists.forward->data;
+
+				session->lists.forward =
+					g_slist_remove(session->lists.forward, user);
+
+				add_buddy(servconn, user);
+			}
+
+			session->syncing_lists = FALSE;
+			session->lists_synced  = TRUE;
+		}
 	}
 
 	return TRUE;
@@ -987,6 +1605,21 @@
 	return TRUE;
 }
 
+static gboolean
+syn_cmd(MsnServConn *servconn, const char *command, const char **params,
+		size_t param_count)
+{
+	MsnSession *session = servconn->session;
+
+	if (session->protocol_ver >= 8)
+	{
+		session->total_users  = atoi(params[2]);
+		session->total_groups = atoi(params[3]);
+	}
+
+	return TRUE;
+}
+
 /**************************************************************************
  * Misc commands
  **************************************************************************/
@@ -1239,6 +1872,12 @@
 	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
 		session->passport_info.mspauth = g_strdup(value);
 
+	if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
+		session->passport_info.client_ip = g_strdup(value);
+
+	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+		session->passport_info.client_port = ntohs(atoi(value));
+
 	return TRUE;
 }
 
@@ -1347,7 +1986,7 @@
 						   "signed out at that time.  Please finish any "
 						   "conversations in progress.\n\nAfter the "
 						   "maintenance has been completed, you will be "
-						   "able to successfully sign in.", 
+						   "able to successfully sign in.",
 						   "The MSN server will shut down for maintenance "
 						   "in %d minutes. You will automatically be "
 						   "signed out at that time.  Please finish any "
@@ -1375,6 +2014,8 @@
 	MsnSession *session = notification->session;
 	GaimAccount *account = session->account;
 	GaimConnection *gc = gaim_account_get_connection(account);
+	char proto_vers[256];
+	size_t i;
 
 	if (source == -1) {
 		gaim_connection_error(session->account->gc, _("Unable to connect"));
@@ -1384,8 +2025,21 @@
 	if (notification->fd != source)
 		notification->fd = source;
 
-	if (!msn_servconn_send_command(notification, "VER",
-								   "MSNP7 MSNP6 MSNP5 MSNP4 CVR0")) {
+	proto_vers[0] = '\0';
+
+	for (i = session->protocol_ver; i >= 7; i--)
+	{
+		char old_buf[256];
+
+		strcpy(old_buf, proto_vers);
+
+		g_snprintf(proto_vers, sizeof(proto_vers), "MSNP%d %s", i, old_buf);
+	}
+
+	strncat(proto_vers, "CVR0", sizeof(proto_vers));
+
+	if (!msn_servconn_send_command(notification, "VER", proto_vers))
+	{
 		gaim_connection_error(gc, _("Unable to write to server"));
 		return FALSE;
 	}
@@ -1429,6 +2083,7 @@
 		msn_servconn_register_command(notification, "BPR",       bpr_cmd);
 		msn_servconn_register_command(notification, "CHG",       blank_cmd);
 		msn_servconn_register_command(notification, "CHL",       chl_cmd);
+		msn_servconn_register_command(notification, "CVR",       cvr_cmd);
 		msn_servconn_register_command(notification, "FLN",       fln_cmd);
 		msn_servconn_register_command(notification, "GTC",       blank_cmd);
 		msn_servconn_register_command(notification, "ILN",       iln_cmd);
@@ -1448,7 +2103,7 @@
 		msn_servconn_register_command(notification, "REM",       rem_cmd);
 		msn_servconn_register_command(notification, "RMG",       rmg_cmd);
 		msn_servconn_register_command(notification, "RNG",       rng_cmd);
-		msn_servconn_register_command(notification, "SYN",       blank_cmd);
+		msn_servconn_register_command(notification, "SYN",       syn_cmd);
 		msn_servconn_register_command(notification, "URL",       url_cmd);
 		msn_servconn_register_command(notification, "USR",       usr_cmd);
 		msn_servconn_register_command(notification, "VER",       ver_cmd);
--- a/src/protocols/msn/notification.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/notification.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/page.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/page.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/page.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/page.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/servconn.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/servconn.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/servconn.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/servconn.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -22,6 +22,8 @@
 #ifndef _MSN_SERVCONN_H_
 #define _MSN_SERVCONN_H_
 
+#include "proxy.h"
+
 typedef struct _MsnServConn MsnServConn;
 
 #include "msg.h"
--- a/src/protocols/msn/session.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/session.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -39,6 +39,8 @@
 	session->users  = msn_users_new();
 	session->groups = msn_groups_new();
 
+	session->protocol_ver = 9;
+
 	return session;
 }
 
--- a/src/protocols/msn/session.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/session.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +24,8 @@
 
 typedef struct _MsnSession MsnSession;
 
+#include "sslconn.h"
+
 #include "servconn.h"
 #include "switchboard.h"
 #include "user.h"
@@ -34,6 +36,8 @@
 	GaimAccount *account;
 	MsnUser *user;
 
+	int protocol_ver;
+
 	char *dispatch_server;
 	int dispatch_port;
 
@@ -44,6 +48,13 @@
 
 	unsigned int trId;
 
+	char *ssl_url;
+	char *ssl_login_host;
+	char *ssl_login_path;
+	char *ssl_login_params;
+	GHashTable *ssl_challenge_data;
+	GaimSslConnection *ssl_conn;
+
 	MsnUsers *users;
 	MsnGroups *groups;
 
@@ -66,12 +77,22 @@
 		char *mspauth;
 		unsigned long sl;
 		char *file;
+		char *client_ip;
+		int client_port;
 
 	} passport_info;
 
 	/* You have no idea how much I hate all that is below. */
 	GaimPlugin *prpl;
 
+	/* For MSNP8 and MSNP9. */
+	int num_users;
+	int total_users;
+	int num_groups;
+	int total_groups;
+	MsnUser *last_user_added;
+
+	/* For MSNP7 and lower. */
 	gboolean syncing_lists;
 	gboolean lists_synced;
 
--- a/src/protocols/msn/state.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/state.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/state.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/state.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/switchboard.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/switchboard.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +24,8 @@
 
 typedef struct _MsnSwitchBoard MsnSwitchBoard;
 
+#include "conversation.h"
+
 #include "servconn.h"
 #include "msg.h"
 #include "user.h"
--- a/src/protocols/msn/user.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/user.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -35,7 +35,6 @@
 		user->session = session;
 
 		msn_user_set_passport(user, passport);
-		msn_user_set_group_id(user, -1);
 
 		msn_users_add(session->users, user);
 	}
@@ -65,6 +64,9 @@
 	if (user->clientcaps != NULL)
 		g_hash_table_destroy(user->clientcaps);
 
+	if (user->group_ids != NULL)
+		g_list_free(user->group_ids);
+
 	if (user->passport != NULL) g_free(user->passport);
 	if (user->name     != NULL) g_free(user->name);
 
@@ -127,14 +129,32 @@
 }
 
 void
-msn_user_set_group_id(MsnUser *user, int id)
+msn_user_set_group_ids(MsnUser *user, GList *ids)
 {
 	g_return_if_fail(user != NULL);
 
-	user->group_id = id;
+	user->group_ids = ids;
 }
 
 void
+msn_user_add_group_id(MsnUser *user, int id)
+{
+	g_return_if_fail(user != NULL);
+	g_return_if_fail(id > -1);
+
+	if (!g_list_find(user->group_ids, GINT_TO_POINTER(id)))
+		user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id));
+}
+
+void
+msn_user_remove_group_id(MsnUser *user, int id)
+{
+	g_return_if_fail(user != NULL);
+	g_return_if_fail(id > -1);
+
+	user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id));
+}
+void
 msn_user_set_home_phone(MsnUser *user, const char *number)
 {
 	g_return_if_fail(user != NULL);
@@ -184,12 +204,12 @@
 	return user->name;
 }
 
-int
-msn_user_get_group_id(const MsnUser *user)
+GList *
+msn_user_get_group_ids(const MsnUser *user)
 {
-	g_return_val_if_fail(user != NULL, -1);
+	g_return_val_if_fail(user != NULL, NULL);
 
-	return user->group_id;
+	return user->group_ids;
 }
 
 const char *
--- a/src/protocols/msn/user.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/user.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
@@ -47,7 +47,7 @@
 
 	gboolean mobile;        /**< Signed up with MSN Mobile. */
 
-	int group_id;           /**< The group ID.              */
+	GList *group_ids;       /**< The group IDs.             */
 
 	size_t ref_count;       /**< The reference count.       */
 
@@ -71,7 +71,7 @@
 
 /**
  * Creates a new user structure.
- * 
+ *
  * @param session  The MSN session.
  * @param passport The initial passport.
  * @param name     The initial friendly name.
@@ -125,12 +125,28 @@
 void msn_user_set_name(MsnUser *user, const char *name);
 
 /**
- * Sets the group ID for a user.
+ * Sets the group ID list for a user.
+ *
+ * @param user The user.
+ * @param ids  The group ID list.
+ */
+void msn_user_set_group_ids(MsnUser *user, GList *ids);
+
+/**
+ * Adds the group ID for a user.
  *
  * @param user The user.
  * @param id   The group ID.
  */
-void msn_user_set_group_id(MsnUser *user, int id);
+void msn_user_add_group_id(MsnUser *user, int id);
+
+/**
+ * Removes the group ID from a user.
+ *
+ * @param user The user.
+ * @param id   The group ID.
+ */
+void msn_user_remove_group_id(MsnUser *user, int id);
 
 /**
  * Sets the home phone number for a user.
@@ -175,13 +191,13 @@
 const char *msn_user_get_name(const MsnUser *user);
 
 /**
- * Returns the group ID for a user.
+ * Returns the group IDs for a user.
  *
  * @param user The user.
  *
- * @return The group ID.
+ * @return The group IDs.
  */
-int msn_user_get_group_id(const MsnUser *user);
+GList *msn_user_get_group_ids(const MsnUser *user);
 
 /**
  * Returns the home phone number for a user.
@@ -268,7 +284,7 @@
  * Returns the number of users in a users list.
  *
  * @param users The users list.
- * 
+ *
  * @return The number of users.
  */
 size_t msn_users_get_count(const MsnUsers *users);
--- a/src/protocols/msn/utils.c	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/utils.c	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or
--- a/src/protocols/msn/utils.h	Tue Sep 02 04:28:34 2003 +0000
+++ b/src/protocols/msn/utils.h	Tue Sep 02 04:32:16 2003 +0000
@@ -4,7 +4,7 @@
  * gaim
  *
  * 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
  * the Free Software Foundation; either version 2 of the License, or