add a new generic auth callback that can be used with any subclass to do
authorDan Winship <danw@src.gnome.org>
Fri, 18 Jan 2008 14:53:35 +0000 (14:53 +0000)
committerDan Winship <danw@src.gnome.org>
Fri, 18 Jan 2008 14:53:35 +0000 (14:53 +0000)
* libsoup/soup-auth-domain.c
(soup_auth_domain_set_generic_auth_callback):
(soup_auth_domain_check_password): add a new generic auth callback
that can be used with any subclass to do cleartext password
checking against messages. Suggested by Mathias Hasselmann.

* libsoup/soup-auth-domain-basic.c: Implement generic auth

* libsoup/soup-auth-domain-digest.c: Implement generic auth.
(soup_auth_domain_digest_evil_check_password): Gone, use the
generic version now.

svn path=/trunk/; revision=1048

ChangeLog
libsoup/soup-auth-domain-basic.c
libsoup/soup-auth-domain-digest.c
libsoup/soup-auth-domain-digest.h
libsoup/soup-auth-domain.c
libsoup/soup-auth-domain.h

index 83c6001..e41c718 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2008-01-18  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-domain.c
+       (soup_auth_domain_set_generic_auth_callback):
+       (soup_auth_domain_check_password): add a new generic auth callback
+       that can be used with any subclass to do cleartext password
+       checking against messages. Suggested by Mathias Hasselmann.
+
+       * libsoup/soup-auth-domain-basic.c: Implement generic auth
+
+       * libsoup/soup-auth-domain-digest.c: Implement generic auth.
+       (soup_auth_domain_digest_evil_check_password): Gone, use the
+       generic version now.
+
 2008-01-17  Dan Winship  <danw@gnome.org>
 
        * libsoup/soup-auth-digest.c (soup_auth_digest_compute_hex_urp)
index c3c64e2..b8b8fc5 100644 (file)
@@ -42,11 +42,15 @@ typedef struct {
 
 G_DEFINE_TYPE (SoupAuthDomainBasic, soup_auth_domain_basic, SOUP_TYPE_AUTH_DOMAIN)
 
-static char *accepts   (SoupAuthDomain *domain,
-                       SoupMessage    *msg,
-                       const char     *header);
-static char *challenge (SoupAuthDomain *domain,
-                       SoupMessage    *msg);
+static char    *accepts        (SoupAuthDomain *domain,
+                               SoupMessage    *msg,
+                               const char     *header);
+static char    *challenge      (SoupAuthDomain *domain,
+                               SoupMessage    *msg);
+static gboolean check_password (SoupAuthDomain *domain,
+                               SoupMessage    *msg,
+                               const char     *username,
+                               const char     *password);
 
 static void set_property (GObject *object, guint prop_id,
                          const GValue *value, GParamSpec *pspec);
@@ -79,8 +83,9 @@ soup_auth_domain_basic_class_init (SoupAuthDomainBasicClass *basic_class)
 
        g_type_class_add_private (basic_class, sizeof (SoupAuthDomainBasicPrivate));
 
-       auth_domain_class->accepts   = accepts;
-       auth_domain_class->challenge = challenge;
+       auth_domain_class->accepts        = accepts;
+       auth_domain_class->challenge      = challenge;
+       auth_domain_class->check_password = check_password;
 
        object_class->finalize     = finalize;
        object_class->set_property = set_property;
@@ -241,40 +246,53 @@ pw_free (char *pw)
        g_free (pw);
 }
 
-static char *
-accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+static gboolean
+parse_basic (SoupMessage *msg, const char *header,
+            char **username, char **password)
 {
-       SoupAuthDomainBasicPrivate *priv =
-               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
        char *decoded, *colon;
        gsize len, plen;
-       char *username, *password;
-       gboolean ok = FALSE;
-
-       if (!priv->auth_callback)
-               return NULL;
 
        if (strncmp (header, "Basic ", 6) != 0)
-               return NULL;
+               return FALSE;
 
        decoded = (char *)g_base64_decode (header + 6, &len);
        if (!decoded)
-               return NULL;
+               return FALSE;
 
        colon = memchr (decoded, ':', len);
        if (!colon) {
                pw_free (decoded);
-               return NULL;
+               return FALSE;
        }
        *colon = '\0';
        plen = len - (colon - decoded) - 1;
 
-       password = g_strndup (colon + 1, plen);
+       *password = g_strndup (colon + 1, plen);
        memset (colon + 1, 0, plen);
-       username = decoded;
+       *username = decoded;
+       return TRUE;
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
+       char *username, *password;
+       gboolean ok = FALSE;
+
+       if (!parse_basic (msg, header, &username, &password))
+               return NULL;
+
+       if (priv->auth_callback) {
+               ok = priv->auth_callback (domain, msg, username, password,
+                                         priv->auth_data);
+       } else {
+               ok = soup_auth_domain_try_generic_auth_callback (
+                       domain, msg, username);
+       }
 
-       ok = priv->auth_callback (domain, msg, username, password,
-                                 priv->auth_data);
        pw_free (password);
 
        if (ok)
@@ -292,3 +310,25 @@ challenge (SoupAuthDomain *domain, SoupMessage *msg)
        return g_strdup_printf ("Basic realm=\"%s\"",
                                soup_auth_domain_get_realm (domain));
 }
+
+static gboolean
+check_password (SoupAuthDomain *domain,
+               SoupMessage    *msg,
+               const char     *username,
+               const char     *password)
+{
+       const char *header;
+       char *msg_username, *msg_password;
+       gboolean ok;
+
+       header = soup_message_headers_get (msg->request_headers, "Authorization");
+       if (!parse_basic (msg, header, &msg_username, &msg_password))
+               return FALSE;
+
+       ok = (!strcmp (username, msg_username) &&
+             !strcmp (password, msg_password));
+       g_free (msg_username);
+       pw_free (msg_password);
+
+       return ok;
+}
index b303ce8..a98bf74 100644 (file)
@@ -47,11 +47,15 @@ typedef struct {
 
 G_DEFINE_TYPE (SoupAuthDomainDigest, soup_auth_domain_digest, SOUP_TYPE_AUTH_DOMAIN)
 
-static char *accepts   (SoupAuthDomain *domain,
-                       SoupMessage    *msg,
-                       const char     *header);
-static char *challenge (SoupAuthDomain *domain,
-                       SoupMessage    *msg);
+static char    *accepts        (SoupAuthDomain *domain,
+                               SoupMessage    *msg,
+                               const char     *header);
+static char    *challenge      (SoupAuthDomain *domain,
+                               SoupMessage    *msg);
+static gboolean check_password (SoupAuthDomain *domain,
+                               SoupMessage    *msg,
+                               const char     *username,
+                               const char     *password);
 
 static void set_property (GObject *object, guint prop_id,
                          const GValue *value, GParamSpec *pspec);
@@ -84,8 +88,9 @@ soup_auth_domain_digest_class_init (SoupAuthDomainDigestClass *digest_class)
 
        g_type_class_add_private (digest_class, sizeof (SoupAuthDomainDigestPrivate));
 
-       auth_domain_class->accepts   = accepts;
-       auth_domain_class->challenge = challenge;
+       auth_domain_class->accepts        = accepts;
+       auth_domain_class->challenge      = challenge;
+       auth_domain_class->check_password = check_password;
 
        object_class->finalize     = finalize;
        object_class->set_property = set_property;
@@ -229,40 +234,32 @@ soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain,
        g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA);
 }
 
-static char *
-accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+static gboolean
+check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
+              GHashTable *params, const char *username,
+              const char *hex_urp)
 {
-       SoupAuthDomainDigestPrivate *priv =
-               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
-       GHashTable *params;
-       const char *uri, *qop, *realm, *username;
+       const char *uri, *qop, *realm, *msg_username;
        const char *nonce, *nc, *cnonce, *response;
-       char *hex_urp, hex_a1[33], computed_response[33], *ret_user;
+       char hex_a1[33], computed_response[33];
        int nonce_count;
        SoupURI *dig_uri, *req_uri;
-       gboolean accept = FALSE;
-
-       if (!priv->auth_callback)
-               return NULL;
-
-       if (strncmp (header, "Digest ", 7) != 0)
-               return NULL;
 
-       params = soup_header_parse_param_list (header + 7);
-       if (!params)
-               return NULL;
+       msg_username = g_hash_table_lookup (params, "username");
+       if (!msg_username || strcmp (msg_username, username) != 0)
+               return FALSE;
 
        /* Check uri */
        uri = g_hash_table_lookup (params, "uri");
        if (!uri)
-               goto DONE;
+               return FALSE;
 
        req_uri = soup_message_get_uri (msg);
        dig_uri = soup_uri_new (uri);
        if (dig_uri) {
                if (!soup_uri_equal (dig_uri, req_uri)) {
                        soup_uri_free (dig_uri);
-                       goto DONE;
+                       return FALSE;
                }
                soup_uri_free (dig_uri);
        } else {        
@@ -271,7 +268,7 @@ accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
                req_path = soup_uri_to_string (req_uri, TRUE);
                if (strcmp (uri, req_path) != 0) {
                        g_free (req_path);
-                       goto DONE;
+                       return FALSE;
                }
                g_free (req_path);
        }
@@ -279,46 +276,75 @@ accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
        /* Check qop; we only support "auth" for now */
        qop = g_hash_table_lookup (params, "qop");
        if (!qop || strcmp (qop, "auth") != 0)
-               goto DONE;
+               return FALSE;
 
        /* Check realm */
        realm = g_hash_table_lookup (params, "realm");
        if (!realm || strcmp (realm, soup_auth_domain_get_realm (domain)) != 0)
-               goto DONE;
+               return FALSE;
 
-       username = g_hash_table_lookup (params, "username");
-       if (!username)
-               goto DONE;
        nonce = g_hash_table_lookup (params, "nonce");
        if (!nonce)
-               goto DONE;
+               return FALSE;
        nc = g_hash_table_lookup (params, "nc");
        if (!nc)
-               goto DONE;
+               return FALSE;
        nonce_count = atoi (nc);
        if (nonce_count <= 0)
-               goto DONE;
+               return FALSE;
        cnonce = g_hash_table_lookup (params, "cnonce");
        if (!cnonce)
-               goto DONE;
+               return FALSE;
        response = g_hash_table_lookup (params, "response");
        if (!response)
-               goto DONE;
+               return FALSE;
+
+       soup_auth_digest_compute_hex_a1 (hex_urp,
+                                        SOUP_AUTH_DIGEST_ALGORITHM_MD5,
+                                        nonce, cnonce, hex_a1);
+       soup_auth_digest_compute_response (msg->method, uri,
+                                          hex_a1,
+                                          SOUP_AUTH_DIGEST_QOP_AUTH,
+                                          nonce, cnonce, nonce_count,
+                                          computed_response);
+       return strcmp (response, computed_response) == 0;
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+       GHashTable *params;
+       const char *username;
+       gboolean accept = FALSE;
+       char *ret_user;
+
+       if (strncmp (header, "Digest ", 7) != 0)
+               return NULL;
+
+       params = soup_header_parse_param_list (header + 7);
+       if (!params)
+               return NULL;
+
+       username = g_hash_table_lookup (params, "username");
+       if (!username) {
+               soup_header_free_param_list (params);
+               return NULL;
+       }
 
-       hex_urp = priv->auth_callback (domain, msg, username, priv->auth_data);
-       if (hex_urp) {
-               soup_auth_digest_compute_hex_a1 (hex_urp,
-                                                SOUP_AUTH_DIGEST_ALGORITHM_MD5,
-                                                nonce, cnonce, hex_a1);
+       if (priv->auth_callback) {
+               char *hex_urp;
+
+               hex_urp = priv->auth_callback (domain, msg, username,
+                                              priv->auth_data);
+               accept = check_hex_urp (domain, msg, params, username, hex_urp);
                g_free (hex_urp);
-               soup_auth_digest_compute_response (msg->method, uri, hex_a1,
-                                                  SOUP_AUTH_DIGEST_QOP_AUTH,
-                                                  nonce, cnonce, nonce_count,
-                                                  computed_response);
-               accept = (strcmp (response, computed_response) == 0);
+       } else {
+               accept = soup_auth_domain_try_generic_auth_callback (
+                       domain, msg, username);
        }
 
- DONE:
        ret_user = accept ? g_strdup (username) : NULL;
        soup_header_free_param_list (params);
        return ret_user;
@@ -378,77 +404,36 @@ soup_auth_domain_digest_encode_password (const char *username,
        return g_strdup (hex_urp);
 }
 
-static char *
-evil_auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
-                   const char *username, gpointer encoded_password)
-{
-       return g_strdup (encoded_password);
-}
-
-/**
- * soup_auth_domain_digest_evil_check_password:
- * @domain: the auth domain
- * @msg: the possibly-authenticated request
- * @username: the username to check @msg against
- * @password: the password to check @msg against
- *
- * Checks if @msg correctly authenticates @username via @password in
- * @domain.
- *
- * Don't use this method; it's evil. It requires you to have a
- * cleartext password database, which means that if your server is
- * compromised, the attackers will have access to all of your users'
- * passwords, which may also be their passwords on other servers. It
- * is much better to store the passwords encoded in some format (eg,
- * via soup_auth_domain_digest_encode_password() when using Digest
- * authentication), so that if the server is compromised, the
- * attackers won't be able to use the encoded passwords elsewhere.
- *
- * At any rate, even if you do have a cleartext password database, you
- * still don't need to use this method, as you can just call
- * soup_auth_domain_digest_encode_password() on the cleartext password
- * from the #SoupAuthDomainDigestAuthCallback anyway. This method
- * really only exists so as not to break certain libraries written
- * against libsoup 2.2 whose public APIs depend on the existence of a
- * "check this password against this request" method.
- *
- * Return value: %TRUE if @msg matches @username and @password,
- * %FALSE if not.
- **/
-gboolean
-soup_auth_domain_digest_evil_check_password (SoupAuthDomain *domain,
-                                            SoupMessage    *msg,
-                                            const char     *username,
-                                            const char     *password)
+static gboolean
+check_password (SoupAuthDomain *domain,
+               SoupMessage    *msg,
+               const char     *username,
+               const char     *password)
 {
-       SoupAuthDomainDigestPrivate *priv =
-               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
-       char *encoded_password;
        const char *header;
-       SoupAuthDomainDigestAuthCallback old_callback;
-       gpointer old_data;
-       char *matched_username;
-
-       encoded_password = soup_auth_domain_digest_encode_password (
-               username, soup_auth_domain_get_realm (domain), password);
-
-       old_callback = priv->auth_callback;
-       old_data = priv->auth_data;
-
-       priv->auth_callback = evil_auth_callback;
-       priv->auth_data = encoded_password;
+       GHashTable *params;
+       const char *msg_username;
+       char hex_urp[33];
+       gboolean accept;
 
        header = soup_message_headers_get (msg->request_headers, "Authorization");
-       matched_username = accepts (domain, msg, header);
-
-       priv->auth_callback = old_callback;
-       priv->auth_data = old_data;
+       if (strncmp (header, "Digest ", 7) != 0)
+               return FALSE;
 
-       g_free (encoded_password);
+       params = soup_header_parse_param_list (header + 7);
+       if (!params)
+               return FALSE;
 
-       if (matched_username) {
-               g_free (matched_username);
-               return TRUE;
-       } else
+       msg_username = g_hash_table_lookup (params, "username");
+       if (!msg_username || strcmp (msg_username, username) != 0) {
+               soup_header_free_param_list (params);
                return FALSE;
+       }
+
+       soup_auth_digest_compute_hex_urp (username,
+                                         soup_auth_domain_get_realm (domain),
+                                         password, hex_urp);
+       accept = check_hex_urp (domain, msg, params, username, hex_urp);
+       soup_header_free_param_list (params);
+       return accept;
 }
index 8f8ae10..0a9a36e 100644 (file)
@@ -52,11 +52,4 @@ char   *soup_auth_domain_digest_encode_password    (const char     *username,
                                                    const char     *realm,
                                                    const char     *password);
 
-
-gboolean soup_auth_domain_digest_evil_check_password (SoupAuthDomain *domain,
-                                                     SoupMessage    *msg,
-                                                     const char     *username,
-                                                     const char     *password);
-
-
 #endif /* SOUP_AUTH_DOMAIN_DIGEST_H */
index 8ff77bb..04ee1c4 100644 (file)
@@ -47,6 +47,8 @@ enum {
        PROP_REMOVE_PATH,
        PROP_FILTER,
        PROP_FILTER_DATA,
+       PROP_GENERIC_AUTH_CALLBACK,
+       PROP_GENERIC_AUTH_DATA,
 
        LAST_PROP
 };
@@ -54,10 +56,16 @@ enum {
 typedef struct {
        char *realm;
        gboolean proxy;
+       SoupPathMap *paths;
+
        SoupAuthDomainFilter filter;
        gpointer filter_data;
        GDestroyNotify filter_dnotify;
-       SoupPathMap *paths;
+
+       SoupAuthDomainGenericAuthCallback auth_callback;
+       gpointer auth_data;
+       GDestroyNotify auth_dnotify;
+
 } SoupAuthDomainPrivate;
 
 #define SOUP_AUTH_DOMAIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainPrivate))
@@ -87,6 +95,8 @@ finalize (GObject *object)
 
        if (priv->filter_dnotify)
                priv->filter_dnotify (priv->filter_data);
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
 
        G_OBJECT_CLASS (soup_auth_domain_parent_class)->finalize (object);
 }
@@ -142,6 +152,18 @@ soup_auth_domain_class_init (SoupAuthDomainClass *auth_domain_class)
                                      "Filter data",
                                      "Data to pass to filter",
                                      G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_GENERIC_AUTH_CALLBACK,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK,
+                                     "Generic authentication callback",
+                                     "An authentication callback that can be used with any SoupAuthDomain subclass",
+                                     G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_GENERIC_AUTH_DATA,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA,
+                                     "Authentication callback data",
+                                     "Data to pass to auth callback",
+                                     G_PARAM_READWRITE));
 }
 
 static void
@@ -177,6 +199,16 @@ set_property (GObject *object, guint prop_id,
                }
                priv->filter_data = g_value_get_pointer (value);
                break;
+       case PROP_GENERIC_AUTH_CALLBACK:
+               priv->auth_callback = g_value_get_pointer (value);
+               break;
+       case PROP_GENERIC_AUTH_DATA:
+               if (priv->auth_dnotify) {
+                       priv->auth_dnotify (priv->auth_data);
+                       priv->auth_dnotify = NULL;
+               }
+               priv->auth_data = g_value_get_pointer (value);
+               break;
        default:
                break;
        }
@@ -201,6 +233,12 @@ get_property (GObject *object, guint prop_id,
        case PROP_FILTER_DATA:
                g_value_set_pointer (value, priv->filter_data);
                break;
+       case PROP_GENERIC_AUTH_CALLBACK:
+               g_value_set_pointer (value, priv->auth_callback);
+               break;
+       case PROP_GENERIC_AUTH_DATA:
+               g_value_set_pointer (value, priv->auth_data);
+               break;
        default:
                break;
        }
@@ -256,6 +294,18 @@ soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path)
 }
 
 /**
+ * SoupAuthDomainFilter:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ * @user_data: the data passed to soup_auth_domain_set_filter()
+ *
+ * The prototype for a #SoupAuthDomain filter; see
+ * soup_auth_domain_set_filter() for details.
+ *
+ * Return value: %TRUE if @msg requires authentication, %FALSE if not.
+ **/
+
+/**
  * soup_auth_domain_set_filter:
  * @domain: a #SoupAuthDomain
  * @filter: the auth filter for @domain
@@ -324,6 +374,105 @@ soup_auth_domain_get_realm (SoupAuthDomain *domain)
 }
 
 /**
+ * SoupAuthDomainGenericAuthCallback:
+ * @domain: a #SoupAuthDomain
+ * @msg: the #SoupMessage being authenticated
+ * @username: the username from @msg
+ * @user_data: the data passed to
+ * soup_auth_domain_set_generic_auth_callback()
+ *
+ * The prototype for a #SoupAuthDomain generic authentication callback.
+ *
+ * The callback should look up the user's password, call
+ * soup_auth_domain_check_password(), and use the return value from
+ * that method as its own return value.
+ *
+ * In general, for security reasons, it is preferable to use the
+ * auth-domain-specific auth callbacks (eg,
+ * #SoupAuthDomainBasicAuthCallback and
+ * #SoupAuthDomainDigestAuthCallback), because they don't require
+ * keeping a cleartext password database. Most users will use the same
+ * password for many different sites, meaning if any site with a
+ * cleartext password database is compromised, accounts on other
+ * servers might be compromised as well. For many of the cases where
+ * #SoupServer is used, this is not really relevant, but it may still
+ * be worth considering.
+ *
+ * Return value: %TRUE if @msg is authenticated, %FALSE if not.
+ **/
+
+/**
+ * soup_auth_domain_set_generic_auth_callback:
+ * @domain: a #SoupAuthDomain
+ * @auth_callback: the auth callback
+ * @auth_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @auth_data when @domain
+ * is destroyed
+ *
+ * Sets @auth_callback as an authentication-handling callback for
+ * @domain. Whenever a request comes in to @domain which cannot be
+ * authenticated via a domain-specific auth callback (eg,
+ * #SoupAuthDomainDigestAuthCallback), the generic auth callback
+ * will be invoked. See #SoupAuthDomainGenericAuthCallback for information
+ * on what the callback should do.
+ **/
+void
+soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
+                                           SoupAuthDomainGenericAuthCallback auth_callback,
+                                           gpointer        auth_data,
+                                           GDestroyNotify  dnotify)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
+
+       priv->auth_callback = auth_callback;
+       priv->auth_data = auth_data;
+       priv->auth_dnotify = dnotify;
+
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK);
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA);
+}
+
+gboolean
+soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
+                                           SoupMessage    *msg,
+                                           const char     *username)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       if (priv->auth_callback)
+               return priv->auth_callback (domain, msg, username, priv->auth_data);
+       else
+               return FALSE;
+}
+
+/**
+ * soup_auth_domain_check_password:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ * @username: a username
+ * @password: a password
+ *
+ * Checks if @msg authenticates to @domain via @username and
+ * @password. This would normally be called from a
+ * #SoupAuthDomainGenericAuthCallback.
+ *
+ * Return value: whether or not the message is authenticated
+ **/
+gboolean
+soup_auth_domain_check_password (SoupAuthDomain *domain,
+                                SoupMessage    *msg,
+                                const char     *username,
+                                const char     *password)
+{
+       return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->check_password (domain, msg,
+                                                                   username,
+                                                                   password);
+}
+
+/**
  * soup_auth_domain_covers:
  * @domain: a #SoupAuthDomain
  * @msg: a #SoupMessage
index 8b903f7..8b8617b 100644 (file)
@@ -23,14 +23,17 @@ struct SoupAuthDomain {
 typedef struct {
        GObjectClass parent_class;
 
-       char *   (*accepts)   (SoupAuthDomain  *domain,
-                              SoupMessage     *msg,
-                              const char      *header);
-       char *   (*challenge) (SoupAuthDomain  *domain,
-                              SoupMessage     *msg);
+       char *   (*accepts)        (SoupAuthDomain *domain,
+                                   SoupMessage    *msg,
+                                   const char     *header);
+       char *   (*challenge)      (SoupAuthDomain *domain,
+                                   SoupMessage    *msg);
+       gboolean (*check_password) (SoupAuthDomain *domain,
+                                   SoupMessage    *msg,
+                                   const char     *username,
+                                   const char     *password);
 
        /* Padding for future expansion */
-       void (*_libsoup_reserved1) (void);
        void (*_libsoup_reserved2) (void);
        void (*_libsoup_reserved3) (void);
        void (*_libsoup_reserved4) (void);
@@ -42,8 +45,17 @@ typedef struct {
 #define SOUP_AUTH_DOMAIN_REMOVE_PATH "remove-path"
 #define SOUP_AUTH_DOMAIN_FILTER      "filter"
 #define SOUP_AUTH_DOMAIN_FILTER_DATA "filter-data"
+#define SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK "generic-auth-callback"
+#define SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA     "generic-auth-data"
 
-typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *, SoupMessage *, gpointer);
+typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *domain,
+                                         SoupMessage    *msg,
+                                         gpointer        user_data);
+
+typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain *domain,
+                                                      SoupMessage    *msg,
+                                                      const char     *username,
+                                                      gpointer        user_data);
 
 GType       soup_auth_domain_get_type    (void);
 
@@ -59,6 +71,15 @@ void        soup_auth_domain_set_filter  (SoupAuthDomain       *domain,
 
 const char *soup_auth_domain_get_realm   (SoupAuthDomain       *domain);
 
+void        soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
+                                                       SoupAuthDomainGenericAuthCallback auth_callback,
+                                                       gpointer        auth_data,
+                                                       GDestroyNotify  dnotify);
+gboolean    soup_auth_domain_check_password (SoupAuthDomain    *domain,
+                                            SoupMessage       *msg,
+                                            const char        *username,
+                                            const char        *password);
+
 gboolean    soup_auth_domain_covers      (SoupAuthDomain       *domain,
                                          SoupMessage          *msg);
 char       *soup_auth_domain_accepts     (SoupAuthDomain       *domain,
@@ -66,4 +87,9 @@ char       *soup_auth_domain_accepts     (SoupAuthDomain       *domain,
 void        soup_auth_domain_challenge   (SoupAuthDomain       *domain,
                                          SoupMessage          *msg);
 
+/* protected */
+gboolean    soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
+                                                       SoupMessage    *msg,
+                                                       const char     *username);
+
 #endif /* SOUP_AUTH_DOMAIN_H */