summaryrefslogtreecommitdiff
path: root/libsoup
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup')
-rw-r--r--libsoup/soup-auth.c250
-rw-r--r--libsoup/soup-auth.h58
-rw-r--r--libsoup/soup-context.c252
-rw-r--r--libsoup/soup-context.h4
-rw-r--r--libsoup/soup-message.c152
-rw-r--r--libsoup/soup-private.h22
-rw-r--r--libsoup/soup-queue.c33
7 files changed, 466 insertions, 305 deletions
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
index c8e33052..e9615ebe 100644
--- a/libsoup/soup-auth.c
+++ b/libsoup/soup-auth.c
@@ -62,6 +62,21 @@ basic_parse_func (SoupAuth *auth, const char *header)
soup_header_param_destroy_hash (tokens);
}
+static GSList *
+basic_pspace_func (SoupAuth *auth, const SoupUri *source_uri)
+{
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip query and filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
+}
+
static void
basic_init_func (SoupAuth *auth, const SoupUri *uri)
{
@@ -71,6 +86,20 @@ basic_init_func (SoupAuth *auth, const SoupUri *uri)
user_pass = g_strdup_printf ("%s:%s", uri->user, uri->passwd);
basic->token = soup_base64_encode (user_pass, strlen (user_pass));
g_free (user_pass);
+
+ auth->authenticated = TRUE;
+}
+
+static gboolean
+basic_invalidate_func (SoupAuth *auth)
+{
+ SoupAuthBasic *basic = (SoupAuthBasic *) auth;
+
+ g_free (basic->token);
+ basic->token = NULL;
+ auth->authenticated = FALSE;
+
+ return TRUE;
}
static void
@@ -91,9 +120,12 @@ soup_auth_new_basic (void)
basic = g_new0 (SoupAuthBasic, 1);
auth = (SoupAuth *) basic;
auth->type = SOUP_AUTH_TYPE_BASIC;
+ auth->authenticated = FALSE;
auth->parse_func = basic_parse_func;
auth->init_func = basic_init_func;
+ auth->invalidate_func = basic_invalidate_func;
+ auth->pspace_func = basic_pspace_func;
auth->auth_func = basic_auth_func;
auth->free_func = basic_free;
@@ -126,6 +158,7 @@ typedef struct {
char *nonce;
QOPType qop_options;
AlgorithmType algorithm;
+ char *domain;
/* These are generated by the client */
char *cnonce;
@@ -331,6 +364,7 @@ digest_parse_func (SoupAuth *auth, const char *header)
auth->realm = soup_header_param_copy_token (tokens, "realm");
digest->nonce = soup_header_param_copy_token (tokens, "nonce");
+ digest->domain = soup_header_param_copy_token (tokens, "domain");
tmp = soup_header_param_copy_token (tokens, "qop");
ptr = tmp;
@@ -356,6 +390,50 @@ digest_parse_func (SoupAuth *auth, const char *header)
soup_header_param_destroy_hash (tokens);
}
+static GSList *
+digest_pspace_func (SoupAuth *auth, const SoupUri *source_uri)
+{
+ SoupAuthDigest *digest = (SoupAuthDigest *) auth;
+ GSList *space = NULL;
+ SoupUri *uri;
+ char *domain, *d, *lasts, *dir, *slash;
+
+ if (!digest->domain || !*digest->domain) {
+ /* If no domain directive, the protection space is the
+ * whole server.
+ */
+ return g_slist_prepend (NULL, g_strdup (""));
+ }
+
+ domain = g_strdup (digest->domain);
+ for (d = strtok_r (domain, " ", &lasts); d; d = strtok_r (NULL, " ", &lasts)) {
+ if (*d == '/')
+ dir = g_strdup (d);
+ else {
+ uri = soup_uri_new (d);
+ if (uri && uri->protocol == source_uri->protocol &&
+ uri->port == source_uri->port &&
+ !strcmp (uri->host, source_uri->host))
+ dir = g_strdup (uri->path);
+ else
+ dir = NULL;
+ if (uri)
+ soup_uri_free (uri);
+ }
+
+ if (dir) {
+ slash = strrchr (dir, '/');
+ if (slash && !slash[1])
+ *slash = '\0';
+
+ space = g_slist_prepend (space, dir);
+ }
+ }
+ g_free (domain);
+
+ return space;
+}
+
static void
digest_init_func (SoupAuth *auth, const SoupUri *uri)
{
@@ -392,6 +470,17 @@ digest_init_func (SoupAuth *auth, const SoupUri *uri)
/* hexify A1 */
md5_final (&ctx, d);
digest_hex (d, digest->hex_a1);
+
+ auth->authenticated = TRUE;
+}
+
+static gboolean
+digest_invalidate_func (SoupAuth *auth)
+{
+ /* If we failed, we need to get a new nonce from the server
+ * next time, so this can't be reused.
+ */
+ return FALSE;
}
static void
@@ -400,6 +489,7 @@ digest_free (SoupAuth *auth)
SoupAuthDigest *digest = (SoupAuthDigest *) auth;
g_free (digest->user);
+ g_free (digest->domain);
g_free (digest->nonce);
g_free (digest->cnonce);
@@ -417,9 +507,12 @@ soup_auth_new_digest (void)
auth = (SoupAuth *) digest;
auth->type = SOUP_AUTH_TYPE_DIGEST;
+ auth->authenticated = FALSE;
auth->parse_func = digest_parse_func;
auth->init_func = digest_init_func;
+ auth->invalidate_func = digest_invalidate_func;
+ auth->pspace_func = digest_pspace_func;
auth->auth_func = digest_auth_func;
auth->free_func = digest_free;
@@ -452,15 +545,14 @@ static gchar *
ntlm_auth (SoupAuth *sa, SoupMessage *msg)
{
SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
- gchar *ret;
+ char *ret;
- if (sa->status == SOUP_AUTH_STATUS_PENDING)
+ if (!sa->authenticated)
return soup_ntlm_request ();
/* Otherwise, return the response; but only once */
ret = auth->response;
auth->response = NULL;
-
return ret;
}
@@ -495,13 +587,20 @@ ntlm_parse (SoupAuth *sa, const char *header)
g_strstrip (auth->header);
}
+static GSList *
+ntlm_pspace (SoupAuth *auth, const SoupUri *source_uri)
+{
+ /* The protection space is the whole server. */
+ return g_slist_prepend (NULL, g_strdup (""));
+}
+
static void
ntlm_init (SoupAuth *sa, const SoupUri *uri)
{
SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
gchar *host, *domain, *nonce;
- if (strlen (auth->header) < sizeof ("NTLM"))
+ if (!auth->header || strlen (auth->header) < sizeof ("NTLM"))
return;
if (auth->response)
@@ -525,14 +624,24 @@ ntlm_init (SoupAuth *sa, const SoupUri *uri)
g_free (host);
g_free (domain);
- /* Set this now so that if the server returns 401,
- * soup will fail instead of looping (since that
- * probably means the password was incorrect).
- */
- sa->status = SOUP_AUTH_STATUS_SUCCESSFUL;
+ g_free (auth->header);
+ auth->header = NULL;
+
+ sa->authenticated = TRUE;
+}
+
+static gboolean
+ntlm_invalidate (SoupAuth *sa)
+{
+ SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
+ g_free (auth->response);
+ auth->response = NULL;
g_free (auth->header);
auth->header = NULL;
+
+ sa->authenticated = FALSE;
+ return TRUE;
}
static void
@@ -545,16 +654,20 @@ ntlm_free (SoupAuth *sa)
g_free (auth);
}
-static SoupAuth *
-ntlm_new (void)
+SoupAuth *
+soup_auth_new_ntlm (void)
{
SoupAuthNTLM *auth;
auth = g_new0 (SoupAuthNTLM, 1);
auth->auth.type = SOUP_AUTH_TYPE_NTLM;
+ auth->auth.authenticated = FALSE;
+ auth->auth.realm = g_strdup ("");
auth->auth.parse_func = ntlm_parse;
auth->auth.init_func = ntlm_init;
+ auth->auth.invalidate_func = ntlm_invalidate;
+ auth->auth.pspace_func = ntlm_pspace;
auth->auth.auth_func = ntlm_auth;
auth->auth.free_func = ntlm_free;
@@ -566,92 +679,6 @@ ntlm_new (void)
* Generic Authentication Interface
*/
-SoupAuth *
-soup_auth_lookup (SoupContext *ctx)
-{
- GHashTable *auth_hash = ctx->server->valid_auths;
- SoupAuth *ret = NULL;
- gchar *mypath, *dir;
-
- if (!auth_hash) return NULL;
-
- mypath = g_strdup (ctx->uri->path);
- dir = mypath;
-
- do {
- ret = g_hash_table_lookup (auth_hash, mypath);
- if (ret) break;
-
- dir = strrchr (mypath, '/');
- if (dir) *dir = '\0';
- } while (dir);
-
- g_free (mypath);
- return ret;
-}
-
-void
-soup_auth_invalidate (SoupAuth *auth, SoupContext *ctx)
-{
- SoupHost *server;
- const SoupUri *uri;
- SoupAuth *old_auth;
- char *old_path;
-
- g_return_if_fail (ctx != NULL);
- g_return_if_fail (auth != NULL);
-
- server = ctx->server;
-
- if (!server->valid_auths)
- return;
-
- uri = soup_context_get_uri (ctx);
- if (g_hash_table_lookup_extended (server->valid_auths,
- uri->path,
- (gpointer *) &old_path,
- (gpointer *) &old_auth)) {
- g_hash_table_remove (server->valid_auths, old_path);
- g_free (old_path);
- soup_auth_free (old_auth);
- }
-}
-
-void
-soup_auth_set_context (SoupAuth *auth, SoupContext *ctx)
-{
- SoupHost *server;
- SoupAuth *old_auth = NULL;
- gchar *old_path;
- const SoupUri *uri;
-
- g_return_if_fail (ctx != NULL);
- g_return_if_fail (auth != NULL);
-
- server = ctx->server;
- uri = soup_context_get_uri (ctx);
-
- if (!server->valid_auths) {
- server->valid_auths = g_hash_table_new (g_str_hash,
- g_str_equal);
- }
- else if (g_hash_table_lookup_extended (server->valid_auths,
- uri->path,
- (gpointer *) &old_path,
- (gpointer *) &old_auth)) {
- if (auth == old_auth)
- return;
-
- g_hash_table_remove (server->valid_auths, old_path);
- g_free (old_path);
- soup_auth_free (old_auth);
- }
-
- g_hash_table_insert (server->valid_auths,
- g_strdup (uri->path),
- auth);
-}
-
typedef SoupAuth *(*SoupAuthNewFn) (void);
typedef struct {
@@ -662,7 +689,7 @@ typedef struct {
static AuthScheme known_auth_schemes [] = {
{ "Basic", soup_auth_new_basic, 0 },
- { "NTLM", ntlm_new, 2 },
+ { "NTLM", soup_auth_new_ntlm, 2 },
{ "Digest", soup_auth_new_digest, 3 },
{ NULL }
};
@@ -727,6 +754,14 @@ soup_auth_initialize (SoupAuth *auth, const SoupUri *uri)
auth->init_func (auth, uri);
}
+gboolean
+soup_auth_invalidate (SoupAuth *auth)
+{
+ g_return_val_if_fail (auth != NULL, FALSE);
+
+ return auth->invalidate_func (auth);
+}
+
gchar *
soup_auth_authorize (SoupAuth *auth, SoupMessage *msg)
{
@@ -744,3 +779,22 @@ soup_auth_free (SoupAuth *auth)
g_free (auth->realm);
auth->free_func (auth);
}
+
+GSList *
+soup_auth_get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+{
+ g_return_val_if_fail (auth != NULL, NULL);
+ g_return_val_if_fail (source_uri != NULL, NULL);
+
+ return auth->pspace_func (auth, source_uri);
+}
+
+void
+soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
+{
+ GSList *s;
+
+ for (s = space; s; s = s->next)
+ g_free (s->data);
+ g_slist_free (space);
+}
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index e1c919d9..756029ea 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -18,49 +18,47 @@
typedef enum _SoupAuthStatus SoupAuthStatus;
typedef struct _SoupAuth SoupAuth;
-enum _SoupAuthStatus {
- SOUP_AUTH_STATUS_INVALID = 0,
- SOUP_AUTH_STATUS_PENDING,
- SOUP_AUTH_STATUS_FAILED,
- SOUP_AUTH_STATUS_SUCCESSFUL
-};
-
struct _SoupAuth {
- SoupAuthType type;
- gchar *realm;
+ SoupAuthType type;
+ char *realm;
+ gboolean authenticated;
- SoupAuthStatus status;
- SoupMessage *controlling_msg;
+ void (*parse_func) (SoupAuth *auth,
+ const gchar *header);
- void (*parse_func) (SoupAuth *auth,
- const gchar *header);
+ void (*init_func) (SoupAuth *auth,
+ const SoupUri *uri);
- void (*init_func) (SoupAuth *auth,
- const SoupUri *uri);
+ gboolean (*invalidate_func) (SoupAuth *auth);
- char *(*auth_func) (SoupAuth *auth,
- SoupMessage *message);
+ char *(*auth_func) (SoupAuth *auth,
+ SoupMessage *message);
- void (*free_func) (SoupAuth *auth);
+ GSList *(*pspace_func) (SoupAuth *auth,
+ const SoupUri *source_uri);
+
+ void (*free_func) (SoupAuth *auth);
};
-SoupAuth *soup_auth_lookup (SoupContext *ctx);
+SoupAuth *soup_auth_new_from_header_list (const SoupUri *uri,
+ const GSList *header);
+
+SoupAuth *soup_auth_new_ntlm (void);
-void soup_auth_set_context (SoupAuth *auth,
- SoupContext *ctx);
+void soup_auth_initialize (SoupAuth *auth,
+ const SoupUri *uri);
-void soup_auth_invalidate (SoupAuth *auth,
- SoupContext *ctx);
+gboolean soup_auth_invalidate (SoupAuth *auth);
-SoupAuth *soup_auth_new_from_header_list (const SoupUri *uri,
- const GSList *header);
+void soup_auth_free (SoupAuth *auth);
-void soup_auth_initialize (SoupAuth *auth,
- const SoupUri *uri);
+gchar *soup_auth_authorize (SoupAuth *auth,
+ SoupMessage *msg);
-void soup_auth_free (SoupAuth *auth);
+GSList *soup_auth_get_protection_space (SoupAuth *auth,
+ const SoupUri *source_uri);
+void soup_auth_free_protection_space (SoupAuth *auth,
+ GSList *space);
-gchar *soup_auth_authorize (SoupAuth *auth,
- SoupMessage *msg);
#endif /* SOUP_AUTH_H */
diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c
index e5dc6e9a..4c4cd30e 100644
--- a/libsoup/soup-context.c
+++ b/libsoup/soup-context.c
@@ -193,13 +193,18 @@ soup_context_ref (SoupContext *ctx)
ctx->refcnt++;
}
-static gboolean
-remove_auth (gchar *path, SoupAuth *auth)
+static void
+free_path (gpointer path, gpointer realm, gpointer unused)
{
g_free (path);
- soup_auth_free (auth);
+ g_free (realm);
+}
- return TRUE;
+static void
+free_auth (gpointer realm, gpointer auth, gpointer unused)
+{
+ g_free (realm);
+ soup_auth_free (auth);
}
/**
@@ -231,12 +236,15 @@ soup_context_unref (SoupContext *ctx)
/*
* Free all cached SoupAuths
*/
- if (serv->valid_auths) {
- g_hash_table_foreach_remove (
- serv->valid_auths,
- (GHRFunc) remove_auth,
- NULL);
- g_hash_table_destroy (serv->valid_auths);
+ if (serv->auth_realms) {
+ g_hash_table_foreach (serv->auth_realms,
+ free_path, NULL);
+ g_hash_table_destroy (serv->auth_realms);
+ }
+ if (serv->auths) {
+ g_hash_table_foreach (serv->auths,
+ free_auth, NULL);
+ g_hash_table_destroy (serv->auths);
}
g_hash_table_destroy (serv->contexts);
@@ -257,10 +265,8 @@ connection_free (SoupConnection *conn)
conn->server->connections =
g_slist_remove (conn->server->connections, conn);
- if (conn->auth) {
- soup_auth_invalidate (conn->auth, conn->context);
+ if (conn->auth)
soup_auth_free (conn->auth);
- }
g_io_channel_unref (conn->channel);
soup_context_unref (conn->context);
@@ -770,3 +776,223 @@ soup_connection_purge_idle (void)
connection_free (i->data);
g_slist_free (idle_conns);
}
+
+
+/* Authentication */
+
+SoupAuth *
+soup_context_lookup_auth (SoupContext *ctx, SoupMessage *msg)
+{
+ char *path, *dir;
+ const char *realm;
+
+ g_return_val_if_fail (ctx != NULL, NULL);
+
+ if (ctx->server->use_ntlm && msg && msg->connection) {
+ if (!msg->connection->auth)
+ msg->connection->auth = soup_auth_new_ntlm ();
+ return msg->connection->auth;
+ }
+
+ if (!ctx->server->auth_realms)
+ return NULL;
+
+ path = g_strdup (ctx->uri->path);
+ dir = path;
+ do {
+ realm = g_hash_table_lookup (ctx->server->auth_realms, path);
+ if (realm)
+ break;
+
+ dir = strrchr (path, '/');
+ if (dir)
+ *dir = '\0';
+ } while (dir);
+
+ g_free (path);
+ if (realm)
+ return g_hash_table_lookup (ctx->server->auths, realm);
+ else
+ return NULL;
+}
+
+static gboolean
+update_auth_internal (SoupContext *ctx, SoupConnection *conn,
+ const GSList *headers, gboolean prior_auth_failed)
+{
+ SoupHost *server = ctx->server;
+ SoupAuth *new_auth, *prior_auth, *old_auth;
+ gpointer old_path, old_realm;
+ const char *path;
+ char *realm;
+ GSList *pspace, *p;
+
+ if (server->use_ntlm && conn && conn->auth) {
+ if (conn->auth->authenticated) {
+ /* This is a "permission denied", not a
+ * "password incorrect". There's nothing more
+ * we can do.
+ */
+ return FALSE;
+ }
+
+ /* Free the intermediate auth */
+ soup_auth_free (conn->auth);
+ conn->auth = NULL;
+ }
+
+ /* Try to construct a new auth from the headers; if we can't,
+ * there's no way we'll be able to authenticate.
+ */
+ new_auth = soup_auth_new_from_header_list (ctx->uri, headers);
+ if (!new_auth)
+ return FALSE;
+
+ /* See if this auth is the same auth we used last time */
+ prior_auth = soup_context_lookup_auth (ctx, NULL);
+ if (prior_auth && prior_auth->type == new_auth->type &&
+ !strcmp (prior_auth->realm, new_auth->realm)) {
+ soup_auth_free (new_auth);
+ if (prior_auth_failed) {
+ /* The server didn't like the username/password
+ * we provided before.
+ */
+ soup_context_invalidate_auth (ctx, prior_auth);
+ return FALSE;
+ } else {
+ /* The user is trying to preauthenticate using
+ * information we already have, so there's nothing
+ * that needs to be done.
+ */
+ return TRUE;
+ }
+ }
+
+ if (new_auth->type == SOUP_AUTH_TYPE_NTLM) {
+ server->use_ntlm = TRUE;
+ if (conn) {
+ conn->auth = new_auth;
+ return soup_context_authenticate_auth (ctx, new_auth);
+ } else {
+ soup_auth_free (new_auth);
+ return FALSE;
+ }
+ }
+
+ if (!server->auth_realms) {
+ server->auth_realms = g_hash_table_new (g_str_hash, g_str_equal);
+ server->auths = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ /* Record where this auth realm is used */
+ realm = g_strdup_printf ("%d:%s", new_auth->type, new_auth->realm);
+ pspace = soup_auth_get_protection_space (new_auth, ctx->uri);
+ for (p = pspace; p; p = p->next) {
+ path = p->data;
+ if (g_hash_table_lookup_extended (server->auth_realms, path,
+ &old_path, &old_realm)) {
+ g_hash_table_remove (server->auth_realms, old_path);
+ g_free (old_path);
+ g_free (old_realm);
+ }
+
+ g_hash_table_insert (server->auth_realms,
+ g_strdup (path), g_strdup (realm));
+ }
+ soup_auth_free_protection_space (new_auth, pspace);
+
+ /* Now, make sure the auth is recorded. (If there's a
+ * pre-existing auth, we keep that rather than the new one,
+ * since the old one might already be authenticated.)
+ */
+ old_auth = g_hash_table_lookup (server->auths, realm);
+ if (old_auth) {
+ g_free (realm);
+ soup_auth_free (new_auth);
+ new_auth = old_auth;
+ } else
+ g_hash_table_insert (server->auths, realm, new_auth);
+
+ /* Try to authenticate if needed. */
+ if (!new_auth->authenticated)
+ return soup_context_authenticate_auth (ctx, new_auth);
+
+ return TRUE;
+}
+
+gboolean
+soup_context_update_auth (SoupContext *ctx, SoupMessage *msg)
+{
+ const GSList *headers;
+
+ g_return_val_if_fail (ctx != NULL, FALSE);
+ g_return_val_if_fail (msg != NULL, FALSE);
+
+ if (msg->errorcode == SOUP_ERROR_PROXY_UNAUTHORIZED) {
+ headers = soup_message_get_header_list (msg->response_headers,
+ "Proxy-Authenticate");
+ } else {
+ headers = soup_message_get_header_list (msg->response_headers,
+ "WWW-Authenticate");
+ }
+
+ return update_auth_internal (ctx, msg->connection, headers, TRUE);
+}
+
+void
+soup_context_preauthenticate (SoupContext *ctx, const char *header)
+{
+ GSList *headers;
+
+ g_return_if_fail (ctx != NULL);
+ g_return_if_fail (header != NULL);
+
+ headers = g_slist_append (NULL, (char *)header);
+ update_auth_internal (ctx, NULL, headers, FALSE);
+ g_slist_free (headers);
+}
+
+gboolean
+soup_context_authenticate_auth (SoupContext *ctx, SoupAuth *auth)
+{
+ const SoupUri *uri = ctx->uri;
+
+ if (!uri->user && soup_auth_fn) {
+ (*soup_auth_fn) (auth->type,
+ (SoupUri *) uri,
+ auth->realm,
+ soup_auth_fn_user_data);
+ }
+
+ if (!uri->user)
+ return FALSE;
+
+ soup_auth_initialize (auth, uri);
+ return TRUE;
+}
+
+void
+soup_context_invalidate_auth (SoupContext *ctx, SoupAuth *auth)
+{
+ char *realm;
+ gpointer key, value;
+
+ g_return_if_fail (ctx != NULL);
+ g_return_if_fail (auth != NULL);
+
+ /* Try to just clean up the auth without removing it. */
+ if (soup_auth_invalidate (auth))
+ return;
+
+ /* Nope, need to remove it completely */
+ realm = g_strdup_printf ("%d:%s", auth->type, auth->realm);
+
+ if (g_hash_table_lookup_extended (ctx->server->auths, realm,
+ &key, &value) &&
+ auth == (SoupAuth *)value) {
+ g_hash_table_remove (ctx->server->auths, realm);
+ g_free (key);
+ soup_auth_free (auth);
+ }
+ g_free (realm);
+}
diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h
index 581dd841..79853d11 100644
--- a/libsoup/soup-context.h
+++ b/libsoup/soup-context.h
@@ -65,4 +65,8 @@ void soup_connection_release (SoupConnection *conn);
void soup_connection_purge_idle (void);
+
+void soup_context_preauthenticate (SoupContext *ctx,
+ const char *header);
+
#endif /*SOUP_CONTEXT_H*/
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 92079c1d..254394e7 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -8,6 +8,8 @@
* Copyright (C) 2000-2002, Ximian, Inc.
*/
+#include <string.h>
+
#include "soup-auth.h"
#include "soup-error.h"
#include "soup-message.h"
@@ -623,157 +625,19 @@ soup_message_send (SoupMessage *msg)
}
static void
-maybe_validate_auth (SoupMessage *msg, gpointer user_data)
-{
- gboolean proxy = GPOINTER_TO_INT (user_data);
- int auth_failure;
- SoupContext *ctx;
- SoupAuth *auth;
-
- if (proxy) {
- ctx = soup_get_proxy ();
- auth_failure = SOUP_ERROR_PROXY_UNAUTHORIZED; /* 407 */
- }
- else {
- ctx = msg->context;
- auth_failure = SOUP_ERROR_UNAUTHORIZED; /* 401 */
- }
-
- auth = soup_auth_lookup (ctx);
- if (!auth)
- return;
-
- if (msg->errorcode == auth_failure) {
- /* Pass through */
- }
- else if (msg->errorclass == SOUP_ERROR_CLASS_SERVER_ERROR) {
- /*
- * We have no way of knowing whether our auth is any good
- * anymore, so just invalidate it and start from the
- * beginning next time.
- */
- soup_auth_invalidate (auth, ctx);
- }
- else {
- auth->status = SOUP_AUTH_STATUS_SUCCESSFUL;
- auth->controlling_msg = NULL;
- }
-} /* maybe_validate_auth */
-
-static void
authorize_handler (SoupMessage *msg, gboolean proxy)
{
- const GSList *vals;
- SoupAuth *auth;
SoupContext *ctx;
- const SoupUri *uri;
-
- if (msg->connection->auth &&
- msg->connection->auth->status == SOUP_AUTH_STATUS_SUCCESSFUL)
- goto THROW_CANT_AUTHENTICATE;
ctx = proxy ? soup_get_proxy () : msg->context;
- uri = soup_context_get_uri (ctx);
-
- vals = soup_message_get_header_list (msg->response_headers,
- proxy ?
- "Proxy-Authenticate" :
- "WWW-Authenticate");
- if (!vals) goto THROW_CANT_AUTHENTICATE;
-
- auth = soup_auth_lookup (ctx);
- if (auth && auth->type == SOUP_AUTH_TYPE_NTLM)
- auth = NULL;
-
- if (auth) {
- g_assert (auth->status != SOUP_AUTH_STATUS_INVALID);
-
- if (auth->status == SOUP_AUTH_STATUS_PENDING) {
- if (auth->controlling_msg == msg) {
- auth->status = SOUP_AUTH_STATUS_FAILED;
- goto THROW_CANT_AUTHENTICATE;
- }
- else {
- soup_message_requeue (msg);
- return;
- }
- }
- else if (auth->status == SOUP_AUTH_STATUS_FAILED ||
- auth->status == SOUP_AUTH_STATUS_SUCCESSFUL) {
- /*
- * We've failed previously, but we'll give it
- * another go, or we've been successful
- * previously, but it's not working anymore.
- *
- * Invalidate the auth, so it's removed from the
- * hash and try it again as if we never had it
- * in the first place.
- */
- soup_auth_invalidate (auth, ctx);
- soup_message_requeue (msg);
- return;
- }
- }
-
- if (!auth) {
- auth = soup_auth_new_from_header_list (uri, vals);
-
- if (!auth) {
- soup_message_set_error_full (
- msg,
- proxy ?
- SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
- SOUP_ERROR_CANT_AUTHENTICATE,
- proxy ?
- "Unknown authentication scheme "
- "required by proxy" :
- "Unknown authentication scheme "
- "required");
- return;
- }
-
- auth->status = SOUP_AUTH_STATUS_PENDING;
- auth->controlling_msg = msg;
- soup_message_add_handler (msg, SOUP_HANDLER_PRE_BODY,
- maybe_validate_auth,
- GINT_TO_POINTER (proxy));
- }
-
- /*
- * Call registered authenticate handler
- */
- if (!uri->user && soup_auth_fn)
- (*soup_auth_fn) (auth->type,
- (SoupUri *) uri,
- auth->realm,
- soup_auth_fn_user_data);
-
- if (!uri->user) {
- soup_auth_free (auth);
- goto THROW_CANT_AUTHENTICATE;
- }
-
- /*
- * Initialize with auth data (possibly returned from
- * auth callback).
- */
- soup_auth_initialize (auth, uri);
-
- if (auth->type == SOUP_AUTH_TYPE_NTLM &&
- auth->status == SOUP_AUTH_STATUS_SUCCESSFUL)
- msg->connection->auth = auth;
- else
- soup_auth_set_context (auth, ctx);
-
- soup_message_requeue (msg);
-
- return;
-
- THROW_CANT_AUTHENTICATE:
- soup_message_set_error (msg,
- proxy ?
+ if (soup_context_update_auth (ctx, msg))
+ soup_message_requeue (msg);
+ else {
+ soup_message_set_error (msg,
+ proxy ?
SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
SOUP_ERROR_CANT_AUTHENTICATE);
+ }
}
static void
diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h
index 50db591d..d67535c4 100644
--- a/libsoup/soup-private.h
+++ b/libsoup/soup-private.h
@@ -46,9 +46,11 @@ extern gpointer soup_auth_fn_user_data;
typedef struct {
gchar *host;
- GSList *connections; /* CONTAINS: SoupConnection */
- GHashTable *contexts; /* KEY: uri->path, VALUE: SoupContext */
- GHashTable *valid_auths; /* KEY: uri->path, VALUE: SoupAuth */
+ GSList *connections; /* CONTAINS: SoupConnection */
+ GHashTable *contexts; /* KEY: uri->path, VALUE: SoupContext */
+ gboolean use_ntlm;
+ GHashTable *auth_realms; /* KEY: uri->path, VALUE: scheme:realm */
+ GHashTable *auths; /* KEY: scheme:realm, VALUE: SoupAuth */
} SoupHost;
struct _SoupSocket {
@@ -124,6 +126,20 @@ struct _SoupMessagePrivate {
SoupServerMessage *server_msg;
};
+/* from soup-context.c */
+
+SoupAuth *soup_context_lookup_auth (SoupContext *ctx,
+ SoupMessage *msg);
+
+gboolean soup_context_update_auth (SoupContext *ctx,
+ SoupMessage *msg);
+
+gboolean soup_context_authenticate_auth (SoupContext *ctx,
+ SoupAuth *auth);
+
+void soup_context_invalidate_auth (SoupContext *ctx,
+ SoupAuth *auth);
+
/* from soup-message.c */
void soup_message_issue_callback (SoupMessage *req);
diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c
index 76955839..7e5060c0 100644
--- a/libsoup/soup-queue.c
+++ b/libsoup/soup-queue.c
@@ -295,23 +295,22 @@ soup_encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
ctx = proxy_auth ? soup_get_proxy () : msg->context;
- if (msg->connection->auth)
- auth = msg->connection->auth;
- else
- auth = soup_auth_lookup (ctx);
-
- if (auth) {
- token = soup_auth_authorize (auth, msg);
- if (token) {
- g_string_sprintfa (header,
- "%s: %s\r\n",
- proxy_auth ?
- "Proxy-Authorization" :
- "Authorization",
- token);
- g_free (token);
- }
- }
+ auth = soup_context_lookup_auth (ctx, msg);
+ if (!auth)
+ return;
+ if (!auth->authenticated &&
+ !soup_context_authenticate_auth (ctx, auth))
+ return;
+
+ token = soup_auth_authorize (auth, msg);
+ if (token) {
+ g_string_sprintfa (header, "%s: %s\r\n",
+ proxy_auth ?
+ "Proxy-Authorization" :
+ "Authorization",
+ token);
+ g_free (token);
+ }
}
struct SoupUsedHeaders {