2003-09-10 Dan Winship <danw@ximian.com>
+ * libsoup/soup-session.c: Add "authenticate" and "reauthenticate"
+ signals.
+ (invalidate_auth): Remove the call to soup_auth_invalidate.
+ (authenticate_auth): soup_auth_fn is gone. If the URI doesn't
+ contain authentication, then emit "authenticate" or
+ "reauthenticate" (depending on whether or not this is the first
+ time we've asked for a password for this auth).
+ (update_auth_internal): If the server rejects our
+ username/password, don't bail out immediately. Try doing a
+ "reauthenticate" first.
+
+ * libsoup/soup-misc.c (soup_set_authorize_callback): Gone
+
+ * libsoup/soup-auth.c (soup_auth_new_from_header_list): Remove the
+ "pref" arg.
+ (soup_auth_invalidate): Remove this; it doesn't actually do
+ anything useful for us.
+
+ * libsoup/soup-auth-basic.c (invalidate): Remove
+ * libsoup/soup-auth-digest.c: (invalidate): Remove
+ * libsoup/soup-auth-ntlm.c: (invalidate): Remove
+
+ * libsoup/soup-uri.c: Remove all references to "authmech".
+ (soup_uri_set_auth): Remove this too.
+
+ * tests/auth-test.c: Update to use the "authenticate" and
+ "reauthenticate" signals instead of encoding usernames and
+ passwords in the URIs. Add a few more test cases.
+
+2003-09-10 Dan Winship <danw@ximian.com>
+
* libsoup/soup-message-private.h (SoupMessagePrivate): Remove the
"status" field from here, since it's mostly used by SoupSession,
which shouldn't need access to SoupMessagePrivate.
static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
static const char *get_realm (SoupAuth *auth);
static void authenticate (SoupAuth *auth, const char *username, const char *password);
-static gboolean invalidate (SoupAuth *auth);
static gboolean is_authenticated (SoupAuth *auth);
static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
auth_class->get_protection_space = get_protection_space;
auth_class->get_realm = get_realm;
auth_class->authenticate = authenticate;
- auth_class->invalidate = invalidate;
auth_class->is_authenticated = is_authenticated;
auth_class->get_authorization = get_authorization;
}
static gboolean
-invalidate (SoupAuth *auth)
-{
- SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
-
- g_free (basic->priv->token);
- basic->priv->token = NULL;
-
- return TRUE;
-}
-
-static gboolean
is_authenticated (SoupAuth *auth)
{
SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
static const char *get_realm (SoupAuth *auth);
static void authenticate (SoupAuth *auth, const char *username, const char *password);
-static gboolean invalidate (SoupAuth *auth);
static gboolean is_authenticated (SoupAuth *auth);
static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
auth_class->get_realm = get_realm;
auth_class->construct = construct;
auth_class->authenticate = authenticate;
- auth_class->invalidate = invalidate;
auth_class->is_authenticated = is_authenticated;
auth_class->get_authorization = get_authorization;
}
static gboolean
-invalidate (SoupAuth *auth)
-{
- /* An invalidated Digest auth is useless. You need to get
- * a new nonce from the server before you can start using it
- * again.
- */
- return FALSE;
-}
-
-static gboolean
is_authenticated (SoupAuth *auth)
{
SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
static const char *get_realm (SoupAuth *auth);
static void authenticate (SoupAuth *auth, const char *username, const char *password);
-static gboolean invalidate (SoupAuth *auth);
static gboolean is_authenticated (SoupAuth *auth);
static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
auth_class->get_protection_space = get_protection_space;
auth_class->get_realm = get_realm;
auth_class->authenticate = authenticate;
- auth_class->invalidate = invalidate;
auth_class->is_authenticated = is_authenticated;
auth_class->get_authorization = get_authorization;
}
static gboolean
-invalidate (SoupAuth *auth)
-{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
-
- g_free (ntlm->priv->response);
- ntlm->priv->response = NULL;
- g_free (ntlm->priv->header);
- ntlm->priv->header = NULL;
-
- ntlm->priv->authenticated = FALSE;
-
- return TRUE;
-}
-
-static gboolean
is_authenticated (SoupAuth *auth)
{
SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
};
SoupAuth *
-soup_auth_new_from_header_list (const GSList *vals, const char *pref)
+soup_auth_new_from_header_list (const GSList *vals)
{
char *header = NULL;
AuthScheme *scheme = NULL, *iter;
char *tryheader = vals->data;
for (iter = known_auth_schemes; iter->scheme; iter++) {
- if (pref &&
- g_strncasecmp (pref, iter->scheme,
- strlen (iter->scheme)) != 0)
- continue;
if (!g_strncasecmp (tryheader, iter->scheme,
strlen (iter->scheme))) {
if (!scheme ||
}
gboolean
-soup_auth_invalidate (SoupAuth *auth)
-{
- g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
-
- return SOUP_AUTH_GET_CLASS (auth)->invalidate (auth);
-}
-
-gboolean
soup_auth_is_authenticated (SoupAuth *auth)
{
g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
void (*authenticate) (SoupAuth *auth,
const char *username,
const char *password);
- gboolean (*invalidate) (SoupAuth *auth);
gboolean (*is_authenticated) (SoupAuth *auth);
char * (*get_authorization) (SoupAuth *auth,
GType soup_auth_get_type (void);
-SoupAuth *soup_auth_new_from_header_list (const GSList *header,
- const char *pref);
+SoupAuth *soup_auth_new_from_header_list (const GSList *header);
const char *soup_auth_get_scheme_name (SoupAuth *auth);
const char *soup_auth_get_realm (SoupAuth *auth);
void soup_auth_authenticate (SoupAuth *auth,
const char *username,
const char *password);
-gboolean soup_auth_invalidate (SoupAuth *auth);
gboolean soup_auth_is_authenticated (SoupAuth *auth);
char *soup_auth_get_authorization (SoupAuth *auth,
NONE:INT
NONE:OBJECT
NONE:POINTER
+NONE:POINTER,OBJECT
LAST_SIGNAL
};
-guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
static void got_headers (SoupMessage *req);
static void got_chunk (SoupMessage *req);
*key_file = ssl_key_file;
}
-SoupAuthorizeFn soup_auth_fn = NULL;
-gpointer soup_auth_fn_user_data = NULL;
-
-/**
- * soup_set_authorize_callback:
- * @authfn: A %SoupAuthorizeFn function to be called when authorization
- * is needed to complete a request.
- * @user_data: A pointer to be passed @authfn.
- *
- * Sets the authorization callback to be called when a %SoupMessage fails with a
- * 401 or 407 response, and no authorization data is present in the URI (and the
- * request is not covered by a prior successful authorization attempt).
- *
- * The callback should call %soup_uri_set_auth on the passed URI in order to try
- * the request again.
- **/
-void
-soup_set_authorize_callback (SoupAuthorizeFn authfn,
- gpointer user_data)
-{
- soup_auth_fn = authfn;
- soup_auth_fn_user_data = user_data;
-}
-
-
typedef struct {
gpointer instance;
guint real_id, self_id;
void soup_get_ssl_cert_files (const gchar **cert_file,
const gchar **key_file);
-/* Authentication callback */
-
-typedef void (*SoupAuthorizeFn) (const char *scheme_name,
- SoupUri *uri,
- const char *realm,
- gpointer user_data);
-
-void soup_set_authorize_callback (SoupAuthorizeFn authfn,
- gpointer user_data);
-
/* Base64 encoding/decoding */
gchar *soup_base64_encode (const gchar *text,
extern gboolean soup_initialized;
-extern SoupAuthorizeFn soup_auth_fn;
-extern gpointer soup_auth_fn_user_data;
-
#ifdef HAVE_IPV6
#define soup_sockaddr_max sockaddr_in6
#else
#include "soup-session.h"
#include "soup-connection.h"
+#include "soup-marshal.h"
#include "soup-message-queue.h"
#include "soup-private.h"
#define PARENT_TYPE G_TYPE_OBJECT
static GObjectClass *parent_class;
+enum {
+ AUTHENTICATE,
+ REAUTHENTICATE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
static void
init (GObject *object)
{
/* virtual method override */
object_class->finalize = finalize;
+
+ /* signals */
+ signals[AUTHENTICATE] =
+ g_signal_new ("authenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupSessionClass, authenticate),
+ NULL, NULL,
+ soup_marshal_NONE__POINTER_OBJECT,
+ G_TYPE_NONE, 2,
+ G_TYPE_POINTER,
+ SOUP_TYPE_MESSAGE);
+ signals[REAUTHENTICATE] =
+ g_signal_new ("reauthenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupSessionClass, reauthenticate),
+ NULL, NULL,
+ soup_marshal_NONE__POINTER_OBJECT,
+ G_TYPE_NONE, 2,
+ G_TYPE_POINTER,
+ SOUP_TYPE_MESSAGE);
}
SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
char *realm;
gpointer key, value;
- /* 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 ("%s:%s",
soup_auth_get_scheme_name (auth),
soup_auth_get_realm (auth));
}
static gboolean
-authenticate_auth (SoupAuth *auth, SoupMessage *msg)
+authenticate_auth (SoupSession *session, SoupAuth *auth,
+ SoupMessage *msg, gboolean prior_auth_failed)
{
const SoupUri *uri = soup_message_get_uri (msg);
- if (!uri->user && soup_auth_fn) {
- (*soup_auth_fn) (soup_auth_get_scheme_name (auth),
- (SoupUri *) uri,
- soup_auth_get_realm (auth),
- soup_auth_fn_user_data);
+ if (uri->passwd && !prior_auth_failed) {
+ soup_auth_authenticate (auth, uri->user, uri->passwd);
+ return TRUE;
}
- if (!uri->user)
- return FALSE;
-
- soup_auth_authenticate (auth, uri->user, uri->passwd);
- return TRUE;
+ g_signal_emit (session, signals[prior_auth_failed ? REAUTHENTICATE : AUTHENTICATE], 0, auth, msg);
+ return soup_auth_is_authenticated (auth);
}
static gboolean
update_auth_internal (SoupSession *session, SoupMessage *msg,
const GSList *headers, gboolean proxy,
- gboolean prior_auth_failed)
+ gboolean got_unauthorized)
{
SoupSessionHost *host;
SoupAuth *new_auth, *prior_auth, *old_auth;
const char *path;
char *realm;
GSList *pspace, *p;
+ gboolean prior_auth_failed = FALSE;
host = get_host_for_message (session, msg);
g_return_val_if_fail (host != NULL, FALSE);
* there's no way we'll be able to authenticate.
*/
msg_uri = soup_message_get_uri (msg);
- new_auth = soup_auth_new_from_header_list (headers, msg_uri->authmech);
+ new_auth = soup_auth_new_from_header_list (headers);
if (!new_auth)
return FALSE;
G_OBJECT_TYPE (prior_auth) == G_OBJECT_TYPE (new_auth) &&
!strcmp (soup_auth_get_realm (prior_auth),
soup_auth_get_realm (new_auth))) {
- g_object_unref (new_auth);
- if (prior_auth_failed) {
- /* The server didn't like the username/password
- * we provided before.
- */
- invalidate_auth (host, 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.
+ if (!got_unauthorized) {
+ /* The user is just trying to preauthenticate
+ * using information we already have, so
+ * there's nothing more that needs to be done.
*/
+ g_object_unref (new_auth);
return TRUE;
}
+
+ /* The server didn't like the username/password we
+ * provided before. Invalidate it and note this fact.
+ */
+ invalidate_auth (host, prior_auth);
+ prior_auth_failed = TRUE;
}
if (!host->auth_realms) {
} else
g_hash_table_insert (host->auths, realm, new_auth);
- /* Try to authenticate if needed. */
- if (!soup_auth_is_authenticated (new_auth))
- return authenticate_auth (new_auth, msg);
+ /* If we need to authenticate, try to do it. */
+ if (!soup_auth_is_authenticated (new_auth)) {
+ return authenticate_auth (session, new_auth,
+ msg, prior_auth_failed);
+ }
+ /* Otherwise we're good. */
return TRUE;
}
{
SoupSession *session = user_data;
const char *new_loc;
- const SoupUri *old_uri;
SoupUri *new_uri;
new_loc = soup_message_get_header (msg->response_headers, "Location");
if (!new_uri)
goto INVALID_REDIRECT;
- old_uri = soup_message_get_uri (msg);
-
- /* Copy auth info from original URI. */
- if (old_uri->user && !new_uri->user)
- soup_uri_set_auth (new_uri,
- old_uri->user,
- old_uri->passwd,
- old_uri->authmech);
-
soup_message_set_uri (msg, new_uri);
soup_uri_free (new_uri);
if (!auth)
return;
if (!soup_auth_is_authenticated (auth) &&
- !authenticate_auth (auth, msg))
+ !authenticate_auth (session, auth, msg, FALSE))
return;
token = soup_auth_get_authorization (auth, msg);
#ifndef SOUP_SESSION_H
#define SOUP_SESSION_H 1
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-auth.h>
#include <libsoup/soup-message.h>
#define SOUP_TYPE_SESSION (soup_session_get_type ())
typedef struct {
GObjectClass parent_class;
+ /* signals */
+ void (*authenticate) (SoupSession *, SoupAuth *, SoupMessage *);
+ void (*reauthenticate) (SoupSession *, SoupAuth *, SoupMessage *);
+
} SoupSessionClass;
GType soup_session_get_type (void);
soup_uri_new_with_base (const SoupUri *base, const char *uri_string)
{
SoupUri *uri;
- const char *end, *hash, *colon, *semi, *at, *slash, *question;
+ const char *end, *hash, *colon, *at, *slash, *question;
const char *p;
uri = g_new0 (SoupUri, 1);
colon = at;
}
- semi = strchr (uri_string, ';');
- if (semi && semi < colon &&
- !strncasecmp (semi, ";auth=", 6)) {
- uri->authmech = g_strndup (semi + 6,
- colon - semi - 6);
- soup_uri_decode (uri->authmech);
- } else {
- uri->authmech = NULL;
- semi = colon;
- }
-
- uri->user = g_strndup (uri_string, semi - uri_string);
+ uri->user = g_strndup (uri_string, colon - uri_string);
soup_uri_decode (uri->user);
uri_string = at + 1;
} else
- uri->user = uri->passwd = uri->authmech = NULL;
+ uri->user = uri->passwd = NULL;
/* Find host and port. */
colon = strchr (uri_string, ':');
else if (base && !uri->protocol) {
uri->protocol = base->protocol;
uri->user = g_strdup (base->user);
- uri->authmech = g_strdup (base->authmech);
uri->passwd = g_strdup (base->passwd);
uri->host = g_strdup (base->host);
uri->port = base->port;
g_string_append (str, "//");
if (uri->user) {
append_uri_encoded (str, uri->user, ":;@/");
- if (uri->authmech && *uri->authmech) {
- g_string_append (str, ";auth=");
- append_uri_encoded (str, uri->authmech, ":@/");
- }
g_string_append_c (str, '@');
}
append_uri_encoded (str, uri->host, ":/");
dup = g_new0 (SoupUri, 1);
dup->protocol = uri->protocol;
dup->user = g_strdup (uri->user);
- dup->authmech = g_strdup (uri->authmech);
dup->passwd = g_strdup (uri->passwd);
dup->host = g_strdup (uri->host);
dup->port = uri->port;
if (u1->protocol != u2->protocol ||
u1->port != u2->port ||
!parts_equal (u1->user, u2->user) ||
- !parts_equal (u1->authmech, u2->authmech) ||
!parts_equal (u1->passwd, u2->passwd) ||
!parts_equal (u1->host, u2->host) ||
!parts_equal (u1->path, u2->path) ||
g_return_if_fail (uri != NULL);
g_free (uri->user);
- g_free (uri->authmech);
g_free (uri->passwd);
g_free (uri->host);
g_free (uri->path);
g_free (uri);
}
-void
-soup_uri_set_auth (SoupUri *uri,
- const char *user,
- const char *passwd,
- const char *authmech)
-{
- g_return_if_fail (uri != NULL);
-
- g_free (uri->user);
- g_free (uri->passwd);
- g_free (uri->authmech);
-
- uri->user = g_strdup (user);
- uri->passwd = g_strdup (passwd);
- uri->authmech = g_strdup (authmech);
-}
-
/* From RFC 2396 2.4.3, the characters that should always be encoded */
static const char uri_encoded_char[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 - 0x0f */
SoupProtocol protocol;
char *user;
- char *authmech;
char *passwd;
char *host;
void soup_uri_free (SoupUri *uri);
-void soup_uri_set_auth (SoupUri *uri,
- const char *user,
- const char *passwd,
- const char *authmech);
-
char *soup_uri_encode (const char *part,
const char *escape_extra);
void soup_uri_decode (char *part);
int errors = 0;
typedef struct {
+ /* Explanation of what you should see */
const char *explanation;
+
+ /* URL to test against */
const char *url;
+
+ /* Provided passwords, 1 character each. ('1', '2', and '3'
+ * mean the correct passwords for "realm1", "realm2", and
+ * "realm3" respectively. '4' means "use the wrong password".)
+ * The first password (if present) will be used by
+ * authenticate(), and the second (if present) will be used by
+ * reauthenticate().
+ */
+ const char *provided;
+
+ /* Expected passwords, 1 character each. (As with the provided
+ * passwords, with the addition that '0' means "no
+ * Authorization header expected".) Used to verify that soup
+ * used the password it was supposed to at each step.
+ */
const char *expected;
- gboolean success;
+
+ /* What the final status code should be. */
+ guint final_status;
} SoupAuthTest;
SoupAuthTest tests[] = {
{ "No auth available, should fail",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
- "0", FALSE },
+ "", "0", SOUP_STATUS_UNAUTHORIZED },
{ "Should fail with no auth, fail again with bad password, and give up",
- "http://user4:realm4@primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
- "04", FALSE },
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
+ "4", "04", SOUP_STATUS_UNAUTHORIZED },
{ "Known realm, auth provided, so should succeed immediately",
- "http://user1:realm1@primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
- "1", TRUE },
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+ "1", "1", SOUP_STATUS_OK },
{ "Now should automatically reuse previous auth",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Subdir should also automatically reuse auth",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/subdir/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Subdir should retry last auth, but will fail this time",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
- "1", FALSE },
+ "", "1", SOUP_STATUS_UNAUTHORIZED },
{ "Now should use provided auth on first try",
- "http://user2:realm2@primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
- "2", TRUE },
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
+ "2", "2", SOUP_STATUS_OK },
{ "Reusing last auth. Should succeed on first try",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Reuse will fail, but 2nd try will succeed because it's a known realm",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/realm1/index.txt",
- "21", TRUE },
+ "", "21", SOUP_STATUS_OK },
{ "Should succeed on first try. (Known realm with cached password)",
"http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
- { "Fail once, then use password",
- "http://user3:realm3@primates.ximian.com/~danw/soup-test/Basic/realm3/index.txt",
- "03", TRUE },
+ { "Fail once, then use typoed password, then use right password",
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm3/index.txt",
+ "43", "043", SOUP_STATUS_OK },
{ "No auth available, should fail",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
- "0", FALSE },
+ "", "0", SOUP_STATUS_UNAUTHORIZED },
{ "Should fail with no auth, fail again with bad password, and give up",
- "http://user4:realm4@primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
- "04", FALSE },
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
+ "4", "04", SOUP_STATUS_UNAUTHORIZED },
{ "Known realm, auth provided, so should succeed immediately",
- "http://user1:realm1@primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
- "1", TRUE },
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+ "1", "1", SOUP_STATUS_OK },
{ "Now should automatically reuse previous auth",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Subdir should also automatically reuse auth",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/subdir/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Subdir should retry last auth, but will fail this time",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
- "1", FALSE },
+ "", "1", SOUP_STATUS_UNAUTHORIZED },
{ "Now should use provided auth on first try",
- "http://user2:realm2@primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
- "2", TRUE },
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
+ "2", "2", SOUP_STATUS_OK },
{ "Reusing last auth. Should succeed on first try",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Should succeed on first try because of earlier domain directive",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Should succeed on first try. (Known realm with cached password)",
"http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
- { "Fail once, then use password",
- "http://user3:realm3@primates.ximian.com/~danw/soup-test/Digest/realm3/index.txt",
- "03", TRUE },
+ { "Fail once, then use typoed password, then use right password",
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm3/index.txt",
+ "43", "043", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Basic/realm3/index.txt",
- "3", TRUE },
+ "", "3", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/realm1/index.txt",
- "1", TRUE },
+ "", "1", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
- "2", TRUE },
+ "", "2", SOUP_STATUS_OK },
{ "Make sure we haven't forgotten anything",
"http://primates.ximian.com/~danw/soup-test/Digest/realm3/index.txt",
- "3", TRUE }
+ "", "3", SOUP_STATUS_OK },
+
+ { "Now the server will reject the formerly-good password",
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm1/not/index.txt",
+ "1" /* should not be used */, "1", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Make sure we've forgotten it",
+ "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+ "", "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Likewise, reject the formerly-good Digest password",
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm1/not/index.txt",
+ "1" /* should not be used */, "1", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Make sure we've forgotten it",
+ "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+ "", "0", SOUP_STATUS_UNAUTHORIZED }
};
int ntests = sizeof (tests) / sizeof (tests[0]);
num = 0;
}
+ g_assert (num >= 0 && num <= 4);
+
return num;
}
}
}
+static void
+authenticate (SoupSession *session, SoupAuth *auth, SoupMessage *msg, gpointer data)
+{
+ char user[6], password[7];
+ int *i = data;
+
+ if (tests[*i].provided[0]) {
+ sprintf (user, "user%c", tests[*i].provided[0]);
+ sprintf (password, "realm%c", tests[*i].provided[0]);
+ soup_auth_authenticate (auth, user, password);
+ }
+}
+
+static void
+reauthenticate (SoupSession *session, SoupAuth *auth, SoupMessage *msg, gpointer data)
+{
+ char user[6], password[7];
+ int *i = data;
+
+ if (tests[*i].provided[0] && tests[*i].provided[1]) {
+ sprintf (user, "user%c", tests[*i].provided[1]);
+ sprintf (password, "realm%c", tests[*i].provided[1]);
+ soup_auth_authenticate (auth, user, password);
+ }
+}
+
int
main (int argc, char **argv)
{
int i;
g_type_init ();
+
session = soup_session_new_default ();
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), &i);
+ g_signal_connect (session, "reauthenticate",
+ G_CALLBACK (reauthenticate), &i);
for (i = 0; i < ntests; i++) {
printf ("Test %d: %s\n", i + 1, tests[i].explanation);
msg->status_code != SOUP_STATUS_OK) {
printf (" %d %s !\n", msg->status_code,
msg->reason_phrase);
+ errors++;
}
if (*expected) {
printf (" expected %d more round(s)\n",
}
g_free (expected);
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) !=
- tests[i].success) {
- printf (" expected %s\n",
- tests[i].success ? "success" : "failure");
- errors++;
- }
+ if (msg->status_code != tests[i].final_status)
+ printf (" expected %d\n", tests[i].final_status);
printf ("\n");