New client authentication (basic/digest) code from Joe Shaw
authorAlex Graveley <alex@ximian.com>
Mon, 9 Jul 2001 20:37:52 +0000 (20:37 +0000)
committerAlex Graveley <orph@src.gnome.org>
Mon, 9 Jul 2001 20:37:52 +0000 (20:37 +0000)
2001-07-09  Alex Graveley  <alex@ximian.com>

* src/soup-core/soup-auth.[ch]: New client authentication (basic/digest)
code from Joe Shaw (joe@ximian.com).

* src/soup-core/soup-queue.c (soup_encode_http_auth): Updated to
use soup-auth.
(soup_get_request_header): Pass the SoupMessage to
soup_encode_http_auth instead of just the URI. Check for
req->action in header creation.
(soup_queue_error_cb): Reset read_tag and write_tag to avoid
double free.
(soup_encode_http_auth): Use soup_auth_authorize().

* src/soup-core/soup-private.h: Add SoupAuth to SoupContext.

* src/soup-core/soup-context.c (soup_context_unref): Free auth.

* src/soup-core/soup-cgi.c: Flog.

* src/soup-core/soup-message.c (soup_message_new): Create handlers
for 401 (Authorization Required) and 407 (Proxy-Authorization
Required) response codes.
(soup_message_redirect): Rename to redirect_handler.
(redirect_handler): Don't unref existing context if new context
creation fails.
(soup_message_set_header): Check for value before insertion.

* src/soup-core/soup-transfer.c (soup_transfer_read_cancel): Free
recv_buf contents if no callback has been issued.
(soup_transfer_read_cb): Set callback_issued.

ChangeLog
libsoup/Makefile.am
libsoup/soup-auth.c [new file with mode: 0644]
libsoup/soup-auth.h [new file with mode: 0644]
libsoup/soup-context.c
libsoup/soup-message.c
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-transfer.c

index 0dead07..278a86a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2001-07-09  Alex Graveley  <alex@ximian.com>
+
+       * src/soup-core/soup-auth.[ch]: New client authentication (basic/digest)
+       code from Joe Shaw (joe@ximian.com).
+
+       * src/soup-core/soup-queue.c (soup_encode_http_auth): Updated to
+       use soup-auth.
+       (soup_get_request_header): Pass the SoupMessage to
+       soup_encode_http_auth instead of just the URI. Check for
+       req->action in header creation.
+       (soup_queue_error_cb): Reset read_tag and write_tag to avoid
+       double free.
+       (soup_encode_http_auth): Use soup_auth_authorize().
+
+       * src/soup-core/soup-private.h: Add SoupAuth to SoupContext.
+
+       * src/soup-core/soup-context.c (soup_context_unref): Free auth.
+
+       * src/soup-core/soup-cgi.c: Flog.
+
+       * src/soup-core/soup-message.c (soup_message_new): Create handlers
+       for 401 (Authorization Required) and 407 (Proxy-Authorization
+       Required) response codes.
+       (soup_message_redirect): Rename to redirect_handler.
+       (redirect_handler): Don't unref existing context if new context
+       creation fails.
+       (soup_message_set_header): Check for value before insertion.
+
+       * src/soup-core/soup-transfer.c (soup_transfer_read_cancel): Free
+       recv_buf contents if no callback has been issued.
+       (soup_transfer_read_cb): Set callback_issued.
+
 2001-07-05  Alex Graveley  <alex@ximian.com>
 
        * src/soup-core/soup-socks.c (soup_connect_socks_proxy): Use const uris.
index 5158382..3eba6ae 100644 (file)
@@ -39,9 +39,9 @@ libsoup_la_LIBADD =           \
 libsoup_la_SOURCES =           \
        md5-utils.h             \
        md5-utils.c             \
+       soup-auth.h             \
+       soup-auth.c             \
        soup-context.c          \
-       soup-digest.h           \
-       soup-digest.c           \
        soup-env.c              \
        soup-fault.c            \
        soup-headers.h          \
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
new file mode 100644 (file)
index 0000000..6c90183
--- /dev/null
@@ -0,0 +1,609 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth.c: HTTP Authentication schemes (basic and digest)
+ *
+ * Authors:
+ *      Joe Shaw (joe@ximian.com)
+ *      Jeffrey Steadfast (fejj@ximian.com)
+ *
+ * Copyright (C) 2001, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "md5-utils.h"
+#include "soup-context.h"
+#include "soup-message.h"
+#include "soup-private.h"
+
+typedef enum {
+       SOUP_AUTH_BASIC,
+       SOUP_AUTH_DIGEST
+} SoupAuthType;
+
+typedef char *(*SoupAuthFunc) (SoupAuth *auth, SoupMessage *message);
+
+typedef void (*SoupAuthFreeFn) (SoupAuth *auth);
+
+struct _SoupAuth {
+       SoupContext    *context;
+       SoupAuthType    type;
+       SoupAuthFunc    auth_func;
+       SoupAuthFreeFn  free_func;
+};
+
+typedef struct {
+       SoupAuth auth;
+       char *realm;
+} SoupAuthBasic;
+
+typedef enum {
+       QOP_NONE     = 0,
+       QOP_AUTH     = 1 << 0,
+       QOP_AUTH_INT = 1 << 1
+} QOPType;
+
+typedef enum {
+       ALGORITHM_MD5      = 1 << 0,
+       ALGORITHM_MD5_SESS = 1 << 1
+} AlgorithmType;
+
+typedef struct {
+       SoupAuth auth;
+       
+       /* These are provided by the server */
+       char *realm;
+       char *nonce;
+       QOPType qop_options;
+       AlgorithmType algorithm;
+       gboolean stale;
+
+       /* These are generated by the client */
+       char *cnonce;
+       int nc;
+       QOPType qop;
+} SoupAuthDigest;
+
+static char *
+basic_auth_func (SoupAuth *auth, SoupMessage *message)
+{
+       const SoupUri *uri;
+       char *user_pass;
+       char *base64;
+       char *out;
+
+       g_return_val_if_fail (auth->context, NULL);
+
+       uri = soup_context_get_uri (auth->context);
+
+       user_pass = g_strdup_printf ("%s:%s", uri->user, uri->passwd);
+       base64 = soup_base64_encode (user_pass, strlen (user_pass));
+       g_free (user_pass);
+
+       out = g_strdup_printf ("Basic %s", base64);
+       g_free (base64);
+
+       return out;
+}
+
+static void
+digest_hex (guchar *digest, guchar hex[33])
+{
+       guchar *s, *p;
+       
+       /* lowercase hexify that bad-boy... */
+       for (s = digest, p = hex; p < hex + 32; s++, p += 2)
+               sprintf (p, "%.2x", *s);
+}
+
+static char *
+compute_response (SoupAuthDigest *digest, SoupMessage *msg)
+{
+       SoupAuth *auth = (SoupAuth *) digest;
+       const SoupUri *uri;
+       guchar hex_a1[33], hex_a2[33], o[33];
+       guchar d[16];
+       MD5Context ctx;
+
+       uri = soup_context_get_uri (auth->context);
+
+       /* compute A1 */
+       md5_init (&ctx);
+       md5_update (&ctx, uri->user, strlen (uri->user));
+       md5_update (&ctx, ":", 1);
+       md5_update (&ctx, digest->realm, strlen (digest->realm));
+       md5_update (&ctx, ":", 1);
+       md5_update (&ctx, uri->passwd, strlen (uri->passwd));
+
+       if (digest->algorithm == ALGORITHM_MD5_SESS) {
+               md5_final (&ctx, d);
+
+               md5_init (&ctx);
+               md5_update (&ctx, d, 16);
+               md5_update (&ctx, ":", 1);
+               md5_update (&ctx, digest->nonce, strlen (digest->nonce));
+               md5_update (&ctx, ":", 1);
+               md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
+       }
+       
+       /* hexify A1 */
+       md5_final (&ctx, d);
+       digest_hex (d, hex_a1);
+       
+       /* compute A2 */
+       md5_init (&ctx);
+       md5_update (&ctx, msg->method, strlen (msg->method));
+       md5_update (&ctx, ":", 1);
+       md5_update (&ctx, uri->path, strlen (uri->path));
+
+       if (digest->qop == QOP_AUTH_INT) {
+               /* FIXME: Actually implement. Ugh. */
+               md5_update (&ctx, ":", 1);
+               md5_update (&ctx, "00000000000000000000000000000000", 32);
+       }
+
+       /* now hexify A2 */
+       md5_final (&ctx, d);
+       digest_hex (d, hex_a2);
+       
+       /* compute KD */
+       md5_init (&ctx);
+       md5_update (&ctx, hex_a1, 32);
+       md5_update (&ctx, ":", 1);
+       md5_update (&ctx, digest->nonce, strlen (digest->nonce));
+       md5_update (&ctx, ":", 1);
+
+       if (digest->qop) {
+               char *tmp;
+
+               tmp = g_strdup_printf ("%.8x", digest->nc);
+
+               md5_update (&ctx, tmp, strlen (tmp));
+               g_free (tmp);
+               md5_update (&ctx, ":", 1);
+               md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
+               md5_update (&ctx, ":", 1);
+
+               if (digest->qop == QOP_AUTH)
+                       tmp = "auth";
+               else if (digest->qop == QOP_AUTH_INT)
+                       tmp = "auth-int";
+               else
+                       g_assert_not_reached ();
+
+               md5_update (&ctx, tmp, strlen (tmp));
+               md5_update (&ctx, ":", 1);
+       }
+
+       md5_update (&ctx, hex_a2, 32);
+       md5_final (&ctx, d);
+       
+       digest_hex (d, o);
+
+       return g_strdup (o);
+}
+
+static char *
+digest_auth_func (SoupAuth *auth, SoupMessage *message)
+{
+       SoupAuthDigest *digest = (SoupAuthDigest *) auth;
+       const SoupUri *uri;
+       char *response;
+       char *qop = NULL;
+       char *nc;
+       char *out;
+
+       g_return_val_if_fail (auth->context, NULL);
+       g_return_val_if_fail (message, NULL);
+
+       response = compute_response (digest, message);
+
+       if (digest->qop == QOP_AUTH)
+               qop = "auth";
+       else if (digest->qop == QOP_AUTH_INT)
+               qop = "auth-int";
+       else
+               g_assert_not_reached ();
+
+       uri = soup_context_get_uri (auth->context);
+
+       nc = g_strdup_printf ("%.8x", digest->nc);
+       
+       out = g_strdup_printf (
+               "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", %s%s%s "
+               "%s%s%s %s%s%s uri=\"%s\", response=\"%s\"",
+               uri->user, 
+               digest->realm, 
+               digest->nonce,
+
+               digest->qop ? "cnonce=\"" : "", 
+               digest->qop ? digest->cnonce : "",
+               digest->qop ? "\"," : "",
+
+               digest->qop ? "nc=" : "",
+               digest->qop ? nc : "",
+               digest->qop ? "," : "",
+
+               digest->qop ? "qop=" : "",
+               digest->qop ? qop : "",
+               digest->qop ? "," : "",
+
+               uri->path, 
+               response);
+
+       g_free (nc);
+
+       digest->nc++;
+
+       return out;
+}
+
+static void
+decode_lwsp (char **in)
+{
+       char *inptr = *in;
+
+       while (isspace (*inptr))
+               inptr++;
+
+       *in = inptr;
+}
+
+static char *
+decode_quoted_string (char **in)
+{
+       char *inptr = *in;
+       char *out = NULL, *outptr;
+       int outlen;
+       int c;
+
+       decode_lwsp (&inptr);
+       if (*inptr == '"') {
+               char *intmp;
+               int skip = 0;
+               
+                /* first, calc length */
+                inptr++;
+                intmp = inptr;
+                while ( (c = *intmp++) && c != '"') {
+                        if (c == '\\' && *intmp) {
+                                intmp++;
+                                skip++;
+                        }
+                }
+                
+                outlen = intmp - inptr - skip;
+                out = outptr = g_malloc (outlen + 1);
+                
+                while ( (c = *inptr++) && c != '"') {
+                        if (c == '\\' && *inptr) {
+                                c = *inptr++;
+                        }
+                        *outptr++ = c;
+                }
+                *outptr = 0;
+        }
+        
+        *in = inptr;
+        
+        return out;
+}
+
+static char *
+decode_token (char **in)
+{
+       char *inptr = *in;
+       char *start;
+
+       decode_lwsp (&inptr);
+       start = inptr;
+
+       while (*inptr && *inptr != '=' && *inptr != ',')
+               inptr++;
+
+       if (inptr > start) {
+               *in = inptr;
+               return g_strndup (start, inptr - start);
+       }
+       else
+               return NULL;
+}
+
+static char *
+decode_value (char **in)
+{
+       char *inptr = *in;
+
+       decode_lwsp (&inptr);
+       if (*inptr == '"')
+               return decode_quoted_string (in);
+       else
+               return decode_token (in);
+}
+
+static GHashTable *
+parse_param_list (const char *header)
+{
+       GHashTable *params = g_hash_table_new (
+               soup_str_case_hash, soup_str_case_equal);
+       char *ptr;
+       gboolean added = FALSE;
+
+       ptr = (char *) header;
+       while (ptr && *ptr) {
+               char *name;
+               char *value;
+
+               name = decode_token (&ptr);
+               if (*ptr == '=') {
+                       ptr++;
+                       value = decode_value (&ptr);
+                       g_hash_table_insert (params, name, value);
+                       added = TRUE;
+               }
+
+               if (*ptr == ',')
+                       ptr++;
+       }
+
+       if (!added) {
+               g_hash_table_destroy (params);
+               params = NULL;
+       }
+
+       return params;
+}
+
+static void
+destroy_param_hash_elements (gpointer key, gpointer value, gpointer user_data)
+{
+       g_free (key);
+       g_free (value);
+}
+
+static void
+destroy_param_hash (GHashTable *table)
+{
+       g_hash_table_foreach (table, destroy_param_hash_elements, NULL);
+       g_hash_table_destroy (table);
+}
+
+typedef struct {
+       char *name;
+       guint type;
+} DataType;
+
+static DataType qop_types[] = {
+       { "auth",     QOP_AUTH     },
+       { "auth-int", QOP_AUTH_INT }
+};
+
+static DataType algorithm_types[] = {
+       { "MD5",      ALGORITHM_MD5      },
+       { "MD5-sess", ALGORITHM_MD5_SESS }
+};
+
+static guint
+decode_data_type (DataType *dtype, const char *name)
+{
+        int i;
+        
+        for (i = 0; dtype[i].name; i++) {
+                if (!g_strcasecmp (dtype[i].name, name))
+                       return dtype[i].type;
+        }
+
+       return 0;
+}
+
+static inline guint 
+decode_qop (const char *name)
+{
+       return decode_data_type (qop_types, name);
+}
+
+static inline guint 
+decode_algorithm (const char *name)
+{
+       return decode_data_type (algorithm_types, name);
+}
+
+static char *
+copy_token_if_exists (GHashTable *tokens, char *t)
+{
+       char *data;
+
+       g_return_val_if_fail (tokens, NULL);
+       g_return_val_if_fail (t, NULL);
+
+       if ( (data = g_hash_table_lookup (tokens, t)))
+               return g_strdup (data);
+       else
+               return NULL;
+}
+
+static void
+soup_auth_basic_parse_header (SoupAuth *auth, const char *header)
+{
+       SoupAuthBasic *basic = (SoupAuthBasic *) auth;
+       GHashTable *tokens = parse_param_list (header);
+
+       g_return_if_fail (tokens);
+
+       basic->realm = copy_token_if_exists (tokens, "realm");
+
+       destroy_param_hash (tokens);
+}
+
+static void
+soup_auth_digest_parse_header (SoupAuth *auth, const char *header)
+{
+       SoupAuthDigest *digest = (SoupAuthDigest *) auth;
+       GHashTable *tokens = parse_param_list (header);
+       char *tmp, *ptr;
+
+       g_return_if_fail (tokens);
+
+       digest->realm = copy_token_if_exists (tokens, "realm");
+       digest->nonce = copy_token_if_exists (tokens, "nonce");
+
+       tmp = copy_token_if_exists (tokens, "qop");
+       ptr = tmp;
+
+       while (ptr && *ptr) {
+               char *token;
+
+               token = decode_token (&ptr);
+               if (token)
+                       digest->qop_options |= decode_qop (token);
+               g_free (token);
+
+               if (*ptr == ',')
+                       ptr++;
+       }
+
+       g_free (tmp);
+
+       tmp = copy_token_if_exists (tokens, "stale");
+
+       if (tmp && g_strcasecmp (tmp, "true") == 0)
+               digest->stale = TRUE;
+       else
+               digest->stale = FALSE;
+
+       g_free (tmp);
+
+       tmp = copy_token_if_exists (tokens, "algorithm");
+       digest->algorithm = decode_algorithm (tmp);
+       g_free (tmp);
+       
+       destroy_param_hash (tokens);
+}
+       
+static void
+basic_free (SoupAuth *auth)
+{
+       SoupAuthBasic *basic = (SoupAuthBasic *) auth;
+
+       g_free (basic->realm);
+       g_free (basic);
+}
+
+static SoupAuth *
+soup_auth_new_basic (void)
+{
+       SoupAuthBasic *basic;
+       SoupAuth *auth;
+
+       basic = g_new0 (SoupAuthBasic, 1);
+       auth = (SoupAuth *) basic;
+       auth->type = SOUP_AUTH_BASIC;
+       auth->auth_func = basic_auth_func;
+       auth->free_func = basic_free;
+
+       return auth;
+}
+
+static void
+digest_free (SoupAuth *auth)
+{
+       SoupAuthDigest *digest = (SoupAuthDigest *) auth;
+
+       g_free (digest->realm);
+       g_free (digest->nonce);
+       g_free (digest->cnonce);
+       g_free (digest);
+}
+
+static SoupAuth *
+soup_auth_new_digest (void)
+{
+       SoupAuthDigest *digest;
+       SoupAuth *auth;
+       char *bgen;
+
+       digest = g_new0 (SoupAuthDigest, 1);
+       auth = (SoupAuth *) digest;
+       auth->type = SOUP_AUTH_DIGEST;
+       auth->auth_func = digest_auth_func;
+       auth->free_func = digest_free;
+
+       bgen = g_strdup_printf ("%p:%lu:%lu", 
+                              auth, 
+                              (unsigned long) getpid (),
+                              (unsigned long) time (0));
+       digest->cnonce = soup_base64_encode (bgen, strlen (bgen));
+       digest->nc = 1;
+       /* We're just going to do qop=auth for now */
+       digest->qop = QOP_AUTH;
+
+       return auth;
+}
+
+SoupAuth *
+soup_auth_new_from_header (SoupContext *context, const char *header)
+{
+       SoupAuth *auth;
+
+       if (g_strncasecmp (header, "Basic", 5) == 0) {
+               auth = soup_auth_new_basic ();
+               soup_auth_basic_parse_header (auth, header + 6);
+       }
+       else if (g_strncasecmp (header, "Digest", 6) == 0) {
+               auth = soup_auth_new_digest ();
+               soup_auth_digest_parse_header (auth, header + 7);
+       }
+       else {
+               g_warning ("Authentication type not supported");
+               return NULL;
+       }
+
+       auth->context = context;
+
+       return auth;
+}
+
+gchar *
+soup_auth_authorize (SoupAuth *auth, SoupMessage *msg)
+{
+       g_return_val_if_fail (auth != NULL, NULL);
+       g_return_val_if_fail (msg != NULL, NULL);
+
+       return auth->auth_func (auth, msg);
+}
+
+void
+soup_auth_free (SoupAuth *auth)
+{
+       g_return_if_fail (auth != NULL);
+
+       auth->free_func (auth);
+}
+
+gboolean
+soup_auth_invalidates_prior (SoupAuth *auth)
+{
+       g_return_val_if_fail (auth != NULL, FALSE);
+
+       switch (auth->type) {
+       case SOUP_AUTH_DIGEST:
+               return ((SoupAuthDigest *) auth)->stale;
+       case SOUP_AUTH_BASIC:
+       default:
+               return FALSE;
+       }
+}
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
new file mode 100644 (file)
index 0000000..f950bec
--- /dev/null
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth.h: Authentication schemes
+ *
+ * Authors:
+ *      Joe Shaw (joe@ximian.com)
+ *
+ * Copyright (C) 2001, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_H
+#define SOUP_AUTH_H 1
+
+#include "soup-context.h"
+#include "soup-message.h"
+
+typedef struct _SoupAuth SoupAuth;
+
+SoupAuth *soup_auth_new_from_header   (SoupContext *context, 
+                                      const char  *header);
+
+void      soup_auth_free              (SoupAuth *auth);
+
+gchar    *soup_auth_authorize         (SoupAuth    *auth, 
+                                      SoupMessage *msg);
+
+gboolean  soup_auth_invalidates_prior (SoupAuth *auth);
+
+#endif /* SOUP_AUTH_H */
index 4774dec..17d9877 100644 (file)
@@ -31,6 +31,7 @@
 #include <netinet/in.h>
 #endif
 
+#include "soup-auth.h"
 #include "soup-context.h"
 #include "soup-private.h"
 #include "soup-misc.h"
@@ -90,6 +91,7 @@ soup_context_uri_hash (gconstpointer key)
                if (uri->passwd) 
                        ret += g_str_hash (uri->passwd);
        }
+
        ret += g_str_hash (uri->path);
 
        return ret;
@@ -226,7 +228,8 @@ soup_context_unref (SoupContext *ctx)
                        g_hash_table_destroy (serv->contexts);
                        g_free (serv);
                }
-                       
+
+               if (ctx->auth) soup_auth_free (ctx->auth);
                soup_uri_free (ctx->uri);
                g_free (ctx);
        }
index 8827e22..de64d58 100644 (file)
@@ -9,10 +9,50 @@
  */
 
 #include "soup-message.h"
+#include "soup-misc.h"
 #include "soup-context.h"
 #include "soup-private.h"
 #include "soup-transfer.h"
 
+static SoupErrorCode 
+authorize_handler (SoupMessage *msg, gboolean proxy)
+{
+       const char *auth_header;
+       SoupAuth *auth;
+       SoupContext *ctx;
+
+       ctx = proxy ? soup_get_proxy () : msg->context;
+
+       auth_header = 
+               soup_message_get_response_header (
+                       msg, 
+                       proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
+       if (!auth_header) return SOUP_ERROR_CANT_AUTHENTICATE;
+
+        auth = soup_auth_new_from_header (ctx, auth_header);
+       if (!auth) return SOUP_ERROR_MALFORMED_HEADER;
+
+       if (ctx->auth) {
+               if (soup_auth_invalidates_prior (auth))
+                       soup_auth_free (ctx->auth);
+               else {
+                       soup_auth_free (auth);
+                       return SOUP_ERROR_CANT_AUTHENTICATE;
+               }
+       }
+
+       ctx->auth = auth;
+
+       if (msg->priv->req_header) {
+               g_string_free (msg->priv->req_header, TRUE);
+               msg->priv->req_header = NULL;
+       }
+
+       soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
+
+        return SOUP_ERROR_NONE;
+}
+
 /**
  * soup_message_new:
  * @context: a %SoupContext for the destination endpoint.
@@ -29,7 +69,7 @@ soup_message_new (SoupContext *context, SoupAction action)
 {
        SoupMessage *ret;
 
-       g_return_val_if_fail(context, NULL);
+       g_return_val_if_fail (context, NULL);
 
        ret          = g_new0 (SoupMessage, 1);
        ret->priv    = g_new0 (SoupMessagePrivate, 1);
@@ -40,6 +80,29 @@ soup_message_new (SoupContext *context, SoupAction action)
 
        soup_context_ref (context);
 
+       /*
+        * Add a 401 (Authorization Required) response code handler if the
+        * context URI has a login user name.
+        */
+       if (soup_context_get_uri (context)->user)
+               soup_message_add_response_code_handler (
+                       ret, 
+                       401, 
+                       SOUP_HANDLER_POST_BODY, 
+                       (SoupHandlerFn) authorize_handler, 
+                       GINT_TO_POINTER (FALSE));
+
+       /*
+        * Always add a 407 (Proxy-Authorization Required) handler, in case the
+        * proxy is reset after message creation.
+        */
+       soup_message_add_response_code_handler (
+                       ret, 
+                       407, 
+                       SOUP_HANDLER_POST_BODY, 
+                       (SoupHandlerFn) authorize_handler, 
+                       GINT_TO_POINTER (TRUE));
+
        return ret;
 }
 
@@ -230,7 +293,8 @@ soup_message_set_header (GHashTable  **hash,
                g_free (old_value);
        }
 
-       g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
+       if (value)
+               g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
 }
 
 /**
@@ -239,7 +303,8 @@ soup_message_set_header (GHashTable  **hash,
  * @name: header name.
  * @value: header value.
  * 
- * Adds a new transport header to be sent on an outgoing request.
+ * Adds a new transport header to be sent on an outgoing request. Passing a NULL
+ * @value will remove the header name supplied.
  */
 void
 soup_message_set_request_header (SoupMessage *req,
@@ -283,7 +348,8 @@ soup_message_get_request_header (SoupMessage *req,
  * @name: header name.
  * @value: header value.
  * 
- * Adds a new transport header to be sent on an outgoing response.
+ * Adds a new transport header to be sent on an outgoing response. Passing a
+ * NULL @value will remove the header name supplied.
  */
 void
 soup_message_set_response_header (SoupMessage *req,
@@ -517,7 +583,7 @@ soup_message_remove_handler (SoupMessage   *msg,
 }
 
 static SoupErrorCode 
-soup_message_redirect (SoupMessage *msg, gpointer user_data)
+redirect_handler (SoupMessage *msg, gpointer user_data)
 {
        const gchar *new_url;
 
@@ -536,11 +602,13 @@ soup_message_redirect (SoupMessage *msg, gpointer user_data)
                return SOUP_ERROR_NONE;
 
        new_url = soup_message_get_response_header (msg, "Location");
+
        if (new_url) {
-               soup_context_unref (msg->context);
-               msg->context = soup_context_get (new_url);
+               SoupContext *new_ctx = soup_context_get (new_url);
+               if (!new_ctx) return SOUP_ERROR_MALFORMED_HEADER;
 
-               if (!msg->context) return SOUP_ERROR_MALFORMED_HEADER;
+               soup_context_unref (msg->context);
+               msg->context = new_ctx;
 
                soup_message_queue (msg,
                                    msg->priv->callback, 
@@ -571,11 +639,11 @@ soup_message_set_flags (SoupMessage *msg, guint flags)
                soup_message_add_header_handler (msg,
                                                 "Location",
                                                 SOUP_HANDLER_PRE_BODY,
-                                                soup_message_redirect,
+                                                redirect_handler,
                                                 NULL);
        else if (REMOVED_FLAG (msg, flags, SOUP_MESSAGE_FOLLOW_REDIRECT))
                soup_message_remove_handler (msg, 
-                                            soup_message_redirect,
+                                            redirect_handler,
                                             NULL);
 
        msg->priv->msg_flags = flags;
index da67141..9eb7169 100644 (file)
@@ -30,6 +30,7 @@
 #include <winuser.h>
 #endif
 
+#include "soup-auth.h"
 #include "soup-context.h"
 #include "soup-message.h"
 #include "soup-server.h"
@@ -69,6 +70,7 @@ struct _SoupSocket {
 struct _SoupContext {
        SoupUri      *uri;
        SoupServer   *server;
+       SoupAuth     *auth;
        guint         refcnt;
 };
 
index a60aa7f..1117c3f 100644 (file)
@@ -165,6 +165,9 @@ soup_queue_error_cb (gboolean     body_started,
 
        soup_connection_set_keep_alive (req->priv->conn, FALSE);
 
+       req->priv->read_tag = 0;
+       req->priv->write_tag = 0;
+
        switch (req->status) {
        case SOUP_STATUS_IDLE:
        case SOUP_STATUS_QUEUED:
@@ -205,21 +208,25 @@ soup_queue_error_cb (gboolean     body_started,
 }
 
 static void
-soup_encode_http_auth (const SoupUri *uri, GString *header, gboolean proxy_auth)
+soup_encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
 {
-       if (!uri->authmech) {
-               gchar *authpass, *encoded;
-               authpass = g_strconcat (uri->user, ":", uri->passwd, NULL);
-               encoded = soup_base64_encode (authpass, strlen (authpass));
-               g_string_sprintfa (header,
-                                  "%s: Basic %s\r\n",
-                                  proxy_auth ? 
-                                          "Proxy-Authorization" : 
-                                          "Authorization",
-                                  encoded);
-               g_free (encoded);
-               g_free (authpass);
-       }
+       SoupContext *ctx;
+
+       ctx = proxy_auth ? soup_get_proxy () : msg->context;
+
+       if (ctx->auth) {
+               char *token;
+
+               token = soup_auth_authorize (ctx->auth, msg);
+
+               g_string_sprintfa (
+                       header, 
+                       "%s: %s\r\n",
+                       proxy_auth ? "Proxy-Authorization" : "Authorization",
+                       token);
+
+               g_free (token);
+       }
 }
 
 struct SoupUsedHeaders {
@@ -325,9 +332,9 @@ soup_get_request_header (SoupMessage *req)
                           hdrs.host ? "" : "Host: ",
                           hdrs.host ? "" : suri->host,
                           hdrs.host ? "" : "\r\n",
-                          hdrs.soapaction ? "" : "SOAPAction: ",
-                          hdrs.soapaction ? "" : req->action,
-                          hdrs.soapaction ? "" : "\r\n",
+                          hdrs.soapaction && req->action ? "" : "SOAPAction: ",
+                          hdrs.soapaction && req->action ? "" : req->action,
+                          hdrs.soapaction && req->action ? "" : "\r\n",
                           hdrs.content_type ? "" : "Content-Type: text/xml; ",
                           hdrs.content_type ? "" : "charset=utf-8\r\n",
                           hdrs.connection ? "" : "Connection: keep-alive\r\n",
@@ -335,13 +342,11 @@ soup_get_request_header (SoupMessage *req)
 
        /* Proxy-Authorization from the proxy Uri */
        if (!hdrs.proxy_auth && proxy && soup_context_get_uri (proxy)->user)
-               soup_encode_http_auth (soup_context_get_uri (proxy), 
-                                      header, 
-                                      TRUE);
+               soup_encode_http_auth (req, header, TRUE);
 
        /* Authorization from the context Uri */
        if (!hdrs.auth && suri->user)
-               soup_encode_http_auth (suri, header, FALSE);
+               soup_encode_http_auth (req, header, FALSE);
 
        g_string_append (header, "\r\n");
 
index 62f5a9d..a926b68 100644 (file)
@@ -27,6 +27,13 @@ typedef struct {
        guint                  read_tag;
        guint                  err_tag;
 
+       /* 
+        * If TRUE, a callback has been issed which references recv_buf.  
+        * If the tranfer is cancelled before a reference exists, the contents
+        * of recv_buf are free'd.
+        */
+       gboolean               callback_issued;
+
        GByteArray            *recv_buf;
        guint                  header_len;
 
@@ -71,7 +78,7 @@ soup_transfer_read_cancel (guint tag)
        source_remove (r->read_tag);
        source_remove (r->err_tag);
 
-       g_byte_array_free (r->recv_buf, FALSE);
+       g_byte_array_free (r->recv_buf, r->callback_issued ? FALSE : TRUE);
 
        g_free (r);
 }
@@ -234,6 +241,8 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
                buf.length = r->recv_buf->len - 1;
                buf.body = r->recv_buf->data;
 
+               r->callback_issued = TRUE;
+
                cont = (*r->read_chunk_cb) (&buf, r->user_data);
 
                g_byte_array_remove_index (r->recv_buf, r->recv_buf->len - 1);
@@ -250,6 +259,8 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
                buf.length = r->recv_buf->len - 1;
                buf.body = r->recv_buf->data;
 
+               r->callback_issued = TRUE;
+
                (*r->read_done_cb) (&buf, r->user_data);
        }