aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--src/IO/http.c14
-rw-r--r--src/Makefile.am2
-rw-r--r--src/auth.c514
-rw-r--r--src/auth.h19
-rw-r--r--src/bw.h1
-rw-r--r--src/cache.c67
-rw-r--r--src/dialog.cc79
-rw-r--r--src/dialog.hh4
-rw-r--r--src/dillo.cc2
10 files changed, 702 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index b28ac092..b932fdb9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@ Dillo project
dillo-2.1
+- Added ipv6 addresses iteration and ipv4 fallback.
- Patch: James Turner, Jorge Arellano
+ Patch: James Turner, Jorge Arellano Cid
+- Added support for numeric IPv6 addresses entered into the url bar.
- Used the URL authority part instead of stripped default port in HTTP query.
Patches: Justus Winter
@@ -26,6 +26,8 @@ dillo-2.1
- Set the ScrollGroup as the resizable widget in downloads dpi.
- Cleaned up and normalized D_SUN_LEN usage.
Patches: Jeremy Henty
++- Implemented Basic authentication!
+ Patch: Jeremy Henty, Jorge Arellano Cid
+- Allowed compilation with older machines by removing a few C99isms.
- Added use of inttypes.h when stdint.h isn't found.
Patches: Dan Fandrich
@@ -36,12 +38,12 @@ dillo-2.1
+- Made the parser recognize "[^ ]/>"-terminated XML elements.
Patch: Johannes Hofmann
+- Added the "middle_click_drags_page" dillorc option.
- Patch: Jorge Arellano, Thomas Orgis
+ Patch: Jorge Arellano Cid, Thomas Orgis
+- Set the File menu label to hide when the File menu-button is shown.
? Trying a new iconv() test in configure.in.
- Allowed the rc parser to skip whitespace around the equal sign.
- Fixed the parser not to call Html_tag_close_* functions twice.
- Patches: Jorge Arellano
+ Patches: Jorge Arellano Cid
dw
diff --git a/src/IO/http.c b/src/IO/http.c
index 2c50e829..9deb961a 100644
--- a/src/IO/http.c
+++ b/src/IO/http.c
@@ -33,6 +33,7 @@
#include "../dns.h"
#include "../web.hh"
#include "../cookies.h"
+#include "../auth.h"
#include "../prefs.h"
#include "../misc.h"
@@ -196,7 +197,7 @@ Dstr *Http_make_content_type(const DilloUrl *url)
*/
Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
{
- char *ptr, *cookies, *referer;
+ char *ptr, *cookies, *auth, *referer;
Dstr *query = dStr_new(""),
*full_path = dStr_new(""),
*proxy_auth = dStr_new("");
@@ -219,6 +220,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
}
cookies = a_Cookies_get_query(url);
+ auth = a_Auth_get_auth_str(url);
referer = Http_get_referer(url);
if (URL_FLAGS(url) & URL_Post) {
Dstr *content_type = Http_make_content_type(url);
@@ -228,15 +230,16 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
"Connection: close\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
+ "%s" /* auth */
"Host: %s\r\n"
"%s"
"%s"
"User-Agent: Dillo/%s\r\n"
"Content-Length: %ld\r\n"
"Content-Type: %s\r\n"
- "%s"
+ "%s" /* cookies */
"\r\n",
- full_path->str, URL_AUTHORITY(url),
+ full_path->str, auth ? auth : "", URL_AUTHORITY(url),
proxy_auth->str, referer, VERSION,
URL_DATA(url)->len, content_type->str,
cookies);
@@ -250,16 +253,17 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
"Connection: close\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
+ "%s" /* auth */
"Host: %s\r\n"
"%s"
"%s"
"User-Agent: Dillo/%s\r\n"
- "%s"
+ "%s" /* cookies */
"\r\n",
full_path->str,
(URL_FLAGS(url) & URL_E2EQuery) ?
"Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
- URL_AUTHORITY(url),
+ auth ? auth : "", URL_AUTHORITY(url),
proxy_auth->str, referer, VERSION, cookies);
}
dFree(referer);
diff --git a/src/Makefile.am b/src/Makefile.am
index d4d03eea..8da631ea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,6 +28,8 @@ dillo_SOURCES = \
bw.c \
cookies.c \
cookies.h \
+ auth.c \
+ auth.h \
colors.c \
colors.h \
binaryconst.h \
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 00000000..81e97188
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,514 @@
+/*
+ * File: auth.c
+ *
+ * Copyright 2008 Jeremy Henty <onepoint@starurchin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Handling of HTTP AUTH takes place here.
+ * This implementation aims to follow RFC 2617:
+ * http://www.ietf.org/rfc/rfc2617.txt
+ */
+
+
+#include <ctype.h> /* for parsing */
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "auth.h"
+#include "msg.h"
+#include "misc.h"
+#include "dialog.hh"
+#include "IO/Url.h"
+#include "../dlib/dlib.h"
+
+
+typedef struct {
+ int ok;
+ const char *realm;
+} AuthParse_t;
+
+typedef struct {
+ char *name;
+ Dlist *paths; /* stripped of any trailing '/', so the root path is "" */
+ char *authorization; /* the authorization request header */
+} AuthRealm_t;
+
+typedef struct {
+ char *scheme;
+ char *authority;
+ Dlist *realms;
+} AuthHost_t;
+
+typedef struct {
+ const char *realm_name;
+ const DilloUrl *url;
+} AuthDialogData_t;
+
+/*
+ * Local data
+ */
+static Dlist *auth_hosts;
+
+/*
+ * Initialize the auth module.
+ */
+void a_Auth_init(void)
+{
+ auth_hosts = dList_new(1);
+}
+
+static AuthParse_t *Auth_parse_new()
+{
+ AuthParse_t *auth_parse = dNew(AuthParse_t, 1);
+ auth_parse->ok = 0;
+ auth_parse->realm = NULL;
+ return auth_parse;
+}
+
+static void Auth_parse_free(AuthParse_t *auth_parse)
+{
+ if (auth_parse) {
+ dFree((void *) auth_parse->realm);
+ }
+}
+
+static int Auth_path_is_inside(const char *path1, const char *path2, int len)
+{
+ /*
+ * path2 is effectively truncated to length len. Typically len will be
+ * strlen(path2), or 1 less when we want to ignore a trailing '/'.
+ */
+ return
+ strncmp(path1, path2, len) == 0 &&
+ (path1[len] == '\0' || path1[len] == '/');
+}
+
+/*
+ * Check valid chars.
+ * Return: 0 if invalid, 1 otherwise.
+ */
+static int Auth_is_token_char(char c)
+{
+ const char *invalid = "\"()<>@,;:\\[]¿?=/{} \t";
+ return (strchr(invalid, c) || iscntrl(c)) ? 0 : 1;
+}
+
+static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char *auth)
+{
+ int token_value_pairs_found;
+ char *realm = NULL;
+ static const char realm_token[] = "realm";
+
+ /* parse comma-separated token-value pairs */
+ token_value_pairs_found = 0;
+ while (1) {
+ char *token, *value;
+ int token_size, value_size;
+
+ /* skip host and comma characters */
+ while (*auth == ' ' || *auth == ',')
+ auth++;
+
+ /* end of string? */
+ if (!*auth)
+ goto end_parse;
+
+ /* parse a token */
+ token = auth;
+ token_size = 0;
+ while (Auth_is_token_char(*auth)) {
+ auth++;
+ token_size++;
+ }
+ if (token_size == 0) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token\n");
+ goto end_parse;
+ }
+
+ /* skip space characters */
+ while (*auth == ' ')
+ auth++;
+
+ /* parse the '=' */
+ switch (*auth++) {
+ case '=':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token value\n");
+ goto end_parse;
+ break;
+ default:
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "garbage after Basic auth token\n");
+ goto end_parse;
+ break;
+ }
+
+ /* skip space characters */
+ while (*auth == ' ')
+ auth++;
+
+ /* parse a quoted string */
+
+ /* parse a '"' */
+ switch (*auth++) {
+ case '"':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token value after '='\n");
+ goto end_parse;
+ break;
+ default:
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "garbage in Basic auth after '='\n");
+ goto end_parse;
+ break;
+ }
+
+ /* parse the rest of a quoted string */
+ value = auth;
+ value_size = 0;
+ while (1) {
+ switch (*auth++) {
+ case '"':
+ goto end_quoted_string;
+ break;
+ case '\0':
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "auth string ended inside quoted string value\n");
+ goto end_parse;
+ break;
+ case '\\':
+ /* end of string? */
+ if (!*auth++) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "auth string ended inside quoted string value "
+ "immediately after \\\n");
+ goto end_parse;
+ }
+ /* fall through to the next case */
+ default:
+ value_size++;
+ break;
+ }
+ } /* parse quoted string */
+ end_quoted_string:
+
+ token_value_pairs_found = 1;
+
+ if (realm == NULL &&
+ strncasecmp(realm_token,token,token_size) == 0 &&
+ strlen(realm_token) == token_size) {
+ /* unquote and save the value */
+ char c, *value_ptr, *realm_ptr;
+ realm = dNew(char, value_size + 1);
+ value_ptr = value;
+ realm_ptr = realm;
+ while ((c = *value_ptr++) != '"')
+ *realm_ptr++ = (c == '\\') ? *value_ptr++ : c;
+ *realm_ptr = '\0';
+ auth_parse->ok = 1;
+ auth_parse->realm = realm;
+ _MSG("auth.c: Auth_parse_auth_basic: realm: '%s'\n", realm);
+ return;
+ }
+ }
+ end_parse:
+
+ if (!token_value_pairs_found) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token-value pairs\n");
+ return;
+ }
+
+ if (!realm) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth realm\n");
+ return;
+ }
+}
+
+static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth)
+{
+ _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth);
+ if (strncasecmp(auth, "Basic ", 6) == 0) {
+ Auth_parse_auth_basic(auth_parse, auth + 6);
+ } else {
+ MSG("auth.c: Auth_parse_auth: "
+ "unknown authorization scheme: auth = {%s}\n",
+ auth);
+ }
+}
+
+/*
+ * Return the host that contains a URL, or NULL if there is no such host.
+ */
+static AuthHost_t *Auth_host_by_url(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ int i;
+
+ for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++)
+ if (((dStrcasecmp(URL_SCHEME(url), host->scheme) == 0) &&
+ (dStrcasecmp(URL_AUTHORITY(url), host->authority) == 0)))
+ return host;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the given name.
+ */
+static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host,
+ const char *name)
+{
+ AuthRealm_t *realm;
+ int i;
+
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++)
+ if (strcmp(realm->name,name) == 0)
+ return realm;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the best-matching path.
+ */
+static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host,
+ const char *path)
+{
+ AuthRealm_t *realm_best, *realm;
+ int i, j;
+ int match_length;
+
+ realm_best = NULL;
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) {
+ char *realm_path;
+
+ for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) {
+ int realm_path_length;
+
+ realm_path_length = strlen(realm_path);
+ if (Auth_path_is_inside(path, realm_path, realm_path_length) &&
+ !(realm_best && match_length >= realm_path_length)) {
+ realm_best = realm;
+ match_length = realm_path_length;
+ }
+ } /* for (j = 0; (path = ... */
+ } /* for (i = 0; (realm = ... */
+
+ return realm_best;
+}
+
+static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path)
+{
+ int i;
+ char *realm_path;
+
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++)
+ if (Auth_path_is_inside(path, realm_path, strlen(realm_path)))
+ return 1;
+
+ return 0;
+}
+
+static void Auth_realm_add_path(AuthRealm_t *realm, const char *path)
+{
+ int len, i;
+ char *realm_path, *n_path;
+
+ n_path = strdup(path);
+ len = strlen(n_path);
+
+ /* remove trailing '/' */
+ if (len && n_path[len - 1] == '/')
+ n_path[--len] = 0;
+
+ /* delete existing paths that are inside the new one */
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) {
+ if (Auth_path_is_inside(realm_path, path, len)) {
+ dList_remove_fast(realm->paths, realm_path);
+ dFree(realm_path);
+ i--; /* reconsider this slot */
+ }
+ }
+
+ dList_append(realm->paths, n_path);
+}
+
+/*
+ * Return the authorization header for an HTTP query.
+ */
+char *a_Auth_get_auth_str(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ return
+ ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_path(host, URL_PATH(url)))) ?
+ realm->authorization : NULL;
+}
+
+/*
+ * Determine whether the user needs to authenticate.
+ */
+static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url)
+{
+ /*
+ * TO DO: I dislike the way that this code must decide whether we
+ * sent authentication during the request and trust us to resend it
+ * after the reload. Could it be more robust if every DilloUrl
+ * recorded its authentication, and whether it was accepted? (JCH)
+ */
+
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ /*
+ * The size of the following comments reflects the concerns in the
+ * TO DO at the top of this function. It should not be so hard to
+ * explain why code is correct! (JCH)
+ */
+
+ /*
+ * If we have authentication but did not send it (because we did
+ * not know this path was in the realm) then we update the realm.
+ * We do not re-authenticate because our authentication is probably
+ * OK. Thanks to the updated realm the forthcoming reload will
+ * make us send the authentication. If our authentication is not
+ * OK the server will challenge us again after the reload and then
+ * we will re-authenticate.
+ */
+ if ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_name(host, realm_name)) &&
+ (!Auth_realm_includes_path(realm, URL_PATH(url)))) {
+ _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n",
+ realm_name, URL_STR(url));
+ Auth_realm_add_path(realm, URL_PATH(url));
+ return 0;
+ }
+
+ /*
+ * Either we had no authentication or we sent it and the server
+ * rejected it, so we must re-authenticate.
+ */
+ return 1;
+}
+
+static void Auth_do_auth_dialog_cb(const char *user, const char *password,
+ void *vData)
+{
+ AuthDialogData_t *data;
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+ char *user_password, *response, *authorization, *authorization_old;
+
+ data = (AuthDialogData_t *)vData;
+
+ /* find or create the host */
+ if (!(host = Auth_host_by_url(data->url))) {
+ /* create a new host */
+ host = dNew(AuthHost_t, 1);
+ host->scheme = dStrdup(URL_SCHEME(data->url));
+ host->authority = dStrdup(URL_AUTHORITY(data->url));
+ host->realms = dList_new(1);
+ dList_append(auth_hosts, host);
+ }
+
+ /* find or create the realm */
+ if (!(realm = Auth_realm_by_name(host, data->realm_name))) {
+ /* create a new realm */
+ realm = dNew(AuthRealm_t, 1);
+ realm->name = dStrdup(data->realm_name);
+ realm->paths = dList_new(1);
+ realm->authorization = NULL;
+ dList_append(host->realms, realm);
+ }
+
+ Auth_realm_add_path(realm, URL_PATH(data->url));
+
+ /* create and set the authorization */
+ user_password = dStrconcat(user, ":", password, NULL);
+ response = a_Misc_encode_base64(user_password);
+ authorization =
+ dStrconcat("Authorization: Basic ", response, "\r\n", NULL);
+ authorization_old = realm->authorization;
+ realm->authorization = authorization;
+ dFree(authorization_old);
+ dFree(user_password);
+ dFree(response);
+}
+
+static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url)
+{
+ int ret;
+ char *message;
+ AuthDialogData_t *data;
+
+ _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm);
+ message = dStrconcat("Enter a user and password for \"",
+ realm, "\".", NULL);
+ data = dNew(AuthDialogData_t, 1);
+ data->realm_name = dStrdup(realm);
+ data->url = a_Url_dup(url);
+ ret = a_Dialog_user_password(message, Auth_do_auth_dialog_cb, data);
+ dFree(message);
+ dFree((void*)data->realm_name);
+ a_Url_free((void*)data->url);
+ dFree(data);
+ return ret;
+}
+
+/*
+ * Do authorization for an auth string.
+ */
+static int Auth_do_auth(char *auth, const DilloUrl *url)
+{
+ int reload;
+ AuthParse_t *auth_parse;
+
+ _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth);
+ reload = 0;
+ auth_parse = Auth_parse_new();
+ Auth_parse_auth(auth_parse, auth);
+ if (auth_parse->ok)
+ reload =
+ Auth_do_auth_required(auth_parse->realm, url) ?
+ Auth_do_auth_dialog(auth_parse->realm, url)
+ : 1;
+ Auth_parse_free(auth_parse);
+
+ return reload;
+}
+
+/*
+ * Do authorization for a set of auth strings.
+ */
+int a_Auth_do_auth(Dlist *auths, const DilloUrl *url)
+{
+ int reload, i;
+ char *auth;
+
+ reload = 0;
+ for (i = 0; (auth = dList_nth_data(auths, i)); ++i)
+ if (Auth_do_auth(auth, url))
+ reload = 1;
+
+ return reload;
+}
+
diff --git a/src/auth.h b/src/auth.h
new file mode 100644
index 00000000..8813fc37
--- /dev/null
+++ b/src/auth.h
@@ -0,0 +1,19 @@
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+
+
+char *a_Auth_get_auth_str(const DilloUrl *request_url);
+int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url);
+void a_Auth_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__AUTH_H__ */
diff --git a/src/bw.h b/src/bw.h
index f0d8dd7d..4345bf54 100644
--- a/src/bw.h
+++ b/src/bw.h
@@ -84,6 +84,7 @@ void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc);
void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url);
void a_Bw_cleanup(BrowserWindow *bw);
+typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data);
#ifdef __cplusplus
}
diff --git a/src/cache.c b/src/cache.c
index a6342da1..831ad411 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -32,6 +32,7 @@
#include "misc.h"
#include "capi.h"
#include "decode.h"
+#include "auth.h"
#include "timeout.hh"
#include "uicmd.hh"
@@ -54,6 +55,7 @@ typedef struct {
char *TypeMeta; /* MIME type string from META HTTP-EQUIV */
Dstr *Header; /* HTTP header */
const DilloUrl *Location; /* New URI for redirects */
+ Dlist *Auth; /* Authentication fields */
Dstr *Data; /* Pointer to raw data */
Dstr *UTF8Data; /* Data after charset translation */
int DataRefcount; /* Reference count */
@@ -87,6 +89,7 @@ static uint_t DelayedQueueIdleId = 0;
*/
static void Cache_process_queue(CacheEntry_t *entry);
static void Cache_delayed_process_queue(CacheEntry_t *entry);
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw);
/*
@@ -206,6 +209,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
NewEntry->TypeMeta = NULL;
NewEntry->Header = dStr_new("");
NewEntry->Location = NULL;
+ NewEntry->Auth = NULL;
NewEntry->Data = dStr_sized_new(8*1024);
NewEntry->UTF8Data = NULL;
NewEntry->DataRefcount = 0;
@@ -288,6 +292,18 @@ void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
}
/*
+ * Free Authentication fields.
+ */
+static void Cache_auth_free(Dlist *auth)
+{
+ int i;
+ void *auth_field;
+ for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i)
+ dFree(auth_field);
+ dList_free(auth);
+}
+
+/*
* Free the components of a CacheEntry_t struct.
*/
static void Cache_entry_free(CacheEntry_t *entry)
@@ -298,6 +314,7 @@ static void Cache_entry_free(CacheEntry_t *entry)
dFree(entry->TypeMeta);
dStr_free(entry->Header, TRUE);
a_Url_free((DilloUrl *)entry->Location);
+ Cache_auth_free(entry->Auth);
dStr_free(entry->Data, 1);
dStr_free(entry->UTF8Data, 1);
if (entry->CharsetDecoder)
@@ -655,6 +672,9 @@ static void Cache_parse_header(CacheEntry_t *entry)
}
dFree(location_str);
+ } else if (strncmp(header + 9, "401", 3) == 0) {
+ entry->Auth =
+ Cache_parse_multiple_fields(header, "WWW-Authenticate");
} else if (strncmp(header + 9, "404", 3) == 0) {
entry->Flags |= CA_NotFound;
}
@@ -927,6 +947,50 @@ static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
return 0;
}
+typedef struct {
+ Dlist *auth;
+ DilloUrl *url;
+ BrowserWindow *bw;
+} CacheAuthData_t;
+
+/*
+ * Ask for user/password and reload the page.
+ */
+static void Cache_auth_callback(void *vdata)
+{
+ CacheAuthData_t *data = (CacheAuthData_t *)vdata;
+ if (a_Auth_do_auth(data->auth, data->url))
+ a_Nav_reload(data->bw);
+ Cache_auth_free(data->auth);
+ a_Url_free(data->url);
+ dFree(data);
+ Cache_auth_entry(NULL, NULL);
+ a_Timeout_remove();
+}
+
+/*
+ * Set a timeout function to ask for user/password.
+ */
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
+{
+ static int busy = 0;
+ CacheAuthData_t *data;
+
+ if (!entry) {
+ busy = 0;
+ } else if (busy) {
+ MSG_WARN("Cache_auth_entry: caught busy!\n");
+ } else if (entry->Auth) {
+ busy = 1;
+ data = dNew(CacheAuthData_t, 1);
+ data->auth = entry->Auth;
+ data->url = a_Url_dup(entry->Url);
+ data->bw = bw;
+ entry->Auth = NULL;
+ a_Timeout_add(0.0, Cache_auth_callback, data);
+ }
+}
+
/*
* Check whether a URL scheme is downloadable.
* Return: 1 enabled, 0 disabled.
@@ -1114,6 +1178,9 @@ static void Cache_process_queue(CacheEntry_t *entry)
a_UIcmd_save_link(Client_bw, url);
}
a_Url_free(url);
+
+ } else if (entry->Auth && (entry->Flags & CA_GotData)) {
+ Cache_auth_entry(entry, Client_bw);
}
/* Trigger cleanup when there are no cache clients */
diff --git a/src/dialog.cc b/src/dialog.cc
index 8408782f..ca457c40 100644
--- a/src/dialog.cc
+++ b/src/dialog.cc
@@ -20,6 +20,9 @@
#include <fltk/ReturnButton.h>
#include <fltk/TextDisplay.h>
#include <fltk/HighlightButton.h>
+#include <fltk/WordwrapOutput.h>
+#include <fltk/Input.h>
+#include <fltk/SecretInput.h>
#include "msg.h"
#include "dialog.hh"
@@ -227,3 +230,79 @@ int a_Dialog_choice5(const char *QuestionTxt,
return choice5_answer;
}
+
+/*--------------------------------------------------------------------------*/
+static int ok_answer = 1, cancel_answer = 0;
+static void Dialog_user_password_cb(Widget *button, void *vIntPtr)
+{
+ int ok = VOIDP2INT(vIntPtr);
+ _MSG("Dialog_user_password_cb: %d\n", ok);
+ button->window()->make_exec_return(ok);
+}
+
+/*
+ * Make a user/password dialog.
+ * Call the callback with the result (OK or not) and the given user and
+ * password if OK.
+ */
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp)
+{
+ int ok,
+ window_w = 300, window_h = 280,
+ input_x = 80, input_w = 200, input_h = 30,
+ button_y = 230, button_h = 30;
+
+ Window *window =
+ new Window(window_w,window_h,"User/Password");
+ window->begin();
+
+ /* message */
+ WordwrapOutput *message_output =
+ new WordwrapOutput(20,20,window_w-40,100);
+ message_output->box(DOWN_BOX);
+ message_output->text(message);
+ message_output->textfont(HELVETICA_BOLD_ITALIC);
+ message_output->textsize(14);
+
+ /* inputs */
+ Input *user_input =
+ new Input(input_x,140,input_w,input_h,"User");
+ user_input->labelsize(14);
+ user_input->textsize(14);
+ SecretInput *password_input =
+ new SecretInput(input_x,180,input_w,input_h,"Password");
+ password_input->labelsize(14);
+ password_input->textsize(14);
+
+ /* "OK" button */
+ Button *ok_button =
+ new Button(200,button_y,50,button_h,"OK");
+ ok_button->labelsize(14);
+ ok_button->callback(Dialog_user_password_cb);
+ ok_button->user_data(&ok_answer);
+
+ /* "Cancel" button */
+ Button *cancel_button =
+ new Button(50,button_y,100,button_h,"Cancel");
+ cancel_button->labelsize(14);
+ cancel_button->callback(Dialog_user_password_cb);
+ cancel_button->user_data(&cancel_answer);
+
+ window->end();
+ window->size_range(window_w,window_h,window_w,window_h);
+ window->resizable(window);
+
+ if ((ok = window->exec())) {
+ /* call the callback */
+ const char *user, *password;
+ user = user_input->value();
+ password = password_input->value();
+ _MSG("a_Dialog_user_passwd: ok = %d\n", ok);
+ (*cb)(user, password, vp);
+ }
+
+ delete window;
+
+ return ok;
+}
+
diff --git a/src/dialog.hh b/src/dialog.hh
index 17c326cb..d82eb0cc 100644
--- a/src/dialog.hh
+++ b/src/dialog.hh
@@ -5,12 +5,16 @@
extern "C" {
#endif /* __cplusplus */
+typedef void (*UserPasswordCB)(const char *user, const char *password,
+ void *vp);
+
void a_Dialog_msg(const char *msg);
int a_Dialog_choice3(const char *msg,
const char *b0, const char *b1, const char *b2);
int a_Dialog_choice5(const char *QuestionTxt,
const char *alt1, const char *alt2, const char *alt3,
const char *alt4, const char *alt5);
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp);
const char *a_Dialog_input(const char *msg);
const char *a_Dialog_passwd(const char *msg);
const char *a_Dialog_save_file(const char *msg,
diff --git a/src/dillo.cc b/src/dillo.cc
index c20d1f02..10430f29 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -45,6 +45,7 @@
#include "capi.h"
#include "dicache.h"
#include "cookies.h"
+#include "auth.h"
/*
@@ -102,6 +103,7 @@ int main(int argc, char **argv)
a_Dicache_init();
a_Bw_init();
a_Cookies_init();
+ a_Auth_init();
// Sets WM_CLASS hint on X11
fltk::Window::xclass("dillo");