diff options
author | Sebastian Geerken <devnull@localhost> | 2012-09-14 11:34:19 +0200 |
---|---|---|
committer | Sebastian Geerken <devnull@localhost> | 2012-09-14 11:34:19 +0200 |
commit | e4367b16dc131f34936bbb8fd09557b5aa5acbd7 (patch) | |
tree | 487a35941bf20bbc95a3d0b1dee420b00771f5b6 /src | |
parent | abd446c2eebe1f96764b6d95f1c6c61ae9bc40b2 (diff) | |
parent | 94e451ffa5ece79a3b071ee553650bf8bd869a46 (diff) |
Merge of <http://hg.dillo.org/dillo>.
Diffstat (limited to 'src')
-rw-r--r-- | src/IO/about.c | 69 | ||||
-rw-r--r-- | src/IO/http.c | 35 | ||||
-rw-r--r-- | src/IO/mime.c | 4 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/auth.c | 542 | ||||
-rw-r--r-- | src/auth.h | 23 | ||||
-rw-r--r-- | src/binaryconst.h | 5 | ||||
-rw-r--r-- | src/cache.c | 42 | ||||
-rw-r--r-- | src/capi.c | 23 | ||||
-rw-r--r-- | src/colors.c | 4 | ||||
-rw-r--r-- | src/cookies.c | 16 | ||||
-rw-r--r-- | src/css.cc | 258 | ||||
-rw-r--r-- | src/css.hh | 35 | ||||
-rw-r--r-- | src/cssparser.cc | 77 | ||||
-rw-r--r-- | src/decode.c | 29 | ||||
-rw-r--r-- | src/dialog.cc | 24 | ||||
-rw-r--r-- | src/digest.c | 204 | ||||
-rw-r--r-- | src/digest.h | 16 | ||||
-rw-r--r-- | src/dillo.cc | 5 | ||||
-rw-r--r-- | src/dns.c | 89 | ||||
-rw-r--r-- | src/form.cc | 118 | ||||
-rw-r--r-- | src/form.hh | 1 | ||||
-rw-r--r-- | src/html.cc | 554 | ||||
-rw-r--r-- | src/html_common.hh | 7 | ||||
-rw-r--r-- | src/keys.cc | 14 | ||||
-rw-r--r-- | src/keysrc | 2 | ||||
-rw-r--r-- | src/list.h | 4 | ||||
-rw-r--r-- | src/md5.c | 390 | ||||
-rw-r--r-- | src/md5.h | 100 | ||||
-rw-r--r-- | src/menu.cc | 17 | ||||
-rw-r--r-- | src/misc.c | 55 | ||||
-rw-r--r-- | src/nav.c | 7 | ||||
-rw-r--r-- | src/png.c | 2 | ||||
-rw-r--r-- | src/prefsparser.cc | 14 | ||||
-rw-r--r-- | src/styleengine.cc | 117 | ||||
-rw-r--r-- | src/styleengine.hh | 2 | ||||
-rw-r--r-- | src/table.cc | 94 | ||||
-rw-r--r-- | src/table.hh | 4 | ||||
-rw-r--r-- | src/ui.cc | 8 | ||||
-rw-r--r-- | src/uicmd.cc | 4 | ||||
-rw-r--r-- | src/url.c | 18 | ||||
-rw-r--r-- | src/web.cc | 2 |
42 files changed, 2035 insertions, 1003 deletions
diff --git a/src/IO/about.c b/src/IO/about.c index 77af318f..78cf086e 100644 --- a/src/IO/about.c +++ b/src/IO/about.c @@ -121,10 +121,6 @@ const char *const AboutSplash= " <tr>\n" " <td> \n" " <td>\n" -" <a href='http://www.linux.org.uk/Portaloo.cs'>Linux.org.uk</a>\n" -" <tr>\n" -" <td> \n" -" <td>\n" " <a href='http://www.commondreams.org/'>C. Dreams</a>\n" " <tr>\n" " <td> \n" @@ -156,7 +152,7 @@ const char *const AboutSplash= " <td><a href='http://www.gutenberg.org/'>P. Gutenberg</a>\n" " <tr>\n" " <td> \n" -" <td><a href='http://freshmeat.net/'>FreshMeat</a>\n" +" <td><a href='http://freecode.com/'>Freecode</a>\n" " <tr>\n" " <td> \n" " <td><a href='http://www.gnu.org/gnu/thegnuproject.html'>GNU\n" @@ -211,8 +207,8 @@ const char *const AboutSplash= " <td bgcolor='#FFFFFF'>\n" " <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n" " <p>\n" -" Dillo is Free Software under the terms of version 3 of the\n" -" <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n" +" The Dillo web browser is Free Software under the terms of version 3 of\n" +" the <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n" " This means you have four basic freedoms:\n" " <ul>\n" " <li>Freedom to use the program any way you see fit.\n" @@ -234,25 +230,24 @@ const char *const AboutSplash= "<tr>\n" " <td bgcolor='#CCCCCC'>\n" " <h4>Release overview</h4>\n" -" September 06, 2011\n" +" December 05, 2011\n" "<tr>\n" " <td bgcolor='#FFFFFF'>\n" " <table border='0' cellspacing='0' cellpadding='5'>\n" " <tr>\n" " <td>\n" "<p>\n" -"Dillo-3.0 is a port to FLTK-1.3, which is big news because FLTK-1.3.0 was\n" -"<a href='http://fltk.org/articles.php?L1086'>released</a>\n" -"in June, clearing the way for Dillo to return to those distributions\n" -"which had excluded Dillo2 due to FLTK2 never being officially released.\n" +"dillo-3.0.2 brings you some new bits and pieces, as listed below :)\n" "<p>\n" -"Dillo-3.0 also has plenty of improvements and bugfixes.\n" -"<p>\n" -"After this release, the core team will focus on implementing the CSS\n" +"After this release, the core team plans to focus on implementing the CSS\n" "feature of floating elements. This will <em>greatly</em> improve dillo's\n" "web page rendering since many sites have adopted floats instead of tables.\n" "<p>\n" -"The new dillo3 has shown excellent stability in our experience.\n" +"Dillo3 uses the FLTK GUI toolkit's 1.3.x series. In June, fltk-1.3.0 was\n" +"<a href='http://fltk.org/articles.php?L1086'>released</a>,\n" +"clearing the way for Dillo to return to those distributions\n" +"which had excluded Dillo2 due to FLTK2 never being officially released.\n" +"<p>\n" "The core team welcomes developers willing to join our workforce.\n" "<p>\n" " </table>\n" @@ -275,39 +270,11 @@ const char *const AboutSplash= " <tr>\n" " <td>\n" "<ul>\n" -"<li>Ported Dillo to FLTK-1.3.\n" -"<li>Native build on OSX.\n" -"<li>Default binding for close-all changed from Alt-q to Ctrl-q.\n" -"<li>Default binding for close-tab changed from Ctrl-q to Ctrl-w.\n" -"<li>Default binding for left-tab changed to Shift-Ctrl-Tab.\n" -"<li>Rewrote the User Interface: much simpler design and event handling.\n" -"<li>Added on-the-fly panel resize (tiny/small/medium and normal/small icons).\n" -"<li>'hide-panels' key action now hides the findbar if present,\n" -" and toggles display of the control panels otherwise.\n" -"<li>Allow multiple search engines to be set in dillorc, with a menu\n" -" in the web search dialog to select between them.\n" -"<li>Added an optional label to dillorc's search_url.\n" -" Format: \"[<label> ]<url>\"\n" -"<li>Add right_click_closes_tab preference (default is middle click).\n" -"<li>Allow binding to non-ASCII keys and multimedia keys.\n" -"<li>Avoid a certificate dialog storm on some HTTPS sites (BUG#868).\n" -"<li>Enable line wrapping for <textarea>. (BUG#903)\n" -"<li>Avoid double render after going Back or Forward\n" -" (it takes half the time now!)\n" -"<li>Implemented a custom tabs handler (to allow fine control of it).\n" -"<li>Rewrote dw's crossing-events dispatcher (avoids redundant events).\n" -"<li>Fixed a years old bug: stamped tooltips when scrolling with keyboard.\n" -"<li>Fixed a border case in URL resolver: empty path + {query|fragment}\n" -" (BUG#948)\n" -"<li>Cancel the expected URL after offering a download (BUG#982)\n" -"<li>Eliminated a pack of 22 compiler warnings (gcc-4.6.1 amd64)\n" -"<li>Removed 'large' option of panel_size preference.\n" -"<li>Removed --enable-ansi configure option.\n" -"<li>Limit saved cookie size.\n" -"<li>Wrap image alt text.\n" -"<li>Added support for CSS adjacent sibling selectors.\n" -"<li>Fix redraw loops and reenabled limit_text_width dillorc option.\n" -"<li>Collapse parent's and first child's top margin.\n" +"<li>Digest authentication.\n" +"<li>Rework line breaking and fix white-space:nowrap handling.\n" +"<li>text-transform property.\n" +"<li>Locale-independent ASCII character case handling (fixes Turkic locales).\n" +"<li>Bind Ctrl-{PageUp,PageDown} to tab-{previous,next}.\n" "</ul>\n" " </table>\n" "</table>\n" @@ -328,12 +295,12 @@ const char *const AboutSplash= "<ul>\n" " <li> There's a\n" " <a href='http://www.dillo.org/dillorc'>dillorc</a>\n" -" (readable config) file within the tarball; It is well-commented\n" +" (readable config) file inside the tarball. It is well-commented\n" " and has plenty of options to customize dillo, so <STRONG>copy\n" " it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n" " modify it to your taste.\n" " <li> Documentation for developers is in the <CODE>/doc</CODE>\n" -" dir within the tarball; you can find directions on everything\n" +" dir inside the tarball; you can find directions on everything\n" " else at the home page.\n" " <li> The right mouse button brings up a context-sensitive menu\n" " (available on pages, links, images, forms, the Back and Forward buttons,\n" diff --git a/src/IO/http.c b/src/IO/http.c index 41ee137a..3e87f912 100644 --- a/src/IO/http.c +++ b/src/IO/http.c @@ -171,10 +171,11 @@ static void Http_connect_queued_sockets(HostConnection_t *hc) } else if (a_Web_valid(sd->web)) { /* start connecting the socket */ if (Http_connect_socket(sd->Info) < 0) { + ChainLink *Info = sd->Info; MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err)); - a_Chain_bfcb(OpAbort, sd->Info, NULL, "Both"); - dFree(sd->Info); - Http_socket_free(VOIDP2INT(sd->Info->LocalKey)); + a_Chain_bfcb(OpAbort, Info, NULL, "Both"); + Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */ + dFree(Info); } else { sd->connected_to = hc->host; hc->active_connections++; @@ -202,7 +203,7 @@ static void Http_socket_free(int SKey) Http_connect_queued_sockets(hc); if (hc->active_connections == 0) Http_host_connection_remove(hc); - } + } dFree(S); } } @@ -274,23 +275,22 @@ static Dstr *Http_make_content_type(const DilloUrl *url) Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, bool_t use_proxy) { - const char *auth; - char *ptr, *cookies, *referer; + char *ptr, *cookies, *referer, *auth; Dstr *query = dStr_new(""), - *full_path = dStr_new(""), + *request_uri = dStr_new(""), *proxy_auth = dStr_new(""); if (use_proxy) { - dStr_sprintfa(full_path, "%s%s", + dStr_sprintfa(request_uri, "%s%s", URL_STR(url), (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/"); - if ((ptr = strrchr(full_path->str, '#'))) - dStr_truncate(full_path, ptr - full_path->str); + if ((ptr = strrchr(request_uri->str, '#'))) + dStr_truncate(request_uri, ptr - request_uri->str); if (HTTP_Proxy_Auth_base64) dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n", HTTP_Proxy_Auth_base64); } else { - dStr_sprintfa(full_path, "%s%s%s%s", + dStr_sprintfa(request_uri, "%s%s%s%s", URL_PATH(url), URL_QUERY_(url) ? "?" : "", URL_QUERY(url), @@ -298,7 +298,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, } cookies = a_Cookies_get_query(url, requester); - auth = a_Auth_get_auth_str(url); + auth = a_Auth_get_auth_str(url, request_uri->str); referer = Http_get_referer(url); if (URL_FLAGS(url) & URL_Post) { Dstr *content_type = Http_make_content_type(url); @@ -319,7 +319,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, "Content-Type: %s\r\n" "%s" /* cookies */ "\r\n", - full_path->str, HTTP_Language_hdr, auth ? auth : "", + request_uri->str, HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent, (long)URL_DATA(url)->len, content_type->str, cookies); @@ -342,7 +342,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, "User-Agent: %s\r\n" "%s" /* cookies */ "\r\n", - full_path->str, + request_uri->str, (URL_FLAGS(url) & URL_E2EQuery) ? "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "", HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), @@ -350,8 +350,9 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, } dFree(referer); dFree(cookies); + dFree(auth); - dStr_free(full_path, TRUE); + dStr_free(request_uri, TRUE); dStr_free(proxy_auth, TRUE); _MSG("Query: {%s}\n", dStr_printable(query, 8192)); return query; @@ -479,7 +480,7 @@ static int Http_must_use_proxy(const DilloUrl *url) for (p = np; (tok = dStrsep(&p, " ")); ) { int start = host_len - strlen(tok); - if (start >= 0 && dStrcasecmp(host + start, tok) == 0) { + if (start >= 0 && dStrAsciiCasecmp(host + start, tok) == 0) { /* no_proxy token is suffix of host string */ ret = 0; break; @@ -718,7 +719,7 @@ static HostConnection_t *Http_host_connection_get(const char *host) for (i = 0; i < dList_length(host_connections); i++) { hc = (HostConnection_t*) dList_nth_data(host_connections, i); - if (dStrcasecmp(host, hc->host) == 0) + if (dStrAsciiCasecmp(host, hc->host) == 0) return hc; } diff --git a/src/IO/mime.c b/src/IO/mime.c index 19dc601a..3606d23c 100644 --- a/src/IO/mime.c +++ b/src/IO/mime.c @@ -67,7 +67,7 @@ static Viewer_t Mime_minor_type_fetch(const char *Key, uint_t Size) if (Size) { for ( i = 0; i < MimeMinItemsSize; ++i ) - if (dStrncasecmp(Key, MimeMinItems[i].Name, Size) == 0) + if (dStrnAsciiCasecmp(Key, MimeMinItems[i].Name, Size) == 0) return MimeMinItems[i].Data; } return NULL; @@ -83,7 +83,7 @@ static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size) if (Size) { for ( i = 0; i < MimeMajItemsSize; ++i ) - if (dStrncasecmp(Key, MimeMajItems[i].Name, Size) == 0) + if (dStrnAsciiCasecmp(Key, MimeMajItems[i].Name, Size) == 0) return MimeMajItems[i].Data; } return NULL; diff --git a/src/Makefile.am b/src/Makefile.am index 9de7eed5..5e887d2c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,6 +35,10 @@ dillo_SOURCES = \ cookies.h \ auth.c \ auth.h \ + md5.c \ + md5.h \ + digest.c \ + digest.h \ colors.c \ colors.h \ binaryconst.h \ @@ -2,6 +2,7 @@ * File: auth.c * * Copyright 2008 Jeremy Henty <onepoint@starurchin.org> + * Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de> * * 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 @@ -20,28 +21,29 @@ #include "msg.h" #include "misc.h" #include "dialog.hh" +#include "digest.h" #include "../dlib/dlib.h" - typedef struct { int ok; + enum AuthParseHTTPAuthType_t type; const char *realm; + const char *nonce; + const char *opaque; + int stale; + enum AuthParseDigestAlgorithm_t algorithm; + const char *domain; + enum AuthParseDigestQOP_t qop; } 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 AuthParse_t *auth_parse; const DilloUrl *url; } AuthDialogData_t; @@ -62,7 +64,14 @@ static AuthParse_t *Auth_parse_new() { AuthParse_t *auth_parse = dNew(AuthParse_t, 1); auth_parse->ok = 0; + auth_parse->type = TYPENOTSET; auth_parse->realm = NULL; + auth_parse->nonce = NULL; + auth_parse->opaque = NULL; + auth_parse->stale = 0; + auth_parse->algorithm = ALGORITHMNOTSET; + auth_parse->domain = NULL; + auth_parse->qop = QOPNOTSET; return auth_parse; } @@ -70,6 +79,9 @@ static void Auth_parse_free(AuthParse_t *auth_parse) { if (auth_parse) { dFree((void *)auth_parse->realm); + dFree((void *)auth_parse->nonce); + dFree((void *)auth_parse->opaque); + dFree((void *)auth_parse->domain); dFree(auth_parse); } } @@ -92,191 +104,250 @@ static int Auth_path_is_inside(const char *path1, const char *path2, int len) static int Auth_is_token_char(char c) { const char *invalid = "\"()<>@,;:\\[]?=/{} \t"; - return (strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1; + return (!isascii(c) || strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1; } /* - * Unquote the content of a quoted string. + * Unquote the content of a (potentially) quoted string. * Return: newly allocated unquoted content. * * Arguments: - * quoted: pointer to the first char *after* the initial double quote. - * size: the number of chars in the result, *not* including a final '\0'. + * valuep: pointer to a pointer to the first char. * * Preconditions: - * quoted points to a correctly quoted and escaped string. - * size is the number of characters in the quoted string, *after* - * removing escape characters. + * *valuep points to a correctly quoted and escaped string. + * + * Postconditions: + * *valuep points to the first not processed char. * */ -static const char *Auth_unquote_value(const char *quoted, int size) +static Dstr *Auth_unquote_value(char **valuep) { - char c, *value, *value_ptr; - value_ptr = value = dNew(char, size + 1); - while ((c = *quoted++) != '"') - *value_ptr++ = (c == '\\') ? *quoted++ : c; - *value_ptr = '\0'; - return value; + char c, quoted; + char *value = *valuep; + Dstr *result; + + while (*value == ' ' || *value == '\t') + value++; + + if ((quoted = *value == '"')) + value++; + + result = dStr_new(NULL); + while ((c = *value) && + (( quoted && c != '"') || + (!quoted && Auth_is_token_char(c)))) { + dStr_append_c(result, (c == '\\' && value[1]) ? *++value : c); + value++; + } + + if (quoted && *value == '\"') + value++; + *valuep = value; + return result; } +typedef int (Auth_parse_token_value_callback_t)(AuthParse_t *auth_parse, + char *token, + const char *value); + + /* - * Parse a quoted string. Save the result as the auth realm if required. + * Parse authentication challenge into token-value pairs + * and feed them into the callback function. + * + * The parsing is aborted should the callback function return 0. + * * Return: 1 if the parse succeeds, 0 otherwise. */ -static int Auth_parse_quoted_string(AuthParse_t *auth_parse, int set_realm, - char **auth) +static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth, + Auth_parse_token_value_callback_t *callback) { - char *value; - int size; - - /* parse the '"' */ - switch (*(*auth)++) { - case '"': - break; - case '\0': - case ',': - MSG("auth.c: missing Basic auth token value after '='\n"); - return 0; - break; - default: - MSG("auth.c: garbage in Basic auth after '='\n"); - return 0; - break; - } + char keep_going, expect_quoted; + char *token, *beyond_token; + Dstr *value; + size_t *token_size; - /* parse the rest */ - value = *auth; - size = 0; - while (1) { + while (**auth) { + _MSG("Auth_parse_token_value: remaining: %s\n", *auth); + + /* parse a token */ + token = *auth; + + token_size = 0; + while (Auth_is_token_char(**auth)) { + (*auth)++; + token_size++; + } + if (token_size == 0) { + MSG("Auth_parse_token_value: missing auth token\n"); + return 0; + } + beyond_token = *auth; + /* skip linear whitespace characters */ + while (**auth == ' ' || **auth == '\t') + (*auth)++; + + /* parse the '=' */ switch (*(*auth)++) { - case '"': - if (set_realm) { - dFree((void *)auth_parse->realm); - auth_parse->realm = Auth_unquote_value(value, size); - auth_parse->ok = 1; - } - return 1; + case '=': + *beyond_token = '\0'; break; case '\0': - MSG("auth.c: auth string ended inside quoted string value\n"); + case ',': + MSG("Auth_parse_token_value: missing auth token value\n"); return 0; break; - case '\\': - /* end of string? */ - if (!*(*auth)++) { - MSG("auth.c: " - "auth string ended inside quoted string value " - "immediately after \\\n"); - return 0; - } - /* fall through to the next case */ default: - size++; + MSG("Auth_parse_token_value: garbage after auth token\n"); + return 0; break; } - } -} -/* - * Parse a token-value pair. - * Return: 1 if the parse succeeds, 0 otherwise. - */ -static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth) -{ - char *token; - int token_size, set_realm; - static const char realm_token[] = "realm"; - - /* 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_token_value: " - "missing Basic auth token\n"); - return 0; - } + value = Auth_unquote_value(auth); + expect_quoted = !(strcmp(token, "stale") == 0 || + strcmp(token, "algorithm") == 0); - /* skip space characters */ - while (**auth == ' ') - (*auth)++; - - /* parse the '=' */ - switch (*(*auth)++) { - case '=': - break; - case '\0': - case ',': - MSG("auth.c: Auth_parse_token_value: " - "missing Basic auth token value\n"); - return 0; - break; - default: - MSG("auth.c: Auth_parse_token_value: " - "garbage after Basic auth token\n"); - return 0; - break; - } + if (((*auth)[-1] == '"') != expect_quoted) + MSG_WARN("Auth_parse_token_value: " + "Values for key %s should%s be quoted.\n", + token, expect_quoted ? "" : " not"); - /* skip space characters */ - while (**auth == ' ') - (*auth)++; + keep_going = callback(auth_parse, token, value->str); + dStr_free(value, 1); + if (!keep_going) + break; - /* is this value the realm? */ - set_realm = - auth_parse->realm == NULL && - dStrncasecmp(realm_token,token,token_size) == 0 && - strlen(realm_token) == (size_t)token_size; + /* skip ' ' and ',' */ + while ((**auth == ' ') || (**auth == ',')) + (*auth)++; + } + return 1; +} - return Auth_parse_quoted_string(auth_parse, set_realm, auth); +static int Auth_parse_basic_challenge_cb(AuthParse_t *auth_parse, char *token, + const char *value) +{ + if (dStrAsciiCasecmp("realm", token) == 0) { + if (!auth_parse->realm) + auth_parse->realm = strdup(value); + return 0; /* end parsing */ + } else + MSG("Auth_parse_basic_challenge_cb: Ignoring unknown parameter: %s = " + "'%s'\n", token, value); + return 1; } -static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char **auth) +static int Auth_parse_digest_challenge_cb(AuthParse_t *auth_parse, char *token, + const char *value) { - int token_value_pairs_found; + const char *const fn = "Auth_parse_digest_challenge_cb"; + + if (!dStrAsciiCasecmp("realm", token) && !auth_parse->realm) + auth_parse->realm = strdup(value); + else if (!strcmp("domain", token) && !auth_parse->domain) + auth_parse->domain = strdup(value); + else if (!strcmp("nonce", token) && !auth_parse->nonce) + auth_parse->nonce = strdup(value); + else if (!strcmp("opaque", token) && !auth_parse->opaque) + auth_parse->opaque = strdup(value); + else if (strcmp("stale", token) == 0) { + if (dStrAsciiCasecmp("true", value) == 0) + auth_parse->stale = 1; + else if (dStrAsciiCasecmp("false", value) == 0) + auth_parse->stale = 0; + else { + MSG("%s: Invalid stale value: %s\n", fn, value); + return 0; + } + } else if (strcmp("algorithm", token) == 0) { + if (strcmp("MD5", value) == 0) + auth_parse->algorithm = MD5; + else if (strcmp("MD5-sess", value) == 0) { + /* auth_parse->algorithm = MD5SESS; */ + MSG("%s: MD5-sess algorithm disabled (not tested because 'not " + "correctly implemented yet' in Apache 2.2)\n", fn); + return 0; + } else { + MSG("%s: Unknown algorithm: %s\n", fn, value); + return 0; + } + } else if (strcmp("qop", token) == 0) { + while (*value) { + int len = strcspn(value, ", \t"); + if (len == 4 && strncmp("auth", value, 4) == 0) { + auth_parse->qop = AUTH; + break; + } + if (len == 8 && strncmp("auth-int", value, 8) == 0) { + /* auth_parse->qop = AUTHINT; */ + /* Keep searching; maybe we'll find an "auth" yet. */ + MSG("%s: auth-int qop disabled (not tested because 'not " + "implemented yet' in Apache 2.2)\n", fn); + } else { + MSG("%s: Unknown qop value in %s\n", fn, value); + } + value += len; + while (*value == ' ' || *value == '\t') + value++; + if (*value == ',') + value++; + while (*value == ' ' || *value == '\t') + value++; + } + } else { + MSG("%s: Ignoring unknown parameter: %s = '%s'\n", fn, token, value); + } + return 1; +} +static void Auth_parse_challenge_args(AuthParse_t *auth_parse, + char **challenge, + Auth_parse_token_value_callback_t *cb) +{ /* parse comma-separated token-value pairs */ - token_value_pairs_found = 0; while (1) { /* skip space and comma characters */ - while (**auth == ' ' || **auth == ',') - (*auth)++; + while (**challenge == ' ' || **challenge == ',') + (*challenge)++; /* end of string? */ - if (!**auth) + if (!**challenge) break; /* parse token-value pair */ - if (!Auth_parse_token_value(auth_parse, auth)) + if (!Auth_parse_token_value(auth_parse, challenge, cb)) break; - token_value_pairs_found = 1; } - if (!token_value_pairs_found) { - MSG("auth.c: Auth_parse_auth_basic: " - "missing Basic auth token-value pairs\n"); - return; - } - - if (!auth_parse->realm) { - MSG("auth.c: Auth_parse_auth_basic: " - "missing Basic auth realm\n"); - return; + if (auth_parse->type == BASIC) { + if (auth_parse->realm) { + auth_parse->ok = 1; + } else { + MSG("Auth_parse_challenge_args: missing Basic auth realm\n"); + return; + } + } else if (auth_parse->type == DIGEST) { + if (auth_parse->realm && auth_parse->nonce) { + auth_parse->ok = 1; + } else { + MSG("Auth_parse_challenge_args: Digest challenge incomplete\n"); + return; + } } } -static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth) +static void Auth_parse_challenge(AuthParse_t *auth_parse, char *challenge) { - _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth); - if (dStrncasecmp(auth, "Basic ", 6) == 0) { - auth += 6; - Auth_parse_auth_basic(auth_parse, &auth); + Auth_parse_token_value_callback_t *cb; + + MSG("auth.c: Auth_parse_challenge: challenge = '%s'\n", challenge); + if (auth_parse->type == DIGEST) { + challenge += 7; + cb = Auth_parse_digest_challenge_cb; } else { - MSG("auth.c: Auth_parse_auth: " - "unknown authorization scheme: auth = {%s}\n", - auth); + challenge += 6; + cb = Auth_parse_basic_challenge_cb; } + Auth_parse_challenge_args(auth_parse, &challenge, cb); } /* @@ -288,8 +359,8 @@ static AuthHost_t *Auth_host_by_url(const DilloUrl *url) 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))) + if (((dStrAsciiCasecmp(URL_SCHEME(url), host->scheme) == 0) && + (dStrAsciiCasecmp(URL_AUTHORITY(url), host->authority) == 0))) return host; return NULL; @@ -305,7 +376,7 @@ static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host, int i; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) - if (strcmp(realm->name,name) == 0) + if (strcmp(realm->name, name) == 0) return realm; return NULL; @@ -319,9 +390,8 @@ static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host, { AuthRealm_t *realm_best, *realm; int i, j; - int match_length; + int match_length = 0; - match_length = 0; realm_best = NULL; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) { char *realm_path; @@ -339,6 +409,24 @@ static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host, return realm_best; } +static void Auth_realm_delete(AuthRealm_t *realm) +{ + int i; + + MSG("Auth_realm_delete: \"%s\"\n", realm->name); + for (i = dList_length(realm->paths) - 1; i >= 0; i--) + dFree(dList_nth_data(realm->paths, i)); + dList_free(realm->paths); + dFree(realm->name); + dFree(realm->username); + dFree(realm->authorization); + dFree(realm->cnonce); + dFree(realm->nonce); + dFree(realm->opaque); + dFree(realm->domain); + dFree(realm); +} + static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path) { int i; @@ -377,22 +465,33 @@ static void Auth_realm_add_path(AuthRealm_t *realm, const char *path) /* * Return the authorization header for an HTTP query. + * request_uri is a separate argument because we want it precisely as + * formatted in the request. */ -const char *a_Auth_get_auth_str(const DilloUrl *url) +char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri) { + char *ret = NULL; 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; + if ((host = Auth_host_by_url(url)) && + (realm = Auth_realm_by_path(host, URL_PATH(url)))) { + if (realm->type == BASIC) + ret = dStrdup(realm->authorization); + else if (realm->type == DIGEST) + ret = a_Digest_authorization_hdr(realm, url, request_uri); + else + MSG("a_Auth_get_auth_str() got an unknown realm type: %i.\n", + realm->type); + } + return ret; } /* * Determine whether the user needs to authenticate. */ -static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url) +static int Auth_do_auth_required(const AuthParse_t *auth_parse, + const DilloUrl *url) { /* * TO DO: I dislike the way that this code must decide whether we @@ -420,12 +519,20 @@ static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url) * 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; + (realm = Auth_realm_by_name(host, auth_parse->realm))) { + if (!Auth_realm_includes_path(realm, URL_PATH(url))) { + _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n", + auth_parse->realm, URL_STR(url)); + Auth_realm_add_path(realm, URL_PATH(url)); + return 0; + } + + if (auth_parse->type == DIGEST && auth_parse->stale) { + /* we do have valid credentials but our nonce is old */ + dFree((void *)realm->nonce); + realm->nonce = dStrdup(auth_parse->nonce); + return 0; + } } /* @@ -441,7 +548,6 @@ static void Auth_do_auth_dialog_cb(const char *user, const char *password, AuthDialogData_t *data; AuthHost_t *host; AuthRealm_t *realm; - char *user_password, *response, *authorization, *authorization_old; data = (AuthDialogData_t *)vData; @@ -456,45 +562,78 @@ static void Auth_do_auth_dialog_cb(const char *user, const char *password, } /* 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); + if (!(realm = Auth_realm_by_name(host, data->auth_parse->realm))) { + realm = dNew0(AuthRealm_t, 1); + realm->name = dStrdup(data->auth_parse->realm); realm->paths = dList_new(1); - realm->authorization = NULL; dList_append(host->realms, realm); } + realm->type = data->auth_parse->type; + dFree(realm->authorization); + realm->authorization = NULL; 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); + if (realm->type == BASIC) { + char *user_password = dStrconcat(user, ":", password, NULL); + char *response = a_Misc_encode_base64(user_password); + char *authorization = + dStrconcat("Authorization: Basic ", response, "\r\n", NULL); + dFree(realm->authorization); + realm->authorization = authorization; + dFree(response); + dStrshred(user_password); + dFree(user_password); + } else if (realm->type == DIGEST) { + dFree(realm->username); + realm->username = dStrdup(user); + realm->nonce_count = 0; + dFree(realm->nonce); + realm->nonce = dStrdup(data->auth_parse->nonce); + dFree(realm->opaque); + realm->opaque = dStrdup(data->auth_parse->opaque); + realm->algorithm = data->auth_parse->algorithm; + dFree(realm->domain); + realm->domain = dStrdup(data->auth_parse->domain); + realm->qop = data->auth_parse->qop; + dFree(realm->cnonce); + if (realm->qop != QOPNOTSET) + realm->cnonce = a_Digest_create_cnonce(); + if (!a_Digest_compute_digest(realm, user, password)) { + MSG("Auth_do_auth_dialog_cb: a_Digest_compute_digest failed.\n"); + dList_remove_fast(host->realms, realm); + Auth_realm_delete(realm); + } + } else { + MSG("Auth_do_auth_dialog_cb: Unknown auth type: %i\n", + realm->type); + } + dStrshred((char *)password); } -static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url) +/* + * Return: Nonzero if we got new credentials from the user and everything + * seems fine. + */ +static int Auth_do_auth_dialog(const AuthParse_t *auth_parse, + const DilloUrl *url) { int ret; char *message; AuthDialogData_t *data; + const char *typestr = auth_parse->type == DIGEST ? "Digest" : "Basic"; + + _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", auth_parse->realm); - _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm); message = dStrconcat("The server at ", URL_HOST(url), " requires a username" - " and password for \"", realm, "\".", NULL); + " and password for \"", auth_parse->realm, "\".\n\n" + "Authentication scheme: ", typestr, NULL); data = dNew(AuthDialogData_t, 1); - data->realm_name = dStrdup(realm); + data->auth_parse = auth_parse; 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); + a_Url_free((void *)data->url); dFree(data); return ret; } @@ -502,19 +641,20 @@ static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url) /* * Do authorization for an auth string. */ -static int Auth_do_auth(char *auth, const DilloUrl *url) +static int Auth_do_auth(char *challenge, enum AuthParseHTTPAuthType_t type, + const DilloUrl *url) { - int reload; AuthParse_t *auth_parse; + int reload = 0; - _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth); - reload = 0; + _MSG("auth.c: Auth_do_auth: challenge={%s}\n", challenge); auth_parse = Auth_parse_new(); - Auth_parse_auth(auth_parse, auth); + auth_parse->type = type; + Auth_parse_challenge(auth_parse, challenge); if (auth_parse->ok) reload = - Auth_do_auth_required(auth_parse->realm, url) ? - Auth_do_auth_dialog(auth_parse->realm, url) + Auth_do_auth_required(auth_parse, url) ? + Auth_do_auth_dialog(auth_parse, url) : 1; Auth_parse_free(auth_parse); @@ -522,18 +662,24 @@ static int Auth_do_auth(char *auth, const DilloUrl *url) } /* - * Do authorization for a set of auth strings. + * Given authentication challenge(s), prepare authorization. + * Return: 0 on failure + * nonzero on success. A new query will be sent to the server. */ -int a_Auth_do_auth(Dlist *auths, const DilloUrl *url) +int a_Auth_do_auth(Dlist *challenges, const DilloUrl *url) { - int reload, i; - char *auth; + int i; + char *chal; - reload = 0; - for (i = 0; (auth = dList_nth_data(auths, i)); ++i) - if (Auth_do_auth(auth, url)) - reload = 1; + for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) + if (!dStrnAsciiCasecmp(chal, "Digest ", 7)) + if (Auth_do_auth(chal, DIGEST, url)) + return 1; + for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) + if (!dStrnAsciiCasecmp(chal, "Basic ", 6)) + if (Auth_do_auth(chal, BASIC, url)) + return 1; - return reload; + return 0; } @@ -7,8 +7,29 @@ extern "C" { #include "url.h" +enum AuthParseHTTPAuthType_t { TYPENOTSET, BASIC, DIGEST }; +enum AuthParseDigestAlgorithm_t { ALGORITHMNOTSET, MD5, MD5SESS }; +enum AuthParseDigestQOP_t { QOPNOTSET, AUTH, AUTHINT }; -const char *a_Auth_get_auth_str(const DilloUrl *request_url); +typedef struct { + enum AuthParseHTTPAuthType_t type; + char *name; + Dlist *paths; /* stripped of any trailing '/', so the root path is "" */ + char *authorization; /* BASIC: the authorization request header */ + /* DIGEST: the hexdigest of A1 */ + /* digest state ahead */ + char *username; + char *cnonce; + unsigned int nonce_count; + char *nonce; + char *opaque; + enum AuthParseDigestAlgorithm_t algorithm; + char *domain; /* NOT USED */ + enum AuthParseDigestQOP_t qop; +} AuthRealm_t; + + +char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri); int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url); void a_Auth_init(void); diff --git a/src/binaryconst.h b/src/binaryconst.h index b5b9735b..09d4e7fa 100644 --- a/src/binaryconst.h +++ b/src/binaryconst.h @@ -1,6 +1,11 @@ #ifndef __BINARYCONST_H__ #define __BINARYCONST_H__ +/* binaryconst.h was integrated into the Dillo project in April 2004, and + * presumably comes from the ancestor of the code found at + * http://cprog.tomsweb.net/binconst.txt + */ + /* Macros for allowing binary constants in C * By Tom Torfs - donated to the public domain */ diff --git a/src/cache.c b/src/cache.c index 6094a8d5..ea9d9a1f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -13,9 +13,9 @@ * Dillo's cache module */ -#include <ctype.h> /* for tolower */ #include <sys/types.h> +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -143,7 +143,9 @@ static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web, static int ClientKey = 0; /* Provide a primary key for each client */ CacheClient_t *NewClient; - if (++ClientKey <= 0) + if (ClientKey < INT_MAX) /* check for integer overflow */ + ClientKey++; + else ClientKey = 1; NewClient = dNew(CacheClient_t, 1); @@ -521,7 +523,7 @@ const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype, /* META only gives charset; use detected MIME type too */ entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL); } else if (*from == 'm' && - !dStrncasecmp(ctype, "text/xhtml", 10)) { + !dStrnAsciiCasecmp(ctype, "text/xhtml", 10)) { /* WORKAROUND: doxygen uses "text/xhtml" in META */ entry->TypeNorm = dStrdup(entry->TypeDet); } @@ -584,7 +586,7 @@ static char *Cache_parse_field(const char *header, const char *fieldname) for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) - if (tolower(fieldname[j]) != tolower(header[i + j])) + if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ @@ -620,7 +622,7 @@ static Dlist *Cache_parse_multiple_fields(const char *header, for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) - if (tolower(fieldname[j]) != tolower(header[i + j])) + if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ @@ -675,21 +677,27 @@ static void Cache_parse_header(CacheEntry_t *entry) entry->Header = dStr_new(""); return; } - if (header[9] == '3' && header[10] == '0') { + if (header[9] == '3' && header[10] == '0' && + (location_str = Cache_parse_field(header, "Location"))) { /* 30x: URL redirection */ - if ((location_str = Cache_parse_field(header, "Location"))) { - DilloUrl *location_url; - + DilloUrl *location_url = a_Url_new(location_str,URL_STR_(entry->Url)); + + if (prefs.filter_auto_requests == PREFS_FILTER_SAME_DOMAIN && + !a_Url_same_organization(entry->Url, location_url)) { + /* don't redirect; just show body like usual (if any) */ + MSG("Redirection not followed from %s to %s\n", + URL_HOST(entry->Url), URL_STR(location_url)); + a_Url_free(location_url); + } else { entry->Flags |= CA_Redirect; if (header[11] == '1') entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */ else if (header[11] == '2') entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */ - location_url = a_Url_new(location_str, URL_STR_(entry->Url)); if (URL_FLAGS(location_url) & (URL_Post + URL_Get) && - dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 && - dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { + dStrAsciiCasecmp(URL_SCHEME(location_url), "dpi") == 0 && + dStrAsciiCasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { /* Forbid dpi GET and POST from non dpi-generated urls */ MSG("Redirection Denied! '%s' -> '%s'\n", URL_STR(entry->Url), URL_STR(location_url)); @@ -697,8 +705,8 @@ static void Cache_parse_header(CacheEntry_t *entry) } else { entry->Location = location_url; } - dFree(location_str); } + dFree(location_str); } else if (strncmp(header + 9, "401", 3) == 0) { entry->Auth = Cache_parse_multiple_fields(header, "WWW-Authenticate"); @@ -728,7 +736,7 @@ static void Cache_parse_header(CacheEntry_t *entry) * If Transfer-Encoding is present, Content-Length must be ignored. * If the Transfer-Encoding is non-identity, it is an error. */ - if (dStrcasecmp(encoding, "identity")) + if (dStrAsciiCasecmp(encoding, "identity")) MSG_HTTP("Content-Length and non-identity Transfer-Encoding " "headers both present.\n"); } else { @@ -1039,9 +1047,9 @@ static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw) */ int a_Cache_download_enabled(const DilloUrl *url) { - if (!dStrcasecmp(URL_SCHEME(url), "http") || - !dStrcasecmp(URL_SCHEME(url), "https") || - !dStrcasecmp(URL_SCHEME(url), "ftp")) + if (!dStrAsciiCasecmp(URL_SCHEME(url), "http") || + !dStrAsciiCasecmp(URL_SCHEME(url), "https") || + !dStrAsciiCasecmp(URL_SCHEME(url), "ftp")) return 1; return 0; } @@ -229,17 +229,17 @@ int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url) const DilloUrl *referer; int allow = FALSE; - if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0) { + if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0) { if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) { allow = TRUE; } else if (!(URL_FLAGS(url) & URL_Post) && - strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) { + strncmp(URL_PATH(url), "/vsource/", 9) == 0) { allow = TRUE; } else { /* only allow GET&POST dpi-requests from dpi-generated urls */ if (a_Nav_stack_size(bw)) { referer = a_History_get_url(NAV_TOP_UIDX(bw)); - if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) { + if (dStrAsciiCasecmp(URL_SCHEME(referer), "dpi") == 0) { allow = TRUE; } } @@ -266,10 +266,10 @@ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr) char *p, *server = NULL, *url_str = URL_STR(url); Dstr *tmp; - if ((dStrncasecmp(url_str, "http:", 5) == 0) || - (dStrncasecmp(url_str, "about:", 6) == 0)) { + if ((dStrnAsciiCasecmp(url_str, "http:", 5) == 0) || + (dStrnAsciiCasecmp(url_str, "about:", 6) == 0)) { /* URL doesn't use dpi (server = NULL) */ - } else if (dStrncasecmp(url_str, "dpi:/", 5) == 0) { + } else if (dStrnAsciiCasecmp(url_str, "dpi:/", 5) == 0) { /* dpi prefix, get this server's name */ if ((p = strchr(url_str + 5, '/')) != NULL) { server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5)); @@ -388,7 +388,8 @@ static bool_t Capi_filters_test(const DilloUrl *wanted, *want_host = URL_HOST(wanted); if (want_host[0] == '\0') { ret = (req_host[0] == '\0' || - !dStrcasecmp(URL_SCHEME(wanted), "data")) ? TRUE : FALSE; + !dStrAsciiCasecmp(URL_SCHEME(wanted), "data")) + ? TRUE : FALSE; } else { /* This will regard "www.dillo.org" and "www.dillo.org." as * different, but it doesn't seem worth caring about. @@ -454,9 +455,9 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData) } else if (Capi_url_uses_dpi(web->url, &server)) { /* dpi request */ if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) { - if (dStrcasecmp(scheme, "dpi") == 0) { + if (dStrAsciiCasecmp(scheme, "dpi") == 0) { if (strcmp(server, "vsource") == 0) { - /* don't reload the "view source" page */ + /* allow "view source" reload upon user request */ } else { /* make the other "dpi:/" prefixed urls always reload. */ a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery); @@ -478,7 +479,7 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData) } dFree(server); - } else if (!dStrcasecmp(scheme, "http")) { + } else if (!dStrAsciiCasecmp(scheme, "http")) { /* http request */ if (reload) { a_Capi_conn_abort_by_url(web->url); @@ -491,7 +492,7 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData) } use_cache = 1; - } else if (!dStrcasecmp(scheme, "about")) { + } else if (!dStrAsciiCasecmp(scheme, "about")) { /* internal request */ use_cache = 1; } diff --git a/src/colors.c b/src/colors.c index 5b647bb2..dcb147e3 100644 --- a/src/colors.c +++ b/src/colors.c @@ -201,7 +201,7 @@ static const struct key { #endif }; -#define NCOLORS (sizeof(color_keyword) / sizeof(struct key)) +#define NCOLORS (sizeof(color_keyword) / sizeof(color_keyword[0])) /* * Parse a color in hex (RRGGBB) or (RGB) @@ -262,7 +262,7 @@ int32_t a_Color_parse (const char *subtag, int32_t default_color, int *err) high = NCOLORS - 1; while (low <= high) { mid = (low + high) / 2; - if ((ret = dStrcasecmp(cp, color_keyword[mid].key)) < 0) + if ((ret = dStrAsciiCasecmp(cp, color_keyword[mid].key)) < 0) high = mid - 1; else if (ret > 0) low = mid + 1; diff --git a/src/cookies.c b/src/cookies.c index d1451dc1..0a1e94ff 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -197,8 +197,8 @@ char *a_Cookies_get_query(const DilloUrl *query_url, const DilloUrl *requester) if (requester == NULL) { /* request made by user */ } else if (!a_Url_same_organization(query_url, requester)) { - MSG("Cookies: No cookies sent for third-party request by '%s' for " - "'%s'\n", URL_HOST(requester), URL_STR(query_url)); + MSG("Cookies: not sent for request by '%s' for '%s'\n", + URL_HOST(requester), URL_HOST(query_url)); return dStrdup(""); } @@ -284,11 +284,11 @@ static int Cookie_control_init(void) rule[j++] = line[i++]; rule[j] = '\0'; - if (dStrcasecmp(rule, "ACCEPT") == 0) + if (dStrAsciiCasecmp(rule, "ACCEPT") == 0) cc.action = COOKIE_ACCEPT; - else if (dStrcasecmp(rule, "ACCEPT_SESSION") == 0) + else if (dStrAsciiCasecmp(rule, "ACCEPT_SESSION") == 0) cc.action = COOKIE_ACCEPT_SESSION; - else if (dStrcasecmp(rule, "DENY") == 0) + else if (dStrAsciiCasecmp(rule, "DENY") == 0) cc.action = COOKIE_DENY; else { MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n", @@ -297,7 +297,7 @@ static int Cookie_control_init(void) } cc.domain = dStrdup(domain); - if (dStrcasecmp(cc.domain, "DEFAULT") == 0) { + if (dStrAsciiCasecmp(cc.domain, "DEFAULT") == 0) { /* Set the default action */ default_action = cc.action; dFree(cc.domain); @@ -338,13 +338,13 @@ static CookieControlAction Cookies_control_check_domain(const char *domain) if (ccontrol[i].domain[0] == '.') { diff = strlen(domain) - strlen(ccontrol[i].domain); if (diff >= 0) { - if (dStrcasecmp(domain + diff, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain + diff, ccontrol[i].domain) != 0) continue; } else { continue; } } else { - if (dStrcasecmp(domain, ccontrol[i].domain) != 0) + if (dStrAsciiCasecmp(domain, ccontrol[i].domain) != 0) continue; } @@ -11,11 +11,9 @@ #include <stdio.h> #include "../dlib/dlib.h" -#include "misc.h" #include "msg.h" #include "html_common.hh" #include "css.hh" -#include "cssparser.hh" using namespace dw::core::style; @@ -29,6 +27,7 @@ CssPropertyList::CssPropertyList (const CssPropertyList &p, bool deep) : lout::misc::SimpleVector <CssProperty> (p) { refCount = 0; + safe = p.safe; if (deep) { for (int i = 0; i < size (); i++) { CssProperty *p = getRef(i); @@ -60,6 +59,9 @@ void CssPropertyList::set (CssPropertyName name, CssValueType type, CssPropertyValue value) { CssProperty *prop; + if (name == CSS_PROPERTY_DISPLAY || name == CSS_PROPERTY_BACKGROUND_IMAGE) + safe = false; + for (int i = 0; i < size (); i++) { prop = getRef (i); @@ -104,7 +106,7 @@ CssSelector::CssSelector () { cs = selectorList->getRef (selectorList->size () - 1); cs->notMatchingBefore = -1; - cs->combinator = CHILD; + cs->combinator = COMB_NONE; cs->selector = new CssSimpleSelector (); }; @@ -117,55 +119,47 @@ CssSelector::~CssSelector () { /** * \brief Return whether selector matches at a given node in the document tree. */ -bool CssSelector::match (Doctree *docTree, const DoctreeNode *node) { - CssSimpleSelector *sel; - Combinator comb = CHILD; - int *notMatchingBefore; - const DoctreeNode *n; - - for (int i = selectorList->size () - 1; i >= 0; i--) { - struct CombinatorAndSelector *cs = selectorList->getRef (i); +bool CssSelector::match (Doctree *docTree, const DoctreeNode *node, + int i, Combinator comb) { + assert (node); - sel = cs->selector; - notMatchingBefore = &cs->notMatchingBefore; - - if (node == NULL) - return false; + if (i < 0) + return true; - switch (comb) { - case CHILD: - case ADJACENT_SIBLING: - if (!sel->match (node)) - return false; - break; - case DESCENDANT: - n = node; - - while (true) { - if (node == NULL || node->num <= *notMatchingBefore) { - *notMatchingBefore = n->num; - return false; - } + struct CombinatorAndSelector *cs = selectorList->getRef (i); + CssSimpleSelector *sel = cs->selector; - if (sel->match (node)) - break; + switch (comb) { + case COMB_NONE: + break; + case COMB_CHILD: + node = docTree->parent (node); + break; + case COMB_ADJACENT_SIBLING: + node = docTree->sibling (node); + break; + case COMB_DESCENDANT: + node = docTree->parent (node); - node = docTree->parent (node); - } - break; - default: - return false; // \todo implement other combinators - } + for (const DoctreeNode *n = node; + n && n->num > cs->notMatchingBefore; n = docTree->parent (n)) + if (sel->match (n) && match (docTree, n, i - 1, cs->combinator)) + return true; - comb = cs->combinator; + if (node) // remember that it didn't match to avoid future tests + cs->notMatchingBefore = node->num; - if (comb == ADJACENT_SIBLING) - node = docTree->sibling (node); - else - node = docTree->parent (node); + return false; + break; + default: + return false; // \todo implement other combinators } - return true; + if (!node || !sel->match (node)) + return false; + + // tail recursion should be optimized by the compiler + return match (docTree, node, i - 1, cs->combinator); } void CssSelector::addSimpleSelector (Combinator c) { @@ -179,6 +173,13 @@ void CssSelector::addSimpleSelector (Combinator c) { cs->selector = new CssSimpleSelector (); } +bool CssSelector::checksPseudoClass () { + for (int i = 0; i < selectorList->size (); i++) + if (selectorList->getRef (i)->selector->getPseudoClass ()) + return true; + return false; +} + /** * \brief Return the specificity of the selector. * @@ -200,13 +201,13 @@ void CssSelector::print () { if (i < selectorList->size () - 1) { switch (selectorList->getRef (i + 1)->combinator) { - case CHILD: + case COMB_CHILD: fprintf (stderr, "> "); break; - case DESCENDANT: + case COMB_DESCENDANT: fprintf (stderr, "\" \" "); break; - case ADJACENT_SIBLING: + case COMB_ADJACENT_SIBLING: fprintf (stderr, "+ "); break; default: @@ -262,19 +263,20 @@ void CssSimpleSelector::setSelect (SelectType t, const char *v) { * the document tree. */ bool CssSimpleSelector::match (const DoctreeNode *n) { + assert (n); if (element != ELEMENT_ANY && element != n->element) return false; if (pseudo != NULL && - (n->pseudo == NULL || dStrcasecmp (pseudo, n->pseudo) != 0)) + (n->pseudo == NULL || dStrAsciiCasecmp (pseudo, n->pseudo) != 0)) return false; - if (id != NULL && (n->id == NULL || dStrcasecmp (id, n->id) != 0)) + if (id != NULL && (n->id == NULL || dStrAsciiCasecmp (id, n->id) != 0)) return false; if (klass != NULL) { for (int i = 0; i < klass->size (); i++) { bool found = false; if (n->klass != NULL) { for (int j = 0; j < n->klass->size (); j++) { - if (dStrcasecmp (klass->get(i), n->klass->get(j)) == 0) { + if (dStrAsciiCasecmp (klass->get(i), n->klass->get(j)) == 0) { found = true; break; } @@ -364,23 +366,6 @@ void CssStyleSheet::RuleList::insert (CssRule *rule) { *getRef (i) = rule; } -CssStyleSheet::CssStyleSheet () { - for (int i = 0; i < ntags; i++) - elementTable[i] = new RuleList (); - - idTable = new RuleMap (); - classTable = new RuleMap (); - anyTable = new RuleList (); -} - -CssStyleSheet::~CssStyleSheet () { - for (int i = 0; i < ntags; i++) - delete elementTable[i]; - delete idTable; - delete classTable; - delete anyTable; -} - /** * \brief Insert a rule into CssStyleSheet. * @@ -394,26 +379,26 @@ void CssStyleSheet::addRule (CssRule *rule) { if (top->getId ()) { string = new lout::object::ConstString (top->getId ()); - ruleList = idTable->get (string); + ruleList = idTable.get (string); if (ruleList == NULL) { ruleList = new RuleList (); - idTable->put (string, ruleList); + idTable.put (string, ruleList); } else { delete string; } } else if (top->getClass () && top->getClass ()->size () > 0) { string = new lout::object::ConstString (top->getClass ()->get (0)); - ruleList = classTable->get (string); + ruleList = classTable.get (string); if (ruleList == NULL) { ruleList = new RuleList; - classTable->put (string, ruleList); + classTable.put (string, ruleList); } else { delete string; } } else if (top->getElement () >= 0 && top->getElement () < ntags) { - ruleList = elementTable[top->getElement ()]; + ruleList = &elementTable[top->getElement ()]; } else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) { - ruleList = anyTable; + ruleList = &anyTable; } if (ruleList) { @@ -439,7 +424,7 @@ void CssStyleSheet::apply (CssPropertyList *props, if (node->id) { lout::object::ConstString idString (node->id); - ruleList[numLists] = idTable->get (&idString); + ruleList[numLists] = idTable.get (&idString); if (ruleList[numLists]) numLists++; } @@ -453,17 +438,17 @@ void CssStyleSheet::apply (CssPropertyList *props, lout::object::ConstString classString (node->klass->get (i)); - ruleList[numLists] = classTable->get (&classString); + ruleList[numLists] = classTable.get (&classString); if (ruleList[numLists]) numLists++; } } - ruleList[numLists] = elementTable[node->element]; + ruleList[numLists] = &elementTable[node->element]; if (ruleList[numLists]) numLists++; - ruleList[numLists] = anyTable; + ruleList[numLists] = &anyTable; if (ruleList[numLists]) numLists++; @@ -477,13 +462,15 @@ void CssStyleSheet::apply (CssPropertyList *props, int minSpecIndex = -1; for (int i = 0; i < numLists; i++) { - if (ruleList[i] && ruleList[i]->size () > index[i] && - (ruleList[i]->get(index[i])->specificity () < minSpec || - (ruleList[i]->get(index[i])->specificity () == minSpec && - ruleList[i]->get(index[i])->position () < minPos))) { + RuleList *rl = ruleList[i]; + + if (rl && rl->size () > index[i] && + (rl->get(index[i])->specificity () < minSpec || + (rl->get(index[i])->specificity () == minSpec && + rl->get(index[i])->position () < minPos))) { - minSpec = ruleList[i]->get(index[i])->specificity (); - minPos = ruleList[i]->get(index[i])->position (); + minSpec = rl->get(index[i])->specificity (); + minPos = rl->get(index[i])->position (); minSpecIndex = i; } } @@ -500,19 +487,6 @@ void CssStyleSheet::apply (CssPropertyList *props, CssContext::CssContext () { pos = 0; - - memset (sheet, 0, sizeof(sheet)); - sheet[CSS_PRIMARY_USER_AGENT] = new CssStyleSheet (); - sheet[CSS_PRIMARY_USER] = new CssStyleSheet (); - sheet[CSS_PRIMARY_USER_IMPORTANT] = new CssStyleSheet (); - - buildUserAgentStyle (); - buildUserStyle (); -} - -CssContext::~CssContext () { - for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++) - delete sheet[o]; } /** @@ -528,29 +502,24 @@ void CssContext::apply (CssPropertyList *props, Doctree *docTree, DoctreeNode *node, CssPropertyList *tagStyle, CssPropertyList *tagStyleImportant, CssPropertyList *nonCssHints) { - if (sheet[CSS_PRIMARY_USER_AGENT]) - sheet[CSS_PRIMARY_USER_AGENT]->apply (props, docTree, node); - if (sheet[CSS_PRIMARY_USER]) - sheet[CSS_PRIMARY_USER]->apply (props, docTree, node); + sheet[CSS_PRIMARY_USER_AGENT].apply (props, docTree, node); + sheet[CSS_PRIMARY_USER].apply (props, docTree, node); if (nonCssHints) nonCssHints->apply (props); - if (sheet[CSS_PRIMARY_AUTHOR]) - sheet[CSS_PRIMARY_AUTHOR]->apply (props, docTree, node); + sheet[CSS_PRIMARY_AUTHOR].apply (props, docTree, node); if (tagStyle) tagStyle->apply (props); - if (sheet[CSS_PRIMARY_AUTHOR_IMPORTANT]) - sheet[CSS_PRIMARY_AUTHOR_IMPORTANT]->apply (props, docTree, node); + sheet[CSS_PRIMARY_AUTHOR_IMPORTANT].apply (props, docTree, node); if (tagStyleImportant) tagStyleImportant->apply (props); - if (sheet[CSS_PRIMARY_USER_IMPORTANT]) - sheet[CSS_PRIMARY_USER_IMPORTANT]->apply (props, docTree, node); + sheet[CSS_PRIMARY_USER_IMPORTANT].apply (props, docTree, node); } void CssContext::addRule (CssSelector *sel, CssPropertyList *props, @@ -559,74 +528,13 @@ void CssContext::addRule (CssSelector *sel, CssPropertyList *props, if (props->size () > 0) { CssRule *rule = new CssRule (sel, props, pos++); - if (sheet[order] == NULL) - sheet[order] = new CssStyleSheet (); - - sheet[order]->addRule (rule); - } -} - -/** - * \brief Create the user agent style. - * - * The user agent style defines how dillo renders HTML in the absence of - * author or user styles. - */ -void CssContext::buildUserAgentStyle () { - const char *cssBuf = - "body {margin: 5px}" - "big {font-size: 1.17em}" - "blockquote, dd {margin-left: 40px; margin-right: 40px}" - "center {text-align: center}" - "dt {font-weight: bolder}" - ":link {color: blue; text-decoration: underline; cursor: pointer}" - ":visited {color: #800080; text-decoration: underline; cursor: pointer}" - "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder}" - "i, em, cite, address, var {font-style: italic}" - ":link img, :visited img {border: 1px solid}" - "frameset, ul, ol, dir {margin-left: 40px}" - "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0}" - "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0}" - "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0}" - "h4 {margin-top: 1.12em; margin-bottom: 0}" - "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0}" - "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0}" - "hr {width: 100%; border: 1px inset}" - "li {margin-top: 0.1em}" - "pre {white-space: pre}" - "ol {list-style-type: decimal}" - "ul {list-style-type: disc}" - "ul ul {list-style-type: circle}" - "ul ul ul {list-style-type: square}" - "ul ul ul ul {list-style-type: disc}" - "u {text-decoration: underline}" - "small, sub, sup {font-size: 0.83em}" - "sub {vertical-align: sub}" - "sup {vertical-align: super}" - "s, strike, del {text-decoration: line-through}" - "table {border-spacing: 2px}" - "td, th {padding: 2px}" - "thead, tbody, tfoot {vertical-align: middle}" - "th {font-weight: bolder; text-align: center}" - "code, tt, pre, samp, kbd {font-family: monospace}" - /* WORKAROUND: Reset font properties in tables as some - * some pages rely on it (e.g. gmail). - * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode - * has a detailed description of the issue. - */ - "table, caption {font-size: medium; font-weight: normal}"; - - CssParser::parse (NULL, NULL, this, cssBuf, strlen (cssBuf), - CSS_ORIGIN_USER_AGENT); -} - -void CssContext::buildUserStyle () { - Dstr *style; - char *filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL); - - if ((style = a_Misc_file2dstr(filename))) { - CssParser::parse (NULL,NULL,this,style->str, style->len,CSS_ORIGIN_USER); - dStr_free (style, 1); + if ((order == CSS_PRIMARY_AUTHOR || + order == CSS_PRIMARY_AUTHOR_IMPORTANT) && + !rule->isSafe ()) { + MSG_WARN ("Ignoring unsafe author style that might reveal browsing history\n"); + delete rule; + } else { + sheet[order].addRule (rule); + } } - dFree (filename); } @@ -223,6 +223,7 @@ typedef enum { CSS_PROPERTY_X_COLSPAN, CSS_PROPERTY_X_ROWSPAN, PROPERTY_X_LINK, + PROPERTY_X_LANG, PROPERTY_X_IMG, PROPERTY_X_TOOLTIP, CSS_PROPERTY_LAST @@ -297,11 +298,13 @@ class CssProperty { class CssPropertyList : public lout::misc::SimpleVector <CssProperty> { int refCount; bool ownerOfStrings; + bool safe; public: inline CssPropertyList(bool ownerOfStrings = false) : lout::misc::SimpleVector <CssProperty> (1) { refCount = 0; + safe = true; this->ownerOfStrings = ownerOfStrings; }; CssPropertyList(const CssPropertyList &p, bool deep = false); @@ -310,6 +313,7 @@ class CssPropertyList : public lout::misc::SimpleVector <CssProperty> { void set (CssPropertyName name, CssValueType type, CssPropertyValue value); void apply (CssPropertyList *props); + bool isSafe () { return safe; }; void print (); inline void ref () { refCount++; } inline void unref () { if (--refCount == 0) delete this; } @@ -355,9 +359,10 @@ class CssSimpleSelector { class CssSelector { public: typedef enum { - DESCENDANT, - CHILD, - ADJACENT_SIBLING, + COMB_NONE, + COMB_DESCENDANT, + COMB_CHILD, + COMB_ADJACENT_SIBLING, } Combinator; private: @@ -370,6 +375,8 @@ class CssSelector { int refCount; lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList; + bool match (Doctree *dt, const DoctreeNode *node, int i, Combinator comb); + public: CssSelector (); ~CssSelector (); @@ -378,8 +385,11 @@ class CssSelector { return selectorList->getRef (selectorList->size () - 1)->selector; }; inline int size () { return selectorList->size (); }; - bool match (Doctree *dt, const DoctreeNode *node); + inline bool match (Doctree *dt, const DoctreeNode *node) { + return match (dt, node, selectorList->size () - 1, COMB_NONE); + }; int specificity (); + bool checksPseudoClass (); void print (); inline void ref () { refCount++; } inline void unref () { if (--refCount == 0) delete this; } @@ -403,6 +413,9 @@ class CssRule { void apply (CssPropertyList *props, Doctree *docTree, const DoctreeNode *node); + inline bool isSafe () { + return !selector->checksPseudoClass () || props->isSafe (); + }; inline int specificity () { return spec; }; inline int position () { return pos; }; void print (); @@ -439,15 +452,11 @@ class CssStyleSheet { }; static const int ntags = 90; // \todo replace 90 - RuleList *elementTable[ntags]; - RuleMap *idTable; - RuleMap *classTable; - RuleList *anyTable; + RuleList elementTable[ntags], anyTable; + RuleMap idTable, classTable; public: - CssStyleSheet(); - ~CssStyleSheet(); void addRule (CssRule *rule); void apply (CssPropertyList *props, Doctree *docTree, const DoctreeNode *node); @@ -458,15 +467,11 @@ class CssStyleSheet { */ class CssContext { private: - CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1]; + CssStyleSheet sheet[CSS_PRIMARY_USER_IMPORTANT + 1]; int pos; - void buildUserAgentStyle (); - void buildUserStyle (); - public: CssContext (); - ~CssContext (); void addRule (CssSelector *sel, CssPropertyList *props, CssPrimaryOrder order); diff --git a/src/cssparser.cc b/src/cssparser.cc index f6e60731..bd065234 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -71,8 +71,8 @@ static const char *const Css_cursor_enum_vals[] = { }; static const char *const Css_display_enum_vals[] = { - "block", "inline", "list-item", "none", "table", "table-row-group", - "table-header-group", "table-footer-group", "table-row", + "block", "inline", "inline-block", "list-item", "none", "table", + "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-cell", NULL }; @@ -125,6 +125,10 @@ static const char *const Css_text_decoration_enum_vals[] = { "underline", "overline", "line-through", "blink", NULL }; +static const char *const Css_text_transform_enum_vals[] = { + "none", "capitalize", "uppercase", "lowercase", NULL +}; + static const char *const Css_vertical_align_vals[] = { "top", "bottom", "middle", "baseline", "sub", "super", "text-top", "text-bottom", NULL @@ -232,7 +236,8 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { Css_text_decoration_enum_vals}, {"text-indent", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL}, {"text-shadow", {CSS_TYPE_UNUSED}, NULL}, - {"text-transform", {CSS_TYPE_UNUSED}, NULL}, + {"text-transform", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, + Css_text_transform_enum_vals}, {"top", {CSS_TYPE_UNUSED}, NULL}, {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL}, {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals}, @@ -411,7 +416,7 @@ static const CssShorthandInfo Css_shorthand_info[] = { }; #define CSS_SHORTHAND_NUM \ - (sizeof(Css_shorthand_info) / sizeof(CssShorthandInfo)) + (sizeof(Css_shorthand_info) / sizeof(Css_shorthand_info[0])) /* ---------------------------------------------------------------------- * Parsing @@ -673,7 +678,7 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) case CSS_TYPE_ENUM: if (ttype == CSS_TK_SYMBOL) { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) return true; } @@ -681,11 +686,11 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) case CSS_TYPE_MULTI_ENUM: if (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "none") == 0) { + if (dStrAsciiCasecmp(tval, "none") == 0) { return true; } else { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) { - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) return true; } @@ -702,14 +707,14 @@ bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type) case CSS_TYPE_SIGNED_LENGTH: if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT || - (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0)) + (ttype == CSS_TK_SYMBOL && dStrAsciiCasecmp(tval, "auto") == 0)) return true; break; case CSS_TYPE_COLOR: if ((ttype == CSS_TK_COLOR || ttype == CSS_TK_SYMBOL) && - (dStrcasecmp(tval, "rgb") == 0 || + (dStrAsciiCasecmp(tval, "rgb") == 0 || a_Color_parse(tval, -1, &err) != -1)) return true; break; @@ -837,7 +842,7 @@ bool CssParser::parseValue(CssPropertyName prop, case CSS_TYPE_ENUM: if (ttype == CSS_TK_SYMBOL) { for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) { val->intVal = i; ret = true; @@ -852,10 +857,10 @@ bool CssParser::parseValue(CssPropertyName prop, ret = true; while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "none") != 0) { + if (dStrAsciiCasecmp(tval, "none") != 0) { for (i = 0, found = false; !found && Css_property_info[prop].enum_symbols[i]; i++) { - if (dStrcasecmp(tval, + if (dStrAsciiCasecmp(tval, Css_property_info[prop].enum_symbols[i]) == 0) val->intVal |= (1 << i); } @@ -876,32 +881,32 @@ bool CssParser::parseValue(CssPropertyName prop, if (!spaceSeparated && ttype == CSS_TK_SYMBOL) { ret = true; - if (dStrcasecmp(tval, "px") == 0) { + if (dStrAsciiCasecmp(tval, "px") == 0) { lentype = CSS_LENGTH_TYPE_PX; nextToken(); - } else if (dStrcasecmp(tval, "mm") == 0) { + } else if (dStrAsciiCasecmp(tval, "mm") == 0) { lentype = CSS_LENGTH_TYPE_MM; nextToken(); - } else if (dStrcasecmp(tval, "cm") == 0) { + } else if (dStrAsciiCasecmp(tval, "cm") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= 10; nextToken(); - } else if (dStrcasecmp(tval, "in") == 0) { + } else if (dStrAsciiCasecmp(tval, "in") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= 25.4; nextToken(); - } else if (dStrcasecmp(tval, "pt") == 0) { + } else if (dStrAsciiCasecmp(tval, "pt") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= (25.4 / 72); nextToken(); - } else if (dStrcasecmp(tval, "pc") == 0) { + } else if (dStrAsciiCasecmp(tval, "pc") == 0) { lentype = CSS_LENGTH_TYPE_MM; fval *= (25.4 / 6); nextToken(); - } else if (dStrcasecmp(tval, "em") == 0) { + } else if (dStrAsciiCasecmp(tval, "em") == 0) { lentype = CSS_LENGTH_TYPE_EM; nextToken(); - } else if (dStrcasecmp(tval, "ex") == 0) { + } else if (dStrAsciiCasecmp(tval, "ex") == 0) { lentype = CSS_LENGTH_TYPE_EX; nextToken(); } else { @@ -926,7 +931,7 @@ bool CssParser::parseValue(CssPropertyName prop, ret = true; val->intVal = CSS_CREATE_LENGTH(fval, lentype); - } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) { + } else if (ttype == CSS_TK_SYMBOL && !dStrAsciiCasecmp(tval, "auto")) { ret = true; val->intVal = CSS_LENGTH_TYPE_AUTO; nextToken(); @@ -942,7 +947,7 @@ bool CssParser::parseValue(CssPropertyName prop, ret = true; nextToken(); } else if (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "rgb") == 0) { + if (dStrAsciiCasecmp(tval, "rgb") == 0) { nextToken(); if (parseRgbColor(&val->intVal)) ret = true; @@ -1021,7 +1026,7 @@ bool CssParser::parseWeight() if (ttype == CSS_TK_CHAR && tval[0] == '!') { nextToken(); if (ttype == CSS_TK_SYMBOL && - dStrcasecmp(tval, "important") == 0) { + dStrAsciiCasecmp(tval, "important") == 0) { nextToken(); return true; } @@ -1035,7 +1040,7 @@ bool CssParser::parseWeight() */ static int Css_property_info_cmp(const void *a, const void *b) { - return dStrcasecmp(((CssPropertyInfo *) a)->symbol, + return dStrAsciiCasecmp(((CssPropertyInfo *) a)->symbol, ((CssPropertyInfo *) b)->symbol); } @@ -1045,7 +1050,7 @@ static int Css_property_info_cmp(const void *a, const void *b) */ static int Css_shorthand_info_cmp(const void *a, const void *b) { - return dStrcasecmp(((CssShorthandInfo *) a)->symbol, + return dStrAsciiCasecmp(((CssShorthandInfo *) a)->symbol, ((CssShorthandInfo *) b)->symbol); } @@ -1291,13 +1296,13 @@ CssSelector *CssParser::parseSelector() (tval[0] == ',' || tval[0] == '{')) { break; } else if (ttype == CSS_TK_CHAR && tval[0] == '>') { - selector->addSimpleSelector (CssSelector::CHILD); + selector->addSimpleSelector (CssSelector::COMB_CHILD); nextToken(); } else if (ttype == CSS_TK_CHAR && tval[0] == '+') { - selector->addSimpleSelector (CssSelector::ADJACENT_SIBLING); + selector->addSimpleSelector (CssSelector::COMB_ADJACENT_SIBLING); nextToken(); } else if (ttype != CSS_TK_END && spaceSeparated) { - selector->addSimpleSelector (CssSelector::DESCENDANT); + selector->addSimpleSelector (CssSelector::COMB_DESCENDANT); } else { delete selector; selector = NULL; @@ -1390,7 +1395,7 @@ char * CssParser::parseUrl() Dstr *urlStr = NULL; if (ttype != CSS_TK_SYMBOL || - dStrcasecmp(tval, "url") != 0) + dStrAsciiCasecmp(tval, "url") != 0) return NULL; nextToken(); @@ -1436,7 +1441,7 @@ void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl) nextToken(); if (ttype == CSS_TK_SYMBOL && - dStrcasecmp(tval, "url") == 0) + dStrAsciiCasecmp(tval, "url") == 0) urlStr = parseUrl(); else if (ttype == CSS_TK_STRING) urlStr = dStrdup (tval); @@ -1448,8 +1453,8 @@ void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl) mediaSyntaxIsOK = false; mediaIsSelected = false; while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "all") == 0 || - dStrcasecmp(tval, "screen") == 0) + if (dStrAsciiCasecmp(tval, "all") == 0 || + dStrAsciiCasecmp(tval, "screen") == 0) mediaIsSelected = true; nextToken(); if (ttype == CSS_TK_CHAR && tval[0] == ',') { @@ -1490,8 +1495,8 @@ void CssParser::parseMedia() /* parse a comma-separated list of media */ while (ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(tval, "all") == 0 || - dStrcasecmp(tval, "screen") == 0) + if (dStrAsciiCasecmp(tval, "all") == 0 || + dStrAsciiCasecmp(tval, "screen") == 0) mediaIsSelected = true; nextToken(); if (ttype == CSS_TK_CHAR && tval[0] == ',') { @@ -1577,11 +1582,11 @@ void CssParser::parse(DilloHtml *html, DilloUrl *url, CssContext * context, parser.tval[0] == '@') { parser.nextToken(); if (parser.ttype == CSS_TK_SYMBOL) { - if (dStrcasecmp(parser.tval, "import") == 0 && + if (dStrAsciiCasecmp(parser.tval, "import") == 0 && html != NULL && importsAreAllowed) { parser.parseImport(html, url); - } else if (dStrcasecmp(parser.tval, "media") == 0) { + } else if (dStrAsciiCasecmp(parser.tval, "media") == 0) { parser.parseMedia(); } else { parser.ignoreStatement(); diff --git a/src/decode.c b/src/decode.c index 24067318..7ea3dc25 100644 --- a/src/decode.c +++ b/src/decode.c @@ -190,7 +190,7 @@ Decode *a_Decode_transfer_init(const char *format) { Decode *dc = NULL; - if (format && !dStrcasecmp(format, "chunked")) { + if (format && !dStrAsciiCasecmp(format, "chunked")) { int *chunk_remaining = dNew(int, 1); *chunk_remaining = 0; dc = dNew(Decode, 1); @@ -215,7 +215,8 @@ Decode *a_Decode_content_init(const char *format) Decode *dc = NULL; if (format && *format) { - if (!dStrcasecmp(format, "gzip") || !dStrcasecmp(format, "x-gzip")) { + if (!dStrAsciiCasecmp(format, "gzip") || + !dStrAsciiCasecmp(format, "x-gzip")) { z_stream *zs; _MSG("gzipped data!\n"); @@ -245,17 +246,17 @@ Decode *a_Decode_content_init(const char *format) */ static int Decode_is_ascii(const char *str) { - return (!(dStrcasecmp(str, "ASCII") && - dStrcasecmp(str, "US-ASCII") && - dStrcasecmp(str, "us") && - dStrcasecmp(str, "IBM367") && - dStrcasecmp(str, "cp367") && - dStrcasecmp(str, "csASCII") && - dStrcasecmp(str, "ANSI_X3.4-1968") && - dStrcasecmp(str, "iso-ir-6") && - dStrcasecmp(str, "ANSI_X3.4-1986") && - dStrcasecmp(str, "ISO_646.irv:1991") && - dStrcasecmp(str, "ISO646-US"))); + return (!(dStrAsciiCasecmp(str, "ASCII") && + dStrAsciiCasecmp(str, "US-ASCII") && + dStrAsciiCasecmp(str, "us") && + dStrAsciiCasecmp(str, "IBM367") && + dStrAsciiCasecmp(str, "cp367") && + dStrAsciiCasecmp(str, "csASCII") && + dStrAsciiCasecmp(str, "ANSI_X3.4-1968") && + dStrAsciiCasecmp(str, "iso-ir-6") && + dStrAsciiCasecmp(str, "ANSI_X3.4-1986") && + dStrAsciiCasecmp(str, "ISO_646.irv:1991") && + dStrAsciiCasecmp(str, "ISO646-US"))); } /* @@ -271,7 +272,7 @@ Decode *a_Decode_charset_init(const char *format) if (format && strlen(format) && - dStrcasecmp(format,"UTF-8") && + dStrAsciiCasecmp(format,"UTF-8") && !Decode_is_ascii(format)) { iconv_t ic = iconv_open("UTF-8", format); diff --git a/src/dialog.cc b/src/dialog.cc index 15e5d7a9..f830f2d3 100644 --- a/src/dialog.cc +++ b/src/dialog.cc @@ -95,6 +95,24 @@ public: }; }; +class EnterButton : public Fl_Button { +public: + EnterButton (int x,int y,int w,int h, const char* label = 0) : + Fl_Button (x,y,w,h,label) {}; + int handle(int e); +}; + +int EnterButton::handle(int e) +{ + if (e == FL_KEYBOARD && Fl::focus() == this && Fl::event_key() == FL_Enter){ + set_changed(); + simulate_key_action(); + do_callback(); + return 1; + } + return Fl_Button::handle(e); +} + //---------------------------------------------------------------------------- @@ -354,7 +372,7 @@ int a_Dialog_choice5(const char *QuestionTxt, bw = (ww - gap)/nb - gap; xpos += gap; for (int i=1; i <= nb; ++i) { - b = new Fl_Button(xpos, wh-bh, bw, bh, txt[i]); + b = new EnterButton(xpos, wh-bh, bw, bh, txt[i]); b->align(FL_ALIGN_WRAP|FL_ALIGN_CLIP); b->box(FL_UP_BOX); b->callback(choice5_cb, INT2VOIDP(i)); @@ -426,14 +444,14 @@ int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp) /* "OK" button */ y += input_h + 20; - Fl_Button *ok_button = new Fl_Button(200, y, 50, button_h, "OK"); + Fl_Button *ok_button = new EnterButton(200, y, 50, button_h, "OK"); ok_button->labelsize(14); ok_button->callback(Dialog_user_password_cb); window->add(ok_button); /* "Cancel" button */ Fl_Button *cancel_button = - new Fl_Button(50, y, 100, button_h, "Cancel"); + new EnterButton(50, y, 100, button_h, "Cancel"); cancel_button->labelsize(14); cancel_button->callback(Dialog_user_password_cb); window->add(cancel_button); diff --git a/src/digest.c b/src/digest.c new file mode 100644 index 00000000..d5461950 --- /dev/null +++ b/src/digest.c @@ -0,0 +1,204 @@ +/* + * File: digest.c + * + * Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de> + * + * 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. + */ + +#include <stdlib.h> +#include "digest.h" +#include "md5.h" +#include "msg.h" +#include "../dlib/dlib.h" + +static const char *ALGORITHM2STR[] = { NULL, "MD5", "MD5-sess" }; +static const char *QOP2STR[] = { NULL, "auth", "auth-int" }; +static const char hexchars[] = "0123456789abcdef"; + +static Dstr *md5hexdigest(const Dstr *data) +{ + md5_state_t state; + md5_byte_t digest[16]; + Dstr *result = dStr_sized_new(33); + int i; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)data->str, data->len); + md5_finish(&state, digest); + + for (i = 0; i < 16; i++) + dStr_sprintfa(result, "%02x", digest[i]); + return result; +} + +/* + * Returns a pointer to a newly allocated string containing a cnonce + */ +char *a_Digest_create_cnonce(void) +{ + int i; + char *result = dNew(char, 33); + for (i = 0; i < 32; i++) + result[i] = hexchars[rand() % 16]; + result[32] = 0; + return result; +} + +/* + * This portion only has to be calculated once. + */ +int a_Digest_compute_digest(AuthRealm_t *realm, const char *username, + const char *passwd) +{ + Dstr *a1; + Dstr *digest; + + if (realm->algorithm == MD5 || realm->algorithm == ALGORITHMNOTSET) { + /* A1 = unq(username-value) ":" unq(realm-value) ":" passwd */ + a1 = dStr_new(NULL); + dStr_sprintf(a1, "%s:%s:%s", username, realm->name, passwd); + } else if (realm->algorithm == MD5SESS) { + /* A1 = H( unq(username-value) ":" unq(realm-value) + ** ":" passwd ) + ** ":" unq(nonce-value) ":" unq(cnonce-value) + */ + Dstr *a0 = dStr_new(NULL); + dStr_sprintf(a0, "%s:%s:%s", username, realm->name, passwd); + Dstr *ha0 = md5hexdigest(a0); + a1 = dStr_new(NULL); + dStr_sprintf(a1, "%s:%s:%s", ha0, realm->nonce, realm->cnonce); + dStr_free(a0, 1); + dStr_free(ha0, 1); + } else { + MSG("a_Digest_create_auth: Unknown algorithm.\n"); + return 0; + } + + digest = md5hexdigest(a1); + realm->authorization = digest->str; + dStr_shred(a1); + dStr_free(a1, 1); + dStr_free(digest, 0); + return 1; +} + +/* + * This portion is calculatd for each request. + */ +static Dstr *Digest_create_response(AuthRealm_t *realm, const char *method, + const char *digest_uri, + const Dstr *entity_body) +{ + Dstr *ha2; + Dstr *result; + + if (realm->qop == QOPNOTSET || realm->qop == AUTH) { + /* A2 = Method ":" digest-uri-value */ + Dstr *a2 = dStr_new(NULL); + dStr_sprintf(a2, "%s:%s", method, digest_uri); + ha2 = md5hexdigest(a2); + dStr_free(a2, 1); + } else if (realm->qop == AUTHINT) { + /* A2 = Method ":" digest-uri-value ":" H(entity-body) */ + Dstr *hentity = md5hexdigest(entity_body); + Dstr *a2 = dStr_new(NULL); + dStr_sprintf(a2, "%s:%s:%s", method, digest_uri, hentity->str); + ha2 = md5hexdigest(a2); + dStr_free(hentity, 1); + dStr_free(a2, 1); + } else { + MSG("a_Digest_create_auth: Unknown qop value.\n"); + return NULL; + } + result = dStr_new(NULL); + + if (realm->qop == AUTH || realm->qop == AUTHINT) { + dStr_sprintf(result, + "%s:%s:%08x:%s:%s:%s", + realm->authorization, + realm->nonce, + realm->nonce_count, + realm->cnonce, + QOP2STR[realm->qop], + ha2->str); + } else { + dStr_sprintf(result, + "%s:%s:%s", + realm->authorization, + realm->nonce, + ha2->str); + } + + Dstr *request_digest = md5hexdigest(result); + dStr_free(result, 1); + dStr_free(ha2, 1); + return request_digest; +} + +static void Digest_Dstr_append_token_value(Dstr *str, int delimiter, + const char *token, + const char *value, int quoted) +{ + char c; + dStr_sprintfa(str, "%s%s=", (delimiter ? ", " : ""), token); + if (quoted) { + dStr_append_c(str, '"'); + while ((c = *value++)) { + if (c == '"') + dStr_append_c(str, '\\'); + dStr_append_c(str, c); + } + dStr_append_c(str, '"'); + } else { + dStr_append(str, value); + } +} + +/* + * Construct Digest Authorization header. + * + * Field ordering: furaisanjin reports that his DVD recorder requires the + * order that IE happens to use: "username, realm, nonce, uri, cnonce, nc, + * algorithm, response, qop". It apparently doesn't use "opaque", so that's + * been left where it already was. + */ +char *a_Digest_authorization_hdr(AuthRealm_t *realm, const DilloUrl *url, + const char *digest_uri) +{ + char *ret; + Dstr *response, *result; + const char *method = URL_FLAGS(url) & URL_Post ? "POST" : "GET"; + + realm->nonce_count++; + response = Digest_create_response(realm, method, digest_uri, URL_DATA(url)); + if (!response) + return NULL; + result = dStr_new("Authorization: Digest "); + Digest_Dstr_append_token_value(result, 0, "username", realm->username, 1); + Digest_Dstr_append_token_value(result, 1, "realm", realm->name, 1); + Digest_Dstr_append_token_value(result, 1, "nonce", realm->nonce, 1); + Digest_Dstr_append_token_value(result, 1, "uri", digest_uri, 1); + if (realm->qop != QOPNOTSET) { + Digest_Dstr_append_token_value(result, 1, "cnonce", realm->cnonce, 1); + dStr_sprintfa(result, ", nc=%08x", realm->nonce_count); + } + if (realm->algorithm != ALGORITHMNOTSET) { + Digest_Dstr_append_token_value(result, 1, "algorithm", + ALGORITHM2STR[realm->algorithm], 0); + } + Digest_Dstr_append_token_value(result, 1, "response", response->str, 1); + if (realm->opaque) + Digest_Dstr_append_token_value(result, 1, "opaque", realm->opaque, 1); + if (realm->qop != QOPNOTSET) + Digest_Dstr_append_token_value(result, 1, "qop", QOP2STR[realm->qop], 1); + dStr_sprintfa(result, "\r\n"); + + dStr_free(response, 1); + ret = result->str; + dStr_free(result, 0); + return ret; +} diff --git a/src/digest.h b/src/digest.h new file mode 100644 index 00000000..2723c645 --- /dev/null +++ b/src/digest.h @@ -0,0 +1,16 @@ +#ifndef __DIGEST_H__ +#define __DIGEST_H__ + +#include "auth.h" +#include "../dlib/dlib.h" + + +char *a_Digest_create_cnonce(void); +int a_Digest_compute_digest(AuthRealm_t *realm, + const char *username, + const char *passwd); +char *a_Digest_authorization_hdr(AuthRealm_t *realm, + const DilloUrl *url, + const char *uri); + +#endif /* !__DIGEST_H__ */ diff --git a/src/dillo.cc b/src/dillo.cc index b05cb16a..7552676d 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -410,8 +410,9 @@ int main(int argc, char **argv) if (idx == argc) { /* No URLs/files on cmdline. Send startup screen */ - if (strcmp(URL_STR(prefs.start_page), "about:blank") == 0) - a_UIcmd_open_url(bw, NULL); + if (dStrAsciiCasecmp(URL_SCHEME(prefs.start_page), "about") == 0 && + strcmp(URL_PATH(prefs.start_page), "blank") == 0) + a_UIcmd_open_url(bw, NULL); // NULL URL focuses location else a_UIcmd_open_url(bw, prefs.start_page); } else { @@ -24,11 +24,13 @@ #endif +#include <assert.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> +#include <fcntl.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> @@ -38,7 +40,7 @@ #include "msg.h" #include "dns.h" #include "list.h" -#include "timeout.hh" +#include "IO/iowatch.hh" /* Maximum dns resolving threads */ @@ -48,13 +50,17 @@ # define D_DNS_MAX_SERVERS 1 #endif +typedef enum { + DNS_SERVER_IDLE, + DNS_SERVER_PROCESSING, + DNS_SERVER_RESOLVED, +} DnsServerState_t; typedef struct { int channel; /* Index of this channel [0 based] */ - bool_t in_use; /* boolean to tell if server is doing a lookup */ - bool_t ip_ready; /* boolean: is IP lookup done? */ + DnsServerState_t state; Dlist *addr_list; /* IP address */ - char *hostname; /* Adress to resolve */ + char *hostname; /* Address to resolve */ int status; /* errno code for resolving function */ #ifdef D_DNS_THREADED pthread_t th1; /* Thread id */ @@ -77,7 +83,7 @@ typedef struct { /* * Forward declarations */ -static void Dns_timeout_client(void *data); +static void Dns_timeout_client(int fd, void *data); /* * Local Data @@ -88,6 +94,7 @@ static GDnsCache *dns_cache; static int dns_cache_size, dns_cache_size_max; static GDnsQueue *dns_queue; static int dns_queue_size, dns_queue_size_max; +static int dns_notify_pipe[2]; /* ---------------------------------------------------------------------- @@ -113,7 +120,7 @@ static int Dns_queue_find(const char *hostname) int i; for (i = 0; i < dns_queue_size; i++) - if (!strcmp(hostname, dns_queue[i].hostname)) + if (!dStrAsciiCasecmp(hostname, dns_queue[i].hostname)) return i; return -1; @@ -169,7 +176,7 @@ static void Dns_cache_add(char *hostname, Dlist *addr_list) */ void a_Dns_init(void) { - int i; + int res, i; #ifdef D_DNS_THREADED MSG("dillo_dns_init: Here we go! (threaded)\n"); @@ -187,11 +194,15 @@ void a_Dns_init(void) num_servers = D_DNS_MAX_SERVERS; + res = pipe(dns_notify_pipe); + assert(res == 0); + fcntl(dns_notify_pipe[0], F_SETFL, O_NONBLOCK); + a_IOwatch_add_fd(dns_notify_pipe[0], DIO_READ, Dns_timeout_client, NULL); + /* Initialize servers data */ for (i = 0; i < num_servers; ++i) { dns_server[i].channel = i; - dns_server[i].in_use = FALSE; - dns_server[i].ip_ready = FALSE; + dns_server[i].state = DNS_SERVER_IDLE; dns_server[i].addr_list = NULL; dns_server[i].hostname = NULL; dns_server[i].status = 0; @@ -323,7 +334,9 @@ static void *Dns_server(void *data) MSG(" (nil)\n"); } dns_server[channel].addr_list = hosts; - dns_server[channel].ip_ready = TRUE; + dns_server[channel].state = DNS_SERVER_RESOLVED; + + write(dns_notify_pipe[1], ".", 1); return NULL; /* (avoids a compiler warning) */ } @@ -339,16 +352,11 @@ static void Dns_server_req(int channel, const char *hostname) static int thrATTRInitialized = 0; #endif - dns_server[channel].in_use = TRUE; - dns_server[channel].ip_ready = FALSE; + dns_server[channel].state = DNS_SERVER_PROCESSING; dFree(dns_server[channel].hostname); dns_server[channel].hostname = dStrdup(hostname); - /* Let's set a timeout client to poll the server channel (5 times/sec) */ - a_Timeout_add(0.2,Dns_timeout_client, - INT2VOIDP(dns_server[channel].channel)); - #ifdef D_DNS_THREADED /* set the thread attribute to the detached state */ if (!thrATTRInitialized) { @@ -377,7 +385,7 @@ void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data) /* check for cache hit. */ for (i = 0; i < dns_cache_size; i++) - if (!strcmp(hostname, dns_cache[i].hostname)) + if (!dStrAsciiCasecmp(hostname, dns_cache[i].hostname)) break; if (i < dns_cache_size) { @@ -393,7 +401,7 @@ void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data) /* Find a channel we can send the request to */ for (channel = 0; channel < num_servers; channel++) - if (!dns_server[channel].in_use) + if (dns_server[channel].state == DNS_SERVER_IDLE) break; if (channel < num_servers) { /* Found a free channel! */ @@ -422,8 +430,6 @@ static void Dns_serve_channel(int channel) --i; } } - /* set current channel free */ - srv->in_use = FALSE; } /* @@ -434,7 +440,7 @@ static void Dns_assign_channels(void) int ch, i, j; for (ch = 0; ch < num_servers; ++ch) { - if (dns_server[ch].in_use == FALSE) { + if (dns_server[ch].state == DNS_SERVER_IDLE) { /* Find the next query in the queue (we're a FIFO) */ for (i = 0; i < dns_queue_size; i++) if (dns_queue[i].channel == -2) @@ -445,8 +451,10 @@ static void Dns_assign_channels(void) * with the same hostname*/ for (j = i; j < dns_queue_size; j++) if (dns_queue[j].channel == -2 && - !strcmp(dns_queue[j].hostname, dns_queue[i].hostname)) + !dStrAsciiCasecmp(dns_queue[j].hostname, + dns_queue[i].hostname)) { dns_queue[j].channel = ch; + } Dns_server_req(ch, dns_queue[i].hostname); } else return; @@ -455,27 +463,29 @@ static void Dns_assign_channels(void) } /* - * This is a timeout function that - * reads the DNS results and resumes the stopped jobs. + * This function is called on the main thread and + * reads the DNS results. */ -static void Dns_timeout_client(void *data) +static void Dns_timeout_client(int fd, void *data) { - int channel = VOIDP2INT(data); - DnsServer *srv = &dns_server[channel]; + int i; + char buf[16]; - if (srv->ip_ready) { - if (srv->addr_list != NULL) { - /* DNS succeeded, let's cache it */ - Dns_cache_add(srv->hostname, srv->addr_list); - } - Dns_serve_channel(channel); - Dns_assign_channels(); - a_Timeout_remove(); /* Done! */ + while (read(dns_notify_pipe[0], buf, sizeof(buf)) > 0); - } else { - /* IP not already resolved, keep on trying... */ - a_Timeout_repeat(0.2, Dns_timeout_client, data); + for (i = 0; i < num_servers; ++i) { + DnsServer *srv = &dns_server[i]; + + if (srv->state == DNS_SERVER_RESOLVED) { + if (srv->addr_list != NULL) { + /* DNS succeeded, let's cache it */ + Dns_cache_add(srv->hostname, srv->addr_list); + } + Dns_serve_channel(i); + srv->state = DNS_SERVER_IDLE; + } } + Dns_assign_channels(); } @@ -495,6 +505,9 @@ void a_Dns_freeall(void) dFree(dList_nth_data(dns_cache[i].addr_list, j)); dList_free(dns_cache[i].addr_list); } + a_IOwatch_remove_fd(dns_notify_pipe[0], DIO_READ); + close(dns_notify_pipe[0]); + close(dns_notify_pipe[1]); dFree(dns_cache); } diff --git a/src/form.cc b/src/form.cc index f70bea8c..8275c12b 100644 --- a/src/form.cc +++ b/src/form.cc @@ -271,7 +271,7 @@ static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name) for (int idx = 0; idx < inputs->size(); idx++) { DilloHtmlInput *input = inputs->get(idx); if (input->type == DILLO_HTML_INPUT_RADIO && - input->name && !dStrcasecmp(input->name, name)) + input->name && !dStrAsciiCasecmp(input->name, name)) return input; } } @@ -279,8 +279,7 @@ static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name) } /* - * Get the current input. - * Note that this _assumes_ that there _is_ a current input. + * Get the current input if available. */ static DilloHtmlInput *Html_get_current_input(DilloHtml *html) { @@ -291,7 +290,8 @@ static DilloHtmlInput *Html_get_current_input(DilloHtml *html) else inputs = html->inputs_outside_form; - return inputs->get (inputs->size() - 1); + return (inputs && inputs->size() > 0) ? + inputs->get (inputs->size() - 1) : NULL; } /* @@ -318,9 +318,9 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) method = DILLO_HTML_METHOD_GET; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "method"))) { - if (!dStrcasecmp(attrbuf, "post")) { + if (!dStrAsciiCasecmp(attrbuf, "post")) { method = DILLO_HTML_METHOD_POST; - } else if (dStrcasecmp(attrbuf, "get")) { + } else if (dStrAsciiCasecmp(attrbuf, "get")) { BUG_MSG("Unknown form submission method \"%s\"\n", attrbuf); } } @@ -333,7 +333,7 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) content_type = DILLO_HTML_ENC_URLENCODED; if ((method == DILLO_HTML_METHOD_POST) && ((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) { - if (!dStrcasecmp(attrbuf, "multipart/form-data")) + if (!dStrAsciiCasecmp(attrbuf, "multipart/form-data")) content_type = DILLO_HTML_ENC_MULTIPART; } charset = NULL; @@ -343,9 +343,9 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) char *ptr = first = dStrdup(attrbuf); while (ptr && !charset) { char *curr = dStrsep(&ptr, " ,"); - if (!dStrcasecmp(curr, "utf-8")) { + if (!dStrAsciiCasecmp(curr, "utf-8")) { charset = curr; - } else if (!dStrcasecmp(curr, "UNKNOWN")) { + } else if (!dStrAsciiCasecmp(curr, "UNKNOWN")) { /* defined to be whatever encoding the document is in */ charset = html->charset; } @@ -441,18 +441,18 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) init_str = NULL; inp_type = DILLO_HTML_INPUT_UNKNOWN; - if (!dStrcasecmp(type, "password")) { + if (!dStrAsciiCasecmp(type, "password")) { inp_type = DILLO_HTML_INPUT_PASSWORD; attrbuf = a_Html_get_attr(html, tag, tagsize, "size"); int size = Html_input_get_size(html, attrbuf); resource = factory->createEntryResource (size, true, NULL); init_str = value; - } else if (!dStrcasecmp(type, "checkbox")) { + } else if (!dStrAsciiCasecmp(type, "checkbox")) { inp_type = DILLO_HTML_INPUT_CHECKBOX; resource = factory->createCheckButtonResource(false); init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL); init_str = (value) ? value : dStrdup("on"); - } else if (!dStrcasecmp(type, "radio")) { + } else if (!dStrAsciiCasecmp(type, "radio")) { inp_type = DILLO_HTML_INPUT_RADIO; RadioButtonResource *rb_r = NULL; DilloHtmlInput *input = Html_get_radio_input(html, name); @@ -461,22 +461,22 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) resource = factory->createRadioButtonResource(rb_r, false); init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL); init_str = value; - } else if (!dStrcasecmp(type, "hidden")) { + } else if (!dStrAsciiCasecmp(type, "hidden")) { inp_type = DILLO_HTML_INPUT_HIDDEN; init_str = value; int size = Html_input_get_size(html, NULL); resource = factory->createEntryResource(size, false, name); - } else if (!dStrcasecmp(type, "submit")) { + } else if (!dStrAsciiCasecmp(type, "submit")) { inp_type = DILLO_HTML_INPUT_SUBMIT; init_str = (value) ? value : dStrdup("submit"); resource = factory->createLabelButtonResource(init_str); // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ - } else if (!dStrcasecmp(type, "reset")) { + } else if (!dStrAsciiCasecmp(type, "reset")) { inp_type = DILLO_HTML_INPUT_RESET; init_str = (value) ? value : dStrdup("Reset"); resource = factory->createLabelButtonResource(init_str); // gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ - } else if (!dStrcasecmp(type, "image")) { + } else if (!dStrAsciiCasecmp(type, "image")) { if (URL_FLAGS(html->base_url) & URL_SpamSafe) { /* Don't request the image; make a text submit button instead */ inp_type = DILLO_HTML_INPUT_SUBMIT; @@ -491,7 +491,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) embed = Html_input_image(html, tag, tagsize); init_str = value; } - } else if (!dStrcasecmp(type, "file")) { + } else if (!dStrAsciiCasecmp(type, "file")) { bool valid = true; if (html->InFlags & IN_FORM) { DilloHtmlForm *form = html->getCurrentForm(); @@ -512,24 +512,22 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) init_str = dStrdup("File selector"); resource = factory->createLabelButtonResource(init_str); } - } else if (!dStrcasecmp(type, "button")) { + } else if (!dStrAsciiCasecmp(type, "button")) { inp_type = DILLO_HTML_INPUT_BUTTON; if (value) { init_str = value; resource = factory->createLabelButtonResource(init_str); } - } else if (!dStrcasecmp(type, "text") || !*type) { + } else { /* Text input, which also is the default */ inp_type = DILLO_HTML_INPUT_TEXT; + if (*type && dStrAsciiCasecmp(type, "text")) + BUG_MSG("Unknown input type: \"%s\"\n", type); attrbuf = a_Html_get_attr(html, tag, tagsize, "size"); int size = Html_input_get_size(html, attrbuf); resource = factory->createEntryResource(size, false, NULL); init_str = value; - } else { - /* Unknown input type */ - BUG_MSG("Unknown input type: \"%s\"\n", type); } - if (resource) embed = new Embed (resource); @@ -609,17 +607,8 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize) html->InFlags &= ~IN_FORM; } -/* - * The textarea tag - */ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize) { - const int MAX_COLS=1024, MAX_ROWS=10000; - - char *name; - const char *attrbuf; - int cols, rows; - if (html->InFlags & IN_TEXTAREA) { BUG_MSG("nested <textarea>\n"); html->ReqTagClose = TRUE; @@ -631,6 +620,19 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize) } html->InFlags |= IN_TEXTAREA; +} + +/* + * The textarea tag + */ +void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize) +{ + const int MAX_COLS=1024, MAX_ROWS=10000; + + char *name; + const char *attrbuf; + int cols, rows; + a_Html_stash_init(html); S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM; @@ -684,7 +686,7 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx) DilloHtmlInput *input; int i; - if (html->InFlags & IN_TEXTAREA) { + if (html->InFlags & IN_TEXTAREA && !S_TOP(html)->display_none) { /* Remove the line ending that follows the opening tag */ if (html->Stash->str[0] == '\r') dStr_erase(html->Stash, 0, 1); @@ -705,8 +707,10 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx) /* The HTML3.2 spec says it can have "text and character entities". */ str = a_Html_parse_entities(html, html->Stash->str, html->Stash->len); input = Html_get_current_input(html); - input->init_str = str; - ((MultiLineTextResource *)input->embed->getResource ())->setText(str); + if (input) { + input->init_str = str; + ((MultiLineTextResource *)input->embed->getResource ())->setText(str); + } html->InFlags &= ~IN_TEXTAREA; } @@ -833,11 +837,11 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize) type = a_Html_get_attr_wdef(html, tag, tagsize, "type", ""); - if (!dStrcasecmp(type, "button")) { + if (!dStrAsciiCasecmp(type, "button")) { inp_type = DILLO_HTML_INPUT_BUTTON; - } else if (!dStrcasecmp(type, "reset")) { + } else if (!dStrAsciiCasecmp(type, "reset")) { inp_type = DILLO_HTML_INPUT_BUTTON_RESET; - } else if (!dStrcasecmp(type, "submit") || !*type) { + } else if (!dStrAsciiCasecmp(type, "submit") || !*type) { /* submit button is the default */ inp_type = DILLO_HTML_INPUT_BUTTON_SUBMIT; } else { @@ -1036,7 +1040,7 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit) char *boundary = NULL; iconv_t char_encoder = (iconv_t) -1; - if (submit_charset && dStrcasecmp(submit_charset, "UTF-8")) { + if (submit_charset && dStrAsciiCasecmp(submit_charset, "UTF-8")) { char_encoder = iconv_open(submit_charset, "UTF-8"); if (char_encoder == (iconv_t) -1) { MSG_WARN("Cannot convert to character encoding '%s'\n", @@ -1310,8 +1314,8 @@ void DilloHtmlForm::filesInputMultipartAppend(Dstr* data, (void)a_Misc_get_content_type_from_data(file->str, file->len, &ctype); /* Heuristic: text/plain with ".htm[l]" extension -> text/html */ if ((ext = strrchr(filename, '.')) && - !dStrcasecmp(ctype, "text/plain") && - (!dStrcasecmp(ext, ".html") || !dStrcasecmp(ext, ".htm"))) { + !dStrAsciiCasecmp(ctype, "text/plain") && + (!dStrAsciiCasecmp(ext, ".html") || !dStrAsciiCasecmp(ext, ".htm"))){ ctype = "text/html"; } @@ -1476,7 +1480,7 @@ DilloHtmlInput *DilloHtmlForm::getRadioInput (const char *name) for (int idx = 0; idx < inputs->size(); idx++) { DilloHtmlInput *input = inputs->get(idx); if (input->type == DILLO_HTML_INPUT_RADIO && - input->name && !dStrcasecmp(input->name, name)) + input->name && !dStrAsciiCasecmp(input->name, name)) return input; } return NULL; @@ -1904,29 +1908,21 @@ DilloHtmlOption::~DilloHtmlOption () */ static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize) { - const char *attrbuf; DilloImage *Image; Embed *button = NULL; - DilloUrl *url = NULL; - - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) && - (url = a_Html_url_new(html, attrbuf, NULL, 0))) { - html->styleEngine->setPseudoLink (); - - /* create new image and add it to the button */ - if ((Image = a_Html_image_new(html, tag, tagsize, url))) { - IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ()); - ResourceFactory *factory = HT2LT(html)->getResourceFactory(); - ComplexButtonResource *complex_b_r = - factory->createComplexButtonResource(IM2DW(Image), false); - button = new Embed(complex_b_r); - HT2TB(html)->addWidget (button, html->styleEngine->style ()); -// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ + html->styleEngine->setPseudoLink (); - } else { - a_Url_free(url); - } + /* create new image and add it to the button */ + a_Html_image_attrs(html, tag, tagsize); + if ((Image = a_Html_image_new(html, tag, tagsize))) { + IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ()); + ResourceFactory *factory = HT2LT(html)->getResourceFactory(); + ComplexButtonResource *complex_b_r = + factory->createComplexButtonResource(IM2DW(Image), false); + button = new Embed(complex_b_r); + HT2TB(html)->addWidget (button, html->styleEngine->style ()); +// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */ } if (!button) MSG("Html_input_image: unable to create image submit.\n"); diff --git a/src/form.hh b/src/form.hh index a54cde56..cd04543f 100644 --- a/src/form.hh +++ b/src/form.hh @@ -52,6 +52,7 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx); void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize); +void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize); void Html_tag_close_textarea(DilloHtml *html, int TagIdx); void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize); void Html_tag_close_select(DilloHtml *html, int TagIdx); diff --git a/src/html.cc b/src/html.cc index 98fe7b8b..d3c9f383 100644 --- a/src/html.cc +++ b/src/html.cc @@ -16,7 +16,7 @@ /*----------------------------------------------------------------------------- * Includes *---------------------------------------------------------------------------*/ -#include <ctype.h> /* for isspace and tolower */ +#include <ctype.h> /* for isspace */ #include <string.h> /* for memcpy and memmove */ #include <stdlib.h> #include <stdio.h> /* for sprintf */ @@ -119,6 +119,7 @@ typedef struct { char EndTag; /* Is it Required, Optional or Forbidden */ uchar_t TagLevel; /* Used to heuristically parse bad HTML */ TagOpenFunct open; /* Open function */ + TagOpenFunct content; /* Content function */ TagCloseFunct close; /* Close function */ } TagInfo; extern const TagInfo Tags[]; @@ -284,23 +285,6 @@ static int Html_set_new_link(DilloHtml *html, DilloUrl **url) } /* - * Add a new image to our list. - * image is NULL if dillo will try to load the image immediately. - */ -static void Html_add_new_htmlimage(DilloHtml *html, - DilloUrl **url, DilloImage *image) -{ - DilloHtmlImage *hi = dNew(DilloHtmlImage, 1); - hi->url = *url; - hi->image = image; - a_Image_ref(image); - - int n = html->images->size(); - html->images->increase(); - html->images->set(n, hi); -} - -/* * Evaluates the ALIGN attribute (left|center|right|justify) and * sets the style at the top of the stack. */ @@ -311,16 +295,16 @@ void a_Html_tag_set_align_attr(DilloHtml *html, const char *tag, int tagsize) if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) { TextAlignType textAlignType = TEXT_ALIGN_LEFT; - if (dStrcasecmp (align, "left") == 0) + if (dStrAsciiCasecmp (align, "left") == 0) textAlignType = TEXT_ALIGN_LEFT; - else if (dStrcasecmp (align, "right") == 0) + else if (dStrAsciiCasecmp (align, "right") == 0) textAlignType = TEXT_ALIGN_RIGHT; - else if (dStrcasecmp (align, "center") == 0) + else if (dStrAsciiCasecmp (align, "center") == 0) textAlignType = TEXT_ALIGN_CENTER; - else if (dStrcasecmp (align, "justify") == 0) + else if (dStrAsciiCasecmp (align, "justify") == 0) textAlignType = TEXT_ALIGN_JUSTIFY; #if 0 - else if (dStrcasecmp (align, "char") == 0) { + else if (dStrAsciiCasecmp (align, "char") == 0) { /* TODO: Actually not supported for <p> etc. */ v.textAlign = TEXT_ALIGN_STRING; if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) { @@ -352,11 +336,11 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize) VAlignType valign; if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) { - if (dStrcasecmp (attr, "top") == 0) + if (dStrAsciiCasecmp (attr, "top") == 0) valign = VALIGN_TOP; - else if (dStrcasecmp (attr, "bottom") == 0) + else if (dStrAsciiCasecmp (attr, "bottom") == 0) valign = VALIGN_BOTTOM; - else if (dStrcasecmp (attr, "baseline") == 0) + else if (dStrAsciiCasecmp (attr, "baseline") == 0) valign = VALIGN_BASELINE; else valign = VALIGN_MIDDLE; @@ -435,6 +419,7 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url, stack->getRef(0)->table_mode = DILLO_HTML_TABLE_MODE_NONE; stack->getRef(0)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE; stack->getRef(0)->cell_text_align_set = false; + stack->getRef(0)->display_none = false; stack->getRef(0)->list_type = HTML_LIST_NONE; stack->getRef(0)->list_number = 0; stack->getRef(0)->tag_idx = -1; /* MUST not be used */ @@ -662,12 +647,14 @@ void DilloHtml::loadImages (const DilloUrl *pattern) const DilloUrl *requester = pattern ? NULL : this->page_url; for (int i = 0; i < images->size(); i++) { - if (images->get(i)->image) { - if ((!pattern) || (!a_Url_cmp(images->get(i)->url, pattern))) { - if (Html_load_image(bw, images->get(i)->url, requester, - images->get(i)->image)) { - a_Image_unref (images->get(i)->image); - images->get(i)->image = NULL; // web owns it now + DilloHtmlImage *hi = images->get(i); + + if (hi->image) { + assert(hi->url); + if ((!pattern) || (!a_Url_cmp(hi->url, pattern))) { + if (Html_load_image(bw, hi->url, requester, hi->image)) { + a_Image_unref (hi->image); + hi->image = NULL; // web owns it now } } } @@ -1063,7 +1050,9 @@ static void Html_process_space(DilloHtml *html, const char *space, int i, offset; DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode; - if (parse_mode == DILLO_HTML_PARSE_MODE_STASH) { + if (S_TOP(html)->display_none) { + /* do nothing */ + } else if (parse_mode == DILLO_HTML_PARSE_MODE_STASH) { html->StashSpace = (html->Stash->len > 0); } else if (parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM) { @@ -1112,6 +1101,8 @@ static void Html_process_space(DilloHtml *html, const char *space, } if (spaceCnt) { + // add break possibility for the white-space:pre-wrap case + HT2TB(html)->addBreakOption (html->styleEngine->wordStyle ()); spc = dStrnfill(spaceCnt, ' '); HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ()); dFree(spc); @@ -1146,6 +1137,9 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) char *Pword; DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode; + if (S_TOP(html)->display_none) + return; + if (parse_mode == DILLO_HTML_PARSE_MODE_STASH || parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY) { if (html->StashSpace) { @@ -1213,10 +1207,12 @@ static void Html_process_word(DilloHtml *html, const char *word, int size) Html_process_space(html, word2 + start, i - start); } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) { i += 3; + HT2TB(html)->addBreakOption(html->styleEngine->wordStyle ()); } else if (a_Utf8_ideographic(word2+i, beyond_word2, &len)) { i += len; HT2TB(html)->addText(word2 + start, i - start, html->styleEngine->wordStyle ()); + HT2TB(html)->addBreakOption(html->styleEngine->wordStyle ()); } else { do { i += len; @@ -1241,7 +1237,7 @@ static bool Html_match_tag(const char *tagstr, char *tag, int tagsize) int i; for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) { - if (tolower(tagstr[i]) != tolower(tag[i])) + if (D_ASCII_TOLOWER(tagstr[i]) != D_ASCII_TOLOWER(tag[i])) return false; } /* The test for '/' is for xml compatibility: "empty/>" will be matched. */ @@ -1315,7 +1311,8 @@ static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx) if (s_sz > idx + 1 && toptag.EndTag != 'O') BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name); _MSG("Close: %*s%s\n", size," ", toptag.name); - toptag.close(html, toptag_idx); + if (toptag.close) + toptag.close(html, toptag_idx); Html_real_pop_tag(html); } } @@ -1463,10 +1460,10 @@ static int int i; for (i = 0; val[i]; ++i) - if (!(isalnum(val[i]) || strchr(":_.-", val[i]))) + if (!isascii(val[i]) || !(isalnum(val[i]) || strchr(":_.-", val[i]))) break; - if (val[i] || !isalpha(val[0])) + if (val[i] || !(isascii(val[0]) && isalpha(val[0]))) BUG_MSG("'%s' value is not of the form " "[A-Za-z][A-Za-z0-9:_.-]*\n", attrname); @@ -1531,18 +1528,18 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize) _MSG("New: {%s}\n", ntag); /* The default DT_NONE type is TagSoup */ - if (!dStrncasecmp(ntag, HTML_SGML_sig, strlen(HTML_SGML_sig))) { + if (!dStrnAsciiCasecmp(ntag, HTML_SGML_sig, strlen(HTML_SGML_sig))) { p = ntag + strlen(HTML_SGML_sig) + 1; if (!strncmp(p, HTML401, strlen(HTML401)) && - dStristr(p + strlen(HTML401), HTML401_url)) { + dStriAsciiStr(p + strlen(HTML401), HTML401_url)) { html->DocType = DT_HTML; html->DocTypeVersion = 4.01f; } else if (!strncmp(p, XHTML1, strlen(XHTML1)) && - dStristr(p + strlen(XHTML1), XHTML1_url)) { + dStriAsciiStr(p + strlen(XHTML1), XHTML1_url)) { html->DocType = DT_XHTML; html->DocTypeVersion = 1.0f; } else if (!strncmp(p, XHTML11, strlen(XHTML11)) && - dStristr(p + strlen(XHTML11), XHTML11_url)) { + dStriAsciiStr(p + strlen(XHTML11), XHTML11_url)) { html->DocType = DT_XHTML; html->DocTypeVersion = 1.1f; } else if (!strncmp(p, HTML40, strlen(HTML40))) { @@ -1555,7 +1552,7 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize) html->DocType = DT_HTML; html->DocTypeVersion = 2.0f; } - } else if (!dStrcasecmp(ntag, HTML5_sig)) { + } else if (!dStrAsciiCasecmp(ntag, HTML5_sig)) { BUG_MSG("Document follows HTML5 working draft; treating as HTML4.\n"); html->DocType = DT_HTML; html->DocTypeVersion = 5.0f; @@ -1688,11 +1685,11 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize) if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { BUG_MSG("type attribute is required for <style>\n"); - } else if (dStrcasecmp(attrbuf, "text/css")) { + } else if (dStrAsciiCasecmp(attrbuf, "text/css")) { html->loadCssFromStash = false; } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && - dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) { + dStrAsciiCasecmp(attrbuf, "all") && !dStriAsciiStr(attrbuf, "screen")) { /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive, * but sec. 14.2.3 says that the attribute is case-insensitive. * TODO can be a comma-separated list. @@ -1816,8 +1813,6 @@ static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize) CssPropertyList props; a_Html_tag_set_align_attr (html, tag, tagsize); - html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); } /* @@ -1828,22 +1823,15 @@ static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) { const char *attrbuf; - char *src; DilloUrl *url; - Textblock *textblock; - Widget *bullet; CssPropertyList props; - textblock = HT2TB(html); - if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src"))) return; if (!(url = a_Html_url_new(html, attrbuf, NULL, 0))) return; - src = dStrdup(attrbuf); - if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) { /* visited frame */ html->styleEngine->setPseudoVisited (); @@ -1854,6 +1842,21 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) html->styleEngine->setNonCssHint (PROPERTY_X_LINK, CSS_TYPE_INTEGER, Html_set_new_link(html,&url)); +} + +static void Html_tag_content_frame (DilloHtml *html, const char *tag, int tagsize) +{ + const char *attrbuf; + char *src; + Textblock *textblock; + Widget *bullet; + + textblock = HT2TB(html); + + if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src"))) + return; + + src = dStrdup(attrbuf); textblock->addParbreak (5, html->styleEngine->wordStyle ()); @@ -1861,7 +1864,7 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) textblock->addWidget(bullet, html->styleEngine->wordStyle ()); textblock->addSpace(html->styleEngine->wordStyle ()); - if (tolower(tag[1]) == 'i') { + if (D_ASCII_TOLOWER(tag[1]) == 'i') { /* IFRAME usually comes with very long advertising/spying URLS, * to not break rendering we will force name="IFRAME" */ textblock->addText ("IFRAME", html->styleEngine->wordStyle ()); @@ -1886,7 +1889,7 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize) * TODO: This is just a temporary fix while real frame support * isn't finished. Imitates lynx/w3m's frames. */ -static void Html_tag_open_frameset (DilloHtml *html, +static void Html_tag_content_frameset (DilloHtml *html, const char *tag, int tagsize) { HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); @@ -1899,11 +1902,8 @@ static void Html_tag_open_frameset (DilloHtml *html, */ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize) { - html->styleEngine->inheritBackgroundColor (); a_Html_tag_set_align_attr (html, tag, tagsize); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); - a_Html_stash_init(html); S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_STASH_AND_BODY; @@ -1912,7 +1912,7 @@ static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize) /* * <BR> */ -static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize) +static void Html_tag_content_br(DilloHtml *html, const char *tag, int tagsize) { HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ()); } @@ -1965,56 +1965,23 @@ static void Html_tag_open_abbr(DilloHtml *html, const char *tag, int tagsize) } /* - * <CENTER> - */ -static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize) -{ html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); -} - -/* - * </CENTER>, also used for </TABLE> - */ -static void Html_tag_close_center(DilloHtml *html, int TagIdx) -{ - HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); -} - -/* - * <ADDRESS> - */ -static void Html_tag_open_address(DilloHtml *html, - const char *tag, int tagsize) -{ html->styleEngine->inheritBackgroundColor (); - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); -} - -/* * Read image-associated tag attributes and create new image. */ -DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, - int tagsize, DilloUrl *url) +void a_Html_image_attrs(DilloHtml *html, const char *tag, int tagsize) { - DilloImage *Image; - char *width_ptr, *height_ptr, *alt_ptr; + char *width_ptr, *height_ptr; const char *attrbuf; CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO); CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO); int space, border, w = 0, h = 0; - bool load_now; if (prefs.show_tooltip && (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) { html->styleEngine->setNonCssHint(PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, attrbuf); } - alt_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "alt", NULL); - if ((!alt_ptr || !*alt_ptr) && !prefs.load_images) { - dFree(alt_ptr); - alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode - } width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL); height_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "height", NULL); // Check for malicious values @@ -2044,7 +2011,7 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, dFree(width_ptr); dFree(height_ptr); width_ptr = height_ptr = NULL; - MSG("a_Html_image_new: suspicious image size request %d x %d\n", w, h); + MSG("a_Html_image_attrs: suspicious image size request %d x %d\n", w, h); } else { if (CSS_LENGTH_TYPE(l_w) != CSS_LENGTH_TYPE_AUTO) html->styleEngine->setNonCssHint (CSS_PROPERTY_WIDTH, @@ -2111,27 +2078,58 @@ DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, } /* x_img is an index to a list of {url,image} pairs. - * We know Html_add_new_htmlimage() will use size() as its next index */ + * We know a_Html_image_new() will use size() as its next index */ html->styleEngine->setNonCssHint (PROPERTY_X_IMG, CSS_TYPE_INTEGER, html->images->size()); - /* Add a new image widget to this page */ - Image = a_Image_new(alt_ptr, 0); + + dFree(width_ptr); + dFree(height_ptr); +} + +DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize) +{ + bool load_now; + char *alt_ptr; + const char *attrbuf; + DilloUrl *url; + DilloImage *image; + + if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) || + !(url = a_Html_url_new(html, attrbuf, NULL, 0))) + return NULL; + + alt_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "alt", NULL); + if ((!alt_ptr || !*alt_ptr) && !prefs.load_images) { + dFree(alt_ptr); + alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode + } + + image = a_Image_new(alt_ptr, 0); + if (HT2TB(html)->getBgColor()) - Image->bg_color = HT2TB(html)->getBgColor()->getColor(); + image->bg_color = HT2TB(html)->getBgColor()->getColor(); + + DilloHtmlImage *hi = dNew(DilloHtmlImage, 1); + hi->url = url; + html->images->increase(); + html->images->set(html->images->size() - 1, hi); load_now = prefs.load_images || - !dStrcasecmp(URL_SCHEME(url), "data") || + !dStrAsciiCasecmp(URL_SCHEME(url), "data") || (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached); - bool loading = false; - if (load_now) - loading = Html_load_image(html->bw, url, html->page_url, Image); - Html_add_new_htmlimage(html, &url, loading ? NULL : Image); - dFree(width_ptr); - dFree(height_ptr); + if (load_now && Html_load_image(html->bw, url, html->page_url, image)) { + // hi->image is NULL if dillo tries to load the image immediately + hi->image = NULL; + } else { + // otherwise a reference is kept in html->images + hi->image = image; + a_Image_ref(image); + } + dFree(alt_ptr); - return Image; + return image; } /* @@ -2155,23 +2153,28 @@ static bool Html_load_image(BrowserWindow *bw, DilloUrl *url, return ClientKey != 0; } +static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) +{ + a_Html_image_attrs(html, tag, tagsize); +} + /* * Create a new Image struct and request the image-url to the cache * (If it either hits or misses, is not relevant here; that's up to the * cache functions) */ -static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) +static void Html_tag_content_img(DilloHtml *html, const char *tag, int tagsize) { DilloImage *Image; - DilloUrl *url, *usemap_url; + DilloUrl *usemap_url; const char *attrbuf; /* This avoids loading images. Useful for viewing suspicious HTML email. */ if (URL_FLAGS(html->base_url) & URL_SpamSafe) return; - if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) || - !(url = a_Html_url_new(html, attrbuf, NULL, 0))) + Image = a_Html_image_new(html, tag, tagsize); + if (!Image) return; usemap_url = NULL; @@ -2179,7 +2182,6 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) /* TODO: usemap URLs outside of the document are not used. */ usemap_url = a_Html_url_new(html, attrbuf, NULL, 0); - Image = a_Html_image_new(html, tag, tagsize, url); HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style()); /* Image maps */ @@ -2203,7 +2205,7 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize) /* * <map> */ -static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize) +static void Html_tag_content_map(DilloHtml *html, const char *tag, int tagsize) { char *hash_name; const char *attrbuf; @@ -2277,7 +2279,7 @@ misc::SimpleVector<int> *Html_read_coords(DilloHtml *html, const char *str) /* * <AREA> */ -static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize) +static void Html_tag_content_area(DilloHtml *html, const char *tag, int tagsize) { enum types {UNKNOWN, RECTANGLE, CIRCLE, POLYGON, BACKGROUND}; types type; @@ -2293,15 +2295,15 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize) } attrbuf = a_Html_get_attr(html, tag, tagsize, "shape"); - if (!attrbuf || !*attrbuf || !dStrcasecmp(attrbuf, "rect")) { + if (!attrbuf || !*attrbuf || !dStrAsciiCasecmp(attrbuf, "rect")) { /* the default shape is a rectangle */ type = RECTANGLE; - } else if (dStrcasecmp(attrbuf, "default") == 0) { + } else if (dStrAsciiCasecmp(attrbuf, "default") == 0) { /* "default" is the background */ type = BACKGROUND; - } else if (dStrcasecmp(attrbuf, "circle") == 0) { + } else if (dStrAsciiCasecmp(attrbuf, "circle") == 0) { type = CIRCLE; - } else if (dStrncasecmp(attrbuf, "poly", 4) == 0) { + } else if (dStrnAsciiCasecmp(attrbuf, "poly", 4) == 0) { type = POLYGON; } else { BUG_MSG("<area> unknown shape: \"%s\"\n", attrbuf); @@ -2395,7 +2397,7 @@ static const char* Html_get_javascript_link(DilloHtml *html) char ch, *p1, *p2; Dstr *Buf = html->attr_data; - if (dStrncasecmp("javascript", Buf->str, 10) == 0) { + if (dStrnAsciiCasecmp("javascript", Buf->str, 10) == 0) { i = strcspn(Buf->str, "'\""); ch = Buf->str[i]; if ((ch == '"' || ch == '\'') && @@ -2437,11 +2439,11 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize) /* TODO: add support for MAP with A HREF */ if (html->InFlags & IN_MAP) - Html_tag_open_area(html, tag, tagsize); + Html_tag_content_area(html, tag, tagsize); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "href"))) { /* if it's a javascript link, extract the reference. */ - if (tolower(attrbuf[0]) == 'j') + if (D_ASCII_TOLOWER(attrbuf[0]) == 'j') attrbuf = Html_get_javascript_link(html); url = a_Html_url_new(html, attrbuf, NULL, 0); @@ -2549,11 +2551,11 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) { /* list_style_type explicitly defined */ - if (dStrcasecmp(attrbuf, "disc") == 0) + if (dStrAsciiCasecmp(attrbuf, "disc") == 0) list_style_type = LIST_STYLE_TYPE_DISC; - else if (dStrcasecmp(attrbuf, "circle") == 0) + else if (dStrAsciiCasecmp(attrbuf, "circle") == 0) list_style_type = LIST_STYLE_TYPE_CIRCLE; - else if (dStrcasecmp(attrbuf, "square") == 0) + else if (dStrAsciiCasecmp(attrbuf, "square") == 0) list_style_type = LIST_STYLE_TYPE_SQUARE; else /* invalid value */ @@ -2563,8 +2565,6 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize) CSS_TYPE_ENUM, list_style_type); } - Html_add_textblock(html, 9); - S_TOP(html)->list_type = HTML_LIST_UNORDERED; S_TOP(html)->list_number = 0; S_TOP(html)->ref_list_item = NULL; @@ -2621,8 +2621,6 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize) CSS_TYPE_ENUM, listStyleType); } - Html_add_textblock(html, 9); - S_TOP(html)->list_type = HTML_LIST_ORDERED; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) && @@ -2640,12 +2638,8 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) { Style *style = html->styleEngine->style (); - Style *wordStyle = html->styleEngine->wordStyle (); - Widget **ref_list_item; - ListItem *list_item; int *list_number; const char *attrbuf; - char buf[16]; if (S_TOP(html)->list_type == HTML_LIST_NONE) BUG_MSG("<li> outside <ul> or <ol>\n"); @@ -2654,30 +2648,14 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize) /* Get our parent tag's variables (used as state storage) */ list_number = &html->stack->getRef(html->stack->size()-2)->list_number; - ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item; - - HT2TB(html)->addParbreak (0, wordStyle); - list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width); - HT2TB(html)->addWidget (list_item, style); - HT2TB(html)->addParbreak (0, wordStyle); - *ref_list_item = list_item; - S_TOP(html)->textblock = html->dw = list_item; - - if (style->listStyleType == LIST_STYLE_TYPE_NONE) { - // none - } else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) { + if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) { // ordered if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "value")) && (*list_number = strtol(attrbuf, NULL, 10)) < 0) { BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n"); *list_number = 0; } - numtostr((*list_number)++, buf, 16, style->listStyleType); - list_item->initWithText (buf, wordStyle); - } else { - // unordered - list_item->initWithWidget (new Bullet(), wordStyle); } } @@ -2695,7 +2673,6 @@ static void Html_tag_close_li(DilloHtml *html, int TagIdx) */ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) { - Widget *hruler; char *width_ptr; const char *attrbuf; int32_t size = 0; @@ -2743,6 +2720,11 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize) size_bottom); } +} + +static void Html_tag_content_hr(DilloHtml *html, const char *tag, int tagsize) +{ + Widget *hruler; HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); hruler = new Ruler(); @@ -2795,7 +2777,6 @@ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize) static void Html_tag_close_pre(DilloHtml *html, int TagIdx) { html->InFlags &= ~IN_PRE; - HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ()); } /* @@ -2854,7 +2835,7 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize) } if ((equiv = a_Html_get_attr(html, tag, tagsize, "http-equiv"))) { - if (!dStrcasecmp(equiv, "refresh") && + if (!dStrAsciiCasecmp(equiv, "refresh") && (content = a_Html_get_attr(html, tag, tagsize, "content"))) { /* Get delay, if present, and make a message with it */ @@ -2906,7 +2887,7 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize) a_Url_free(new_url); dFree(mr_url); - } else if (!dStrcasecmp(equiv, "content-type") && + } else if (!dStrAsciiCasecmp(equiv, "content-type") && (content = a_Html_get_attr(html, tag, tagsize, "content"))) { _MSG("Html_tag_open_meta: content={%s}\n", content); /* Cannot ask cache whether the content type was changed, as @@ -3011,14 +2992,14 @@ static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize) dReturn_if_fail (prefs.load_stylesheets); /* CSS stylesheet link */ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) || - dStrcasecmp(attrbuf, "stylesheet")) + dStrAsciiCasecmp(attrbuf, "stylesheet")) return; /* IMPLIED attributes? */ if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) && - dStrcasecmp(attrbuf, "text/css")) || + dStrAsciiCasecmp(attrbuf, "text/css")) || ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) && - !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all"))) + !dStriAsciiStr(attrbuf, "screen") && dStrAsciiCasecmp(attrbuf, "all"))) return; if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) || @@ -3085,14 +3066,6 @@ static void Html_tag_open_default(DilloHtml *html,const char *tag,int tagsize) static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize) { a_Html_tag_set_align_attr (html, tag, tagsize); - Html_add_textblock(html, 0); -} - -/* - * Default close for most tags. - */ -static void Html_tag_close_default(DilloHtml *html, int TagIdx) -{ } /* @@ -3105,8 +3078,13 @@ static void Html_tag_close_par(DilloHtml *html, int TagIdx) /* - * Function index for the open and close functions for each tag - * (Alphabetically sorted for a binary search) + * Function index for the open, content, and close functions for each tag + * (Alphabetically sorted for a binary search). + * The open and close functions are always called. They are used for style + * handling and HTML bug reporting. + * Content creation (e.g. adding new widgets or text) is done in the content + * function, which is not called in the display:none case. + * Note, that many tags don't need a content function (e.g. <div>, <span>, ...). * * Explanation for the 'Flags' field: * @@ -3124,101 +3102,112 @@ static void Html_tag_close_par(DilloHtml *html, int TagIdx) * (flow have both set) */ - const TagInfo Tags[] = { - {"a", B8(010101),'R',2, Html_tag_open_a, Html_tag_close_a}, - {"abbr", B8(010101),'R',2, Html_tag_open_abbr, Html_tag_close_default}, + {"a", B8(010101),'R',2, Html_tag_open_a, NULL, Html_tag_close_a}, + {"abbr", B8(010101),'R',2, Html_tag_open_abbr, NULL, NULL}, /* acronym 010101 */ - {"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par}, - {"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default}, - {"b", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default}, + {"address", B8(010110),'R',2, Html_tag_open_default, NULL, Html_tag_close_par}, + {"area", B8(010001),'F',0, Html_tag_open_default, Html_tag_content_area, + NULL}, + {"b", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"base", B8(100001),'F',0, Html_tag_open_base, NULL, NULL}, /* basefont 010001 */ /* bdo 010101 */ - {"big", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"blockquote", B8(011110),'R',2, Html_tag_open_blockquote, - Html_tag_close_default}, - {"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body}, - {"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default}, - {"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_button}, + {"big", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"blockquote", B8(011110),'R',2, Html_tag_open_blockquote, NULL, + NULL}, + {"body", B8(011110),'O',1, Html_tag_open_body, NULL, Html_tag_close_body}, + {"br", B8(010001),'F',0, Html_tag_open_default, Html_tag_content_br, + NULL}, + {"button", B8(011101),'R',2, Html_tag_open_button, NULL, Html_tag_close_button}, /* caption */ - {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_center}, - {"cite", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"code", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, + {"center", B8(011110),'R',2, Html_tag_open_default, NULL, NULL}, + {"cite", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"code", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, /* col 010010 'F' */ /* colgroup */ - {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_default}, - {"del", B8(011101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"dfn", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"dir", B8(011010),'R',2, Html_tag_open_dir, Html_tag_close_par}, + {"dd", B8(011110),'O',1, Html_tag_open_dd, NULL, NULL}, + {"del", B8(011101),'R',2, Html_tag_open_default, NULL, NULL}, + {"dfn", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"dir", B8(011010),'R',2, Html_tag_open_dir, NULL, Html_tag_close_par}, /* TODO: complete <div> support! */ - {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_default}, - {"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par}, - {"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par}, - {"em", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, + {"div", B8(011110),'R',2, Html_tag_open_div, NULL, NULL}, + {"dl", B8(011010),'R',2, Html_tag_open_dl, NULL, Html_tag_close_par}, + {"dt", B8(010110),'O',1, Html_tag_open_dt, NULL, Html_tag_close_par}, + {"em", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, /* fieldset */ - {"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default}, - {"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form}, - {"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default}, - {"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default}, - {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par}, - {"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head}, - {"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default}, - {"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html}, - {"i", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default}, - {"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default}, - {"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default}, + {"font", B8(010101),'R',2, Html_tag_open_font, NULL, NULL}, + {"form", B8(011110),'R',2, Html_tag_open_form, NULL, Html_tag_close_form}, + {"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_content_frame, + NULL}, + {"frameset", B8(011110),'R',2, Html_tag_open_default, Html_tag_content_frameset, + NULL}, + {"h1", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"h2", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"h3", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"h4", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"h5", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"h6", B8(010110),'R',2, Html_tag_open_h, NULL, NULL}, + {"head", B8(101101),'O',1, Html_tag_open_head, NULL, Html_tag_close_head}, + {"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_content_hr, + NULL}, + {"html", B8(001110),'O',1, Html_tag_open_html, NULL, Html_tag_close_html}, + {"i", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"iframe", B8(011110),'R',2, Html_tag_open_frame, NULL, NULL}, + {"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_content_img, + NULL}, + {"input", B8(010001),'F',0, Html_tag_open_input, NULL, NULL}, /* ins */ - {"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default}, - {"kbd", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, + {"isindex", B8(110001),'F',0, Html_tag_open_isindex, NULL, NULL}, + {"kbd", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, /* label 010101 */ /* legend 01?? */ - {"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_li}, - {"link", B8(100001),'F',0, Html_tag_open_link, Html_tag_close_default}, - {"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map}, + {"li", B8(011110),'O',1, Html_tag_open_li, NULL, Html_tag_close_li}, + {"link", B8(100001),'F',0, Html_tag_open_link, NULL, NULL}, + {"map", B8(011001),'R',2, Html_tag_open_default, Html_tag_content_map, + Html_tag_close_map}, /* menu 1010 -- TODO: not exactly 1010, it can contain LI and inline */ - {"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par}, - {"meta", B8(100001),'F',0, Html_tag_open_meta, Html_tag_close_default}, + {"menu", B8(011010),'R',2, Html_tag_open_menu, NULL, Html_tag_close_par}, + {"meta", B8(100001),'F',0, Html_tag_open_meta, NULL, NULL}, /* noframes 1011 */ /* noscript 1011 */ - {"object", B8(111101),'R',2, Html_tag_open_object, Html_tag_close_default}, - {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_default}, + {"object", B8(111101),'R',2, Html_tag_open_object, NULL, NULL}, + {"ol", B8(011010),'R',2, Html_tag_open_ol, NULL, NULL}, /* optgroup */ - {"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default}, - {"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par}, + {"option", B8(010001),'O',1, Html_tag_open_option, NULL, NULL}, + {"p", B8(010110),'O',1, Html_tag_open_p, NULL, NULL}, /* param 010001 'F' */ - {"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre}, - {"q", B8(010101),'R',2, Html_tag_open_q, Html_tag_close_q}, - {"s", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"samp", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script}, - {"select", B8(010101),'R',2, Html_tag_open_select, Html_tag_close_select}, - {"small", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"span", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"strike", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"strong", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style}, - {"sub", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"sup", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_center}, + {"pre", B8(010110),'R',2, Html_tag_open_pre, NULL, Html_tag_close_pre}, + {"q", B8(010101),'R',2, Html_tag_open_q, NULL, Html_tag_close_q}, + {"s", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"samp", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"script", B8(111001),'R',2, Html_tag_open_script, NULL, Html_tag_close_script}, + {"select", B8(010101),'R',2, Html_tag_open_select, NULL, Html_tag_close_select}, + {"small", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"span", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"strike", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"strong", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"style", B8(100101),'R',2, Html_tag_open_style, NULL, Html_tag_close_style}, + {"sub", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"sup", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_content_table, + NULL}, /* tbody */ - {"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default}, - {"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea}, + {"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_content_td, + NULL}, + {"textarea", B8(010101),'R',2,Html_tag_open_textarea, Html_tag_content_textarea, + Html_tag_close_textarea}, /* tfoot */ - {"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_close_default}, + {"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_content_th, + NULL}, /* thead */ - {"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title}, - {"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default}, - {"tt", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"u", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}, - {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_default}, - {"var", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default} + {"title", B8(100101),'R',2, Html_tag_open_title, NULL, Html_tag_close_title}, + {"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_content_tr, + NULL}, + {"tt", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"u", B8(010101),'R',2, Html_tag_open_default, NULL, NULL}, + {"ul", B8(011010),'R',2, Html_tag_open_ul, NULL, NULL}, + {"var", B8(010101),'R',2, Html_tag_open_default, NULL, NULL} }; #define NTAGS (sizeof(Tags)/sizeof(Tags[0])) @@ -3232,8 +3221,8 @@ const TagInfo Tags[] = { static int Html_tag_compare(const char *p1, const char *p2) { while ( *p2 ) { - if (tolower(*p1) != *p2) - return(tolower(*p1) - *p2); + if (D_ASCII_TOLOWER(*p1) != *p2) + return(D_ASCII_TOLOWER(*p1) - *p2); ++p1; ++p2; } @@ -3456,6 +3445,53 @@ static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize) html->styleEngine->setStyle (attrbuf); } + if (tagsize >= 10) { /* TODO prefs.hyphenate? */ + /* length of "<t lang=i>" */ + attrbuf = Html_get_attr2(html, tag, tagsize, "lang", + HTML_LeftTrim | HTML_RightTrim); + if (attrbuf) + html->styleEngine->setNonCssHint(PROPERTY_X_LANG, CSS_TYPE_STRING, + attrbuf); + } +} + +static void Html_display_block(DilloHtml *html) +{ + //HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ()); + Html_add_textblock(html, 0); +} + +static void Html_display_listitem(DilloHtml *html) +{ + Style *style = html->styleEngine->style (); + Style *wordStyle = html->styleEngine->wordStyle (); + Widget **ref_list_item; + ListItem *list_item; + int *list_number; + char buf[16]; + + /* Get our parent tag's variables (used as state storage) */ + list_number = &html->stack->getRef(html->stack->size()-2)->list_number; + ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item; + + HT2TB(html)->addParbreak (0, wordStyle); + + list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width); + HT2TB(html)->addWidget (list_item, style); + HT2TB(html)->addParbreak (0, wordStyle); + *ref_list_item = list_item; + S_TOP(html)->textblock = html->dw = list_item; + + if (style->listStyleType == LIST_STYLE_TYPE_NONE) { + // none + } else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) { + // ordered + numtostr((*list_number)++, buf, 16, style->listStyleType); + list_item->initWithText (buf, wordStyle); + } else { + // unordered + list_item->initWithWidget (new Bullet(), wordStyle); + } } /* @@ -3475,7 +3511,7 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) if (ni == -1) { /* TODO: doctype parsing is a bit fuzzy, but enough for the time being */ if (!(html->InFlags & IN_HTML)) { - if (tagsize > 9 && !dStrncasecmp(tag, "<!doctype", 9)) + if (tagsize > 9 && !dStrnAsciiCasecmp(tag, "<!doctype", 9)) Html_parse_doctype(html, tag, tagsize); } /* Ignore unknown tags */ @@ -3514,6 +3550,29 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize) /* Call the open function for this tag */ _MSG("Open : %s\n", Tags[ni].name); Tags[ni].open (html, tag, tagsize); + + if (! S_TOP(html)->display_none) { + switch (html->styleEngine->style ()->display) { + case DISPLAY_BLOCK: + Html_display_block(html); + break; + case DISPLAY_LIST_ITEM: + Html_display_listitem(html); + break; + case DISPLAY_NONE: + S_TOP(html)->display_none = true; + break; + case DISPLAY_INLINE: + case DISPLAY_INLINE_BLOCK: // TODO: implement inline-block + default: + break; + } + + if (Tags[ni].content && ! S_TOP(html)->display_none) { + Tags[ni].content (html, tag, tagsize); + } + } + if (html->stop_parser) break; @@ -3592,8 +3651,11 @@ static const char *Html_get_attr2(DilloHtml *html, (tag[i] == '=' || isspace(tag[i]) || tag[i] == '>')))) { state = SEEK_TOKEN_START; --i; - } else if (tolower(tag[i]) != tolower(attrname[attr_pos++])) - state = SEEK_ATTR_START; + } else { + if (D_ASCII_TOLOWER(tag[i]) != D_ASCII_TOLOWER(attrname[attr_pos])) + state = SEEK_ATTR_START; + attr_pos++; + } break; case SEEK_TOKEN_START: diff --git a/src/html_common.hh b/src/html_common.hh index cf5c8114..726fbd0e 100644 --- a/src/html_common.hh +++ b/src/html_common.hh @@ -34,6 +34,7 @@ html->bugMessage(__VA_ARGS__); \ } D_STMT_END + /* * Typedefs */ @@ -103,7 +104,7 @@ struct _DilloHtmlState { DilloHtmlTableMode table_mode; DilloHtmlTableBorderMode table_border_mode; bool cell_text_align_set; - + bool display_none; DilloHtmlListMode list_type; int list_number; @@ -238,8 +239,8 @@ DilloUrl *a_Html_url_new(DilloHtml *html, const char *url_str, const char *base_url, int use_base_url); -DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, - int tagsize, DilloUrl *url); +void a_Html_image_attrs(DilloHtml *html, const char *tag, int tagsize); +DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize); char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize); void a_Html_pop_tag(DilloHtml *html, int TagIdx); diff --git a/src/keys.cc b/src/keys.cc index bd78fd5f..08d50306 100644 --- a/src/keys.cc +++ b/src/keys.cc @@ -103,7 +103,9 @@ static const KeyBinding_t default_keys[] = { { "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' }, { "left-tab" , KEYS_LEFT_TAB , FL_CTRL | FL_SHIFT , FL_Tab }, + { "left-tab" , KEYS_LEFT_TAB , FL_CTRL , FL_Page_Up }, { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab }, + { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Page_Down }, { "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' }, { "find" , KEYS_FIND , FL_CTRL , 'f' }, { "websearch" , KEYS_WEBSEARCH , FL_CTRL , 's' }, @@ -147,7 +149,7 @@ void Keys::init() // Fill our key bindings list bindings = dList_new(32); - for (uint_t i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) { + for (uint_t i = 0; i < sizeof(default_keys) / sizeof(default_keys[0]); i++) { if (default_keys[i].key) { node = dNew(KeyBinding_t, 1); node->name = dStrdup(default_keys[i].name); @@ -242,8 +244,8 @@ void Keys::delKeyCmd(int key, int mod) int Keys::getKeyCode(char *keyName) { uint_t i; - for (i = 0; i < sizeof(keyNames) / sizeof(Mapping_t); i++) { - if (!dStrcasecmp(keyNames[i].name, keyName)) { + for (i = 0; i < sizeof(keyNames) / sizeof(keyNames[0]); i++) { + if (!dStrAsciiCasecmp(keyNames[i].name, keyName)) { return keyNames[i].value; } } @@ -260,7 +262,7 @@ KeysCommand_t Keys::getCmdCode(const char *commandName) uint_t i; for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) { - if (!dStrcasecmp(default_keys[i].name, commandName)) + if (!dStrAsciiCasecmp(default_keys[i].name, commandName)) return default_keys[i].cmd; } return KEYS_INVALID; @@ -273,8 +275,8 @@ KeysCommand_t Keys::getCmdCode(const char *commandName) int Keys::getModifier(char *modifierName) { uint_t i; - for (i = 0; i < sizeof(modifierNames) / sizeof(Mapping_t); i++) { - if (!dStrcasecmp(modifierNames[i].name, modifierName)) { + for (i = 0; i < sizeof(modifierNames) / sizeof(modifierNames[0]); i++) { + if (!dStrAsciiCasecmp(modifierNames[i].name, modifierName)) { return modifierNames[i].value; } } @@ -40,7 +40,9 @@ # "left-tab" and "right-tab" switch to the left/right of the current tab. # <ctrl><shift>tab = left-tab +# <ctrl>PageUp = left-tab # <ctrl>tab = right-tab +# <ctrl>PageDown = right-tab # "back" and "forward" move back/forward through the browser history. #backspace = back @@ -16,12 +16,12 @@ */ #define a_List_resize(list,num_items,alloc_step) \ if (!list) { \ - list = dMalloc(alloc_step * sizeof((*list))); \ + list = dMalloc(alloc_step * sizeof(*list)); \ } \ if (num_items >= alloc_step){ \ while ( num_items >= alloc_step ) \ alloc_step <<= 1; \ - list = dRealloc(list, alloc_step * sizeof((*list))); \ + list = dRealloc(list, alloc_step * sizeof(*list)); \ } diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 00000000..bd1ffb16 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,390 @@ +/* + * md5.c was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter + * Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011. + * + * The code was not modified when integrated into the Dillo project, but you + * should check the source repository to be sure that there have not been + * modifications since this notice. + */ + +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 00000000..184a14b7 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,100 @@ +/* + * md5.h was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter + * Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011. + * + * The code was not modified when integrated into the Dillo project, but you + * should check the source repository to be sure that there have not been + * modifications since this notice. + */ + +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/menu.cc b/src/menu.cc index 658b89bc..fe8ff139 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -207,8 +207,20 @@ static void Menu_form_hiddens_cb(Fl_Widget*, void *user_data) static void Menu_stylesheet_cb(Fl_Widget*, void *vUrl) { + int mb = Fl::event_button(); const DilloUrl *url = (const DilloUrl *) vUrl; - a_UIcmd_open_url(popup_bw, url); + + if (mb == 1) { + a_UIcmd_open_url(popup_bw, url); + } else if (mb == 2) { + if (prefs.middle_click_opens_new_tab) { + int focus = prefs.focus_new_tab ? 1 : 0; + if (Fl::event_state(FL_SHIFT)) focus = !focus; + a_UIcmd_open_url_nt(popup_bw, url, focus); + } else { + a_UIcmd_open_url_nw(popup_bw, url); + } + } } /* @@ -315,7 +327,8 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url, has_bugs == TRUE ? pm[1].activate() : pm[1].deactivate(); - if (strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) + if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0 && + strncmp(URL_PATH(url), "/vsource/", 9) == 0) pm[0].deactivate(); else pm[0].activate(); @@ -141,23 +141,23 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT) /* HTML try */ for (i = 0; i < Size && dIsspace(p[i]); ++i); - if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || - (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || - (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || - (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || + if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) || + (Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) || + (Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) || + (Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ - (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { + (Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) { Type = DT_TEXT_HTML; st = 0; /* Images */ - } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { + } else if (Size >= 4 && !strncmp(p, "GIF8", 4)) { Type = DT_IMAGE_GIF; st = 0; - } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { + } else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) { Type = DT_IMAGE_PNG; st = 0; - } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { + } else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = DT_IMAGE_JPG; @@ -221,20 +221,20 @@ void a_Misc_parse_content_type(const char *type, char **major, char **minor, if (!(str = type)) return; - for (s = str; *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); - s++) ; + for (s = str; *s && isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) && + !strchr(tspecials_space, *s); s++) ; if (major) *major = dStrndup(str, s - str); if (*s == '/') { - for (str = ++s; - *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); s++) ; + for (str = ++s; *s && isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) && + !strchr(tspecials_space, *s); s++) ; if (minor) *minor = dStrndup(str, s - str); } if (charset && *s && - (dStrncasecmp(type, "text/", 5) == 0 || - dStrncasecmp(type, "application/xhtml+xml", 21) == 0)) { + (dStrnAsciiCasecmp(type, "text/", 5) == 0 || + dStrnAsciiCasecmp(type, "application/xhtml+xml", 21) == 0)) { /* "charset" parameter defined for text media type in RFC 2046, * application/xhtml+xml in RFC 3236. * @@ -246,7 +246,7 @@ void a_Misc_parse_content_type(const char *type, char **major, char **minor, const char terminators[] = " ;\t"; const char key[] = "charset"; - if ((s = dStristr(str, key)) && + if ((s = dStriAsciiStr(str, key)) && (s == str || strchr(terminators, s[-1]))) { s += sizeof(key) - 1; for ( ; *s == ' ' || *s == '\t'; ++s); @@ -283,12 +283,12 @@ int a_Misc_content_type_cmp(const char *ct1, const char *ct2) a_Misc_parse_content_type(ct1, &major1, &minor1, &charset1); a_Misc_parse_content_type(ct2, &major2, &minor2, &charset2); - if (major1 && major2 && !dStrcasecmp(major1, major2) && - minor1 && minor2 && !dStrcasecmp(minor1, minor2) && + if (major1 && major2 && !dStrAsciiCasecmp(major1, major2) && + minor1 && minor2 && !dStrAsciiCasecmp(minor1, minor2) && ((!charset1 && !charset2) || - (charset1 && charset2 && !dStrcasecmp(charset1, charset2)) || - (!charset1 && charset2 && !dStrcasecmp(charset2, "UTF-8")) || - (charset1 && !charset2 && !dStrcasecmp(charset1, "UTF-8")))) { + (charset1 && charset2 && !dStrAsciiCasecmp(charset1, charset2)) || + (!charset1 && charset2 && !dStrAsciiCasecmp(charset2, "UTF-8")) || + (charset1 && !charset2 && !dStrAsciiCasecmp(charset1, "UTF-8")))) { ret = 0; } else { ret = 1; @@ -328,22 +328,23 @@ int a_Misc_content_type_check(const char *EntryType, const char *DetectedType) return 0; /* there's no mismatch without server type */ for (i = 1; MimeTypes[i].str; ++i) - if (dStrncasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) == 0) + if (dStrnAsciiCasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) ==0) break; if (!MimeTypes[i].str) { /* type not found, no mismatch */ st = 0; - } else if (dStrncasecmp(EntryType, "image/", 6) == 0 && - !dStrncasecmp(DetectedType,MimeTypes[i].str,MimeTypes[i].len)){ + } else if (dStrnAsciiCasecmp(EntryType, "image/", 6) == 0 && + !dStrnAsciiCasecmp(DetectedType, MimeTypes[i].str, + MimeTypes[i].len)){ /* An image, and there's an exact match */ st = 0; - } else if (dStrncasecmp(EntryType, "text/", 5) || - dStrncasecmp(DetectedType, "application/", 12)) { + } else if (dStrnAsciiCasecmp(EntryType, "text/", 5) || + dStrnAsciiCasecmp(DetectedType, "application/", 12)) { /* Not an application sent as text */ st = 0; - } else if (dStrncasecmp(EntryType, "application/xhtml+xml", 21) && - dStrncasecmp(DetectedType, "text/html", 9)) { + } else if (dStrnAsciiCasecmp(EntryType, "application/xhtml+xml", 21) && + dStrnAsciiCasecmp(DetectedType, "text/html", 9)) { /* XML version of HTML */ st = 0; } @@ -481,9 +481,10 @@ static void Nav_reload_callback(void *data) a_Nav_cancel_expect(bw); if (a_Nav_stack_size(bw)) { h_url = a_History_get_url(NAV_TOP_UIDX(bw)); - if (strncmp(URL_STR(h_url), "dpi:/vsource/", 13) == 0) { - /* disable reload for view source dpi */ - confirmed = 0; + if (dStrAsciiCasecmp(URL_SCHEME(h_url), "dpi") == 0 && + strncmp(URL_PATH(h_url), "/vsource/", 9) == 0) { + /* allow reload for view source dpi */ + confirmed = 1; } else if (URL_FLAGS(h_url) & URL_Post) { /* Attempt to repost data, let's confirm... */ choice = a_Dialog_choice5("Repost form data?", @@ -98,8 +98,6 @@ struct _DilloPng { } DilloPng; #define DATASIZE (png->ipbufsize - png->ipbufstart) -#define BLACK 0 -#define WHITE 255 static diff --git a/src/prefsparser.cc b/src/prefsparser.cc index efe64a0e..e74b5a6f 100644 --- a/src/prefsparser.cc +++ b/src/prefsparser.cc @@ -113,7 +113,7 @@ int PrefsParser::parseOption(char *name, char *value) }; node = NULL; - for (i = 0; i < sizeof(symbols) / sizeof(SymNode_t); i++) { + for (i = 0; i < sizeof(symbols) / sizeof(symbols[0]); i++) { if (!strcmp(symbols[i].name, name)) { node = & (symbols[i]); break; @@ -127,8 +127,8 @@ int PrefsParser::parseOption(char *name, char *value) switch (node->type) { case PREFS_BOOL: - *(bool_t *)node->pref = (!dStrcasecmp(value, "yes") || - !dStrcasecmp(value, "true")); + *(bool_t *)node->pref = (!dStrAsciiCasecmp(value, "yes") || + !dStrAsciiCasecmp(value, "true")); break; case PREFS_COLOR: *(int32_t *)node->pref = a_Color_parse(value, *(int32_t*)node->pref,&st); @@ -167,18 +167,18 @@ int PrefsParser::parseOption(char *name, char *value) &prefs.width, &prefs.height); break; case PREFS_FILTER: - if (!dStrcasecmp(value, "same_domain")) + if (!dStrAsciiCasecmp(value, "same_domain")) prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN; else { - if (dStrcasecmp(value, "allow_all")) + if (dStrAsciiCasecmp(value, "allow_all")) MSG_WARN("prefs: unrecognized value for filter_auto_requests\n"); prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL; } break; case PREFS_PANEL_SIZE: - if (!dStrcasecmp(value, "tiny")) + if (!dStrAsciiCasecmp(value, "tiny")) prefs.panel_size = P_tiny; - else if (!dStrcasecmp(value, "small")) + else if (!dStrAsciiCasecmp(value, "small")) prefs.panel_size = P_small; else /* default to "medium" */ prefs.panel_size = P_medium; diff --git a/src/styleengine.cc b/src/styleengine.cc index a4c31ccb..ed272269 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -12,6 +12,7 @@ #include "../dlib/dlib.h" #include "msg.h" #include "prefs.h" +#include "misc.h" #include "html_common.hh" #include "styleengine.hh" @@ -25,6 +26,8 @@ StyleEngine::StyleEngine (dw::core::Layout *layout) { doctree = new Doctree (); stack = new lout::misc::SimpleVector <Node> (1); cssContext = new CssContext (); + buildUserAgentStyle (); + buildUserStyle (); this->layout = layout; importDepth = 0; @@ -103,7 +106,7 @@ void StyleEngine::setId (const char *id) { DoctreeNode *dn = doctree->top (); assert (dn->id == NULL); dn->id = dStrdup (id); -}; +} /** * \brief split a string at sep chars and return a SimpleVector of strings @@ -134,7 +137,7 @@ void StyleEngine::setClass (const char *klass) { DoctreeNode *dn = doctree->top (); assert (dn->klass == NULL); dn->klass = splitStr (klass, ' '); -}; +} void StyleEngine::setStyle (const char *styleAttr) { Node *n = stack->getRef (stack->size () - 1); @@ -148,7 +151,7 @@ void StyleEngine::setStyle (const char *styleAttr) { n->styleAttrProperties, n->styleAttrPropertiesImportant); } -}; +} /** * \brief Instruct StyleEngine to use the nonCssHints from parent element @@ -303,15 +306,15 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { *c = '\0'; dStrstrip(p->value.strVal); - if (strcmp (p->value.strVal, "serif") == 0) + if (dStrAsciiCasecmp (p->value.strVal, "serif") == 0) fontName = prefs.font_serif; - else if (strcmp (p->value.strVal, "sans-serif") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "sans-serif") == 0) fontName = prefs.font_sans_serif; - else if (strcmp (p->value.strVal, "cursive") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "cursive") == 0) fontName = prefs.font_cursive; - else if (strcmp (p->value.strVal, "fantasy") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "fantasy") == 0) fontName = prefs.font_fantasy; - else if (strcmp (p->value.strVal, "monospace") == 0) + else if (dStrAsciiCasecmp (p->value.strVal, "monospace") == 0) fontName = prefs.font_monospace; else if (Font::exists(layout, p->value.strVal)) fontName = p->value.strVal; @@ -331,30 +334,33 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { if (p->type == CSS_TYPE_ENUM) { switch (p->value.intVal) { case CSS_FONT_SIZE_XX_SMALL: - fontAttrs.size = roundInt(11.0 * prefs.font_factor); + fontAttrs.size = roundInt(8.1 * prefs.font_factor); break; case CSS_FONT_SIZE_X_SMALL: - fontAttrs.size = roundInt(12.0 * prefs.font_factor); + fontAttrs.size = roundInt(9.7 * prefs.font_factor); break; case CSS_FONT_SIZE_SMALL: - fontAttrs.size = roundInt(13.0 * prefs.font_factor); + fontAttrs.size = roundInt(11.7 * prefs.font_factor); break; case CSS_FONT_SIZE_MEDIUM: fontAttrs.size = roundInt(14.0 * prefs.font_factor); break; case CSS_FONT_SIZE_LARGE: + fontAttrs.size = roundInt(16.8 * prefs.font_factor); break; case CSS_FONT_SIZE_X_LARGE: - fontAttrs.size = roundInt(16.0 * prefs.font_factor); + fontAttrs.size = roundInt(20.2 * prefs.font_factor); break; case CSS_FONT_SIZE_XX_LARGE: - fontAttrs.size = roundInt(17.0 * prefs.font_factor); + fontAttrs.size = roundInt(24.2 * prefs.font_factor); break; case CSS_FONT_SIZE_SMALLER: - fontAttrs.size -= roundInt(1.0 * prefs.font_factor); + fontAttrs.size = roundInt(fontAttrs.size * 0.83 * + prefs.font_factor); break; case CSS_FONT_SIZE_LARGER: - fontAttrs.size += roundInt(1.0 * prefs.font_factor); + fontAttrs.size = roundInt(fontAttrs.size * 1.2 * + prefs.font_factor); break; default: assert(false); // invalid font-size enum @@ -565,6 +571,9 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { case CSS_PROPERTY_TEXT_INDENT: computeLength (&attrs->textIndent, p->value.intVal, attrs->font); break; + case CSS_PROPERTY_TEXT_TRANSFORM: + attrs->textTransform = (TextTransform) p->value.intVal; + break; case CSS_PROPERTY_VERTICAL_ALIGN: attrs->valign = (VAlignType) p->value.intVal; break; @@ -595,6 +604,13 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { case PROPERTY_X_LINK: attrs->x_link = p->value.intVal; break; + case PROPERTY_X_LANG: + attrs->x_lang[0] = tolower (p->value.strVal[0]); + if (attrs->x_lang[0]) + attrs->x_lang[1] = tolower (p->value.strVal[1]); + else + attrs->x_lang[1] = 0; + break; case PROPERTY_X_IMG: attrs->x_img = p->value.intVal; break; @@ -804,3 +820,74 @@ void StyleEngine::parse (DilloHtml *html, DilloUrl *url, const char *buf, CssParser::parse (html, url, cssContext, buf, buflen, origin); importDepth--; } + +/** + * \brief Create the user agent style. + * + * The user agent style defines how dillo renders HTML in the absence of + * author or user styles. + */ +void StyleEngine::buildUserAgentStyle () { + const char *cssBuf = + "body {margin: 5px}" + "big {font-size: 1.17em}" + "blockquote, dd {margin-left: 40px; margin-right: 40px}" + "center {text-align: center}" + "dt {font-weight: bolder}" + ":link {color: blue; text-decoration: underline; cursor: pointer}" + ":visited {color: #800080; text-decoration: underline; cursor: pointer}" + "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder}" + "address, center, div, h1, h2, h3, h4, h5, h6, ol, p, ul, pre {display: block}" + "i, em, cite, address, var {font-style: italic}" + ":link img, :visited img {border: 1px solid}" + "frameset, ul, ol, dir {margin-left: 40px}" + /* WORKAROUND: It should be margin: 1em 0 + * but as we don't collapse these margins yet, it + * look better like this. + */ + "p {margin: 0.5em 0}" + "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0}" + "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0}" + "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0}" + "h4 {margin-top: 1.12em; margin-bottom: 0}" + "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0}" + "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0}" + "hr {width: 100%; border: 1px inset}" + "li {margin-top: 0.1em; display: list-item}" + "pre {white-space: pre}" + "ol {list-style-type: decimal}" + "ul {list-style-type: disc}" + "ul ul {list-style-type: circle}" + "ul ul ul {list-style-type: square}" + "ul ul ul ul {list-style-type: disc}" + "u {text-decoration: underline}" + "small, sub, sup {font-size: 0.83em}" + "sub {vertical-align: sub}" + "sup {vertical-align: super}" + "s, strike, del {text-decoration: line-through}" + "table {border-spacing: 2px}" + "td, th {padding: 2px}" + "thead, tbody, tfoot {vertical-align: middle}" + "th {font-weight: bolder; text-align: center}" + "code, tt, pre, samp, kbd {font-family: monospace}" + /* WORKAROUND: Reset font properties in tables as some + * pages rely on it (e.g. gmail). + * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode + * has a detailed description of the issue. + */ + "table, caption {font-size: medium; font-weight: normal}"; + + CssParser::parse (NULL, NULL, cssContext, cssBuf, strlen (cssBuf), + CSS_ORIGIN_USER_AGENT); +} + +void StyleEngine::buildUserStyle () { + Dstr *style; + char *filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL); + + if ((style = a_Misc_file2dstr(filename))) { + CssParser::parse (NULL,NULL,cssContext,style->str, style->len,CSS_ORIGIN_USER); + dStr_free (style, 1); + } + dFree (filename); +} diff --git a/src/styleengine.hh b/src/styleengine.hh index b73a8b5f..7cca1475 100644 --- a/src/styleengine.hh +++ b/src/styleengine.hh @@ -36,6 +36,8 @@ class StyleEngine { Doctree *doctree; int importDepth; + void buildUserAgentStyle (); + void buildUserStyle (); dw::core::style::Style *style0 (int i); dw::core::style::Style *wordStyle0 (); inline void setNonCssHint(CssPropertyName name, CssValueType type, diff --git a/src/table.cc b/src/table.cc index d48a0c45..d66b32b0 100644 --- a/src/table.cc +++ b/src/table.cc @@ -30,14 +30,15 @@ using namespace dw::core::style; static void Html_tag_open_table_cell(DilloHtml *html, const char *tag, int tagsize, - dw::core::style::TextAlignType text_align); + dw::core::style::TextAlignType text_align); +static void Html_tag_content_table_cell(DilloHtml *html, + const char *tag, int tagsize); /* * <TABLE> */ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) { - dw::core::Widget *table; const char *attrbuf; int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1; CssLength cssLength; @@ -81,13 +82,13 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) a_Html_parse_length (html, attrbuf)); if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) { - if (dStrcasecmp (attrbuf, "left") == 0) + if (dStrAsciiCasecmp (attrbuf, "left") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_LEFT); - else if (dStrcasecmp (attrbuf, "right") == 0) + else if (dStrAsciiCasecmp (attrbuf, "right") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT); - else if (dStrcasecmp (attrbuf, "center") == 0) + else if (dStrAsciiCasecmp (attrbuf, "center") == 0) html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_CENTER); } @@ -99,7 +100,7 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) CSS_TYPE_COLOR, bgcolor); } - HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); + html->styleEngine->style (); // evaluate now, so we can build non-css hints for the cells /* The style for the cells */ html->styleEngine->clearNonCssHints (); @@ -135,13 +136,21 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize) CSS_TYPE_LENGTH_PERCENTAGE, cssLength); } +} +void Html_tag_content_table(DilloHtml *html, const char *tag, int tagsize) +{ + dw::core::Widget *table; + + HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); table = new dw::Table(prefs.limit_text_width); HT2TB(html)->addWidget (table, html->styleEngine->style ()); + HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ()); S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP; S_TOP(html)->table_border_mode = DILLO_HTML_TABLE_BORDER_SEPARATE; S_TOP(html)->cell_text_align_set = FALSE; S_TOP(html)->table = table; + } /* @@ -177,8 +186,6 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize) html->styleEngine->inheritBackgroundColor (); - ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ()); - if (bgcolor != -1) { html->styleEngine->setNonCssHint(CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, bgcolor); @@ -188,6 +195,20 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize) default: break; } +} + +void Html_tag_content_tr(DilloHtml *html, const char *tag, int tagsize) +{ + switch (S_TOP(html)->table_mode) { + case DILLO_HTML_TABLE_MODE_NONE: + return; + case DILLO_HTML_TABLE_MODE_TOP: + case DILLO_HTML_TABLE_MODE_TR: + case DILLO_HTML_TABLE_MODE_TD: + ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ()); + default: + break; + } S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR; } @@ -201,6 +222,11 @@ void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize) dw::core::style::TEXT_ALIGN_LEFT); } +void Html_tag_content_td(DilloHtml *html, const char *tag, int tagsize) +{ + Html_tag_content_table_cell (html, tag, tagsize); +} + /* * <TH> */ @@ -210,6 +236,11 @@ void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize) dw::core::style::TEXT_ALIGN_CENTER); } +void Html_tag_content_th(DilloHtml *html, const char *tag, int tagsize) +{ + Html_tag_content_table_cell (html, tag, tagsize); +} + /* * Utilities */ @@ -311,8 +342,6 @@ static void Html_tag_open_table_cell(DilloHtml *html, const char *tag, int tagsize, dw::core::style::TextAlignType text_align) { - Widget *col_tb; - int colspan = 1, rowspan = 1; const char *attrbuf; int32_t bgcolor; @@ -329,16 +358,6 @@ static void Html_tag_open_table_cell(DilloHtml *html, /* continues */ case DILLO_HTML_TABLE_MODE_TR: case DILLO_HTML_TABLE_MODE_TD: - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "colspan"))) { - char *invalid; - colspan = strtol(attrbuf, &invalid, 10); - if ((colspan < 0) || (attrbuf == invalid)) - colspan = 1; - } - /* TODO: check errors? */ - if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan"))) - rowspan = MAX(1, strtol (attrbuf, NULL, 10)); - /* text style */ if (!S_TOP(html)->cell_text_align_set) { html->styleEngine->setNonCssHint (CSS_PROPERTY_TEXT_ALIGN, @@ -347,9 +366,6 @@ static void Html_tag_open_table_cell(DilloHtml *html, if (a_Html_get_attr(html, tag, tagsize, "nowrap")) html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE, CSS_TYPE_ENUM, WHITE_SPACE_NOWRAP); - else - html->styleEngine->setNonCssHint(CSS_PROPERTY_WHITE_SPACE, - CSS_TYPE_ENUM, WHITE_SPACE_NORMAL); a_Html_tag_set_align_attr (html, tag, tagsize); @@ -368,6 +384,38 @@ static void Html_tag_open_table_cell(DilloHtml *html, CSS_TYPE_COLOR, bgcolor); } + default: + /* compiler happiness */ + break; + } +} + +static void Html_tag_content_table_cell(DilloHtml *html, + const char *tag, int tagsize) +{ + int colspan = 1, rowspan = 1; + const char *attrbuf; + Widget *col_tb; + + switch (S_TOP(html)->table_mode) { + case DILLO_HTML_TABLE_MODE_NONE: + return; + + case DILLO_HTML_TABLE_MODE_TOP: + BUG_MSG("<td> or <th> outside <tr>\n"); + /* a_Dw_table_add_cell takes care that dillo does not crash. */ + /* continues */ + case DILLO_HTML_TABLE_MODE_TR: + case DILLO_HTML_TABLE_MODE_TD: + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "colspan"))) { + char *invalid; + colspan = strtol(attrbuf, &invalid, 10); + if ((colspan < 0) || (attrbuf == invalid)) + colspan = 1; + } + /* TODO: check errors? */ + if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan"))) + rowspan = MAX(1, strtol (attrbuf, NULL, 10)); if (html->styleEngine->style ()->textAlign == TEXT_ALIGN_STRING) col_tb = new dw::TableCell ( diff --git a/src/table.hh b/src/table.hh index aca717ba..5417f681 100644 --- a/src/table.hh +++ b/src/table.hh @@ -12,8 +12,12 @@ class DilloHtml; */ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize); +void Html_tag_content_table(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize); +void Html_tag_content_tr(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize); +void Html_tag_content_td(DilloHtml *html, const char *tag, int tagsize); void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize); +void Html_tag_content_th(DilloHtml *html, const char *tag, int tagsize); #endif /* __TABLE_HH__ */ @@ -739,10 +739,6 @@ int UI::handle(int event) a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this)); ret = 1; } else if (cmd == KEYS_GOTO) { - if (Panelmode == UI_HIDDEN) { - panels_toggle(); - temporaryPanels(true); - } focus_location(); ret = 1; } else if (cmd == KEYS_HIDE_PANELS) { @@ -819,6 +815,10 @@ void UI::set_location(const char *str) */ void UI::focus_location() { + if (Panelmode == UI_HIDDEN) { + panels_toggle(); + temporaryPanels(true); + } Location->take_focus(); // Make text selected when already focused. Location->position(Location->size(), 0); diff --git a/src/uicmd.cc b/src/uicmd.cc index 38225047..6ca98280 100644 --- a/src/uicmd.cc +++ b/src/uicmd.cc @@ -597,7 +597,7 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr) /* Filter URL string */ new_urlstr = a_Url_string_strip_delimiters(urlstr); - if (!dStrncasecmp(new_urlstr, "file:", 5)) { + if (!dStrnAsciiCasecmp(new_urlstr, "file:", 5)) { /* file URI */ ch = new_urlstr[5]; if (!ch || ch == '.') { @@ -1177,7 +1177,7 @@ void a_UIcmd_scroll(BrowserWindow *bw, int icmd) }; KeysCommand_t keycmd = (KeysCommand_t)icmd; - for (uint_t i = 0; i < (sizeof(map)/sizeof(mapping_t)); i++) { + for (uint_t i = 0; i < sizeof(map) / sizeof(map[0]); i++) { if (keycmd == map[i].keys_cmd) { layout->scroll(map[i].dw_cmd); break; @@ -54,7 +54,7 @@ static const char *HEX = "0123456789ABCDEF"; #define URL_STR_FIELD_CMP(s1,s2) \ (s1) && (s2) ? strcmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 #define URL_STR_FIELD_I_CMP(s1,s2) \ - (s1) && (s2) ? dStrcasecmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 + (s1) && (s2) ? dStrAsciiCasecmp(s1,s2) : !(s1) && !(s2) ? 0 : (s1) ? 1 : -1 /* * Return the url as a string. @@ -586,8 +586,7 @@ char *a_Url_encode_hex_str(const char *str) newstr = dNew(char, 6*strlen(str)+1); for (c = newstr; *str; str++) - if ((dIsalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str)) - /* we really need isalnum for the "C" locale */ + if ((dIsalnum(*str) && isascii(*str)) || strchr(verbatim, *str)) *c++ = *str; else if (*str == ' ') *c++ = '+'; @@ -652,10 +651,10 @@ static bool_t Url_host_is_ip(const char *host) _MSG("an IPv4 address\n"); return TRUE; } - if (*host == '[' && - (len == strspn(host, "0123456789abcdefABCDEF:.[]"))) { + if (strchr(host, ':') && + (len == strspn(host, "0123456789abcdefABCDEF:."))) { /* The precise format is shown in section 3.2.2 of rfc 3986 */ - _MSG("an IPv6 address\n"); + MSG("an IPv6 address\n"); return TRUE; } return FALSE; @@ -702,7 +701,7 @@ static uint_t Url_host_public_internal_dots(const char *host) for (i = 0; i < tld_num; i++) { if (strlen(tlds[i]) == (uint_t) tld_len && - !dStrncasecmp(tlds[i], host + start, tld_len)) { + !dStrnAsciiCasecmp(tlds[i], host + start, tld_len)) { _MSG("TLD code matched %s\n", tlds[i]); ret++; break; @@ -759,6 +758,7 @@ bool_t a_Url_same_organization(const DilloUrl *u1, const DilloUrl *u2) if (!u1 || !u2) return FALSE; - return dStrcasecmp(Url_host_find_public_suffix(URL_HOST(u1)), - Url_host_find_public_suffix(URL_HOST(u2))) ? FALSE :TRUE; + return dStrAsciiCasecmp(Url_host_find_public_suffix(URL_HOST(u1)), + Url_host_find_public_suffix(URL_HOST(u2))) + ? FALSE : TRUE; } @@ -95,7 +95,7 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web, } else { /* A non-RootUrl. At this moment we only handle image-children */ - if (!dStrncasecmp(Type, "image/", 6)) { + if (!dStrnAsciiCasecmp(Type, "image/", 6)) { dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data); } else { MSG_HTTP("'%s' cannot be displayed as image; has media type '%s'\n", |