diff --git a/configure.ac b/configure.ac
index a0d0f7d..1997535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -343,6 +343,28 @@ fi
 AC_SUBST(ntlm_auth)
 AC_DEFINE_UNQUOTED(NTLM_AUTH, "$ntlm_auth", [Samba's 'winbind' daemon helper 'ntlm_auth' which can be used for NTLM single-sign-on])
 
+dnl **************************
+dnl *** GSSAPI / Negotiate ***
+dnl **************************
+AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin)
+if test "x$KRB5_CONFIG" != "xnone"; then
+    KRB5_LIBS="`${KRB5_CONFIG} --libs gssapi`"
+    KRB5_CFLAGS="`${KRB5_CONFIG} --cflags gssapi`"
+    AC_SUBST(KRB5_CFLAGS)
+    AC_SUBST(KRB5_LIBS)
+    if test "$KRB5_CONFIG" != none; then
+	AC_DEFINE(HAVE_GSSAPI, 1, [Whether or not can gssapi libs are available])
+    fi
+fi
+
+AC_ARG_ENABLE([negotiate-debug],
+	   [AC_HELP_STRING([--enable-negotiate-debug],
+			     [enable negotiate/gssapi debugging output])],[enable_negotiate_debug=yes],[])
+AM_CONDITIONAL([ENABLE_NEGOTIATE_DEBUG], test x"$enable_negotiate_debug" = x"yes")
+if test x"$enable_negotiate_debug" = x"yes"; then
+   AC_DEFINE([ENABLE_NEGOTIATE_DEBUG], [1], [whether negotiate debugging is enabled])
+fi
+
 dnl *************************
 dnl *** Output Everything ***
 dnl *************************
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 096f0fb..a538f37 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -13,6 +13,7 @@ INCLUDES = 				\
 	$(XML_CFLAGS)			\
 	$(SQLITE_CFLAGS)		\
 	$(GNOME_KEYRING_CFLAGS)
+	$(KRB5_CFLAGS)
 
 MARSHAL_GENERATED = soup-marshal.c soup-marshal.h
 MKENUMS_GENERATED = soup-enum-types.c soup-enum-types.h
@@ -49,9 +50,12 @@ soup_headers =			\
 	soup.h			\
 	soup-address.h		\
 	soup-auth.h		\
+	soup-auth-connbased.h	\
 	soup-auth-domain.h	\
 	soup-auth-domain-basic.h  \
 	soup-auth-domain-digest.h \
+	soup-auth-negotiate.h	\
+	soup-auth-ntlm.h	\
 	soup-cache.h		\
 	soup-content-decoder.h	\
 	soup-content-sniffer.h	\
@@ -103,25 +107,28 @@ libsoup_2_4_la_LIBADD =			\
 	$(GLIB_LIBS)			\
 	$(XML_LIBS)			\
 	-lz				\
+	$(KRB5_LIBS)			\
 	$(LIBWS2_32)
 
 libsoup_2_4_la_SOURCES =		\
 	$(BUILT_SOURCES)		\
 	soup-address.c			\
 	soup-auth.c			\
+	soup-auth-connbased.h		\
+	soup-auth-connbased.c		\
 	soup-auth-basic.h		\
 	soup-auth-basic.c		\
 	soup-auth-digest.h		\
 	soup-auth-digest.c		\
 	soup-auth-ntlm.h		\
 	soup-auth-ntlm.c		\
+	soup-auth-negotiate.h		\
+	soup-auth-negotiate.c		\
 	soup-auth-domain.c		\
 	soup-auth-domain-basic.c	\
 	soup-auth-domain-digest.c	\
 	soup-auth-manager.h		\
 	soup-auth-manager.c		\
-	soup-auth-manager-ntlm.h	\
-	soup-auth-manager-ntlm.c	\
 	soup-cache.c			\
 	soup-cache-private.h		\
 	soup-connection.h		\
diff --git a/libsoup/soup-auth-basic.h b/libsoup/soup-auth-basic.h
index 639bf03..f6b7e2d 100644
--- a/libsoup/soup-auth-basic.h
+++ b/libsoup/soup-auth-basic.h
@@ -8,6 +8,7 @@
 
 #include "soup-auth.h"
 
+#define SOUP_TYPE_AUTH_BASIC            (soup_auth_basic_get_type ())
 #define SOUP_AUTH_BASIC(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_BASIC, SoupAuthBasic))
 #define SOUP_AUTH_BASIC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicClass))
 #define SOUP_IS_AUTH_BASIC(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_BASIC))
@@ -24,4 +25,6 @@ typedef struct {
 
 } SoupAuthBasicClass;
 
+GType soup_auth_basic_get_type (void);
+
 #endif /*SOUP_AUTH_BASIC_H*/
diff --git a/libsoup/soup-auth-digest.h b/libsoup/soup-auth-digest.h
index 0165f74..453c40c 100644
--- a/libsoup/soup-auth-digest.h
+++ b/libsoup/soup-auth-digest.h
@@ -8,6 +8,7 @@
 
 #include "soup-auth.h"
 
+#define SOUP_TYPE_AUTH_DIGEST            (soup_auth_digest_get_type ())
 #define SOUP_AUTH_DIGEST(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigest))
 #define SOUP_AUTH_DIGEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestClass))
 #define SOUP_IS_AUTH_DIGEST(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_DIGEST))
@@ -24,6 +25,8 @@ typedef struct {
 
 } SoupAuthDigestClass;
 
+GType soup_auth_digest_get_type (void);
+
 /* Utility routines (also used by SoupAuthDomainDigest) */
 
 typedef enum {
diff --git a/libsoup/soup-auth-manager-ntlm.c b/libsoup/soup-auth-manager-ntlm.c
deleted file mode 100644
index 7c3f239..0000000
--- a/libsoup/soup-auth-manager-ntlm.c
+++ /dev/null
@@ -1,1349 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-auth-manager-ntlm.c: NTLM auth manager
- *
- * Copyright (C) 2001-2007 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <string.h>
-
-#ifdef USE_NTLM_AUTH
-#include <stdlib.h>
-#include <errno.h>
-#endif
-#include "soup-auth-manager-ntlm.h"
-#include "soup-auth-ntlm.h"
-#include "soup-message.h"
-#include "soup-message-private.h"
-#include "soup-misc.h"
-#include "soup-session.h"
-#include "soup-session-feature.h"
-#include "soup-uri.h"
-
-static void soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
-static SoupSessionFeatureInterface *soup_auth_manager_parent_feature_interface;
-
-static void attach (SoupSessionFeature *feature, SoupSession *session);
-static void request_queued (SoupSessionFeature *feature, SoupSession *session,
-			    SoupMessage *msg);
-static void request_started (SoupSessionFeature *feature, SoupSession *session,
-			     SoupMessage *msg, SoupSocket *socket);
-static void request_unqueued (SoupSessionFeature *feature,
-			      SoupSession *session, SoupMessage *msg);
-static gboolean add_feature (SoupSessionFeature *feature, GType type);
-static gboolean remove_feature (SoupSessionFeature *feature, GType type);
-static gboolean has_feature (SoupSessionFeature *feature, GType type);
-
-G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_AUTH_MANAGER,
-			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
-						soup_auth_manager_ntlm_session_feature_init))
-
-typedef enum {
-	SOUP_NTLM_NEW,
-#ifdef USE_NTLM_AUTH
-	SOUP_NTLM_SENT_SSO_REQUEST,
-	SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
-	SOUP_NTLM_SENT_SSO_RESPONSE,
-	SOUP_NTLM_SSO_FAILED,
-#endif
-	SOUP_NTLM_SENT_REQUEST,
-	SOUP_NTLM_RECEIVED_CHALLENGE,
-	SOUP_NTLM_SENT_RESPONSE,
-	SOUP_NTLM_FAILED
-} SoupNTLMState;
-
-typedef struct {
-	SoupSocket *socket;
-	SoupNTLMState state;
-	char *response_header;
-
-	char *nonce, *domain;
-	SoupAuth *auth;
-#ifdef USE_NTLM_AUTH
-	char *challenge_header;
-	int fd_in;
-	int fd_out;
-#endif
-} SoupNTLMConnection;
-
-typedef struct {
-	gboolean use_ntlm;
-
-	SoupSession *session;
-	GHashTable *connections_by_msg;
-	GHashTable *connections_by_id;
-#ifdef USE_NTLM_AUTH
-	gboolean ntlm_auth_accessible;
-#endif
-} SoupAuthManagerNTLMPrivate;
-#define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
-
-static char     *soup_ntlm_request         (void);
-static gboolean  soup_ntlm_parse_challenge (const char  *challenge,
-					    char       **nonce,
-					    char       **default_domain);
-static char     *soup_ntlm_response        (const char  *nonce, 
-					    const char  *user,
-					    const char  *password,
-					    const char  *host, 
-					    const char  *domain);
-#ifdef USE_NTLM_AUTH
-static void sso_ntlm_close (SoupNTLMConnection *conn);
-#endif
-
-static void
-soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-
-	priv->connections_by_id = g_hash_table_new (NULL, NULL);
-	priv->connections_by_msg = g_hash_table_new (NULL, NULL);
-#ifdef USE_NTLM_AUTH
-	priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
-#endif
-}
-
-static void
-free_ntlm_connection (SoupNTLMConnection *conn)
-{
-	g_free (conn->response_header);
-	g_free (conn->nonce);
-	g_free (conn->domain);
-	if (conn->auth)
-		g_object_unref (conn->auth);
-#ifdef USE_NTLM_AUTH
-	g_free (conn->challenge_header);
-	sso_ntlm_close (conn);
-#endif
-	g_slice_free (SoupNTLMConnection, conn);
-}
-
-static void
-free_ntlm_connection_foreach (gpointer key, gpointer value, gpointer user_data)
-{
-	free_ntlm_connection (value);
-}
-
-static void
-finalize (GObject *object)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
-
-	g_hash_table_foreach (priv->connections_by_id,
-			      free_ntlm_connection_foreach, NULL);
-	g_hash_table_destroy (priv->connections_by_id);
-	g_hash_table_destroy (priv->connections_by_msg);
-
-	G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
-}
-
-static void
-soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
-
-	g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
-
-	object_class->finalize = finalize;
-}
-
-static void
-soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
-					     gpointer interface_data)
-{
-	soup_auth_manager_parent_feature_interface =
-		g_type_interface_peek_parent (feature_interface);
-
-	feature_interface->attach = attach;
-	feature_interface->request_queued = request_queued;
-	feature_interface->request_started = request_started;
-	feature_interface->request_unqueued = request_unqueued;
-	feature_interface->add_feature = add_feature;
-	feature_interface->remove_feature = remove_feature;
-	feature_interface->has_feature = has_feature;
-}
-
-static void
-attach (SoupSessionFeature *manager, SoupSession *session)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
-
-	/* FIXME: should support multiple sessions */
-	priv->session = session;
-
-	soup_auth_manager_parent_feature_interface->attach (manager, session);
-}
-
-static void
-delete_conn (SoupSocket *socket, gpointer user_data)
-{
-	SoupAuthManagerNTLMPrivate *priv = user_data;
-	SoupNTLMConnection *conn;
-
-	conn = g_hash_table_lookup (priv->connections_by_id, socket);
-	if (conn)
-		free_ntlm_connection (conn);
-	g_hash_table_remove (priv->connections_by_id, socket);
-	g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
-}
-
-static SoupNTLMConnection *
-get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
-{
-	SoupNTLMConnection *conn;
-
-	conn = g_hash_table_lookup (priv->connections_by_id, socket);
-	if (conn)
-		return conn;
-
-	conn = g_slice_new0 (SoupNTLMConnection);
-	conn->socket = socket;
-	conn->state = SOUP_NTLM_NEW;
-#ifdef USE_NTLM_AUTH
-	conn->fd_in = -1;
-	conn->fd_out = -1;
-#endif
-	g_hash_table_insert (priv->connections_by_id, socket, conn);
-
-	g_signal_connect (socket, "disconnected",
-			  G_CALLBACK (delete_conn), priv);
-	return conn;
-}
-
-static void
-unset_conn (SoupMessage *msg, gpointer user_data)
-{
-	SoupAuthManagerNTLMPrivate *priv = user_data;
-
-	g_hash_table_remove (priv->connections_by_msg, msg);
-	g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
-}
-
-static SoupNTLMConnection *
-set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
-			SoupNTLMConnection *conn)
-{
-	if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
-		g_signal_connect (msg, "finished",
-				  G_CALLBACK (unset_conn), priv);
-		g_signal_connect (msg, "restarted",
-				  G_CALLBACK (unset_conn), priv);
-	}
-	g_hash_table_insert (priv->connections_by_msg, msg, conn);
-
-	return conn;
-}
-
-static SoupNTLMConnection *
-get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
-{
-	return g_hash_table_lookup (priv->connections_by_msg, msg);
-}
-
-#ifdef USE_NTLM_AUTH
-static void
-sso_ntlm_close (SoupNTLMConnection *conn)
-{
-	if (conn->fd_in != -1) {
-		close (conn->fd_in);
-		conn->fd_in = -1;
-	}
-
-	if (conn->fd_out != -1) {
-		close (conn->fd_out);
-		conn->fd_out = -1;
-	}
-}
-
-static gboolean
-sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
-{
-	char *username = NULL, *slash, *domain = NULL;
-	char *argv[9];
-	gboolean ret;
-
-	/* Return if ntlm_auth execution process exist already */
-	if (conn->fd_in != -1 && conn->fd_out != -1)
-		return TRUE;
-	else
-		/* Clean all sso data before re-initiate */
-		sso_ntlm_close (conn);
-
-	if (!priv->ntlm_auth_accessible)
-		goto done;
-
-	username = getenv ("NTLMUSER");
-	if (!username)
-		username = getenv ("USER");
-	if (!username)
-		goto done;
-
-	slash = strpbrk (username, "\\/");
-	if (slash) {
-		domain = g_strdup (username);
-		slash = domain + (slash - username);
-		*slash = '\0';
-		username = slash + 1;
-	}
-
-	argv[0] = NTLM_AUTH;
-	argv[1] = "--helper-protocol";
-	argv[2] = "ntlmssp-client-1";
-	argv[3] = "--use-cached-creds";
-	argv[4] = "--username";
-	argv[5] = username;
-	argv[6] = domain ? "--domain" : NULL;
-	argv[7] = domain;
-	argv[8] = NULL;
-	/* Spawn child process */
-	ret = g_spawn_async_with_pipes (NULL, argv, NULL,
-					G_SPAWN_FILE_AND_ARGV_ZERO,
-					NULL, NULL,
-					NULL, &conn->fd_in, &conn->fd_out,
-					NULL, NULL);
-	if (!ret)
-		goto done;
-	g_free (domain);
-	return TRUE;
-done:
-	g_free (domain);
-	return FALSE;
-}
-
-static char *
-sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
-{
-	ssize_t size;
-	char buf[1024], *response = NULL;
-	char *tmpbuf = buf;
-	size_t	len_in = strlen (input), len_out = sizeof (buf);
-
-	while (len_in > 0) {
-		int written = write (conn->fd_in, input, len_in);
-		if (written == -1) {
-			/* Interrupted by a signal, retry it */
-			if (errno == EINTR)
-				continue;
-			/* write failed if other errors happen */
-			goto done;
-		}
-		input += written;
-		len_in -= written;
-	}
-	/* Read one line */
-	while (len_out > 0) {
-		size = read (conn->fd_out, tmpbuf, len_out);
-		if (size == -1) {
-			if (errno == EINTR)
-				continue;
-			goto done;
-		} else if (size == 0)
-			goto done;
-		else if (tmpbuf[size - 1] == '\n') {
-			tmpbuf[size - 1] = '\0';
-			goto wrfinish;
-		}
-		tmpbuf += size;
-		len_out -= size;
-	}
-	goto done;
-wrfinish:
-	if (conn_state == SOUP_NTLM_NEW &&
-	    g_ascii_strcasecmp (buf, "PW") == 0) {
-		/* Samba/winbind installed but not configured */
-		response = g_strdup ("PW");
-		goto done;
-	}
-	if (conn_state == SOUP_NTLM_NEW &&
-	    g_ascii_strncasecmp (buf, "YR ", 3) != 0)
-		/* invalid response for type 1 message */
-		goto done;
-	if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
-	    g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
-	    g_ascii_strncasecmp (buf, "AF ", 3) != 0)
-		/* invalid response for type 3 message */
-		goto done;
-
-	response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
-	goto done;
-done:
-	return response;
-}
-#endif /* USE_NTLM_AUTH */
-
-static void
-ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-	SoupNTLMConnection *conn;
-	const char *val;
-
-	conn = get_connection_for_msg (priv, msg);
-	if (!conn)
-		return;
-
-	val = soup_message_headers_get_list (msg->response_headers,
-					     "WWW-Authenticate");
-	if (val)
-		val = strstr (val, "NTLM ");
-	if (!val)
-		return;
-
-	if (conn->state > SOUP_NTLM_SENT_REQUEST) {
-		/* We already authenticated, but then got another 401.
-		 * That means "permission denied", so don't try to
-		 * authenticate again.
-		 */
-		conn->state = SOUP_NTLM_FAILED;
-		goto done;
-	}
-
-	if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
-		conn->state = SOUP_NTLM_FAILED;
-		goto done;
-	}
-
-	conn->auth = soup_auth_ntlm_new (conn->domain,
-					 soup_message_get_uri (msg)->host);
-#ifdef USE_NTLM_AUTH
-	conn->challenge_header = g_strdup (val + 5);
-	if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
-		conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
-		goto done;
-	}
-#endif
-	conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
-	soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm), msg,
-					     conn->auth, FALSE);
-
- done:
-	/* Remove the WWW-Authenticate headers so the session won't try
-	 * to do Basic auth too.
-	 */
-	soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
-}
-
-static void
-ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-	SoupNTLMConnection *conn;
-	const char *username = NULL, *password = NULL;
-	char *slash, *domain = NULL;
-
-	conn = get_connection_for_msg (priv, msg);
-	if (!conn || !conn->auth)
-		return;
-
-#ifdef USE_NTLM_AUTH
-	if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
-		char *input;
-		input = g_strdup_printf ("TT %s\n", conn->challenge_header);
-		/* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
-		if (sso_ntlm_initiate (conn, priv)) {
-			conn->response_header = sso_ntlm_response (conn,
-								   input,
-								   conn->state);
-			/* Close ntlm_auth as it is no longer needed for current connection */
-			sso_ntlm_close (conn);
-			if (!conn->response_header) {
-				g_free (input);
-				goto ssofailure;
-			}
-			soup_session_requeue_message (priv->session, msg);
-			g_free (input);
-			goto done;
-		}
-ssofailure:
-		conn->state = SOUP_NTLM_SSO_FAILED;
-		soup_session_requeue_message (priv->session, msg);
-		goto done;
-	}
-#endif
-	username = soup_auth_ntlm_get_username (conn->auth);
-	password = soup_auth_ntlm_get_password (conn->auth);
-	if (!username || !password)
-		goto done;
-
-	slash = strpbrk (username, "\\/");
-	if (slash) {
-		domain = g_strdup (username);
-		slash = domain + (slash - username);
-		*slash = '\0';
-		username = slash + 1;
-	} else
-		domain = conn->domain;
-
-	conn->response_header = soup_ntlm_response (conn->nonce,
-						    username, password,
-						    NULL, domain);
-	soup_session_requeue_message (priv->session, msg);
-
-done:
-	if (domain != conn->domain)
-		g_free (domain);
-	g_free (conn->domain);
-	conn->domain = NULL;
-	g_free (conn->nonce);
-	conn->nonce = NULL;
-	g_object_unref (conn->auth);
-	conn->auth = NULL;
-}
-
-static void
-request_queued (SoupSessionFeature *ntlm, SoupSession *session, SoupMessage *msg)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-
-	if (priv->use_ntlm) {
-		soup_message_add_status_code_handler (
-			msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
-			G_CALLBACK (ntlm_authorize_pre), ntlm);
-		soup_message_add_status_code_handler (
-			msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
-			G_CALLBACK (ntlm_authorize_post), ntlm);
-	}
-
-	soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
-}
-
-static void
-request_started (SoupSessionFeature *ntlm, SoupSession *session,
-		 SoupMessage *msg, SoupSocket *socket)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-	SoupNTLMConnection *conn;
-	char *header = NULL;
-
-	if (!priv->use_ntlm)
-		goto super;
-
-	conn = get_connection (priv, socket);
-	set_connection_for_msg (priv, msg, conn);
-
-	switch (conn->state) {
-	case SOUP_NTLM_NEW:
-#ifdef USE_NTLM_AUTH
-		/* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
-		 * by delegating the NTLM challenge/response protocal to a helper
-		 * in ntlm_auth.
-		 * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
-		 * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
-		 * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
-		 * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
-		 * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
-		 * path of it.
-		 * If NTLM single-sign-on fails, go back to original request handling process.
-		 */
-		if (sso_ntlm_initiate (conn, priv)) {
-			header = sso_ntlm_response (conn, "YR\n", conn->state);
-			if (header) {
-				if (g_ascii_strcasecmp (header, "PW") != 0) {
-					conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
-					break;
-				} else {
-					g_free (header);
-					header = NULL;
-					goto ssofailure;
-				}
-			} else {
-				g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
-				goto ssofailure;
-			}
-		}
-ssofailure:
-#endif
-		header = soup_ntlm_request ();
-		conn->state = SOUP_NTLM_SENT_REQUEST;
-		break;
-#ifdef USE_NTLM_AUTH
-	case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
-		header = conn->response_header;
-		conn->response_header = NULL;
-		conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
-		break;
-	case SOUP_NTLM_SSO_FAILED:
-		/* Restart request without SSO */
-		g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
-		header = soup_ntlm_request ();
-		conn->state = SOUP_NTLM_SENT_REQUEST;
-		break;
-#endif
-	case SOUP_NTLM_RECEIVED_CHALLENGE:
-		header = conn->response_header;
-		conn->response_header = NULL;
-		conn->state = SOUP_NTLM_SENT_RESPONSE;
-		break;
-	default:
-		break;
-	}
-
-	if (header && !soup_message_get_auth (msg)) {
-		soup_message_headers_replace (msg->request_headers,
-					      "Authorization", header);
-		g_free (header);
-	}
-
-super:
-	soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
-}
-
-static void
-request_unqueued (SoupSessionFeature *ntlm, SoupSession *session,
-		  SoupMessage *msg)
-{
-	g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
-	g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
-
-	soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
-}
-
-static gboolean
-add_feature (SoupSessionFeature *feature, GType type)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
-	if (type == SOUP_TYPE_AUTH_NTLM) {
-		priv->use_ntlm = TRUE;
-		return TRUE;
-	}
-
-	return soup_auth_manager_parent_feature_interface->add_feature (feature, type);
-}
-
-static gboolean
-remove_feature (SoupSessionFeature *feature, GType type)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
-	if (type == SOUP_TYPE_AUTH_NTLM) {
-		priv->use_ntlm = FALSE;
-		return TRUE;
-	}
-
-	return soup_auth_manager_parent_feature_interface->remove_feature (feature, type);
-}
-
-static gboolean
-has_feature (SoupSessionFeature *feature, GType type)
-{
-	SoupAuthManagerNTLMPrivate *priv =
-		SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
-	if (type == SOUP_TYPE_AUTH_NTLM)
-		return priv->use_ntlm;
-
-	return soup_auth_manager_parent_feature_interface->has_feature (feature, type);
-}
-
-/* NTLM code */
-
-static void md4sum                (const unsigned char *in, 
-				   int                  nbytes, 
-				   unsigned char        digest[16]);
-
-typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
-
-static void deskey                (DES_KS, unsigned char *, int);
-
-static void des                   (DES_KS, unsigned char *);
-
-static void setup_schedule        (const guchar *key_56, DES_KS ks);
-
-static void calc_response         (const guchar        *key, 
-				   const guchar        *plaintext,
-				   guchar              *results);
-
-#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
-                          "\x4B\x47\x53\x21\x40\x23\x24\x25" \
-			  "\x00\x00\x00\x00\x00"
-
-static void
-lanmanager_hash (const char *password, guchar hash[21])
-{
-	guchar lm_password [15];
-	DES_KS ks;
-	int i;
-
-	for (i = 0; i < 14 && password [i]; i++)
-		lm_password [i] = toupper ((unsigned char) password [i]);
-
-	for (; i < 15; i++)
-		lm_password [i] = '\0';
-
-	memcpy (hash, LM_PASSWORD_MAGIC, 21);
-
-	setup_schedule (lm_password, ks);
-	des (ks, hash);
-
-	setup_schedule (lm_password + 7, ks);
-	des (ks, hash + 8);
-}
-
-static void
-nt_hash (const char *password, guchar hash[21])
-{
-	unsigned char *buf, *p;
-
-	p = buf = g_malloc (strlen (password) * 2);
-
-	while (*password) {
-		*p++ = *password++;
-		*p++ = '\0';
-	}
-
-	md4sum (buf, p - buf, hash);
-	memset (hash + 16, 0, 5);
-
-	g_free (buf);
-}
-
-typedef struct {
-	guint16 length;
-	guint16 length2;
-	guint16 offset;
-	guchar  zero_pad[2];
-} NTLMString;
-
-#define NTLM_CHALLENGE_NONCE_OFFSET         24
-#define NTLM_CHALLENGE_NONCE_LENGTH          8
-#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
-
-#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
-#define NTLM_RESPONSE_FLAGS 0x8202
-
-typedef struct {
-        guchar     header[12];
-
-	NTLMString lm_resp;
-	NTLMString nt_resp;
-	NTLMString domain;
-	NTLMString user;
-	NTLMString host;
-	NTLMString session_key;
-
-        guint32    flags;
-} NTLMResponse;
-
-static void
-ntlm_set_string (NTLMString *string, int *offset, int len)
-{
-	string->offset = GUINT16_TO_LE (*offset);
-	string->length = string->length2 = GUINT16_TO_LE (len);
-	*offset += len;
-}
-
-static char *
-soup_ntlm_request (void)
-{
-	return g_strdup ("NTLM TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
-}
-
-static gboolean
-soup_ntlm_parse_challenge (const char *challenge,
-			   char      **nonce,
-			   char      **default_domain)
-{
-	gsize clen;
-	NTLMString domain;
-	guchar *chall;
-
-	if (strncmp (challenge, "NTLM ", 5) != 0)
-		return FALSE;
-
-	chall = g_base64_decode (challenge + 5, &clen);
-	if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
-	    clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
-		g_free (chall);
-		return FALSE;
-	}
-
-	if (default_domain) {
-		memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
-		domain.length = GUINT16_FROM_LE (domain.length);
-		domain.offset = GUINT16_FROM_LE (domain.offset);
-
-		if (clen < domain.length + domain.offset) {
-			g_free (chall);
-			return FALSE;
-		}
-
-		*default_domain = g_strndup ((char *)chall + domain.offset, domain.length);
-	}
-
-	if (nonce) {
-		*nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
-				   NTLM_CHALLENGE_NONCE_LENGTH);
-	}
-
-	g_free (chall);
-	return TRUE;
-}
-
-static char *
-soup_ntlm_response (const char *nonce, 
-		    const char *user,
-		    const char *password,
-		    const char *host, 
-		    const char *domain)
-{
-	int hlen, dlen, ulen, offset;
-	guchar hash[21], lm_resp[24], nt_resp[24];
-	NTLMResponse resp;
-	char *out, *p;
-	int state, save;
-
-	nt_hash (password, hash);
-	calc_response (hash, (guchar *)nonce, nt_resp);
-	lanmanager_hash (password, hash);
-	calc_response (hash, (guchar *)nonce, lm_resp);
-
-	memset (&resp, 0, sizeof (resp));
-	memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
-	resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
-
-	offset = sizeof (resp);
-
-	dlen = strlen (domain);
-	ntlm_set_string (&resp.domain, &offset, dlen);
-	ulen = strlen (user);
-	ntlm_set_string (&resp.user, &offset, ulen);
-	if (!host)
-		host = "UNKNOWN";
-	hlen = strlen (host);
-	ntlm_set_string (&resp.host, &offset, hlen);
-	ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
-	ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
-
-	out = g_malloc (((offset + 3) * 4) / 3 + 6);
-	strncpy (out, "NTLM ", 5);
-	p = out + 5;
-
-	state = save = 0;
-
-	p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp), 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_step ((const guchar *) domain, dlen, 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_step ((const guchar *) user, ulen, 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_step ((const guchar *) host, hlen, 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_step (lm_resp, sizeof (lm_resp), 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_step (nt_resp, sizeof (nt_resp), 
-				   FALSE, p, &state, &save);
-	p += g_base64_encode_close (FALSE, p, &state, &save);
-	*p = '\0';
-
-	return out;
-}
-
-/* DES utils */
-/* Set up a key schedule based on a 56bit key */
-static void
-setup_schedule (const guchar *key_56, DES_KS ks)
-{
-	guchar key[8];
-	int i, c, bit;
-
-	key[0] = (key_56[0])                                 ;
-	key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
-	key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
-	key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
-	key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
-	key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
-	key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
-	key[7] =                    ((key_56[6] << 1) & 0xFF);
-
-	/* Fix parity */
-	for (i = 0; i < 8; i++) {
-		for (c = bit = 0; bit < 8; bit++)
-			if (key[i] & (1 << bit))
-				c++;
-		if (!(c & 1))
-			key[i] ^= 0x01;
-	}
-
-        deskey (ks, key, 0);
-}
-
-static void
-calc_response (const guchar *key, const guchar *plaintext, guchar *results)
-{
-        DES_KS ks;
-
-	memcpy (results, plaintext, 8);
-	memcpy (results + 8, plaintext, 8);
-	memcpy (results + 16, plaintext, 8);
-
-        setup_schedule (key, ks);
-	des (ks, results);
-
-        setup_schedule (key + 7, ks);
-	des (ks, results + 8);
-
-        setup_schedule (key + 14, ks);
-        des (ks, results + 16);
-}
-
-
-/* 
- * MD4 encoder. (The one everyone else uses is not GPL-compatible;
- * this is a reimplementation from spec.) This doesn't need to be
- * efficient for our purposes, although it would be nice to fix
- * it to not malloc()...
- */
-
-#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
-#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
-#define H(X,Y,Z) ( (X)^(Y)^(Z) )
-#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
-
-static void
-md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
-{
-	unsigned char *M;
-	guint32 A, B, C, D, AA, BB, CC, DD, X[16];
-	int pbytes, nbits = nbytes * 8, i, j;
-
-	pbytes = (120 - (nbytes % 64)) % 64;
-	M = alloca (nbytes + pbytes + 8);
-	memcpy (M, in, nbytes);
-	memset (M + nbytes, 0, pbytes + 8);
-	M[nbytes] = 0x80;
-	M[nbytes + pbytes] = nbits & 0xFF;
-	M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
-	M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
-	M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
-
-	A = 0x67452301;
-	B = 0xEFCDAB89;
-	C = 0x98BADCFE;
-	D = 0x10325476;
-
-	for (i = 0; i < nbytes + pbytes + 8; i += 64) {
-		for (j = 0; j < 16; j++) {
-			X[j] =  (M[i + j*4]) |
-				(M[i + j*4 + 1] << 8) |
-				(M[i + j*4 + 2] << 16) |
-				(M[i + j*4 + 3] << 24);
-		}
-
-		AA = A;
-		BB = B;
-		CC = C;
-		DD = D;
-
-		A = ROT (A + F(B, C, D) + X[0], 3);
-		D = ROT (D + F(A, B, C) + X[1], 7);
-		C = ROT (C + F(D, A, B) + X[2], 11);
-		B = ROT (B + F(C, D, A) + X[3], 19);
-		A = ROT (A + F(B, C, D) + X[4], 3);
-		D = ROT (D + F(A, B, C) + X[5], 7);
-		C = ROT (C + F(D, A, B) + X[6], 11);
-		B = ROT (B + F(C, D, A) + X[7], 19);
-		A = ROT (A + F(B, C, D) + X[8], 3);
-		D = ROT (D + F(A, B, C) + X[9], 7);
-		C = ROT (C + F(D, A, B) + X[10], 11);
-		B = ROT (B + F(C, D, A) + X[11], 19);
-		A = ROT (A + F(B, C, D) + X[12], 3);
-		D = ROT (D + F(A, B, C) + X[13], 7);
-		C = ROT (C + F(D, A, B) + X[14], 11);
-		B = ROT (B + F(C, D, A) + X[15], 19);
-
-		A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
-		D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
-		C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
-		B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
-		A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
-		D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
-		C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
-		B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
-		A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
-		D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
-		C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
-		B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
-		A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
-		D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
-		C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
-		B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
-
-		A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
-		D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
-		C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
-		B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
-		A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
-		D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
-		C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
-		B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
-		A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
-		D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
-		C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
-		B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
-		A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
-		D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
-		C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
-		B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
-
-		A += AA;
-		B += BB;
-		C += CC;
-		D += DD;
-	}
-
-	digest[0]  =  A        & 0xFF;
-	digest[1]  = (A >>  8) & 0xFF;
-	digest[2]  = (A >> 16) & 0xFF;
-	digest[3]  = (A >> 24) & 0xFF;
-	digest[4]  =  B        & 0xFF;
-	digest[5]  = (B >>  8) & 0xFF;
-	digest[6]  = (B >> 16) & 0xFF;
-	digest[7]  = (B >> 24) & 0xFF;
-	digest[8]  =  C        & 0xFF;
-	digest[9]  = (C >>  8) & 0xFF;
-	digest[10] = (C >> 16) & 0xFF;
-	digest[11] = (C >> 24) & 0xFF;
-	digest[12] =  D        & 0xFF;
-	digest[13] = (D >>  8) & 0xFF;
-	digest[14] = (D >> 16) & 0xFF;
-	digest[15] = (D >> 24) & 0xFF;
-}
-
-
-/* Public domain DES implementation from Phil Karn */
-static const guint32 Spbox[8][64] = {
-	{ 0x01010400,0x00000000,0x00010000,0x01010404,
-	  0x01010004,0x00010404,0x00000004,0x00010000,
-	  0x00000400,0x01010400,0x01010404,0x00000400,
-	  0x01000404,0x01010004,0x01000000,0x00000004,
-	  0x00000404,0x01000400,0x01000400,0x00010400,
-	  0x00010400,0x01010000,0x01010000,0x01000404,
-	  0x00010004,0x01000004,0x01000004,0x00010004,
-	  0x00000000,0x00000404,0x00010404,0x01000000,
-	  0x00010000,0x01010404,0x00000004,0x01010000,
-	  0x01010400,0x01000000,0x01000000,0x00000400,
-	  0x01010004,0x00010000,0x00010400,0x01000004,
-	  0x00000400,0x00000004,0x01000404,0x00010404,
-	  0x01010404,0x00010004,0x01010000,0x01000404,
-	  0x01000004,0x00000404,0x00010404,0x01010400,
-	  0x00000404,0x01000400,0x01000400,0x00000000,
-	  0x00010004,0x00010400,0x00000000,0x01010004 },
-	{ 0x80108020,0x80008000,0x00008000,0x00108020,
-	  0x00100000,0x00000020,0x80100020,0x80008020,
-	  0x80000020,0x80108020,0x80108000,0x80000000,
-	  0x80008000,0x00100000,0x00000020,0x80100020,
-	  0x00108000,0x00100020,0x80008020,0x00000000,
-	  0x80000000,0x00008000,0x00108020,0x80100000,
-	  0x00100020,0x80000020,0x00000000,0x00108000,
-	  0x00008020,0x80108000,0x80100000,0x00008020,
-	  0x00000000,0x00108020,0x80100020,0x00100000,
-	  0x80008020,0x80100000,0x80108000,0x00008000,
-	  0x80100000,0x80008000,0x00000020,0x80108020,
-	  0x00108020,0x00000020,0x00008000,0x80000000,
-	  0x00008020,0x80108000,0x00100000,0x80000020,
-	  0x00100020,0x80008020,0x80000020,0x00100020,
-	  0x00108000,0x00000000,0x80008000,0x00008020,
-	  0x80000000,0x80100020,0x80108020,0x00108000 },
-	{ 0x00000208,0x08020200,0x00000000,0x08020008,
-	  0x08000200,0x00000000,0x00020208,0x08000200,
-	  0x00020008,0x08000008,0x08000008,0x00020000,
-	  0x08020208,0x00020008,0x08020000,0x00000208,
-	  0x08000000,0x00000008,0x08020200,0x00000200,
-	  0x00020200,0x08020000,0x08020008,0x00020208,
-	  0x08000208,0x00020200,0x00020000,0x08000208,
-	  0x00000008,0x08020208,0x00000200,0x08000000,
-	  0x08020200,0x08000000,0x00020008,0x00000208,
-	  0x00020000,0x08020200,0x08000200,0x00000000,
-	  0x00000200,0x00020008,0x08020208,0x08000200,
-	  0x08000008,0x00000200,0x00000000,0x08020008,
-	  0x08000208,0x00020000,0x08000000,0x08020208,
-	  0x00000008,0x00020208,0x00020200,0x08000008,
-	  0x08020000,0x08000208,0x00000208,0x08020000,
-	  0x00020208,0x00000008,0x08020008,0x00020200 },
-	{ 0x00802001,0x00002081,0x00002081,0x00000080,
-	  0x00802080,0x00800081,0x00800001,0x00002001,
-	  0x00000000,0x00802000,0x00802000,0x00802081,
-	  0x00000081,0x00000000,0x00800080,0x00800001,
-	  0x00000001,0x00002000,0x00800000,0x00802001,
-	  0x00000080,0x00800000,0x00002001,0x00002080,
-	  0x00800081,0x00000001,0x00002080,0x00800080,
-	  0x00002000,0x00802080,0x00802081,0x00000081,
-	  0x00800080,0x00800001,0x00802000,0x00802081,
-	  0x00000081,0x00000000,0x00000000,0x00802000,
-	  0x00002080,0x00800080,0x00800081,0x00000001,
-	  0x00802001,0x00002081,0x00002081,0x00000080,
-	  0x00802081,0x00000081,0x00000001,0x00002000,
-	  0x00800001,0x00002001,0x00802080,0x00800081,
-	  0x00002001,0x00002080,0x00800000,0x00802001,
-	  0x00000080,0x00800000,0x00002000,0x00802080 },
-	{ 0x00000100,0x02080100,0x02080000,0x42000100,
-	  0x00080000,0x00000100,0x40000000,0x02080000,
-	  0x40080100,0x00080000,0x02000100,0x40080100,
-	  0x42000100,0x42080000,0x00080100,0x40000000,
-	  0x02000000,0x40080000,0x40080000,0x00000000,
-	  0x40000100,0x42080100,0x42080100,0x02000100,
-	  0x42080000,0x40000100,0x00000000,0x42000000,
-	  0x02080100,0x02000000,0x42000000,0x00080100,
-	  0x00080000,0x42000100,0x00000100,0x02000000,
-	  0x40000000,0x02080000,0x42000100,0x40080100,
-	  0x02000100,0x40000000,0x42080000,0x02080100,
-	  0x40080100,0x00000100,0x02000000,0x42080000,
-	  0x42080100,0x00080100,0x42000000,0x42080100,
-	  0x02080000,0x00000000,0x40080000,0x42000000,
-	  0x00080100,0x02000100,0x40000100,0x00080000,
-	  0x00000000,0x40080000,0x02080100,0x40000100 },
-	{ 0x20000010,0x20400000,0x00004000,0x20404010,
-	  0x20400000,0x00000010,0x20404010,0x00400000,
-	  0x20004000,0x00404010,0x00400000,0x20000010,
-	  0x00400010,0x20004000,0x20000000,0x00004010,
-	  0x00000000,0x00400010,0x20004010,0x00004000,
-	  0x00404000,0x20004010,0x00000010,0x20400010,
-	  0x20400010,0x00000000,0x00404010,0x20404000,
-	  0x00004010,0x00404000,0x20404000,0x20000000,
-	  0x20004000,0x00000010,0x20400010,0x00404000,
-	  0x20404010,0x00400000,0x00004010,0x20000010,
-	  0x00400000,0x20004000,0x20000000,0x00004010,
-	  0x20000010,0x20404010,0x00404000,0x20400000,
-	  0x00404010,0x20404000,0x00000000,0x20400010,
-	  0x00000010,0x00004000,0x20400000,0x00404010,
-	  0x00004000,0x00400010,0x20004010,0x00000000,
-	  0x20404000,0x20000000,0x00400010,0x20004010 },
-	{ 0x00200000,0x04200002,0x04000802,0x00000000,
-	  0x00000800,0x04000802,0x00200802,0x04200800,
-	  0x04200802,0x00200000,0x00000000,0x04000002,
-	  0x00000002,0x04000000,0x04200002,0x00000802,
-	  0x04000800,0x00200802,0x00200002,0x04000800,
-	  0x04000002,0x04200000,0x04200800,0x00200002,
-	  0x04200000,0x00000800,0x00000802,0x04200802,
-	  0x00200800,0x00000002,0x04000000,0x00200800,
-	  0x04000000,0x00200800,0x00200000,0x04000802,
-	  0x04000802,0x04200002,0x04200002,0x00000002,
-	  0x00200002,0x04000000,0x04000800,0x00200000,
-	  0x04200800,0x00000802,0x00200802,0x04200800,
-	  0x00000802,0x04000002,0x04200802,0x04200000,
-	  0x00200800,0x00000000,0x00000002,0x04200802,
-	  0x00000000,0x00200802,0x04200000,0x00000800,
-	  0x04000002,0x04000800,0x00000800,0x00200002 },
-	{ 0x10001040,0x00001000,0x00040000,0x10041040,
-	  0x10000000,0x10001040,0x00000040,0x10000000,
-	  0x00040040,0x10040000,0x10041040,0x00041000,
-	  0x10041000,0x00041040,0x00001000,0x00000040,
-	  0x10040000,0x10000040,0x10001000,0x00001040,
-	  0x00041000,0x00040040,0x10040040,0x10041000,
-	  0x00001040,0x00000000,0x00000000,0x10040040,
-	  0x10000040,0x10001000,0x00041040,0x00040000,
-	  0x00041040,0x00040000,0x10041000,0x00001000,
-	  0x00000040,0x10040040,0x00001000,0x00041040,
-	  0x10001000,0x00000040,0x10000040,0x10040000,
-	  0x10040040,0x10000000,0x00040000,0x10001040,
-	  0x00000000,0x10041040,0x00040040,0x10000040,
-	  0x10040000,0x10001000,0x10001040,0x00000000,
-	  0x10041040,0x00041000,0x00041000,0x00001040,
-	  0x00001040,0x00040040,0x10000000,0x10041000 }
-};
-
-#undef F
-#define	F(l,r,key){\
-	work = ((r >> 4) | (r << 28)) ^ key[0];\
-	l ^= Spbox[6][work & 0x3f];\
-	l ^= Spbox[4][(work >> 8) & 0x3f];\
-	l ^= Spbox[2][(work >> 16) & 0x3f];\
-	l ^= Spbox[0][(work >> 24) & 0x3f];\
-	work = r ^ key[1];\
-	l ^= Spbox[7][work & 0x3f];\
-	l ^= Spbox[5][(work >> 8) & 0x3f];\
-	l ^= Spbox[3][(work >> 16) & 0x3f];\
-	l ^= Spbox[1][(work >> 24) & 0x3f];\
-}
-/* Encrypt or decrypt a block of data in ECB mode */
-static void
-des (guint32 ks[16][2], unsigned char block[8])
-{
-	guint32 left,right,work;
-	
-	/* Read input block and place in left/right in big-endian order */
-	left = ((guint32)block[0] << 24)
-	 | ((guint32)block[1] << 16)
-	 | ((guint32)block[2] << 8)
-	 | (guint32)block[3];
-	right = ((guint32)block[4] << 24)
-	 | ((guint32)block[5] << 16)
-	 | ((guint32)block[6] << 8)
-	 | (guint32)block[7];
-
-	/* Hoey's clever initial permutation algorithm, from Outerbridge
-	 * (see Schneier p 478)	
-	 *
-	 * The convention here is the same as Outerbridge: rotate each
-	 * register left by 1 bit, i.e., so that "left" contains permuted
-	 * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32	
-	 * (using origin-1 numbering as in the FIPS). This allows us to avoid
-	 * one of the two rotates that would otherwise be required in each of
-	 * the 16 rounds.
-	 */
-	work = ((left >> 4) ^ right) & 0x0f0f0f0f;
-	right ^= work;
-	left ^= work << 4;
-	work = ((left >> 16) ^ right) & 0xffff;
-	right ^= work;
-	left ^= work << 16;
-	work = ((right >> 2) ^ left) & 0x33333333;
-	left ^= work;
-	right ^= (work << 2);
-	work = ((right >> 8) ^ left) & 0xff00ff;
-	left ^= work;
-	right ^= (work << 8);
-	right = (right << 1) | (right >> 31);
-	work = (left ^ right) & 0xaaaaaaaa;
-	left ^= work;
-	right ^= work;
-	left = (left << 1) | (left >> 31);
-
-	/* Now do the 16 rounds */
-	F(left,right,ks[0]);
-	F(right,left,ks[1]);
-	F(left,right,ks[2]);
-	F(right,left,ks[3]);
-	F(left,right,ks[4]);
-	F(right,left,ks[5]);
-	F(left,right,ks[6]);
-	F(right,left,ks[7]);
-	F(left,right,ks[8]);
-	F(right,left,ks[9]);
-	F(left,right,ks[10]);
-	F(right,left,ks[11]);
-	F(left,right,ks[12]);
-	F(right,left,ks[13]);
-	F(left,right,ks[14]);
-	F(right,left,ks[15]);
-
-	/* Inverse permutation, also from Hoey via Outerbridge and Schneier */
-	right = (right << 31) | (right >> 1);
-	work = (left ^ right) & 0xaaaaaaaa;
-	left ^= work;
-	right ^= work;
-	left = (left >> 1) | (left  << 31);
-	work = ((left >> 8) ^ right) & 0xff00ff;
-	right ^= work;
-	left ^= work << 8;
-	work = ((left >> 2) ^ right) & 0x33333333;
-	right ^= work;
-	left ^= work << 2;
-	work = ((right >> 16) ^ left) & 0xffff;
-	left ^= work;
-	right ^= work << 16;
-	work = ((right >> 4) ^ left) & 0x0f0f0f0f;
-	left ^= work;
-	right ^= work << 4;
-
-	/* Put the block back into the user's buffer with final swap */
-	block[0] = right >> 24;
-	block[1] = right >> 16;
-	block[2] = right >> 8;
-	block[3] = right;
-	block[4] = left >> 24;
-	block[5] = left >> 16;
-	block[6] = left >> 8;
-	block[7] = left;
-}
-
-/* Key schedule-related tables from FIPS-46 */
-
-/* permuted choice table (key) */
-static const unsigned char pc1[] = {
-	57, 49, 41, 33, 25, 17,  9,
-	 1, 58, 50, 42, 34, 26, 18,
-	10,  2, 59, 51, 43, 35, 27,
-	19, 11,  3, 60, 52, 44, 36,
-
-	63, 55, 47, 39, 31, 23, 15,
-	 7, 62, 54, 46, 38, 30, 22,
-	14,  6, 61, 53, 45, 37, 29,
-	21, 13,  5, 28, 20, 12,  4
-};
-
-/* number left rotations of pc1 */
-static const unsigned char totrot[] = {
-	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
-};
-
-/* permuted choice key (table) */
-static const unsigned char pc2[] = {
-	14, 17, 11, 24,  1,  5,
-	 3, 28, 15,  6, 21, 10,
-	23, 19, 12,  4, 26,  8,
-	16,  7, 27, 20, 13,  2,
-	41, 52, 31, 37, 47, 55,
-	30, 40, 51, 45, 33, 48,
-	44, 49, 39, 56, 34, 53,
-	46, 42, 50, 36, 29, 32
-};
-
-/* End of DES-defined tables */
-
-
-/* bit 0 is left-most in byte */
-static const int bytebit[] = {
-	0200,0100,040,020,010,04,02,01
-};
-
-
-/* Generate key schedule for encryption or decryption
- * depending on the value of "decrypt"
- */
-static void
-deskey (DES_KS k, unsigned char *key, int decrypt)
-{
-	unsigned char pc1m[56];		/* place to modify pc1 into */
-	unsigned char pcr[56];		/* place to rotate pc1 into */
-	register int i,j,l;
-	int m;
-	unsigned char ks[8];
-
-	for (j=0; j<56; j++) {		/* convert pc1 to bits of key */
-		l=pc1[j]-1;		/* integer bit location	 */
-		m = l & 07;		/* find bit		 */
-		pc1m[j]=(key[l>>3] &	/* find which key byte l is in */
-			bytebit[m])	/* and which bit of that byte */
-			? 1 : 0;	/* and store 1-bit result */
-	}
-	for (i=0; i<16; i++) {		/* key chunk for each iteration */
-		memset(ks,0,sizeof(ks));	/* Clear key schedule */
-		for (j=0; j<56; j++)	/* rotate pc1 the right amount */
-			pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
-			/* rotate left and right halves independently */
-		for (j=0; j<48; j++){	/* select bits individually */
-			/* check bit that goes to ks[j] */
-			if (pcr[pc2[j]-1]){
-				/* mask it in if it's there */
-				l= j % 6;
-				ks[j/6] |= bytebit[l] >> 2;
-			}
-		}
-		/* Now convert to packed odd/even interleaved form */
-		k[i][0] = ((guint32)ks[0] << 24)
-		 | ((guint32)ks[2] << 16)
-		 | ((guint32)ks[4] << 8)
-		 | ((guint32)ks[6]);
-		k[i][1] = ((guint32)ks[1] << 24)
-		 | ((guint32)ks[3] << 16)
-		 | ((guint32)ks[5] << 8)
-		 | ((guint32)ks[7]);
-	}
-}
diff --git a/libsoup/soup-auth-manager-ntlm.h b/libsoup/soup-auth-manager-ntlm.h
deleted file mode 100644
index f0b4f57..0000000
--- a/libsoup/soup-auth-manager-ntlm.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifndef SOUP_AUTH_MANAGER_NTLM_H
-#define SOUP_AUTH_MANAGER_NTLM_H 1
-
-#include "soup-auth-manager.h"
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_AUTH_MANAGER_NTLM            (soup_auth_manager_ntlm_get_type ())
-#define SOUP_AUTH_MANAGER_NTLM(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLM))
-#define SOUP_AUTH_MANAGER_NTLM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
-#define SOUP_IS_AUTH_MANAGER_NTLM(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER_NTLM))
-#define SOUP_IS_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM))
-#define SOUP_AUTH_MANAGER_NTLM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
-
-typedef struct {
-	SoupAuthManager parent;
-
-} SoupAuthManagerNTLM;
-
-typedef struct {
-	SoupAuthManagerClass parent_class;
-
-} SoupAuthManagerNTLMClass;
-
-#define SOUP_AUTH_MANAGER_NTLM_USE_NTLM "use-ntlm"
-
-GType soup_auth_manager_ntlm_get_type (void);
-
-G_END_DECLS
-
-#endif /* SOUP_AUTH_MANAGER_NTLM_NTLM_H */
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index cc0f3c9..315a775 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -22,6 +22,9 @@
 #include "soup-session-feature.h"
 #include "soup-session-private.h"
 #include "soup-uri.h"
+#include "soup-auth-connbased.h"
+#include "soup-auth-ntlm.h"
+#include "soup-auth-negotiate.h"
 
 static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
 static SoupSessionFeatureInterface *soup_session_feature_default_interface;
@@ -33,9 +36,6 @@ static void request_started  (SoupSessionFeature *feature, SoupSession *session,
 			      SoupMessage *msg, SoupSocket *socket);
 static void request_unqueued  (SoupSessionFeature *feature,
 			       SoupSession *session, SoupMessage *msg);
-static gboolean add_feature (SoupSessionFeature *feature, GType type);
-static gboolean remove_feature (SoupSessionFeature *feature, GType type);
-static gboolean has_feature (SoupSessionFeature *feature, GType type);
 
 enum {
 	AUTHENTICATE,
@@ -47,6 +47,10 @@ static guint signals[LAST_SIGNAL] = { 0 };
 G_DEFINE_TYPE_WITH_CODE (SoupAuthManager, soup_auth_manager, G_TYPE_OBJECT,
 			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
 						soup_auth_manager_session_feature_init))
+typedef struct {
+	SoupSocket *socket;
+	SoupAuthConnBased *auth;
+} SoupAuthConnection;
 
 typedef struct {
 	SoupSession *session;
@@ -54,6 +58,12 @@ typedef struct {
 
 	SoupAuth *proxy_auth;
 	GHashTable *auth_hosts;
+
+	GHashTable *connections_by_msg;
+	GHashTable *connections_by_id;
+
+	GType use_auth_type;
+	gchar *trusted_hosts;
 } SoupAuthManagerPrivate;
 #define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
 
@@ -71,6 +81,22 @@ soup_auth_manager_init (SoupAuthManager *manager)
 	priv->auth_types = g_ptr_array_new ();
 	priv->auth_hosts = g_hash_table_new (soup_uri_host_hash,
 					     soup_uri_host_equal);
+	priv->connections_by_id = g_hash_table_new (NULL, NULL);
+	priv->connections_by_msg = g_hash_table_new (NULL, NULL);
+}
+
+static void
+free_connection (SoupAuthConnection *conn)
+{
+	if (conn->auth)
+		g_object_unref (conn->auth);
+	g_slice_free (SoupAuthConnection, conn);
+}
+
+static void
+free_connection_foreach (gpointer key, gpointer value, gpointer user_data)
+{
+	free_connection (value);
 }
 
 static gboolean
@@ -105,6 +131,13 @@ finalize (GObject *object)
 	if (priv->proxy_auth)
 		g_object_unref (priv->proxy_auth);
 
+	g_hash_table_foreach (priv->connections_by_id,
+			      free_connection_foreach, NULL);
+	g_hash_table_destroy (priv->connections_by_id);
+	g_hash_table_destroy (priv->connections_by_msg);
+
+	g_free (priv->trusted_hosts);
+
 	G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object);
 }
 
@@ -142,9 +175,6 @@ soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_int
 	feature_interface->request_queued = request_queued;
 	feature_interface->request_started = request_started;
 	feature_interface->request_unqueued = request_unqueued;
-	feature_interface->add_feature = add_feature;
-	feature_interface->remove_feature = remove_feature;
-	feature_interface->has_feature = has_feature;
 }
 
 static int
@@ -156,52 +186,46 @@ auth_type_compare_func (gconstpointer a, gconstpointer b)
 	return (*auth1)->strength - (*auth2)->strength;
 }
 
-static gboolean
-add_feature (SoupSessionFeature *feature, GType type)
+void
+soup_auth_manager_add_type (SoupAuthManager *manager, GType type)
 {
-	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
 	SoupAuthClass *auth_class;
 
-	if (!g_type_is_a (type, SOUP_TYPE_AUTH))
-		return FALSE;
+	g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
 
 	auth_class = g_type_class_ref (type);
 	g_ptr_array_add (priv->auth_types, auth_class);
 	g_ptr_array_sort (priv->auth_types, auth_type_compare_func);
-	return TRUE;
 }
 
-static gboolean
-remove_feature (SoupSessionFeature *feature, GType type)
+void
+soup_auth_manager_remove_type (SoupAuthManager *manager, GType type)
 {
-	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
 	SoupAuthClass *auth_class;
 	int i;
 
-	if (!g_type_is_a (type, SOUP_TYPE_AUTH))
-		return FALSE;
+	g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
 
 	auth_class = g_type_class_peek (type);
 	for (i = 0; i < priv->auth_types->len; i++) {
 		if (priv->auth_types->pdata[i] == (gpointer)auth_class) {
 			g_ptr_array_remove_index (priv->auth_types, i);
 			g_type_class_unref (auth_class);
-			return TRUE;
+			return;
 		}
 	}
-
-	return FALSE;
 }
 
-static gboolean
-has_feature (SoupSessionFeature *feature, GType type)
+gboolean
+soup_auth_manager_has_type (SoupAuthManager *manager, GType type)
 {
-	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
 	SoupAuthClass *auth_class;
 	int i;
 
-	if (!g_type_is_a (type, SOUP_TYPE_AUTH))
-		return FALSE;
+	g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), FALSE);
 
 	auth_class = g_type_class_peek (type);
 	for (i = 0; i < priv->auth_types->len; i++) {
@@ -218,6 +242,29 @@ soup_auth_manager_emit_authenticate (SoupAuthManager *manager, SoupMessage *msg,
 	g_signal_emit (manager, signals[AUTHENTICATE], 0, msg, auth, retrying);
 }
 
+void
+soup_auth_manager_set_auth (SoupAuthManager *manager,
+			    GType            type,
+			    const gchar     *hosts)
+{
+	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+	g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH_CONNBASED));
+	g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH_NTLM) || (hosts != NULL));
+
+	priv->use_auth_type = type;
+	if (hosts)
+		priv->trusted_hosts = g_strdup (hosts);
+}
+
+GType
+soup_auth_manager_get_auth (SoupAuthManager *manager)
+{
+	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+	return priv->use_auth_type;
+}
+
 static void
 attach (SoupSessionFeature *manager, SoupSession *session)
 {
@@ -241,6 +288,72 @@ auth_header_for_message (SoupMessage *msg)
 	}
 }
 
+static void
+delete_conn (SoupSocket *socket, gpointer user_data)
+{
+	SoupAuthManagerPrivate *priv = user_data;
+	SoupAuthConnection *conn;
+
+	conn = g_hash_table_lookup (priv->connections_by_id, socket);
+	if (conn)
+		free_connection (conn);
+	g_hash_table_remove (priv->connections_by_id, socket);
+	g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
+}
+
+static SoupAuthConnection *
+get_connection (SoupAuthManagerPrivate *priv, SoupSocket *socket,
+		GType type)
+{
+	SoupAuthConnection *conn;
+
+	conn = g_hash_table_lookup (priv->connections_by_id, socket);
+	if (conn)
+		return conn;
+
+	conn = g_slice_new0 (SoupAuthConnection);
+	conn->socket = socket;
+	conn->auth = g_object_new (type, NULL);
+	if (g_type_is_a (type, SOUP_TYPE_AUTH_NEGOTIATE))
+		soup_auth_negotiate_set_hosts (SOUP_AUTH (conn->auth), priv->trusted_hosts);
+
+	g_hash_table_insert (priv->connections_by_id, socket, conn);
+
+	g_signal_connect (socket, "disconnected",
+			  G_CALLBACK (delete_conn), priv);
+	return conn;
+}
+
+static void
+unset_conn (SoupMessage *msg, gpointer user_data)
+{
+	SoupAuthManagerPrivate *priv = user_data;
+
+	g_hash_table_remove (priv->connections_by_msg, msg);
+	g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
+}
+
+static SoupAuthConnection *
+set_connection_for_msg (SoupAuthManagerPrivate *priv, SoupMessage *msg,
+			SoupAuthConnection *conn)
+{
+	if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
+		g_signal_connect (msg, "finished",
+				  G_CALLBACK (unset_conn), priv);
+		g_signal_connect (msg, "restarted",
+				  G_CALLBACK (unset_conn), priv);
+	}
+	g_hash_table_insert (priv->connections_by_msg, msg, conn);
+
+	return conn;
+}
+
+static SoupAuthConnection *
+get_connection_for_msg (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+	return g_hash_table_lookup (priv->connections_by_msg, msg);
+}
+
 static char *
 extract_challenge (const char *challenges, const char *scheme)
 {
@@ -431,6 +544,22 @@ update_auth (SoupMessage *msg, gpointer manager)
 	char *auth_info, *old_auth_info;
 	GSList *pspace, *p;
 	gboolean prior_auth_failed = FALSE;
+	SoupAuthConnection *conn;
+
+	conn = get_connection_for_msg (priv, msg);
+	if (conn) {
+		if (soup_auth_connbased_authorize_pre (msg, conn->auth) == FALSE)
+			goto done;
+#ifndef USE_NTLM_AUTH
+		soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (manager), msg,
+						     SOUP_AUTH (conn->auth), FALSE);
+#endif
+done:
+		/* Remove the WWW-Authenticate headers so the session won't try
+		 * to do Basic auth too.
+		 */
+		soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
+	}
 
 	host = get_auth_host_for_message (priv, msg);
 
@@ -494,6 +623,17 @@ requeue_if_authenticated (SoupMessage *msg, gpointer manager)
 {
 	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
 	SoupAuth *auth = lookup_auth (priv, msg);
+	SoupAuthConnection *conn;
+
+	conn = get_connection_for_msg (priv, msg);
+	if (conn) {
+		const char *header = NULL;
+
+		soup_auth_connbased_authorize_post (msg, conn->auth);
+		header = soup_auth_connbased_get_response_header (conn->auth);
+		if (header != NULL)
+			soup_session_requeue_message (priv->session, msg);
+	}
 
 	if (auth && soup_auth_is_authenticated (auth))
 		soup_session_requeue_message (priv->session, msg);
@@ -561,6 +701,14 @@ request_started (SoupSessionFeature *feature, SoupSession *session,
 	SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
 	SoupAuth *auth;
 
+	if (priv->use_auth_type) {
+		SoupAuthConnection *conn;
+		conn = get_connection (priv, socket, priv->use_auth_type);
+		set_connection_for_msg (priv, msg, conn);
+
+		soup_auth_connbased_authorize_request_started (msg, conn->auth);
+	}
+
 	auth = lookup_auth (priv, msg);
 	if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE))
 		auth = NULL;
@@ -570,6 +718,7 @@ request_started (SoupSessionFeature *feature, SoupSession *session,
 	if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE))
 		auth = NULL;
 	soup_message_set_proxy_auth (msg, auth);
+
 }
 
 static void
diff --git a/libsoup/soup-auth-manager.h b/libsoup/soup-auth-manager.h
index 493960a..a29c85d 100644
--- a/libsoup/soup-auth-manager.h
+++ b/libsoup/soup-auth-manager.h
@@ -32,11 +32,21 @@ typedef struct {
 
 GType soup_auth_manager_get_type (void);
 
+void soup_auth_manager_add_type          (SoupAuthManager *manager,
+					  GType            type);
+void soup_auth_manager_remove_type       (SoupAuthManager *manager,
+					  GType            type);
+gboolean soup_auth_manager_has_type      (SoupAuthManager *manager,
+					  GType            type);
+
 void soup_auth_manager_emit_authenticate (SoupAuthManager *manager,
 					  SoupMessage     *msg,
 					  SoupAuth        *auth,
 					  gboolean         retrying);
-
+void soup_auth_manager_set_auth          (SoupAuthManager *manager,
+					  GType            type,
+					  const gchar     *hosts);
+GType soup_auth_manager_get_auth         (SoupAuthManager *manager);
 G_END_DECLS
 
 #endif /* SOUP_AUTH_MANAGER_H */
diff --git a/libsoup/soup-auth-ntlm.c b/libsoup/soup-auth-ntlm.c
index 6383311..3d265c0 100644
--- a/libsoup/soup-auth-ntlm.c
+++ b/libsoup/soup-auth-ntlm.c
@@ -9,11 +9,18 @@
 #include <config.h>
 #endif
 
+#include <glib-object.h>
+#ifdef USE_NTLM_AUTH
+#include <stdlib.h>
+#include <errno.h>
+#endif
+#include <ctype.h>
 #include <string.h>
 
 #include "soup-auth-ntlm.h"
 #include "soup-headers.h"
 #include "soup-message.h"
+#include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-uri.h"
 
@@ -23,16 +30,68 @@ static void authenticate (SoupAuth *auth, const char *username, const char *pass
 static gboolean is_authenticated (SoupAuth *auth);
 static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
 
+static gboolean authorize_pre (SoupMessage *msg, SoupAuthConnBased *auth);
+static void authorize_post (SoupMessage *msg, SoupAuthConnBased *auth);
+static void authorize_request_started (SoupMessage *msg, SoupAuthConnBased *auth);
+static const char *get_response_header (SoupAuthConnBased *auth);
+
+typedef enum {
+	SOUP_NTLM_NEW,
+#ifdef USE_NTLM_AUTH
+	SOUP_NTLM_SENT_SSO_REQUEST,
+	SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
+	SOUP_NTLM_SENT_SSO_RESPONSE,
+	SOUP_NTLM_SSO_FAILED,
+#endif
+	SOUP_NTLM_SENT_REQUEST,
+	SOUP_NTLM_RECEIVED_CHALLENGE,
+	SOUP_NTLM_SENT_RESPONSE,
+	SOUP_NTLM_FAILED
+} SoupNTLMState;
+
 typedef struct {
+	SoupNTLMState state;
+	char *response_header;
+
+	char *nonce, *domain;
+#ifdef USE_NTLM_AUTH
+	char *challenge_header;
+	int fd_in;
+	int fd_out;
+	gboolean ntlm_auth_accessible;
+#endif
 	char *username, *password;
 } SoupAuthNTLMPrivate;
 #define SOUP_AUTH_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMPrivate))
 
-G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH)
+static char     *soup_ntlm_request         (void);
+static gboolean  soup_ntlm_parse_challenge (const char  *challenge,
+					    char       **nonce,
+					    char       **default_domain);
+static char     *soup_ntlm_response        (const char  *nonce,
+					    const char  *user,
+					    const char  *password,
+					    const char  *host,
+					    const char  *domain);
+
+#ifdef USE_NTLM_AUTH
+static void sso_ntlm_close (SoupAuthNTLMPrivate *priv);
+#endif
+
+G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH_CONNBASED)
 
 static void
 soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
 {
+	SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
+
+	priv->state = SOUP_NTLM_NEW;
+#ifdef USE_NTLM_AUTH
+	priv->fd_in = -1;
+	priv->fd_out = -1;
+	priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
+#endif
+
 }
 
 static void
@@ -41,17 +100,27 @@ finalize (GObject *object)
 	SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (object);
 
 	g_free (priv->username);
+
 	if (priv->password) {
 		memset (priv->password, 0, strlen (priv->password));
 		g_free (priv->password);
 	}
 
+	g_free (priv->response_header);
+	g_free (priv->nonce);
+	g_free (priv->domain);
+#ifdef USE_NTLM_AUTH
+	g_free (priv->challenge_header);
+	sso_ntlm_close (priv);
+#endif
+
 	G_OBJECT_CLASS (soup_auth_ntlm_parent_class)->finalize (object);
 }
 
 static void
 soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
 {
+	SoupAuthConnBasedClass *auth_connbased_class = SOUP_AUTH_CONNBASED_CLASS (auth_ntlm_class);
 	SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_ntlm_class);
 	GObjectClass *object_class = G_OBJECT_CLASS (auth_ntlm_class);
 
@@ -66,17 +135,20 @@ soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
 	auth_class->is_authenticated = is_authenticated;
 	auth_class->get_authorization = get_authorization;
 
+	auth_connbased_class->authorize_pre = authorize_pre;
+	auth_connbased_class->authorize_post = authorize_post;
+	auth_connbased_class->authorize_request_started = authorize_request_started;
+	auth_connbased_class->get_response_header = get_response_header;
+
 	object_class->finalize = finalize;
 }
 
-SoupAuth *
-soup_auth_ntlm_new (const char *realm, const char *host)
+SoupAuthConnBased *
+soup_auth_ntlm_new (void)
 {
-	SoupAuth *auth;
+	SoupAuthConnBased *auth;
 
 	auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
-			     SOUP_AUTH_REALM, realm,
-			     SOUP_AUTH_HOST, host,
 			     NULL);
 	return auth;
 }
@@ -132,3 +204,1020 @@ soup_auth_ntlm_get_password (SoupAuth *auth)
 
 	return priv->password;
 }
+
+static const char *
+get_response_header (SoupAuthConnBased *auth)
+{
+	SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+	return priv->response_header;
+}
+
+#ifdef USE_NTLM_AUTH
+static void
+sso_ntlm_close (SoupAuthNTLMPrivate *conn)
+{
+	if (conn->fd_in != -1) {
+		close (conn->fd_in);
+		conn->fd_in = -1;
+	}
+
+	if (conn->fd_out != -1) {
+		close (conn->fd_out);
+		conn->fd_out = -1;
+	}
+}
+
+static gboolean
+sso_ntlm_initiate (SoupAuthNTLMPrivate *conn)
+{
+	char *username = NULL, *slash, *domain = NULL;
+	char *argv[9];
+	gboolean ret;
+
+	/* Return if ntlm_auth execution process exist already */
+	if (conn->fd_in != -1 && conn->fd_out != -1)
+		return TRUE;
+	else
+		/* Clean all sso data before re-initiate */
+		sso_ntlm_close (conn);
+
+	if (!conn->ntlm_auth_accessible)
+		goto done;
+
+	username = getenv ("NTLMUSER");
+	if (!username)
+		username = getenv ("USER");
+	if (!username)
+		goto done;
+
+	slash = strpbrk (username, "\\/");
+	if (slash) {
+		domain = g_strdup (username);
+		slash = domain + (slash - username);
+		*slash = '\0';
+		username = slash + 1;
+	}
+
+	argv[0] = NTLM_AUTH;
+	argv[1] = "--helper-protocol";
+	argv[2] = "ntlmssp-client-1";
+	argv[3] = "--use-cached-creds";
+	argv[4] = "--username";
+	argv[5] = username;
+	argv[6] = domain ? "--domain" : NULL;
+	argv[7] = domain;
+	argv[8] = NULL;
+	/* Spawn child process */
+	ret = g_spawn_async_with_pipes (NULL, argv, NULL,
+					G_SPAWN_FILE_AND_ARGV_ZERO,
+					NULL, NULL,
+					NULL, &conn->fd_in, &conn->fd_out,
+					NULL, NULL);
+	if (!ret)
+		goto done;
+	g_free (domain);
+	return TRUE;
+done:
+	g_free (domain);
+	return FALSE;
+}
+
+static char *
+sso_ntlm_response (SoupAuthNTLMPrivate *conn, const char *input, SoupNTLMState conn_state)
+{
+	ssize_t size;
+	char buf[1024], *response = NULL;
+	char *tmpbuf = buf;
+	size_t	len_in = strlen (input), len_out = sizeof (buf);
+
+	while (len_in > 0) {
+		int written = write (conn->fd_in, input, len_in);
+		if (written == -1) {
+			/* Interrupted by a signal, retry it */
+			if (errno == EINTR)
+				continue;
+			/* write failed if other errors happen */
+			goto done;
+		}
+		input += written;
+		len_in -= written;
+	}
+	/* Read one line */
+	while (len_out > 0) {
+		size = read (conn->fd_out, tmpbuf, len_out);
+		if (size == -1) {
+			if (errno == EINTR)
+				continue;
+			goto done;
+		} else if (size == 0)
+			goto done;
+		else if (tmpbuf[size - 1] == '\n') {
+			tmpbuf[size - 1] = '\0';
+			goto wrfinish;
+		}
+		tmpbuf += size;
+		len_out -= size;
+	}
+	goto done;
+wrfinish:
+	if (conn_state == SOUP_NTLM_NEW &&
+	    g_ascii_strcasecmp (buf, "PW") == 0) {
+		/* Samba/winbind installed but not configured */
+		response = g_strdup ("PW");
+		goto done;
+	}
+	if (conn_state == SOUP_NTLM_NEW &&
+	    g_ascii_strncasecmp (buf, "YR ", 3) != 0)
+		/* invalid response for type 1 message */
+		goto done;
+	if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
+	    g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
+	    g_ascii_strncasecmp (buf, "AF ", 3) != 0)
+		/* invalid response for type 3 message */
+		goto done;
+
+	response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
+	goto done;
+done:
+	return response;
+}
+#endif /* USE_NTLM_AUTH */
+
+static gboolean
+authorize_pre (SoupMessage *msg, SoupAuthConnBased *ntlm)
+{
+	SoupAuthNTLMPrivate *conn =
+		SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
+	const char *val;
+	GValue value = { 0, { { 0 } } };
+
+	val = soup_message_headers_get_list (msg->response_headers,
+					     "WWW-Authenticate");
+	if (val)
+		val = strstr (val, "NTLM ");
+	if (!val)
+		return FALSE;
+
+	if (conn->state > SOUP_NTLM_SENT_REQUEST) {
+		/* We already authenticated, but then got another 401.
+		 * That means "permission denied", so don't try to
+		 * authenticate again.
+		 */
+		conn->state = SOUP_NTLM_FAILED;
+		return FALSE;
+	}
+
+	if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
+		conn->state = SOUP_NTLM_FAILED;
+		return FALSE;
+	}
+
+	g_value_init (&value, G_TYPE_STRING);
+	g_assert (G_VALUE_HOLDS_STRING (&value));
+
+	g_value_set_static_string (&value, soup_message_get_uri (msg)->host);
+	g_object_set_property (G_OBJECT (ntlm),
+			       SOUP_AUTH_HOST,
+			       &value);
+
+	g_value_set_static_string (&value, conn->domain);
+	g_object_set_property (G_OBJECT (ntlm),
+			       SOUP_AUTH_REALM,
+			       &value);
+
+#ifdef USE_NTLM_AUTH
+	conn->challenge_header = g_strdup (val + 5);
+	if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
+		conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
+		return TRUE;
+	}
+#endif
+	conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
+	return TRUE;
+}
+
+static void
+authorize_post (SoupMessage *msg, SoupAuthConnBased *ntlm)
+{
+	SoupAuthNTLMPrivate *conn =
+		SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
+	char *slash, *domain = NULL;
+	const char *username = NULL, *password = NULL;
+
+#ifdef USE_NTLM_AUTH
+	if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
+		char *input;
+		input = g_strdup_printf ("TT %s\n", conn->challenge_header);
+		/* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
+		if (sso_ntlm_initiate (conn)) {
+			conn->response_header = sso_ntlm_response (conn,
+								   input,
+								   conn->state);
+			/* Close ntlm_auth as it is no longer needed for current connection */
+			sso_ntlm_close (conn);
+			if (!conn->response_header) {
+				g_free (input);
+				goto ssofailure;
+			}
+			g_free (input);
+			goto done;
+		}
+ssofailure:
+		conn->state = SOUP_NTLM_SSO_FAILED;
+		goto done;
+	}
+#endif
+	username = conn->username;
+	password = conn->password;
+
+	if (!username || !password)
+		goto done;
+
+	slash = strpbrk (username, "\\/");
+	if (slash) {
+		domain = g_strdup (username);
+		slash = domain + (slash - username);
+		*slash = '\0';
+		username = slash + 1;
+	} else
+		domain = conn->domain;
+
+	conn->response_header = soup_ntlm_response (conn->nonce,
+						    username, password,
+						    NULL, domain);
+
+done:
+	if (domain != conn->domain)
+		g_free (domain);
+	g_free (conn->domain);
+	conn->domain = NULL;
+	g_free (conn->nonce);
+	conn->nonce = NULL;
+
+}
+
+static void
+authorize_request_started (SoupMessage *msg, SoupAuthConnBased *ntlm)
+{
+	SoupAuthNTLMPrivate *conn =
+		SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
+
+	switch (conn->state) {
+	case SOUP_NTLM_NEW:
+#ifdef USE_NTLM_AUTH
+		/* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
+		 * by delegating the NTLM challenge/response protocal to a helper
+		 * in ntlm_auth.
+		 * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+		 * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+		 * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+		 * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
+		 * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
+		 * path of it.
+		 * If NTLM single-sign-on fails, go back to original request handling process.
+		 */
+		if (sso_ntlm_initiate (conn)) {
+			conn->response_header = sso_ntlm_response (conn, "YR\n", conn->state);
+			if (conn->response_header) {
+				if (g_ascii_strcasecmp (conn->response_header, "PW") != 0) {
+					conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
+					break;
+				} else {
+					conn->response_header = NULL;
+					goto ssofailure;
+				}
+			} else {
+				g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+				goto ssofailure;
+			}
+		}
+ssofailure:
+#endif
+		conn->response_header = soup_ntlm_request ();
+		conn->state = SOUP_NTLM_SENT_REQUEST;
+		break;
+#ifdef USE_NTLM_AUTH
+	case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
+		conn->response_header = conn->response_header;
+		conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
+		break;
+	case SOUP_NTLM_SSO_FAILED:
+		/* Restart request without SSO */
+		g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+		conn->response_header = soup_ntlm_request ();
+		conn->state = SOUP_NTLM_SENT_REQUEST;
+		break;
+#endif
+	case SOUP_NTLM_RECEIVED_CHALLENGE:
+		conn->state = SOUP_NTLM_SENT_RESPONSE;
+		break;
+	default:
+		break;
+	}
+
+	if (conn->response_header && !soup_message_get_auth (msg)) {
+		soup_message_headers_replace (msg->request_headers,
+					      "Authorization", conn->response_header);
+	}
+
+	g_free (conn->response_header);
+	conn->response_header = NULL;
+}
+
+/* NTLM code */
+
+static void md4sum                (const unsigned char *in,
+				   int                  nbytes,
+				   unsigned char        digest[16]);
+
+typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
+
+static void deskey                (DES_KS, unsigned char *, int);
+
+static void des                   (DES_KS, unsigned char *);
+
+static void setup_schedule        (const guchar *key_56, DES_KS ks);
+
+static void calc_response         (const guchar        *key,
+				   const guchar        *plaintext,
+				   guchar              *results);
+
+#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+			  "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+			  "\x00\x00\x00\x00\x00"
+
+static void
+lanmanager_hash (const char *password, guchar hash[21])
+{
+	guchar lm_password [15];
+	DES_KS ks;
+	int i;
+
+	for (i = 0; i < 14 && password [i]; i++)
+		lm_password [i] = toupper ((unsigned char) password [i]);
+
+	for (; i < 15; i++)
+		lm_password [i] = '\0';
+
+	memcpy (hash, LM_PASSWORD_MAGIC, 21);
+
+	setup_schedule (lm_password, ks);
+	des (ks, hash);
+
+	setup_schedule (lm_password + 7, ks);
+	des (ks, hash + 8);
+}
+
+static void
+nt_hash (const char *password, guchar hash[21])
+{
+	unsigned char *buf, *p;
+
+	p = buf = g_malloc (strlen (password) * 2);
+
+	while (*password) {
+		*p++ = *password++;
+		*p++ = '\0';
+	}
+
+	md4sum (buf, p - buf, hash);
+	memset (hash + 16, 0, 5);
+
+	g_free (buf);
+}
+
+typedef struct {
+	guint16 length;
+	guint16 length2;
+	guint16 offset;
+	guchar  zero_pad[2];
+} NTLMString;
+
+#define NTLM_CHALLENGE_NONCE_OFFSET         24
+#define NTLM_CHALLENGE_NONCE_LENGTH          8
+#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
+
+#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
+#define NTLM_RESPONSE_FLAGS 0x8202
+
+typedef struct {
+	guchar     header[12];
+
+	NTLMString lm_resp;
+	NTLMString nt_resp;
+	NTLMString domain;
+	NTLMString user;
+	NTLMString host;
+	NTLMString session_key;
+
+	guint32    flags;
+} NTLMResponse;
+
+static void
+ntlm_set_string (NTLMString *string, int *offset, int len)
+{
+	string->offset = GUINT16_TO_LE (*offset);
+	string->length = string->length2 = GUINT16_TO_LE (len);
+	*offset += len;
+}
+
+static char *
+soup_ntlm_request (void)
+{
+	return g_strdup ("NTLM TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
+}
+
+static gboolean
+soup_ntlm_parse_challenge (const char *challenge,
+			   char      **nonce,
+			   char      **default_domain)
+{
+	gsize clen;
+	NTLMString domain;
+	guchar *chall;
+
+	if (strncmp (challenge, "NTLM ", 5) != 0)
+		return FALSE;
+
+	chall = g_base64_decode (challenge + 5, &clen);
+	if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
+	    clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
+		g_free (chall);
+		return FALSE;
+	}
+
+	if (default_domain) {
+		memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
+		domain.length = GUINT16_FROM_LE (domain.length);
+		domain.offset = GUINT16_FROM_LE (domain.offset);
+
+		if (clen < domain.length + domain.offset) {
+			g_free (chall);
+			return FALSE;
+		}
+
+		*default_domain = g_strndup ((char *)chall + domain.offset, domain.length);
+	}
+
+	if (nonce) {
+		*nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
+				   NTLM_CHALLENGE_NONCE_LENGTH);
+	}
+
+	g_free (chall);
+	return TRUE;
+}
+
+static char *
+soup_ntlm_response (const char *nonce,
+		    const char *user,
+		    const char *password,
+		    const char *host,
+		    const char *domain)
+{
+	int hlen, dlen, ulen, offset;
+	guchar hash[21], lm_resp[24], nt_resp[24];
+	NTLMResponse resp;
+	char *out, *p;
+	int state, save;
+
+	nt_hash (password, hash);
+	calc_response (hash, (guchar *)nonce, nt_resp);
+	lanmanager_hash (password, hash);
+	calc_response (hash, (guchar *)nonce, lm_resp);
+
+	memset (&resp, 0, sizeof (resp));
+	memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
+	resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
+
+	offset = sizeof (resp);
+
+	dlen = strlen (domain);
+	ntlm_set_string (&resp.domain, &offset, dlen);
+	ulen = strlen (user);
+	ntlm_set_string (&resp.user, &offset, ulen);
+	if (!host)
+		host = "UNKNOWN";
+	hlen = strlen (host);
+	ntlm_set_string (&resp.host, &offset, hlen);
+	ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
+	ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
+
+	out = g_malloc (((offset + 3) * 4) / 3 + 6);
+	strncpy (out, "NTLM ", 5);
+	p = out + 5;
+
+	state = save = 0;
+
+	p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp),
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_step ((const guchar *) domain, dlen,
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_step ((const guchar *) user, ulen,
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_step ((const guchar *) host, hlen,
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_step (lm_resp, sizeof (lm_resp),
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_step (nt_resp, sizeof (nt_resp),
+				   FALSE, p, &state, &save);
+	p += g_base64_encode_close (FALSE, p, &state, &save);
+	*p = '\0';
+
+	return out;
+}
+
+/* DES utils */
+/* Set up a key schedule based on a 56bit key */
+static void
+setup_schedule (const guchar *key_56, DES_KS ks)
+{
+	guchar key[8];
+	int i, c, bit;
+
+	key[0] = (key_56[0])                                 ;
+	key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
+	key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
+	key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
+	key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
+	key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
+	key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
+	key[7] =                    ((key_56[6] << 1) & 0xFF);
+
+	/* Fix parity */
+	for (i = 0; i < 8; i++) {
+		for (c = bit = 0; bit < 8; bit++)
+			if (key[i] & (1 << bit))
+				c++;
+		if (!(c & 1))
+			key[i] ^= 0x01;
+	}
+
+	deskey (ks, key, 0);
+}
+
+static void
+calc_response (const guchar *key, const guchar *plaintext, guchar *results)
+{
+	DES_KS ks;
+
+	memcpy (results, plaintext, 8);
+	memcpy (results + 8, plaintext, 8);
+	memcpy (results + 16, plaintext, 8);
+
+	setup_schedule (key, ks);
+	des (ks, results);
+
+	setup_schedule (key + 7, ks);
+	des (ks, results + 8);
+
+	setup_schedule (key + 14, ks);
+	des (ks, results + 16);
+}
+
+
+/*
+ * MD4 encoder. (The one everyone else uses is not GPL-compatible;
+ * this is a reimplementation from spec.) This doesn't need to be
+ * efficient for our purposes, although it would be nice to fix
+ * it to not malloc()...
+ */
+
+#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
+#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
+#define H(X,Y,Z) ( (X)^(Y)^(Z) )
+#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
+
+static void
+md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
+{
+	unsigned char *M;
+	guint32 A, B, C, D, AA, BB, CC, DD, X[16];
+	int pbytes, nbits = nbytes * 8, i, j;
+
+	pbytes = (120 - (nbytes % 64)) % 64;
+	M = alloca (nbytes + pbytes + 8);
+	memcpy (M, in, nbytes);
+	memset (M + nbytes, 0, pbytes + 8);
+	M[nbytes] = 0x80;
+	M[nbytes + pbytes] = nbits & 0xFF;
+	M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
+	M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
+	M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
+
+	A = 0x67452301;
+	B = 0xEFCDAB89;
+	C = 0x98BADCFE;
+	D = 0x10325476;
+
+	for (i = 0; i < nbytes + pbytes + 8; i += 64) {
+		for (j = 0; j < 16; j++) {
+			X[j] =  (M[i + j*4]) |
+				(M[i + j*4 + 1] << 8) |
+				(M[i + j*4 + 2] << 16) |
+				(M[i + j*4 + 3] << 24);
+		}
+
+		AA = A;
+		BB = B;
+		CC = C;
+		DD = D;
+
+		A = ROT (A + F(B, C, D) + X[0], 3);
+		D = ROT (D + F(A, B, C) + X[1], 7);
+		C = ROT (C + F(D, A, B) + X[2], 11);
+		B = ROT (B + F(C, D, A) + X[3], 19);
+		A = ROT (A + F(B, C, D) + X[4], 3);
+		D = ROT (D + F(A, B, C) + X[5], 7);
+		C = ROT (C + F(D, A, B) + X[6], 11);
+		B = ROT (B + F(C, D, A) + X[7], 19);
+		A = ROT (A + F(B, C, D) + X[8], 3);
+		D = ROT (D + F(A, B, C) + X[9], 7);
+		C = ROT (C + F(D, A, B) + X[10], 11);
+		B = ROT (B + F(C, D, A) + X[11], 19);
+		A = ROT (A + F(B, C, D) + X[12], 3);
+		D = ROT (D + F(A, B, C) + X[13], 7);
+		C = ROT (C + F(D, A, B) + X[14], 11);
+		B = ROT (B + F(C, D, A) + X[15], 19);
+
+		A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
+		D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
+		C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
+		B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
+		A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
+		D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
+		C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
+		B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
+		A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
+		D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
+		C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
+		B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
+		A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
+		D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
+		C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
+		B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
+
+		A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
+		D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
+		C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
+		B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
+		A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
+		D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
+		C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
+		B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
+		A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
+		D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
+		C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
+		B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
+		A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
+		D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
+		C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
+		B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
+
+		A += AA;
+		B += BB;
+		C += CC;
+		D += DD;
+	}
+
+	digest[0]  =  A        & 0xFF;
+	digest[1]  = (A >>  8) & 0xFF;
+	digest[2]  = (A >> 16) & 0xFF;
+	digest[3]  = (A >> 24) & 0xFF;
+	digest[4]  =  B        & 0xFF;
+	digest[5]  = (B >>  8) & 0xFF;
+	digest[6]  = (B >> 16) & 0xFF;
+	digest[7]  = (B >> 24) & 0xFF;
+	digest[8]  =  C        & 0xFF;
+	digest[9]  = (C >>  8) & 0xFF;
+	digest[10] = (C >> 16) & 0xFF;
+	digest[11] = (C >> 24) & 0xFF;
+	digest[12] =  D        & 0xFF;
+	digest[13] = (D >>  8) & 0xFF;
+	digest[14] = (D >> 16) & 0xFF;
+	digest[15] = (D >> 24) & 0xFF;
+}
+
+
+/* Public domain DES implementation from Phil Karn */
+static const guint32 Spbox[8][64] = {
+	{ 0x01010400,0x00000000,0x00010000,0x01010404,
+	  0x01010004,0x00010404,0x00000004,0x00010000,
+	  0x00000400,0x01010400,0x01010404,0x00000400,
+	  0x01000404,0x01010004,0x01000000,0x00000004,
+	  0x00000404,0x01000400,0x01000400,0x00010400,
+	  0x00010400,0x01010000,0x01010000,0x01000404,
+	  0x00010004,0x01000004,0x01000004,0x00010004,
+	  0x00000000,0x00000404,0x00010404,0x01000000,
+	  0x00010000,0x01010404,0x00000004,0x01010000,
+	  0x01010400,0x01000000,0x01000000,0x00000400,
+	  0x01010004,0x00010000,0x00010400,0x01000004,
+	  0x00000400,0x00000004,0x01000404,0x00010404,
+	  0x01010404,0x00010004,0x01010000,0x01000404,
+	  0x01000004,0x00000404,0x00010404,0x01010400,
+	  0x00000404,0x01000400,0x01000400,0x00000000,
+	  0x00010004,0x00010400,0x00000000,0x01010004 },
+	{ 0x80108020,0x80008000,0x00008000,0x00108020,
+	  0x00100000,0x00000020,0x80100020,0x80008020,
+	  0x80000020,0x80108020,0x80108000,0x80000000,
+	  0x80008000,0x00100000,0x00000020,0x80100020,
+	  0x00108000,0x00100020,0x80008020,0x00000000,
+	  0x80000000,0x00008000,0x00108020,0x80100000,
+	  0x00100020,0x80000020,0x00000000,0x00108000,
+	  0x00008020,0x80108000,0x80100000,0x00008020,
+	  0x00000000,0x00108020,0x80100020,0x00100000,
+	  0x80008020,0x80100000,0x80108000,0x00008000,
+	  0x80100000,0x80008000,0x00000020,0x80108020,
+	  0x00108020,0x00000020,0x00008000,0x80000000,
+	  0x00008020,0x80108000,0x00100000,0x80000020,
+	  0x00100020,0x80008020,0x80000020,0x00100020,
+	  0x00108000,0x00000000,0x80008000,0x00008020,
+	  0x80000000,0x80100020,0x80108020,0x00108000 },
+	{ 0x00000208,0x08020200,0x00000000,0x08020008,
+	  0x08000200,0x00000000,0x00020208,0x08000200,
+	  0x00020008,0x08000008,0x08000008,0x00020000,
+	  0x08020208,0x00020008,0x08020000,0x00000208,
+	  0x08000000,0x00000008,0x08020200,0x00000200,
+	  0x00020200,0x08020000,0x08020008,0x00020208,
+	  0x08000208,0x00020200,0x00020000,0x08000208,
+	  0x00000008,0x08020208,0x00000200,0x08000000,
+	  0x08020200,0x08000000,0x00020008,0x00000208,
+	  0x00020000,0x08020200,0x08000200,0x00000000,
+	  0x00000200,0x00020008,0x08020208,0x08000200,
+	  0x08000008,0x00000200,0x00000000,0x08020008,
+	  0x08000208,0x00020000,0x08000000,0x08020208,
+	  0x00000008,0x00020208,0x00020200,0x08000008,
+	  0x08020000,0x08000208,0x00000208,0x08020000,
+	  0x00020208,0x00000008,0x08020008,0x00020200 },
+	{ 0x00802001,0x00002081,0x00002081,0x00000080,
+	  0x00802080,0x00800081,0x00800001,0x00002001,
+	  0x00000000,0x00802000,0x00802000,0x00802081,
+	  0x00000081,0x00000000,0x00800080,0x00800001,
+	  0x00000001,0x00002000,0x00800000,0x00802001,
+	  0x00000080,0x00800000,0x00002001,0x00002080,
+	  0x00800081,0x00000001,0x00002080,0x00800080,
+	  0x00002000,0x00802080,0x00802081,0x00000081,
+	  0x00800080,0x00800001,0x00802000,0x00802081,
+	  0x00000081,0x00000000,0x00000000,0x00802000,
+	  0x00002080,0x00800080,0x00800081,0x00000001,
+	  0x00802001,0x00002081,0x00002081,0x00000080,
+	  0x00802081,0x00000081,0x00000001,0x00002000,
+	  0x00800001,0x00002001,0x00802080,0x00800081,
+	  0x00002001,0x00002080,0x00800000,0x00802001,
+	  0x00000080,0x00800000,0x00002000,0x00802080 },
+	{ 0x00000100,0x02080100,0x02080000,0x42000100,
+	  0x00080000,0x00000100,0x40000000,0x02080000,
+	  0x40080100,0x00080000,0x02000100,0x40080100,
+	  0x42000100,0x42080000,0x00080100,0x40000000,
+	  0x02000000,0x40080000,0x40080000,0x00000000,
+	  0x40000100,0x42080100,0x42080100,0x02000100,
+	  0x42080000,0x40000100,0x00000000,0x42000000,
+	  0x02080100,0x02000000,0x42000000,0x00080100,
+	  0x00080000,0x42000100,0x00000100,0x02000000,
+	  0x40000000,0x02080000,0x42000100,0x40080100,
+	  0x02000100,0x40000000,0x42080000,0x02080100,
+	  0x40080100,0x00000100,0x02000000,0x42080000,
+	  0x42080100,0x00080100,0x42000000,0x42080100,
+	  0x02080000,0x00000000,0x40080000,0x42000000,
+	  0x00080100,0x02000100,0x40000100,0x00080000,
+	  0x00000000,0x40080000,0x02080100,0x40000100 },
+	{ 0x20000010,0x20400000,0x00004000,0x20404010,
+	  0x20400000,0x00000010,0x20404010,0x00400000,
+	  0x20004000,0x00404010,0x00400000,0x20000010,
+	  0x00400010,0x20004000,0x20000000,0x00004010,
+	  0x00000000,0x00400010,0x20004010,0x00004000,
+	  0x00404000,0x20004010,0x00000010,0x20400010,
+	  0x20400010,0x00000000,0x00404010,0x20404000,
+	  0x00004010,0x00404000,0x20404000,0x20000000,
+	  0x20004000,0x00000010,0x20400010,0x00404000,
+	  0x20404010,0x00400000,0x00004010,0x20000010,
+	  0x00400000,0x20004000,0x20000000,0x00004010,
+	  0x20000010,0x20404010,0x00404000,0x20400000,
+	  0x00404010,0x20404000,0x00000000,0x20400010,
+	  0x00000010,0x00004000,0x20400000,0x00404010,
+	  0x00004000,0x00400010,0x20004010,0x00000000,
+	  0x20404000,0x20000000,0x00400010,0x20004010 },
+	{ 0x00200000,0x04200002,0x04000802,0x00000000,
+	  0x00000800,0x04000802,0x00200802,0x04200800,
+	  0x04200802,0x00200000,0x00000000,0x04000002,
+	  0x00000002,0x04000000,0x04200002,0x00000802,
+	  0x04000800,0x00200802,0x00200002,0x04000800,
+	  0x04000002,0x04200000,0x04200800,0x00200002,
+	  0x04200000,0x00000800,0x00000802,0x04200802,
+	  0x00200800,0x00000002,0x04000000,0x00200800,
+	  0x04000000,0x00200800,0x00200000,0x04000802,
+	  0x04000802,0x04200002,0x04200002,0x00000002,
+	  0x00200002,0x04000000,0x04000800,0x00200000,
+	  0x04200800,0x00000802,0x00200802,0x04200800,
+	  0x00000802,0x04000002,0x04200802,0x04200000,
+	  0x00200800,0x00000000,0x00000002,0x04200802,
+	  0x00000000,0x00200802,0x04200000,0x00000800,
+	  0x04000002,0x04000800,0x00000800,0x00200002 },
+	{ 0x10001040,0x00001000,0x00040000,0x10041040,
+	  0x10000000,0x10001040,0x00000040,0x10000000,
+	  0x00040040,0x10040000,0x10041040,0x00041000,
+	  0x10041000,0x00041040,0x00001000,0x00000040,
+	  0x10040000,0x10000040,0x10001000,0x00001040,
+	  0x00041000,0x00040040,0x10040040,0x10041000,
+	  0x00001040,0x00000000,0x00000000,0x10040040,
+	  0x10000040,0x10001000,0x00041040,0x00040000,
+	  0x00041040,0x00040000,0x10041000,0x00001000,
+	  0x00000040,0x10040040,0x00001000,0x00041040,
+	  0x10001000,0x00000040,0x10000040,0x10040000,
+	  0x10040040,0x10000000,0x00040000,0x10001040,
+	  0x00000000,0x10041040,0x00040040,0x10000040,
+	  0x10040000,0x10001000,0x10001040,0x00000000,
+	  0x10041040,0x00041000,0x00041000,0x00001040,
+	  0x00001040,0x00040040,0x10000000,0x10041000 }
+};
+
+#undef F
+#define	F(l,r,key){\
+	work = ((r >> 4) | (r << 28)) ^ key[0];\
+	l ^= Spbox[6][work & 0x3f];\
+	l ^= Spbox[4][(work >> 8) & 0x3f];\
+	l ^= Spbox[2][(work >> 16) & 0x3f];\
+	l ^= Spbox[0][(work >> 24) & 0x3f];\
+	work = r ^ key[1];\
+	l ^= Spbox[7][work & 0x3f];\
+	l ^= Spbox[5][(work >> 8) & 0x3f];\
+	l ^= Spbox[3][(work >> 16) & 0x3f];\
+	l ^= Spbox[1][(work >> 24) & 0x3f];\
+}
+/* Encrypt or decrypt a block of data in ECB mode */
+static void
+des (guint32 ks[16][2], unsigned char block[8])
+{
+	guint32 left,right,work;
+
+	/* Read input block and place in left/right in big-endian order */
+	left = ((guint32)block[0] << 24)
+	 | ((guint32)block[1] << 16)
+	 | ((guint32)block[2] << 8)
+	 | (guint32)block[3];
+	right = ((guint32)block[4] << 24)
+	 | ((guint32)block[5] << 16)
+	 | ((guint32)block[6] << 8)
+	 | (guint32)block[7];
+
+	/* Hoey's clever initial permutation algorithm, from Outerbridge
+	 * (see Schneier p 478)
+	 *
+	 * The convention here is the same as Outerbridge: rotate each
+	 * register left by 1 bit, i.e., so that "left" contains permuted
+	 * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
+	 * (using origin-1 numbering as in the FIPS). This allows us to avoid
+	 * one of the two rotates that would otherwise be required in each of
+	 * the 16 rounds.
+	 */
+	work = ((left >> 4) ^ right) & 0x0f0f0f0f;
+	right ^= work;
+	left ^= work << 4;
+	work = ((left >> 16) ^ right) & 0xffff;
+	right ^= work;
+	left ^= work << 16;
+	work = ((right >> 2) ^ left) & 0x33333333;
+	left ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ left) & 0xff00ff;
+	left ^= work;
+	right ^= (work << 8);
+	right = (right << 1) | (right >> 31);
+	work = (left ^ right) & 0xaaaaaaaa;
+	left ^= work;
+	right ^= work;
+	left = (left << 1) | (left >> 31);
+
+	/* Now do the 16 rounds */
+	F(left,right,ks[0]);
+	F(right,left,ks[1]);
+	F(left,right,ks[2]);
+	F(right,left,ks[3]);
+	F(left,right,ks[4]);
+	F(right,left,ks[5]);
+	F(left,right,ks[6]);
+	F(right,left,ks[7]);
+	F(left,right,ks[8]);
+	F(right,left,ks[9]);
+	F(left,right,ks[10]);
+	F(right,left,ks[11]);
+	F(left,right,ks[12]);
+	F(right,left,ks[13]);
+	F(left,right,ks[14]);
+	F(right,left,ks[15]);
+
+	/* Inverse permutation, also from Hoey via Outerbridge and Schneier */
+	right = (right << 31) | (right >> 1);
+	work = (left ^ right) & 0xaaaaaaaa;
+	left ^= work;
+	right ^= work;
+	left = (left >> 1) | (left  << 31);
+	work = ((left >> 8) ^ right) & 0xff00ff;
+	right ^= work;
+	left ^= work << 8;
+	work = ((left >> 2) ^ right) & 0x33333333;
+	right ^= work;
+	left ^= work << 2;
+	work = ((right >> 16) ^ left) & 0xffff;
+	left ^= work;
+	right ^= work << 16;
+	work = ((right >> 4) ^ left) & 0x0f0f0f0f;
+	left ^= work;
+	right ^= work << 4;
+
+	/* Put the block back into the user's buffer with final swap */
+	block[0] = right >> 24;
+	block[1] = right >> 16;
+	block[2] = right >> 8;
+	block[3] = right;
+	block[4] = left >> 24;
+	block[5] = left >> 16;
+	block[6] = left >> 8;
+	block[7] = left;
+}
+
+/* Key schedule-related tables from FIPS-46 */
+
+/* permuted choice table (key) */
+static const unsigned char pc1[] = {
+	57, 49, 41, 33, 25, 17,  9,
+	 1, 58, 50, 42, 34, 26, 18,
+	10,  2, 59, 51, 43, 35, 27,
+	19, 11,  3, 60, 52, 44, 36,
+
+	63, 55, 47, 39, 31, 23, 15,
+	 7, 62, 54, 46, 38, 30, 22,
+	14,  6, 61, 53, 45, 37, 29,
+	21, 13,  5, 28, 20, 12,  4
+};
+
+/* number left rotations of pc1 */
+static const unsigned char totrot[] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
+};
+
+/* permuted choice key (table) */
+static const unsigned char pc2[] = {
+	14, 17, 11, 24,  1,  5,
+	 3, 28, 15,  6, 21, 10,
+	23, 19, 12,  4, 26,  8,
+	16,  7, 27, 20, 13,  2,
+	41, 52, 31, 37, 47, 55,
+	30, 40, 51, 45, 33, 48,
+	44, 49, 39, 56, 34, 53,
+	46, 42, 50, 36, 29, 32
+};
+
+/* End of DES-defined tables */
+
+
+/* bit 0 is left-most in byte */
+static const int bytebit[] = {
+	0200,0100,040,020,010,04,02,01
+};
+
+
+/* Generate key schedule for encryption or decryption
+ * depending on the value of "decrypt"
+ */
+static void
+deskey (DES_KS k, unsigned char *key, int decrypt)
+{
+	unsigned char pc1m[56];		/* place to modify pc1 into */
+	unsigned char pcr[56];		/* place to rotate pc1 into */
+	register int i,j,l;
+	int m;
+	unsigned char ks[8];
+
+	for (j=0; j<56; j++) {		/* convert pc1 to bits of key */
+		l=pc1[j]-1;		/* integer bit location	 */
+		m = l & 07;		/* find bit		 */
+		pc1m[j]=(key[l>>3] &	/* find which key byte l is in */
+			bytebit[m])	/* and which bit of that byte */
+			? 1 : 0;	/* and store 1-bit result */
+	}
+	for (i=0; i<16; i++) {		/* key chunk for each iteration */
+		memset(ks,0,sizeof(ks));	/* Clear key schedule */
+		for (j=0; j<56; j++)	/* rotate pc1 the right amount */
+			pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
+			/* rotate left and right halves independently */
+		for (j=0; j<48; j++){	/* select bits individually */
+			/* check bit that goes to ks[j] */
+			if (pcr[pc2[j]-1]){
+				/* mask it in if it's there */
+				l= j % 6;
+				ks[j/6] |= bytebit[l] >> 2;
+			}
+		}
+		/* Now convert to packed odd/even interleaved form */
+		k[i][0] = ((guint32)ks[0] << 24)
+		 | ((guint32)ks[2] << 16)
+		 | ((guint32)ks[4] << 8)
+		 | ((guint32)ks[6]);
+		k[i][1] = ((guint32)ks[1] << 24)
+		 | ((guint32)ks[3] << 16)
+		 | ((guint32)ks[5] << 8)
+		 | ((guint32)ks[7]);
+	}
+}
diff --git a/libsoup/soup-auth-ntlm.h b/libsoup/soup-auth-ntlm.h
index 1f56976..5407565 100644
--- a/libsoup/soup-auth-ntlm.h
+++ b/libsoup/soup-auth-ntlm.h
@@ -6,8 +6,9 @@
 #ifndef SOUP_AUTH_NTLM_H
 #define SOUP_AUTH_NTLM_H 1
 
-#include "soup-auth.h"
+#include "soup-auth-connbased.h"
 
+#define SOUP_TYPE_AUTH_NTLM            (soup_auth_ntlm_get_type ())
 #define SOUP_AUTH_NTLM(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
 #define SOUP_AUTH_NTLM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
 #define SOUP_IS_AUTH_NTLM(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NTLM))
@@ -15,18 +16,18 @@
 #define SOUP_AUTH_NTLM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
 
 typedef struct {
-	SoupAuth parent;
+	SoupAuthConnBased parent;
 
 } SoupAuthNTLM;
 
 typedef struct {
-	SoupAuthClass parent_class;
+	SoupAuthConnBasedClass parent_class;
 
 } SoupAuthNTLMClass;
 
-SoupAuth   *soup_auth_ntlm_new          (const char *realm,
-					 const char *host);
+GType soup_auth_ntlm_get_type (void);
+
+SoupAuthConnBased   *soup_auth_ntlm_new (void);
 const char *soup_auth_ntlm_get_username (SoupAuth   *auth);
 const char *soup_auth_ntlm_get_password (SoupAuth   *auth);
-
 #endif /* SOUP_AUTH_NTLM_H */
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
index e6d103d..d70c954 100644
--- a/libsoup/soup-auth.c
+++ b/libsoup/soup-auth.c
@@ -148,7 +148,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
 				     "Realm",
 				     "Authentication realm",
 				     NULL,
-				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+				     G_PARAM_READWRITE));
 	/**
 	 * SOUP_AUTH_HOST:
 	 *
@@ -161,7 +161,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
 				     "Host",
 				     "Authentication host",
 				     NULL,
-				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+				     G_PARAM_READWRITE));
 	/**
 	 * SOUP_AUTH_IS_FOR_PROXY:
 	 *
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index 0a26e8c..453f51d 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -100,15 +100,6 @@ void        soup_auth_has_saved_password    (SoupAuth      *auth,
 					     const char    *password);
 #endif
 
-/* The actual auth types, which can be added/removed as features */
-
-#define SOUP_TYPE_AUTH_BASIC  (soup_auth_basic_get_type ())
-GType soup_auth_basic_get_type  (void);
-#define SOUP_TYPE_AUTH_DIGEST (soup_auth_digest_get_type ())
-GType soup_auth_digest_get_type (void);
-#define SOUP_TYPE_AUTH_NTLM   (soup_auth_ntlm_get_type ())
-GType soup_auth_ntlm_get_type   (void);
-
 G_END_DECLS
 
 #endif /* SOUP_AUTH_H */
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 75d6b1c..b896a41 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -15,9 +15,12 @@
 
 #include "soup-address.h"
 #include "soup-auth.h"
+#include "soup-auth-connbased.h"
+#include "soup-auth-ntlm.h"
+#include "soup-auth-negotiate.h"
 #include "soup-auth-basic.h"
 #include "soup-auth-digest.h"
-#include "soup-auth-manager-ntlm.h"
+#include "soup-auth-manager.h"
 #include "soup-connection.h"
 #include "soup-marshal.h"
 #include "soup-message-private.h"
@@ -190,13 +193,14 @@ soup_session_init (SoupSession *session)
 
 	priv->features_cache = g_hash_table_new (NULL, NULL);
 
-	auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
+	auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER,
+				     NULL);
 	g_signal_connect (auth_manager, "authenticate",
 			  G_CALLBACK (auth_manager_authenticate), session);
-	soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
-					  SOUP_TYPE_AUTH_BASIC);
-	soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
-					  SOUP_TYPE_AUTH_DIGEST);
+	soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_BASIC);
+	soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_DIGEST);
+	soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_NTLM);
+	soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_NEGOTIATE);
 	soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
 	g_object_unref (auth_manager);
 
@@ -970,14 +974,7 @@ set_property (GObject *object, guint prop_id,
 		priv->max_conns_per_host = g_value_get_int (value);
 		break;
 	case PROP_USE_NTLM:
-		feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
-		if (feature) {
-			if (g_value_get_boolean (value))
-				soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
-			else
-				soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
-		} else
-			g_warning ("Trying to set use-ntlm on session with no auth-manager");
+		soup_session_use_auth (session, SOUP_TYPE_AUTH_NTLM, NULL);
 		break;
 	case PROP_SSL_CA_FILE:
 		if (priv->tlsdb) {
@@ -1095,10 +1092,12 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_int (value, priv->max_conns_per_host);
 		break;
 	case PROP_USE_NTLM:
-		feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
-		if (feature)
-			g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
-		else
+		feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+		if (feature) {
+			g_value_set_boolean
+				(value,
+				 soup_auth_manager_get_auth (SOUP_AUTH_MANAGER (feature)) == SOUP_TYPE_AUTH_NTLM);
+		} else
 			g_value_set_boolean (value, FALSE);
 		break;
 	case PROP_SSL_CA_FILE:
@@ -2056,21 +2055,19 @@ soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
 void
 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
 {
+	SoupSessionFeature *feature = NULL;
+
 	g_return_if_fail (SOUP_IS_SESSION (session));
 
 	if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
-		SoupSessionFeature *feature;
-
 		feature = g_object_new (feature_type, NULL);
 		soup_session_add_feature (session, feature);
 		g_object_unref (feature);
 	} else {
-		SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-		GSList *f;
-
-		for (f = priv->features; f; f = f->next) {
-			if (soup_session_feature_add_feature (f->data, feature_type))
-				return;
+		feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+		if (feature) {
+			soup_auth_manager_add_type (SOUP_AUTH_MANAGER (feature), feature_type);
+			return;
 		}
 		g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
 	}
@@ -2123,20 +2120,12 @@ soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
 
 	priv = SOUP_SESSION_GET_PRIVATE (session);
 
-	if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
-	restart:
-		for (f = priv->features; f; f = f->next) {
-			if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
-				soup_session_remove_feature (session, f->data);
-				goto restart;
-			}
-		}
-	} else {
-		for (f = priv->features; f; f = f->next) {
-			if (soup_session_feature_remove_feature (f->data, feature_type))
-				return;
+restart:
+	for (f = priv->features; f; f = f->next) {
+		if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
+			soup_session_remove_feature (session, f->data);
+			goto restart;
 		}
-		g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
 	}
 }
 
@@ -2242,3 +2231,33 @@ soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
 		return NULL;
 	return feature;
 }
+
+/**
+ * soup_session_use_auth:
+ * @session: a #SoupSession
+ * @auth_type: the #GType of the auth to use
+ * @hosts: a comma separated liste of URIS
+ *
+ * Set the auth type to use for the session
+ * auth_type should be connection base (either be SOUP_TYPE_AUTH_NTLM or
+ * SOUP_TYPE_AUTH_NEGOTIATE).
+ * If @auth_type is SOUP_TYPE_AUTH_NEGOTIATE, then @hosts is a comma separate
+ * list of trusted URIS, can not be NULL.
+ * If @auth_type is SOUP_TYPE_AUTH_NTLM, then @hosts does not take effect.
+ *
+ * Since: 2.36
+ **/
+void
+soup_session_use_auth (SoupSession *session, GType auth_type, const gchar *hosts)
+{
+	SoupSessionFeature *feature;
+
+	g_return_if_fail (g_type_is_a (auth_type, SOUP_TYPE_AUTH_CONNBASED));
+
+	g_return_if_fail (g_type_is_a (auth_type, SOUP_TYPE_AUTH_NTLM) || (hosts != NULL));
+
+	feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+	if (feature)
+		soup_auth_manager_set_auth (SOUP_AUTH_MANAGER (feature), auth_type, hosts);
+	return;
+}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index a7aab0e..3d7a27c 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -117,6 +117,9 @@ SoupSessionFeature *soup_session_get_feature            (SoupSession        *ses
 SoupSessionFeature *soup_session_get_feature_for_message(SoupSession        *session,
 							 GType               feature_type,
 							 SoupMessage        *msg);
+void                soup_session_use_auth               (SoupSession        *session,
+							 GType               auth_type,
+							 const gchar        *hosts);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-auth-connbased.c b/libsoup/soup-auth-connbased.c
new file mode 100644
index 0000000..da91358
--- /dev/null
+++ b/libsoup/soup-auth-connbased.c
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-connbased.c: HTTP CONNBASED Authentication helper
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-auth-connbased.h"
+#include "soup-message.h"
+
+G_DEFINE_ABSTRACT_TYPE (SoupAuthConnBased, soup_auth_connbased, SOUP_TYPE_AUTH)
+
+static void
+soup_auth_connbased_init (SoupAuthConnBased *connbased)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	G_OBJECT_CLASS (soup_auth_connbased_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_connbased_class_init (SoupAuthConnBasedClass *auth_connbased_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (auth_connbased_class);
+
+	object_class->finalize = finalize;
+}
+
+gboolean
+soup_auth_connbased_authorize_pre  (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	g_return_val_if_fail (SOUP_IS_AUTH_CONNBASED (auth), FALSE);
+	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+	return SOUP_AUTH_CONNBASED_GET_CLASS (auth)->authorize_pre (msg, auth);
+}
+
+void
+soup_auth_connbased_authorize_post (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	g_return_if_fail (SOUP_IS_AUTH_CONNBASED (auth));
+	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+	return SOUP_AUTH_CONNBASED_GET_CLASS (auth)->authorize_post (msg, auth);
+}
+
+void
+soup_auth_connbased_authorize_request_started
+					    (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	g_return_if_fail (SOUP_IS_AUTH_CONNBASED (auth));
+	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+	return SOUP_AUTH_CONNBASED_GET_CLASS (auth)->authorize_request_started (msg, auth);
+}
+
+const char *
+soup_auth_connbased_get_response_header (SoupAuthConnBased *auth)
+{
+	g_return_val_if_fail (SOUP_IS_AUTH_CONNBASED (auth), NULL);
+
+	return SOUP_AUTH_CONNBASED_GET_CLASS (auth)->get_response_header (auth);
+
+}
diff --git a/libsoup/soup-auth-connbased.h b/libsoup/soup-auth-connbased.h
new file mode 100644
index 0000000..48b9288
--- /dev/null
+++ b/libsoup/soup-auth-connbased.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_AUTH_CONNBASED_H
+#define SOUP_AUTH_CONNBASED_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_TYPE_AUTH_CONNBASED            (soup_auth_connbased_get_type ())
+#define SOUP_AUTH_CONNBASED(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_CONNBASED, SoupAuthConnBased))
+#define SOUP_AUTH_CONNBASED_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_CONNBASED, SoupAuthConnBasedClass))
+#define SOUP_IS_AUTH_CONNBASED(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_CONNBASED))
+#define SOUP_IS_AUTH_CONNBASED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_CONNBASED))
+#define SOUP_AUTH_CONNBASED_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_CONNBASED, SoupAuthConnBasedClass))
+
+typedef struct {
+	SoupAuth parent;
+
+} SoupAuthConnBased;
+
+typedef struct {
+	SoupAuthClass parent_class;
+
+	gboolean (*authorize_pre)             (SoupMessage *msg, SoupAuthConnBased *auth);
+	void     (*authorize_post)            (SoupMessage *msg, SoupAuthConnBased *auth);
+	void     (*authorize_request_started) (SoupMessage *msg, SoupAuthConnBased *auth);
+	const char *
+		 (*get_response_header)    (SoupAuthConnBased *auth);
+} SoupAuthConnBasedClass;
+
+GType soup_auth_connbased_get_type (void);
+
+gboolean soup_auth_connbased_authorize_pre  (SoupMessage *msg, SoupAuthConnBased *auth);
+void     soup_auth_connbased_authorize_post (SoupMessage *msg, SoupAuthConnBased *auth);
+void     soup_auth_connbased_authorize_request_started
+					    (SoupMessage *msg, SoupAuthConnBased *auth);
+const char *soup_auth_connbased_get_response_header (SoupAuthConnBased *auth);
+#endif /* SOUP_AUTH_CONNBASED_H */
diff --git a/libsoup/soup-auth-negotiate.c b/libsoup/soup-auth-negotiate.c
new file mode 100644
index 0000000..8d4dcb4
--- /dev/null
+++ b/libsoup/soup-auth-negotiate.c
@@ -0,0 +1,604 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-negotiate.c: HTTP Negotiate Authentication helper
+ *
+ * Copyright (C) 2009 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+
+#include "soup-auth-negotiate.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+static gboolean authorize_pre (SoupMessage *msg, SoupAuthConnBased *auth);
+static void authorize_post (SoupMessage *msg, SoupAuthConnBased *auth);
+static void authorize_request_started (SoupMessage *msg, SoupAuthConnBased *auth);
+static const char *get_response_header (SoupAuthConnBased *auth);
+
+typedef enum {
+	SOUP_NEGOTIATE_NEW,
+	SOUP_NEGOTIATE_RECEIVED_CHALLENGE, /* received www-authenticate: negotiate */
+	SOUP_NEGOTIATE_SENT_RESPONSE,      /* sent response */
+	SOUP_NEGOTIATE_RECEIVED_RESPONSE,  /* received reply */
+	SOUP_NEGOTIATE_CHECKED_RESPONSE,   /* verified reply */
+	SOUP_NEGOTIATE_FAILED
+} SoupNegotiateState;
+
+typedef struct {
+	char *username;
+
+	SoupNegotiateState state;
+	char *auth_header;
+
+	gss_ctx_id_t context;
+	gss_name_t   server_name;
+	char*        response_header;
+	gchar **trusted_uris;		/* Uris we try negotiate against */
+} SoupAuthNegotiatePrivate;
+#define SOUP_AUTH_NEGOTIATE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiatePrivate))
+
+G_DEFINE_TYPE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_AUTH_CONNBASED)
+
+#define AUTH_GSS_ERROR      -1
+#define AUTH_GSS_COMPLETE    1
+#define AUTH_GSS_CONTINUE    0
+static int soup_gss_client_init (SoupAuth *auth);
+static int soup_gss_client_step (SoupAuth *auth, const char* challenge);
+static void soup_gss_username  (SoupAuth *auth);
+static void output_negotiate_auth (SoupMessage *msg, SoupAuth *auth);
+static gchar** negotiate_parse_trusted_uris (gchar *hosts);
+
+static void
+soup_auth_negotiate_init (SoupAuthNegotiate *negotiate)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (negotiate);
+
+	priv->state = SOUP_NEGOTIATE_NEW;
+}
+
+static void
+finalize (GObject *object)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (object);
+	OM_uint32 min_stat;
+
+	/* FIXME: error handling */
+	gss_release_name(&min_stat, &priv->server_name);
+	g_free (priv->username);
+	priv->username = NULL;
+	g_free (priv->response_header);
+	priv->response_header = NULL;
+	g_free (priv->auth_header);
+	priv->auth_header = NULL;
+	g_strfreev (priv->trusted_uris);
+
+	G_OBJECT_CLASS (soup_auth_negotiate_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_negotiate_class_init (SoupAuthNegotiateClass *auth_negotiate_class)
+{
+	SoupAuthConnBasedClass *auth_connbased_class = SOUP_AUTH_CONNBASED_CLASS (auth_negotiate_class);
+	SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_negotiate_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (auth_negotiate_class);
+
+	g_type_class_add_private (auth_negotiate_class, sizeof (SoupAuthNegotiatePrivate));
+
+	auth_class->scheme_name = "Negotiate";
+	auth_class->strength = 7;
+	auth_class->update = update;
+	auth_class->get_protection_space = get_protection_space;
+	auth_class->authenticate = authenticate;
+	auth_class->is_authenticated = is_authenticated;
+	auth_class->get_authorization = get_authorization;
+
+	auth_connbased_class->authorize_pre = authorize_pre;
+	auth_connbased_class->authorize_post = authorize_post;
+	auth_connbased_class->authorize_request_started = authorize_request_started;
+	auth_connbased_class->get_response_header = get_response_header;
+
+	object_class->finalize = finalize;
+}
+
+SoupAuth *
+soup_auth_negotiate_new (const char *host)
+{
+	SoupAuth *auth;
+
+	auth = g_object_new (SOUP_TYPE_AUTH_NEGOTIATE,
+			     SOUP_AUTH_HOST, host,
+			     NULL);
+	return auth;
+}
+
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+	g_return_val_if_reached (FALSE);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+	SOUP_NEGOTIATE_DEBUG("prot space");
+
+	return g_slist_prepend(NULL, g_strdup (soup_auth_get_host(auth)));
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	g_return_if_fail (username != NULL);
+
+	priv->username = g_strdup (username);
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+	gboolean ret;
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	ret = priv->response_header ? TRUE : FALSE;
+	SOUP_NEGOTIATE_DEBUG("authenticated: %d", ret);
+	return ret;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+	g_return_val_if_reached (NULL);
+}
+
+const char *
+soup_auth_negotiate_get_username (SoupAuth *auth)
+{
+	return SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth)->username;
+}
+
+const char *
+soup_auth_negotiate_get_password (SoupAuth *auth)
+{
+	g_return_val_if_reached (NULL);
+}
+
+void
+soup_auth_negotiate_set_hosts (SoupAuth   *auth, gchar *hosts)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	priv->trusted_uris = negotiate_parse_trusted_uris(hosts);
+}
+
+static const char *
+get_response_header (SoupAuthConnBased *auth)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	return priv->response_header;
+}
+
+static gchar**
+negotiate_parse_trusted_uris(gchar *hosts)
+{
+	gchar **uris = NULL;
+
+	uris = g_strsplit (hosts, ",", -1);
+	return uris;
+}
+
+static gboolean
+match_base_uri (SoupMessage *msg, const gchar *trusted)
+{
+	SoupURI *uri;
+	gboolean ret = FALSE;
+
+	/* params of the trusted uri */
+	gchar **t_s_hp = NULL, **t_h_p = NULL;
+	const gchar *t_h = NULL;
+	gint t_h_len;
+
+	/* params of the msg's uri */
+	const gchar  *h = NULL;
+	gint port;
+	gint h_len;
+
+	uri = soup_message_get_uri (msg);
+	/* split trusted uri into scheme and host/port */
+	if (strstr (trusted, "://")) {
+		t_s_hp = g_strsplit (trusted, "://", -1);
+
+		/* The scheme has to match exactly */
+		if (g_ascii_strcasecmp (t_s_hp[0],
+				  soup_uri_get_scheme(uri))) {
+			goto out;
+		}
+		if (strlen(t_s_hp[1]) == 0) {
+			/* scheme only, we're done */
+			ret = TRUE;
+			goto out;
+		} else
+			t_h = t_s_hp[1];
+	} else {
+		t_h = trusted;
+	}
+
+	t_h_p = g_strsplit (t_h, ":", 2);
+	/* If we got a port in the trusted uri it has to match exactly */
+	if (g_strv_length(t_h_p) > 1) {
+		port = g_ascii_strtoll (t_h_p[1], NULL, 10);
+		if (port != soup_uri_get_port(uri)) {
+			goto out;
+		}
+	}
+
+	t_h = t_h_p[0];
+	h = soup_uri_get_host(uri);
+	if (g_str_has_suffix (h, t_h)) {
+	    /* if the msg host ends with host from the trusted uri, then make
+	     * sure it is either an exact match, or prefixed with a dot. We
+	     * don't want to match "foobar.com" to match "bar.com" */
+		if (g_strcmp0 (h, t_h) == 0) {
+			ret = TRUE;
+			goto out;
+		} else {
+			/* we don't want to example.com to match fooexample.com
+			 * */
+			t_h_len = strlen(t_h);
+			h_len = strlen(soup_uri_get_host(uri));
+			if (h[h_len-t_h_len-1] == '.') {
+				ret = TRUE;
+			}
+		}
+	}
+out:
+	g_strfreev (t_s_hp);
+	g_strfreev (t_h_p);
+	return ret;
+}
+
+static gboolean
+negotiate_auth_url (SoupMessage *msg, SoupAuth *auth)
+{
+	SoupAuthNegotiatePrivate *priv =
+		SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	int i;
+
+	g_return_val_if_fail (msg != NULL, FALSE);
+	g_return_val_if_fail (auth != NULL, FALSE);
+	g_return_val_if_fail (priv != NULL, FALSE);
+
+	if (!priv->trusted_uris) {
+		return FALSE;
+	}
+
+	if (g_ascii_strcasecmp (soup_auth_get_scheme_name(auth), "negotiate"))
+		return FALSE;
+
+	for (i=0; i < g_strv_length(priv->trusted_uris); i++) {
+		if (match_base_uri(msg, priv->trusted_uris[i])) {
+			SOUP_NEGOTIATE_DEBUG("Matched %s", priv->trusted_uris[i]);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+gboolean
+authorize_pre (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	SoupAuthNegotiatePrivate *priv =
+		SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	const char *val;
+	GValue value = { 0, { { 0 } } };
+
+	SOUP_NEGOTIATE_DEBUG_MSG(msg, "auth: %p, state %d", priv, priv->state);
+
+	val = soup_message_headers_get_list (msg->response_headers,
+					     "WWW-Authenticate");
+	if (val)
+		val = strstr (val, "Negotiate");
+	else
+		return FALSE;
+
+	if (priv->state > SOUP_NEGOTIATE_RECEIVED_RESPONSE) {
+		/* We already authenticated, but then got another 401.
+		 * That means "permission denied", so don't try to
+		 * authenticate again.
+		 */
+		priv->state = SOUP_NEGOTIATE_FAILED;
+		return FALSE;
+	}
+
+	priv->state = SOUP_NEGOTIATE_RECEIVED_CHALLENGE;
+
+	g_value_init (&value, G_TYPE_STRING);
+	g_assert (G_VALUE_HOLDS_STRING (&value));
+
+	g_value_set_static_string (&value, soup_message_get_uri (msg)->host);
+	g_object_set_property (G_OBJECT (auth),
+			       SOUP_AUTH_HOST,
+			       &value);
+
+	if (!negotiate_auth_url (msg, SOUP_AUTH (auth))) {
+		priv->state = SOUP_NEGOTIATE_FAILED;
+		return FALSE;
+	}
+
+	if (!soup_gss_client_init (SOUP_AUTH (auth))) {
+		priv->state = SOUP_NEGOTIATE_FAILED;
+		return FALSE;
+	}
+
+	soup_gss_client_step (SOUP_AUTH (auth), "");
+
+	return TRUE;
+}
+
+void
+authorize_post (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	SoupAuthNegotiatePrivate *priv =
+		SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	if (priv->state == SOUP_NEGOTIATE_RECEIVED_CHALLENGE)
+		output_negotiate_auth (msg, SOUP_AUTH (auth));
+
+	SOUP_NEGOTIATE_DEBUG_MSG (msg, "Conn: %p, current state: %d, Header: %s",
+		       priv, priv->state, priv->auth_header);
+
+}
+
+void
+authorize_request_started (SoupMessage *msg, SoupAuthConnBased *auth)
+{
+	SoupAuthNegotiatePrivate *priv =
+		SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+	SOUP_NEGOTIATE_DEBUG("New state: %d", priv->state);
+
+	switch (priv->state) {
+	case SOUP_NEGOTIATE_RECEIVED_CHALLENGE:
+		if (priv->response_header) {
+			SOUP_NEGOTIATE_DEBUG_MSG(msg, "Authdata: %s", priv->response_header);
+			soup_message_headers_replace (msg->request_headers,
+						      "Authorization",
+						      priv->response_header);
+			priv->state = SOUP_NEGOTIATE_SENT_RESPONSE;
+		}
+		else
+			priv->state = SOUP_NEGOTIATE_FAILED;
+		break;
+	default:
+		break;
+	}
+	g_free (priv->response_header);
+	priv->response_header = NULL;
+}
+
+static void
+output_negotiate_auth (SoupMessage *msg, SoupAuth *auth)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	const char *val;
+
+	if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
+		return;
+
+	if (priv->username) {
+		SOUP_NEGOTIATE_DEBUG_MSG(msg, "username already set.");
+		return;
+	}
+
+	val = soup_message_headers_get_list (msg->response_headers,
+					     "WWW-Authenticate");
+	if (val)
+		val = strstr (val, "Negotiate");
+	g_return_if_fail (val != NULL);
+
+	/* Found negotiate, do final step */
+	if (soup_gss_client_step (auth, val+10) != AUTH_GSS_COMPLETE) {
+		SOUP_NEGOTIATE_DEBUG("step failed");
+		/* FIXME: set error */
+	}
+	soup_gss_username(auth);
+
+	SOUP_NEGOTIATE_DEBUG("username %s", priv->username);
+}
+
+/* GSSAPI functions */
+
+static void
+soup_negotiate_error(OM_uint32 err_maj, OM_uint32 err_min)
+{
+	OM_uint32 maj_stat, min_stat, msg_ctx = 0;
+	gss_buffer_desc status;
+	char *buf_maj = NULL, *buf_min = NULL;
+
+	do {
+		maj_stat = gss_display_status (&min_stat,
+				err_maj,
+				GSS_C_GSS_CODE,
+				GSS_C_NO_OID,
+				&msg_ctx,
+				&status);
+		if (GSS_ERROR(maj_stat))
+			break;
+
+		g_free (buf_maj);
+		buf_maj = g_strdup((char*) status.value);
+		gss_release_buffer(&min_stat, &status);
+		/* FIXME: store it per connection so it can be queried */
+		SOUP_NEGOTIATE_DEBUG("%s", buf_maj);
+
+		maj_stat = gss_display_status (&min_stat,
+				err_min,
+				GSS_C_MECH_CODE,
+				GSS_C_NULL_OID,
+				&msg_ctx,
+				&status);
+		if (!GSS_ERROR(maj_stat)) {
+			g_free (buf_min);
+			buf_min = g_strdup((char*) status.value);
+			gss_release_buffer(&min_stat, &status);
+			/* FIXME: store it so it can be queried */
+			SOUP_NEGOTIATE_DEBUG("%s", buf_min);
+		}
+	} while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+}
+
+
+static int
+soup_gss_client_init (SoupAuth *auth)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	OM_uint32 maj_stat, min_stat;
+	char *service = NULL;
+	gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+	gboolean ret = FALSE;
+	gchar *host;
+
+	priv->server_name = GSS_C_NO_NAME;
+	priv->context = GSS_C_NO_CONTEXT;
+
+	host = g_ascii_strdown (soup_auth_get_host(auth), -1);
+	service = g_strconcat("HTTP/", host, NULL);
+
+	token.length = strlen(service);
+	token.value = (char *)service;
+
+	maj_stat = gss_import_name(&min_stat,
+				   &token,
+				   GSS_KRB5_NT_PRINCIPAL_NAME,
+				   &priv->server_name);
+
+	if (GSS_ERROR(maj_stat)) {
+		SOUP_NEGOTIATE_DEBUG("Error parsing name %s", service);
+		soup_negotiate_error(maj_stat, min_stat);
+		ret = FALSE;
+		goto out;
+	}
+
+	SOUP_NEGOTIATE_DEBUG("Init for service %s", service);
+	ret = TRUE;
+out:
+	g_free (host);
+	g_free (service);
+	return ret;
+}
+
+
+static int
+soup_gss_client_step(SoupAuth *auth, const char* challenge)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	OM_uint32 maj_stat, min_stat;
+	gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
+	gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
+	int ret = AUTH_GSS_CONTINUE;
+
+	if (priv->response_header != NULL) {
+		g_free(priv->response_header);
+		priv->response_header = NULL;
+	}
+
+	if (challenge && *challenge) {
+		int len;
+		in.value = g_base64_decode(challenge, &len);
+		in.length = len;
+	}
+
+	maj_stat = gss_init_sec_context(&min_stat,
+			GSS_C_NO_CREDENTIAL,
+			&priv->context,
+			priv->server_name,
+			GSS_C_NO_OID,
+			GSS_C_MUTUAL_FLAG,
+			GSS_C_INDEFINITE,
+			GSS_C_NO_CHANNEL_BINDINGS,
+			&in,
+			NULL,
+			&out,
+			NULL,
+			NULL);
+
+	if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+		SOUP_NEGOTIATE_DEBUG("step failed");
+		soup_negotiate_error(maj_stat, min_stat);
+		ret = AUTH_GSS_ERROR;
+		goto out;
+	}
+
+	ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+	if (out.length) {
+		char* response = g_base64_encode((const unsigned char *)out.value, out.length);
+		priv->response_header = g_strconcat("Negotiate ", response, NULL);
+		g_free(response);
+		maj_stat = gss_release_buffer(&min_stat, &out);
+	}
+
+out:
+	if (out.value)
+		gss_release_buffer (&min_stat, &out);
+	if (in.value)
+		g_free (in.value);
+	return ret;
+}
+
+
+static void
+soup_gss_username(SoupAuth *auth)
+{
+	SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+	OM_uint32 maj_stat, min_stat;
+	gss_buffer_desc token;
+	gss_name_t gssuser = GSS_C_NO_NAME;
+
+	maj_stat = gss_inquire_context(&min_stat,
+				       priv->context,
+				       &gssuser,
+				       NULL,
+				       NULL,
+				       NULL,
+				       NULL,
+				       NULL,
+				       NULL);
+	if (GSS_ERROR(maj_stat)) {
+		SOUP_NEGOTIATE_DEBUG("error getting context");
+		soup_negotiate_error(maj_stat, min_stat);
+		return;
+	}
+
+	token.length = 0;
+	maj_stat = gss_display_name(&min_stat, gssuser, &token, NULL);
+	if (GSS_ERROR(maj_stat)) {
+		if (token.value)
+			gss_release_buffer(&min_stat, &token);
+		gss_release_name(&min_stat, &gssuser);
+
+		SOUP_NEGOTIATE_DEBUG("error getting user name");
+		soup_negotiate_error(maj_stat, min_stat);
+	} else {
+		priv->username = g_strndup (token.value, token.length);
+		gss_release_buffer(&min_stat, &token);
+		gss_release_name(&min_stat, &gssuser);
+	}
+}
diff --git a/libsoup/soup-auth-negotiate.h b/libsoup/soup-auth-negotiate.h
new file mode 100644
index 0000000..e9e6543
--- /dev/null
+++ b/libsoup/soup-auth-negotiate.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifndef SOUP_AUTH_NEGOTIATE_H
+#define SOUP_AUTH_NEGOTIATE_H 1
+
+#include "soup-auth-connbased.h"
+
+#define SOUP_TYPE_AUTH_NEGOTIATE            (soup_auth_negotiate_get_type ())
+#define SOUP_AUTH_NEGOTIATE(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiate))
+#define SOUP_AUTH_NEGOTIATE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiateClass))
+#define SOUP_IS_AUTH_NEGOTIATE(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NEGOTIATE))
+#define SOUP_IS_AUTH_NEGOTIATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_NEGOTIATE))
+#define SOUP_AUTH_NEGOTIATE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiateClass))
+
+typedef struct {
+	SoupAuthConnBased parent;
+
+} SoupAuthNegotiate;
+
+typedef struct {
+	SoupAuthConnBasedClass parent_class;
+
+} SoupAuthNegotiateClass;
+
+GType soup_auth_negotiate_get_type (void);
+
+SoupAuth   *soup_auth_negotiate_new (const char* host);
+const char *soup_auth_negotiate_get_username (SoupAuth   *auth);
+const char *soup_auth_negotiate_get_password (SoupAuth   *auth);
+void        soup_auth_negotiate_set_hosts    (SoupAuth   *auth,
+					      gchar *hosts);
+
+#ifdef ENABLE_NEGOTIATE_DEBUG
+#define SOUP_NEGOTIATE_DEBUG(fmt,...) \
+	g_debug ("(%s): " fmt "\n", __func__, ##__VA_ARGS__)
+#define SOUP_NEGOTIATE_DEBUG_MSG(msg, fmt,...) \
+	g_debug ("(%s, %s): " fmt "\n", __func__, soup_message_get_uri (msg)->path, ##__VA_ARGS__)
+#else
+#define SOUP_NEGOTIATE_DEBUG(fmt,...) \
+	do { } while (0)
+#define SOUP_NEGOTIATE_DEBUG_MSG(msg, fmt,...) \
+	do { } while (0)
+#endif /* !ENABLE_DEBUG */
+
+
+#endif /* SOUP_AUTH_NEGOTIATE_H */
diff --git a/libsoup/soup.h b/libsoup/soup.h
index 97170a8..f90f595 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -12,6 +12,8 @@ extern "C" {
 
 #include <libsoup/soup-address.h>
 #include <libsoup/soup-auth.h>
+#include <libsoup/soup-auth-ntlm.h>
+#include <libsoup/soup-auth-negotiate.h>
 #include <libsoup/soup-auth-domain.h>
 #include <libsoup/soup-auth-domain-basic.h>
 #include <libsoup/soup-auth-domain-digest.h>
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index cd74b15..8cfccf8 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -22,6 +22,7 @@
 #include <glib.h>
 #include <libsoup/soup-address.h>
 #include <libsoup/soup-auth.h>
+#include <libsoup/soup-auth-ntlm.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
 #include <libsoup/soup-session-async.h>
