aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
-rw-r--r--src/form.cc86
-rw-r--r--src/html.cc60
-rw-r--r--src/html_common.hh1
-rw-r--r--src/menu.cc18
13 files changed, 778 insertions, 89 deletions
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 7629aaef..e87c8d74 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");
diff --git a/src/form.cc b/src/form.cc
index c222566e..339150bb 100644
--- a/src/form.cc
+++ b/src/form.cc
@@ -119,7 +119,7 @@ public:
class DilloHtmlReceiver:
public Resource::ActivateReceiver,
- public ButtonResource::ClickedReceiver
+ public Resource::ClickedReceiver
{
friend class DilloHtmlForm;
DilloHtmlForm* form;
@@ -128,7 +128,7 @@ class DilloHtmlReceiver:
void activate (Resource *resource);
void enter (Resource *resource);
void leave (Resource *resource);
- void clicked (ButtonResource *resource, EventButton *event);
+ void clicked (Resource *resource, EventButton *event);
};
class DilloHtmlInput {
@@ -372,8 +372,6 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx)
html->InFlags &= ~IN_SELECT;
html->InFlags &= ~IN_OPTION;
html->InFlags &= ~IN_TEXTAREA;
-
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -698,7 +696,6 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
html->InFlags &= ~IN_TEXTAREA;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -787,8 +784,6 @@ void Html_tag_close_select(DilloHtml *html, int TagIdx)
SelectionResource *res = (SelectionResource*)input->embed->getResource();
select->addOptionsTo (res);
}
-
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -902,7 +897,6 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_close_button(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_BUTTON;
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -950,11 +944,15 @@ DilloHtmlForm::~DilloHtmlForm ()
void DilloHtmlForm::eventHandler(Resource *resource, EventButton *event)
{
MSG("DilloHtmlForm::eventHandler\n");
- DilloHtmlInput *input = getInput(resource);
- if (input) {
- input->activate (this, num_entry_fields, event);
+ if (event && (event->button == 3)) {
+ MSG("Form menu\n");
} else {
- MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ DilloHtmlInput *input = getInput(resource);
+ if (input) {
+ input->activate (this, num_entry_fields, event);
+ } else {
+ MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ }
}
}
@@ -1522,7 +1520,7 @@ void DilloHtmlReceiver::leave (Resource *resource)
a_UIcmd_set_msg(html->bw, "");
}
-void DilloHtmlReceiver::clicked (ButtonResource *resource,
+void DilloHtmlReceiver::clicked (Resource *resource,
EventButton *event)
{
form->eventHandler(resource, event);
@@ -1576,30 +1574,17 @@ void DilloHtmlInput::connectTo(DilloHtmlReceiver *form_receiver)
{
Resource *resource;
if (embed && (resource = embed->getResource())) {
- switch (type) {
- case DILLO_HTML_INPUT_UNKNOWN:
- case DILLO_HTML_INPUT_HIDDEN:
- case DILLO_HTML_INPUT_CHECKBOX:
- case DILLO_HTML_INPUT_RADIO:
- case DILLO_HTML_INPUT_BUTTON:
- case DILLO_HTML_INPUT_TEXTAREA:
- case DILLO_HTML_INPUT_SELECT:
- case DILLO_HTML_INPUT_SEL_LIST:
- // do nothing
- break;
- case DILLO_HTML_INPUT_SUBMIT:
- case DILLO_HTML_INPUT_RESET:
- case DILLO_HTML_INPUT_BUTTON_SUBMIT:
- case DILLO_HTML_INPUT_BUTTON_RESET:
- case DILLO_HTML_INPUT_IMAGE:
- case DILLO_HTML_INPUT_FILE:
- ((ButtonResource *)resource)->connectClicked (form_receiver);
- case DILLO_HTML_INPUT_TEXT:
- case DILLO_HTML_INPUT_PASSWORD:
- case DILLO_HTML_INPUT_INDEX:
- resource->connectActivate (form_receiver);
- break;
- break;
+ resource->connectClicked (form_receiver);
+ if (type == DILLO_HTML_INPUT_SUBMIT ||
+ type == DILLO_HTML_INPUT_RESET ||
+ type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
+ type == DILLO_HTML_INPUT_BUTTON_RESET ||
+ type == DILLO_HTML_INPUT_IMAGE ||
+ type == DILLO_HTML_INPUT_FILE ||
+ type == DILLO_HTML_INPUT_TEXT ||
+ type == DILLO_HTML_INPUT_PASSWORD ||
+ type == DILLO_HTML_INPUT_INDEX) {
+ resource->connectActivate (form_receiver);
}
}
}
@@ -1610,16 +1595,29 @@ void DilloHtmlInput::connectTo(DilloHtmlReceiver *form_receiver)
void DilloHtmlInput::activate(DilloHtmlForm *form, int num_entry_fields,
EventButton *event)
{
- if (type == DILLO_HTML_INPUT_FILE) {
+ switch (type) {
+ case DILLO_HTML_INPUT_FILE:
readFile (form->html->bw);
- } else if (type == DILLO_HTML_INPUT_RESET ||
- type == DILLO_HTML_INPUT_BUTTON_RESET) {
+ break;
+ case DILLO_HTML_INPUT_RESET:
+ case DILLO_HTML_INPUT_BUTTON_RESET:
form->reset();
- } else if ((type != DILLO_HTML_INPUT_TEXT &&
- type != DILLO_HTML_INPUT_PASSWORD) ||
- prefs.enterpress_forces_submit ||
- num_entry_fields == 1) {
+ break;
+ case DILLO_HTML_INPUT_TEXT:
+ case DILLO_HTML_INPUT_PASSWORD:
+ if (!(prefs.enterpress_forces_submit || num_entry_fields == 1)) {
+ break;
+ } else {
+ /* fall through */
+ }
+ case DILLO_HTML_INPUT_SUBMIT:
+ case DILLO_HTML_INPUT_BUTTON_SUBMIT:
+ case DILLO_HTML_INPUT_IMAGE:
+ case DILLO_HTML_INPUT_INDEX:
form->submit(this, event);
+ break;
+ default:
+ break;
}
}
diff --git a/src/html.cc b/src/html.cc
index 431a7a94..772c0bb1 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -467,7 +467,6 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
PrevWasSPC = false;
InVisitedLink = false;
ReqTagClose = false;
- CloseOneTag = false;
TagSoup = true;
NameVal = NULL;
@@ -1277,12 +1276,6 @@ static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx)
int stack_idx, cmp = 1;
int new_idx = TagIdx;
- if (html->CloseOneTag) {
- Html_real_pop_tag(html);
- html->CloseOneTag = false;
- return;
- }
-
/* Look for the candidate tag to close */
stack_idx = html->stack->size() - 1;
while (stack_idx &&
@@ -1308,9 +1301,9 @@ static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx)
Tags[toptag_idx].name);
/* Close this and only this tag */
- html->CloseOneTag = true;
_MSG("Close: %*s%s\n", html->stack->size()," ",Tags[toptag_idx].name);
Tags[toptag_idx].close (html, toptag_idx);
+ Html_real_pop_tag(html);
}
} else {
@@ -1325,14 +1318,6 @@ static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx)
}
/*
- * Cleanup (conditional), and Pop the tag (if it matches)
- */
-void a_Html_pop_tag(DilloHtml *html, int TagIdx)
-{
- Html_tag_cleanup_at_close(html, TagIdx);
-}
-
-/*
* Some parsing routines.
*/
@@ -1546,7 +1531,6 @@ static void Html_tag_close_html(DilloHtml *html, int TagIdx)
/* beware of pages with multiple HTML close tags... :-P */
html->InFlags &= ~IN_HTML;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1590,7 +1574,6 @@ static void Html_tag_close_head(DilloHtml *html, int TagIdx)
if (html->repush_after_head)
a_Nav_repush(html->bw);
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1616,7 +1599,6 @@ static void Html_tag_close_title(DilloHtml *html, int TagIdx)
} else {
BUG_MSG("the TITLE element must be inside the HEAD section\n");
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1636,7 +1618,6 @@ static void Html_tag_open_script(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_script(DilloHtml *html, int TagIdx)
{
/* eventually the stash will be sent to an interpreter for parsing */
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1673,7 +1654,6 @@ static void Html_tag_close_style(DilloHtml *html, int TagIdx)
{
html->styleEngine->parse(html->Stash->str, html->Stash->len,
0, CSS_ORIGIN_AUTHOR);
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1744,7 +1724,6 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx)
/* some tag soup pages use multiple BODY tags... */
html->InFlags &= ~IN_BODY;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1877,7 +1856,6 @@ static void Html_tag_close_h(DilloHtml *html, int TagIdx)
{
a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ());
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2223,7 +2201,6 @@ static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_map(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_MAP;
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2485,7 +2462,6 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_a(DilloHtml *html, int TagIdx)
{
html->InVisitedLink = false;
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2513,6 +2489,31 @@ static void Html_tag_open_blockquote(DilloHtml *html,
}
/*
+ * <Q>
+ */
+static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize)
+{
+ /*
+ * Left Double Quotation Mark, which is wrong in many cases, but
+ * should at least be widely recognized.
+ */
+ const char *U201C = "\xe2\x80\x9c";
+
+ DW2TB(html->dw)->addText (U201C, html->styleEngine->wordStyle ());
+}
+
+/*
+ * </Q>
+ */
+static void Html_tag_close_q(DilloHtml *html, int TagIdx)
+{
+ /* Right Double Quotation Mark */
+ const char *U201D = "\xe2\x80\x9d";
+
+ DW2TB(html->dw)->addText (U201D, html->styleEngine->wordStyle ());
+}
+
+/*
* Handle the <UL> tag.
*/
static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
@@ -2700,7 +2701,6 @@ static void Html_tag_close_li(DilloHtml *html, int TagIdx)
html->InFlags &= ~IN_LI;
html->WordAfterLI = false;
((ListItem *)html->dw)->flush ();
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2803,7 +2803,6 @@ static void Html_tag_close_pre(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_PRE;
DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ());
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -3105,15 +3104,13 @@ static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_div(DilloHtml *html, int TagIdx)
{
DW2TB(html->dw)->addParbreak (0, html->styleEngine->wordStyle ());
- a_Html_pop_tag(html, TagIdx);
}
/*
- * Default close for most tags - just pop the stack.
+ * Default close for most tags.
*/
static void Html_tag_close_default(DilloHtml *html, int TagIdx)
{
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -3122,7 +3119,6 @@ static void Html_tag_close_default(DilloHtml *html, int TagIdx)
static void Html_tag_close_par(DilloHtml *html, int TagIdx)
{
DW2TB(html->dw)->addParbreak (9, html->styleEngine->wordStyle ());
- a_Html_pop_tag(html, TagIdx);
}
@@ -3223,7 +3219,7 @@ const TagInfo Tags[] = {
{"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
/* param 010001 'F' */
{"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
- /* q 010101 */
+ {"q", B8(010101),'R',2, Html_tag_open_q, Html_tag_close_q},
{"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
{"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
{"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
diff --git a/src/html_common.hh b/src/html_common.hh
index 2bc2ef4f..ead9cf14 100644
--- a/src/html_common.hh
+++ b/src/html_common.hh
@@ -197,7 +197,6 @@ public: //BUG: for now everything is public
bool PrevWasSPC; /* Flag to help handling collapsing white space */
bool InVisitedLink; /* used to 'contrast_visited_colors' */
bool ReqTagClose; /* Flag to help handling bad-formed HTML */
- bool CloseOneTag; /* Flag to help Html_tag_cleanup_at_close() */
bool WordAfterLI; /* Flag to help ignoring the 1st <P> after <LI> */
bool TagSoup; /* Flag to enable the parser's cleanup functions */
char *NameVal; /* used for validation of "NAME" and "ID" in <A> */
diff --git a/src/menu.cc b/src/menu.cc
index a86763c4..ac23117d 100644
--- a/src/menu.cc
+++ b/src/menu.cc
@@ -175,7 +175,7 @@ static void Menu_save_page_cb(Widget* )
}
/*
- * Save current page
+ * View current page source
*/
static void Menu_view_page_source_cb(Widget* )
{
@@ -192,18 +192,22 @@ static void Menu_view_page_bugs_cb(Widget* )
/*
* Load images on current page that match URL pattern
- *
- * BUG: assumes that the document is a DilloHtml.
*/
static void Menu_load_images_cb(Widget*, void *user_data)
{
DilloUrl *pattern = (DilloUrl *) user_data ;
if (popup_bw && popup_bw->Docs) {
- int i, n;
- n = dList_length(popup_bw->Docs);
- for (i = 0; i < n; i++) {
- a_Html_load_images(dList_nth_data(popup_bw->Docs, i), pattern);
+ if (dList_find_custom(popup_bw->PageUrls, popup_url,
+ (dCompareFunc)a_Url_cmp)){
+ /* HTML page is still there */
+ int n = dList_length(popup_bw->Docs);
+ if (n == 1) {
+ a_Html_load_images(dList_nth_data(popup_bw->Docs, 0), pattern);
+ } else if (n > 1) {
+ /* e.g. frames implemented and not all containing html */
+ MSG("Menu_load_images_cb multiple Docs not handled\n");
+ }
}
}
}