diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/IO/http.c | 14 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/auth.c | 514 | ||||
-rw-r--r-- | src/auth.h | 19 | ||||
-rw-r--r-- | src/bw.h | 1 | ||||
-rw-r--r-- | src/cache.c | 67 | ||||
-rw-r--r-- | src/dialog.cc | 79 | ||||
-rw-r--r-- | src/dialog.hh | 4 | ||||
-rw-r--r-- | src/dillo.cc | 2 | ||||
-rw-r--r-- | src/form.cc | 86 | ||||
-rw-r--r-- | src/html.cc | 60 | ||||
-rw-r--r-- | src/html_common.hh | 1 | ||||
-rw-r--r-- | src/menu.cc | 18 |
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__ */ @@ -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"); + } } } } |