diff options
Diffstat (limited to 'libsoup')
-rw-r--r-- | libsoup/soup-auth.c | 250 | ||||
-rw-r--r-- | libsoup/soup-auth.h | 58 | ||||
-rw-r--r-- | libsoup/soup-context.c | 252 | ||||
-rw-r--r-- | libsoup/soup-context.h | 4 | ||||
-rw-r--r-- | libsoup/soup-message.c | 152 | ||||
-rw-r--r-- | libsoup/soup-private.h | 22 | ||||
-rw-r--r-- | libsoup/soup-queue.c | 33 |
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 { |